Symfony developer, blogger, micropreneur.
I am a freelance web developer.
As developer I have successfully delivered a lot of PHP projects (some quite complex) on LAMP stack for major italian companies. In the last years I've focussed my development activity mainly on Symfony and Symfony2 (really love it).
I'm curious about agile software development and XP methods. I am definitely addicted to TDD since the first reading of "Test-Driven Development By Example" by Kent Beck(perhaps the most influential reading of my professional life) and I've embraced refactoring philosophy since then.
I love to keep focused on business goals of project, keep high code quality, and have to take decision to respect deadlines. I find extremely challenging and satisfying
seeing software projects growing up (without being a mess) and dev team grow with them.
My main goal is to be challanged. I want to work with a team that let me grow, increase my programming skills, build software that worth and also let me share my knowledge with other team members.
I love teaching and sharing my knowledge, hence the idea to found screenfony.com, a symfony tutorial based startup which is my first real try as entrepreneur.
Screenfony provide premium, high quality, Symfony video screencasts.
Per H-art mi occupo dello sviluppo di applicazioni web complesse basate principalmente sul framework symfony. Ho partecipato allo sviluppo di e-commerce basati su magento implementando moduli custom (importazione utenti/prodotti, gateway di pagamento, gestione contenuti).
Partecipo attivamente al programma di formazione interna sia con affiancamento ai membri più junior del team che tenendo seminari.
Presso seat pagine gialle mi sono occupato principalmente di sviluppo php configurando e customizzando diversi siti web basati su CMS e piattaforme Open source (drupal, wordpress), integrandoli con i web services esistenti(servizi di SSO ma non solo). Durante questa esperienza ho potuto sperimentare lo sviluppo web in team e all'interno di un'azienda grande e strutturata.
Ho lavorato come libero professionista sviluppando siti web di piccole e medie dimensione sia per conto di aziende di informatica della provincia di Ancona che per miei clienti
Anche quest’anno si svolgerà a Verona il phpDay, evento nazionale di riferimento per PHP, ormai elevato a rango internazionale.
Come sempre, Symfony avrà una (meritata) importante presenza alla conferenza. Ecco tutti i talk in programma a riguardo:
Per il programma completo, rimandiamo al sito ufficiale: http://2013.phpday.it/schedule/
Capifony, come sapete, è ormai lo standard de facto per il deploy con Symfony. È un bel progetto, mantenuto attivamente, che quindi spesso viene aggiornato. Dal canto suo, anche Capistrano (a cui Capifony si appoggia), riceve vari aggiornamenti. E qui purtroppo arrivano i problemi: più di una volta mi è capitato di trovarmi con un Capifony non funzionante, a causa di stretti requisiti (un po’ troppo stretti, a mio parere) sulle dipendenze.
Prendiamo come esempio le ultime versioni al momento disponibili: aggiornando oggi le gemme di ruby, ho avuto questa brutta sorpresa:
/usr/local/lib/site_ruby/1.8/rubygems/dependency.rb:247:in `to_specs': Could not find capistrano (<= 2.14.1, >= 2.13.5) amongst [capifony-2.2.7, capistrano-2.14.2, capistrano-maintenance-0.0.2, colored-1.2, highline-1.6.15, inifile-2.0.2, net-scp-1.1.0, net-sftp-2.1.1, net-ssh-2.6.5, net-ssh-gateway-1.2.0] (Gem::LoadError) |
La versione più recente di Capifony richiede Capistrano non più nuovo di 2.14.1, ma il Capistrano più recente è 2.14.2.
Ecco come ho risolto (i comandi sono stati dati su Ubuntu Linux 12.10, your mileage may vary)
sudo gem uninstall capistrano -v 2.14.2 sudo gem install capistrano -v 2.14.1 |
L’ultimo comando serve solo se in precedenza è stata fatta una pulizia delle gemme, tramite sudo gem clean. Purtroppo anche questa va fatta, perché spesso le versioni diverse sono in conflitto tra di loro.
A volte può essere utile avere a disposizione il contenitore di servizi nelle fixture usate nei test funzionali. Un caso tipico è l’uso di FOSUserBundle, che mette a disposizione un servizio UserManager per creare utenti, utilizzabile quindi anche per crearli all’interno delle fixture.
Purtroppo la documentazione a riguardo è alquanto avara di informazioni, per cui condivido qui questa soluzione, mostrando un esempio.
Ecco un possibile file di fixture per gli utenti:
<?php namespace Acme\PippoBundle\DataFixtures\ORM; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; class LoadUserData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface { private $container; public function setContainer(ContainerInterface $container = null) { $this->container = $container; } public function load(ObjectManager $manager) { $userManager = $this->container->get('fos_user.user_manager'); $user1 = $userManager->createUser(); $user1 ->setUsername('pippo') ->setEmail('pippo@example.org') ->setFirstName('Mallo') ->setLastName('Di Noce') ->setBirthday(new \DateTime('1977-07-07')) ->setEnabled(true) ->setPlainPassword('mallodinoce') ; $userManager->updateUser($user1, false); $manager->persist($user1); $this->addReference('user1', $user1); $manager->flush(); } public function getOrder() { return 1; } } |
Questa fixture si può usare in un test in questo modo:
<?php namespace Acme\PippoBundle\Tests\Controller; use Acme\PippoBundle\DataFixtures\ORM\LoadUserData; use Doctrine\Common\DataFixtures\Executor\ORMExecutor; use Doctrine\Common\DataFixtures\Purger\ORMPurger; use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader as Loader; // il trucco è qui.. use Symfony\Bundle\FrameworkBundle\Test\WebTestCase class DefaultControllerTest extends WebTestCase { public function setUp() { $kernel = static::createKernel(); $kernel->boot(); $container = $kernel->getContainer(); $loader = new Loader($container); // ... e qui $loader->addFixture(new LoadUserData); $purger = new ORMPurger(); $executor = new ORMExecutor($this->em, $purger); $executor->execute($loader->getFixtures()); } public function testIndex() { $client = static::createClient(); $crawler = $client->request('GET', '/'); $this->assertTrue($client->getResponse()->isSuccessful()); } } |
Come ampiamente annunciato nella documentazione sul processo di rilascio, la prossima versione di Symfony 2.2 uscirà a fine febbraio prossimo. Oggi intanto ne è uscita la prima beta, mentre una release candidate è prevista per fine mese.
Intanto già si parla di 2.3, la prima LTS! La sua uscita è prevista per fine maggio.
Attenzione anche alla 2.0, perché a fine gennaio scadrà il suo supporto.
Tra un mese esatto, il 5 ottobre 2012, si terrà a Torino il primo evento italiano dedicato completamente a Symfony, il SymfonyDay.
Purtroppo l’evento ha dei posti limitati, nel momento in cui scriviamo ne sono rimasti solamente 7 e probabilmente finiranno in fretta.
Affrettatevi quindi, per non mancare a questo evento imperdibile.
Sul sito del SymfonyDay sono già disponibili tutte le informazioni necessarie, compreso il programma completo.
Ci vediamo a Torino!
Chi avesse l’esigenza di gestire in Sonata un’entità con applicato il behaviour sortable di Doctrine2 si troverebbe con la sgradita sorpresa di non poter operare le comuni operazioni necessarie in questi casi: tipicamente, spostare le righe in su, in giù o in cima alla lista.
Purtroppo tale behaviour non offre alcun metodo che astragga le operazioni di cui sopra: l’unico modo per spostare un oggetto è quello di assegnargli una nuova posizione, dopo di che gli altri oggetti saranno riordinati di conseguenza.
Vediamo dunque come ottenere il risultato desiderato in Sonata. Per farlo, supponiamo di avere un’entità chiamata Article. Supponiamo inoltre che la proprietà che indica la posizione in tale entità si chiami $position.
Per prima cosa, dobbiamo creare un controllore che implementi le nostre nuove azioni di spostamento.
<?php namespace Acme\MioBundle\Controller; use Sonata\AdminBundle\Controller\CRUDController; use Symfony\Component\HttpFoundation\RedirectResponse; class ArticleAdminController extends CRUDController { /** * Move element up * * @param integer $id */ public function moveupAction($id) { $object = $this->admin->getObject($id); if ($object->getPosition() > 0) { $object->setPosition($object->getPosition() - 1); $this->admin->update($object); } if ($this->isXmlHttpRequest()) { return $this->renderJson(array( 'result' => 'ok', 'objectId' => $this->admin->getNormalizedIdentifier($object) )); } $this->get('session')->setFlash('sonata_flash_info', 'Elemento spostato in su.'); return new RedirectResponse($this->admin->generateUrl('list', $this->admin->getFilterParameters())); } /** * Move element top * * @param integer $id */ public function movetopAction($id) { $object = $this->admin->getObject($id); if ($object->getPosition() > 0) { $object->setPosition(0); $this->admin->update($object); } if ($this->isXmlHttpRequest()) { return $this->renderJson(array( 'result' => 'ok', 'objectId' => $this->admin->getNormalizedIdentifier($object) )); } $this->get('session')->setFlash('sonata_flash_info', 'Elemento spostato in cima.'); return new RedirectResponse($this->admin->generateUrl('list', $this->admin->getFilterParameters())); } } |
Le uniche parti da adattare in questo controllore sono il namespace e il nome della classe.
Ora occorre dire a Sonata di usare questo controllore: possiamo farlo nel file di configurazione dei servizi (services.xml o services.yml), semplicemente sostituendo SonataAdminBundle:CRUD con AcmeMioBundle:ArticleAdmin nel terzo argument.
L’ultima parte da modificare è la classe ArticleAdmin.
Innanzitutto sarebbe logico che gli elementi siano ordinati in base alla posizione: aggiungiamo quindi la seguente proprietà:
protected $datagridValues = array( '_page' => 1, '_sort_order' => 'ASC', '_sort_by' => 'position', ); |
Modifichiamo quindi il metodo configureListFields() in questo modo (ovviamente campi e azioni possono variare):
public function configureListFields(ListMapper $listMapper) { $listMapper ->add('title') ->add('abstract') ->add('position') ->add('_action', 'actions', array( 'actions' => array( 'edit' => array(), 'view' => array(), 'delete' => array(), 'moveUp' => array('template' => 'AcmeMioBundle:Article:_moveup.html.twig'), 'moveTop' => array('template' => 'AcmeMioBundle:Article:_movetop.html.twig'), ) )) ; } |
Infine non resta che creare i template:
{# Acme/MioBundle/Resources/view/Article/_moveup.html.twig #}
{% if object.position > 0 %}
<a class="moveup_link" href="{{ path('admin_article_moveup', {id: object.id}) }}" title="Sposta su">↑</a>
{% endif %}
{# Acme/MioBundle/Resources/view/Article/_movetop.html.twig #}
{% if object.position > 1 %}
<a class="movetop_link" href="{{ path('admin_article_movetop', {id: object.id}) }}" title="Sposta in cima">↑↑</a>
{% endif %} |
Purtroppo non ho potuto implementare lo spostamento in giù, non avendo ancora trovato il modo di verificare che la posizione non sia già l’ultima.
In Symfony2 incorporare i form è davvero facile come mostrato nella guida ufficiale di Symfony per la creazione e l’associazione di due form.
In questo tip parleremo della validazione in cascata di sottoform.
Supponiamo di avere un ProductForm che incorpora CategoryForm, entrambi con annotazioni che impostano i diversi vincoli di validazione per ciascuna classe. Seguendo la guida scriveremmo in ProductForm:
$builder->add('category', new CategoryForm()); |
Ci aspettiamo che CategoryForm sia inglobato in ProductForm e ciascun campo automaticamente validato in base ai vincoli di validazione di CategoryForm e ProductForm.
Noteremo con stupore, che dalla versione 2.1 di Symfony CategoryForm non viene validato automaticamente.
Questo è dovuto all’introduzione di un settaggio specifico dei form: cascade_validation
Il parametro, infatti, suggerisce al form builder se validare il form incorporato. Di default questo settaggio è impostato a false, quindi per validare il nostro sottoform con i vincoli di validazione specificati nella classe, c’è bisogno di esplicitare a true l’opzione nella classe CategoryForm, nella funzione setDefaultOptions :
public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'namespace\entita\correlata', 'cascade_validation' => true, )); } |
Così facendo, ProductForm provvederà a validare anche tutti i campi di CategoryForm.
Come probabilmente saprete, Capifony (per chi non lo conoscesse, è lo standard de facto per gestire i deploy su Symfony2) purtroppo ha un’impostazione predefinita che reinstalla tutti i vendor a ogni deploy. Questo vuol dire che ogni volta che facciamo un deploy dobbiamo aspettare che il server di produzione (o comunque il server su cui stiamo eseguendo il deploy stesso) scarichi da github tutte le librerie necessarie. Ogni volta, anche se molto probabilmente le librerie dei vendor non sono state modificate dal deploy precedente. In realtà, quando anche fossero state modificate, per esempio per un aggiornamento di Symfony, non sarebbe più efficiente scaricare solo le modifiche, piuttosto che tutto quanto? Dopotutto, è esattamente ciò che facciamo sulle nostre macchine di sviluppo.
Ecco una possibile soluzione, da riportare nel proprio file di configurazione deploy.rb.
# app/config/deploy.rb # [...] set :vendors_mode, "install" set :update_vendors, true before "symfony:vendors:install", "symfony:copy_vendors" namespace :symfony do desc "Copy vendors from previous release" task :copy_vendors, :except => { :no_release => true } do pretty_print "--> Copying vendors from previous release" run "cp -a #{previous_release}/vendor/* #{latest_release}/vendor/" puts_ok end end |
Aggiornamento: il codice, ampliato e migliorato, è stato pubblicato come ricetta sul sito di Capifony.
Come già sapete, i componenti di Symfony2 sono stati già adottati da due grossi progetti open source, phpBB e Drupal, per i loro prossimi rilasci maggiori.
A questi, da oggi si aggiunge un altro progetto molto importante: eZ publish! Il celebre sistema di gestione di contenuti l’ha infatti annunciato proprio oggi, subito ripreso dal blog ufficiale di Symfony, per la versione 5, tra l’altro già disponibile su gtihub.
Il secondo giorno inizia con un talk in track unica di David Zuelke. Purtroppo si tratta ancora di Designing HTTP interfaces and RESTful web services. Il titolo vi dice qualcosa? Se avete partecipato almeno a una conferenza su PHP nell’ultimo lustro, c’è una buona probabilità che l’abbiate già visto.
Purtroppo la presenza di tre track, invece delle due di ieri, ci consentirà una copertura inferiore dell’evento.
Il talk in francese di William Durand su Propel2 ha evidenziato lo stato attuale di questo ORM, che sta cercando di recuperare la strada perduta rispetto a Doctrine. La buona notizia è che Propel2 sarà un refactoring della versione 1.6, quindi con le stesse funzionalità. Rispetto alla vecchia versione, ci sono numerosi miglioramenti: supporto solo da PHP 5.3.3, coding standard rivisto, aderenza a PSR-0 (Propel2 è nel FIG), componenti di Symfony2 al posto di phing (ma non solo), adapter per poter non usare PDO, supporto ai traits su 5.4.
Gli onnipresenti Stof e Lukas hanno tenuto un talk sui bundle. Dopo una breve introduzione su cosa sono i bundle e su dove sia possibile trovarli (ovviamente su knpbundles!), hanno mostrato una carrellata di best practice per crearne di nuovi: struttura delle cartelle, licenza (meglio MIT), configurazione del DIC, uso di composer, scrittura di test, interazione con travis, ovverride di controllori, gestione di bugfix e di pull request su github. Infine, hanno mostrato alcuni dei bundle più famosi e più usati (molti dei quali scritti da loro stessi), come fosuser, fosrest, stofdoctrineextension, knpmenu, liipimagine, eccetera.
Purtroppo ho dovuto saltare uno slot, per presentare un lighting talk nell’unconference.
In chiusura, David Buchmann, già speaker al phpDay, ha parlato di Symfony2 e agile. Dopo una non breve introduzione all’agile, ha evidenziato come Symfony si adatti bene a chi voglia seguire le pratiche agili: modularità, separazione (grazie al DIC), testabilità, possibilità di sviluppare codice che sia a sua volta modulare (per esempio usando i controllori come servizi). A seguire, diversi consigli su come scrivere i test e quali bundle possono aiutare (per esempio JMSCommandBundle e LiipFunctionalTestBundle). Non poteva mancare l’invio all’utilizzo di travis.
Per completezza, nei track non coperti qui si è parlato di: Service Container, ORM e prestazioni, PHPCR, Redis, Behat, Solr, CMF, un case study su Overblog, sysadmin skills, PostgreSQL, websockets.
Purtroppo è mancato un intervento conclusivo: dopo le ultime sessioni, ci sono stati dei lighting talk in track principale, poco convincenti, anche se a dire il vero sono andato via prima che finissero.
In conclusione: un evento sicuramente migliorato rispetto allo scorso anno (due difetti evidenti, cibo e connessione, sono stati risolti una volta per tutte), ma che non dà ancora l’impressione di essere ai livelli di due anni fa. Speriamo nel 2013.
Per impostazione predefinita NetBeans ignora alcuni file (file nascosti e file legati ai sistemi di controllo versione). Nella maggior parte dei casi questo è un bene ma nel caso del file .gitignore non poterlo modificare direttamente dall’IDE può diventare molto fastidioso. Per risolvere il problema è sufficiente modificare il pattern dei file ignorati con: ^(CVS|SCCS|vssver.?\.scc|#.*#|%.*%|_svn)$|~$|^\.(?!(htaccess|gitignore)$).*$ [...]
Dopo Servergrove anche WooThemes ha deciso di offrire il suo supporto a WebForAll. Grazie alla disponibilità dei fantastici ragazzi di WooThemes durante il WebForAll Weekend potremo utilizzare i loro ottimi template per wordpress e drupal. A loro va il mio caloroso ringraziamento al quale, spero, vorrete unirvi. Tweet to @woothemes
Servergrove ha deciso di sponsorizzare il web4all weekend con un piano hosting completamente gratuito. Web4All cerca di aiutare altre ONLUS con la loro presenza online e, nel corso del primo web4all weekend, 24 sviluppatori lavoreranno ai siti web di circa 10 associazioni no-profit già selezionate. Grazie alla generosa offerta di servergrove non solo saremo in [...]
Tempo fa ho letto l’ottimo post Developer productivity tip: VirtualDocumentRoot sul blog di ServerGrove. Ho trovato il suggerimento di usare VirtualDocumentRoot per definire i mie vhost molto interessante. E’ passato del tempo prima che mi decidessi a rifattorizzare la configurazione del mio apache mettendo in pratica i suggerimenti di ServerGrove ma finalmente sono riuscito a [...]
Ho deciso di partecipare al webforall weekend, il codemotion potrà aspettare l’anno prossimo. Credo e spero che sarà un’esperienza interessante e non vedo l’ora di poter dare una mano. Webforall è un’associazione relativamente (credo) giovane con uno obiettivo semplice ma estremamente pragmatico: mettere professionisti dell’ IT al servizio delle onlus tipicamente per la realizzazione di [...]
Guzzle è un client HTTP per servizi REST che ho scoperto recentemente. Questo vuole essere solamente un breve post introduttivo, sperando di poter trovare presto il tempo di scriverne in maniera più approfondita. Guzzle viene descritto così dai sui sviluppatori: Guzzle is a PHP HTTP client and framework for building RESTful web service clients Il [...]
Scrivendo unit test è molto comune avere a che fare con oggetti mock. Dovendo isolare il componente sotto test, ogni altro oggetto in gioco dovrebbe essere mockato. Questo permette di avere il necessario isolamento del componente sotto test, dal resto del sistema (dalle sue dipendenze). PHPUnit mette a disposizione un set di api per definire [...]
Silex è un micro framework PHP basato sul fratello maggiore Symfony 2 dal quale eredita scelte progettuali e componenti. La definizione “A PHP micro-framework standing on the shoulder of giants” è assolutamente azzeccata, Silex è micro ma nasconde (nemmeno troppo) tante tante potenzialità.Parlando di Silex parliamo di symfony 2, parlando di symfony 2 parliamo di [...]
Questo post descrive l’approccio che ho usato recentemente per l’integrazione di un motore di ricerca all’interno di un’applicazione symfony. I principi alla base del post in realtà sono adattabili a qualunque tipo di applicazione php e non sono legati a symfony in particolar modo. Il problema principale, in un contesto dove si vuole un motore [...]
Chi segue lo sviluppo di symfony conosce sicuramente sismo, ne avrà sentito parlare e, probabilmente, ne avrà atteso il rilascio. A tutti gli altri ricordo solamente che è un server di continuous testing molto semplice e specifico per l’uso “in locale”. Nel mio caso ho installato il tutto su Zend Server Community Edition su OS [...]