Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Symfony2, creare bundle e valore per il cliente

1,588 views

Published on

Un gioco in cui vincono tutti o due piccioni con una fava ;)

Lavorare rivolti alla creazione di valore per il cliente e da questo ottenere una libreria quasi pronta per essere pubblicata

Published in: Technology
  • Be the first to comment

Symfony2, creare bundle e valore per il cliente

  1. 1. Symfony2, creare bundle e valore per il cliente Leonardo Proietti @_leopro_ Symfony day Torino, 5 ottobre 2012
  2. 2. valore per il cliente = benefici / costi
  3. 3. collaborazione
  4. 4. collaborazioneresponsabilità
  5. 5. collaborazione responsabilitàreciproca soddisfazione
  6. 6. Il processo di sviluppo basato sullacollaborazione, sulla reciproca soddisfazione esul senso di responsabilità permette di ottenere un alto valore per il cliente.
  7. 7. bundle = è una cartella con una struttura bendefinita, che può ospitare qualsiasi cosa, dalle classi ai controllori e alle risorse webhttp://symfony.com/it/doc/current/cookbook/bundles/best_practices.html
  8. 8. bundle = uno spazio dei nomi di PHP ovvero un namespace, con con una classe Bundle http://symfony.com/it/doc/current/cookbook/bundles/best_practices.html
  9. 9. bundle = una libreria sufficientemente astrattada essere utilizzata in contesti diversi tra loro
  10. 10. la realizzazione di un bundle non è valore per il cliente
  11. 11. Richiestaun sito web con contenuti aggiornabili disponibili in diverse lingue
  12. 12. Analisigli strumenti esistenti sono valutati come non adatti
  13. 13. Idearelazione uno-a-molti, DIC, eventi
  14. 14. Sarà una libreria riutilizzabile?
  15. 15. Sarà una libreria riutilizzabile?Non è importante perchè lobiettivo principale è produrre valore per il cliente
  16. 16. Ma non vogliamo spaghetti code
  17. 17. Test Driven Developmentovvero sviluppare guidati dai test
  18. 18. Test Driven Developmentovvero sviluppi meglio, vivi meglio
  19. 19. $article->getTitle(); /it/{article} “Il mio articolo” /en/{article} “My article”
  20. 20. Acme CoreBundle Listener LocaleListener.php EntityListener.php AnotherListener.php Locale Locale.php LocaleInterface.php Model Translatable.php TranslatableInterface.php TranslatingInterface.php AnotherModelUtil.php Tests ...
  21. 21. Acme CoreBundle Listener LocaleListener.php EntityListener.php AnotherListener.php Locale Locale.php LocaleInterface.php Model Translatable.php TranslatableInterface.php TranslatingInterface.php AnotherModelUtil.php Tests ...
  22. 22. interface LocaleInterface{ function setLocale($locale); function getLocale(); function getDefaultLocale();}
  23. 23. use AcmeCoreBundleModelTranslatingInterface;use AcmeCoreBundleLocaleLocaleInterface;interface TranslatableInterface{ function addTranslation(TranslatingInterface $translation); function getTranslations(); function setLocale(LocaleInterface $locale);}
  24. 24. use AcmeCoreBundleModelTranslatableInterface;interface TranslatingInterface{ function setTranslatable(TranslatableInterface $translatable); function getLocale(); function setLocale($string);}
  25. 25. class Article{ /** * @ORMOneToMany * (targetEntity="ArticleI18n", mappedBy="translatable") */ protected $translations; public function getTitle() { return $this->getTranslation()->getTitle(); }}
  26. 26. class Article{ /** * @ORMOneToMany * (targetEntity="ArticleI18n", mappedBy="translatable") */ protected $translations; public function getTitle() { return $this->getTranslation()->getTitle(); }}
  27. 27. class ArticleI18n{ /** * @ORMManyToOne(targetEntity="Article", inversedBy="translations" * @ORMJoinColumn(name="article_id", referencedColumnName="id") */ protected $translatable; /** * @ORMColumn(type="string", length=128) */ private $title; public function getTitle() { return $this->title; }}
  28. 28. Symfony2kernel.request
  29. 29. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  30. 30. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  31. 31. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  32. 32. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  33. 33. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  34. 34. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  35. 35. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  36. 36. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  37. 37. public function testOnKernelRequest(){ //SymfonyComponentHttpKernelEventGetResponseEvent $this->event->expects($this->once() ->method(getRequest) ->will($this->returnValue($this->request)); //SymfonyComponentHttpFoundationRequest $this->request->expects($this->once()) ->method(getLocale) ->will($this->returnValue(en)); //AcmeCoreBundleLocaleLocaleInterface $this->locale->expects($this->once()) ->method(setLocale) ->with(en); //AcmeCoreBundleListenerLocaleListener $this->listener = new LocaleListener($this->locale); $this->listener->onKernelRequest($this->event);}
  38. 38. Doctrine2postLoad
  39. 39. public function testPostLoad(){ //AcmeCoreBundleLocaleLocaleInterface $this->locale; //DoctrineORMEventLifecycleEventArgs $this->args->expects($this->once()) ->method(getEntity) ->will($this->returnValue($this->entity)); //AcmeCoreBundleModelTranslatableInterface $this->entity->expects($this->once()) ->method(setLocale) ->with($this->locale); //AcmeCoreBundleListenerEntityListener $this->listener = new TranslatableListener($this->locale); $this->listener->postLoad($this->event);}
  40. 40. public function testPostLoad(){ //AcmeCoreBundleLocaleLocaleInterface $this->locale; //DoctrineORMEventLifecycleEventArgs $this->args->expects($this->once()) ->method(getEntity) ->will($this->returnValue($this->entity)); //AcmeCoreBundleModelTranslatableInterface $this->entity->expects($this->once()) ->method(setLocale) ->with($this->locale); //AcmeCoreBundleListenerEntityListener $this->listener = new TranslatableListener($this->locale); $this->listener->postLoad($this->event);}
  41. 41. public function testPostLoad(){ //AcmeCoreBundleLocaleLocaleInterface $this->locale; //DoctrineORMEventLifecycleEventArgs $this->args->expects($this->once()) ->method(getEntity) ->will($this->returnValue($this->entity)); //AcmeCoreBundleModelTranslatableInterface $this->entity->expects($this->once()) ->method(setLocale) ->with($this->locale); //AcmeCoreBundleListenerEntityListener $this->listener = new TranslatableListener($this->locale); $this->listener->postLoad($this->event);}
  42. 42. public function testPostLoad(){ //AcmeCoreBundleLocaleLocaleInterface $this->locale; //DoctrineORMEventLifecycleEventArgs $this->args->expects($this->once()) ->method(getEntity) ->will($this->returnValue($this->entity)); //AcmeCoreBundleModelTranslatableInterface $this->entity->expects($this->once()) ->method(setLocale) ->with($this->locale); //AcmeCoreBundleListenerEntityListener $this->listener = new TranslatableListener($this->locale); $this->listener->postLoad($this->event);}
  43. 43. public function testPostLoad(){ //AcmeCoreBundleLocaleLocaleInterface $this->locale; //DoctrineORMEventLifecycleEventArgs $this->args->expects($this->once()) ->method(getEntity) ->will($this->returnValue($this->entity)); //AcmeCoreBundleModelTranslatableInterface $this->entity->expects($this->once()) ->method(setLocale) ->with($this->locale); //AcmeCoreBundleListenerEntityListener $this->listener = new TranslatableListener($this->locale); $this->listener->postLoad($this->event);}
  44. 44. public function testPostLoad(){ //AcmeCoreBundleLocaleLocaleInterface $this->locale; //DoctrineORMEventLifecycleEventArgs $this->args->expects($this->once()) ->method(getEntity) ->will($this->returnValue($this->entity)); //AcmeCoreBundleModelTranslatableInterface $this->entity->expects($this->once()) ->method(setLocale) ->with($this->locale); //AcmeCoreBundleListenerEntityListener $this->listener = new TranslatableListener($this->locale); $this->listener->postLoad($this->event);}
  45. 45. //tranlsations, defaultLocale, locale, tranlsationExpectedpublic function getTranslationProvider(){ return array( array( array(it => translationIt, en => translationEn), en, en, translationEn), array( array(en => translationEn, it => translationIt), en, it, translationIt), array( array(en => translationEn), en, it, translationEn), )}
  46. 46. //tranlsations, defaultLocale, locale, tranlsationExpectedpublic function getTranslationProvider(){ return array( array( array(it => translationIt, en => translationEn), en, en, translationEn), array( array(en => translationEn, it => translationIt), en, it, translationIt), array( array(en => translationEn), en, it, translationEn), )}
  47. 47. //tranlsations, defaultLocale, locale, tranlsationExpectedpublic function getTranslationProvider(){ return array( array( array(it => translationIt, en => translationEn), en, en, translationEn), array( array(en => translationEn, it => translationIt), en, it, translationIt), array( array(en => translationEn), en, it, translationEn), )}
  48. 48. //tranlsations, defaultLocale, locale, tranlsationExpectedpublic function getTranslationProvider(){ return array( array( array(it => translationIt, en => translationEn), en, en, translationEn), array( array(en => translationEn, it => translationIt), en, it, translationIt), array( array(en => translationEn), en, it, translationEn), )}
  49. 49. public function testGetTranslation($translations, $defaultLocale, $locale,$translationExpected){ $this->translatable = new Translatable(); $this->translatable->setLocale($this->locale); foreach ($translations as $language => $translation) { $this->$translation->expects($this->exactly(2))->method(getLocale) ->will($this->returnValue($language)); $this->translatable->addTranslation($this->$translation); } $this->locale->expects($this->exactly(1)) ->method(getDefaultLocale) ->will($this->returnValue($defaultLocale)); $this->locale->expects($this->exactly(1)) ->method(getLocale) ->will($this->returnValue($locale)); $result = $this->translatable->getTranslation(); $this->assertEquals($this->$translationExpected, $result);}
  50. 50. public function testGetTranslation($translations, $defaultLocale, $locale,$translationExpected){ $this->translatable = new Translatable(); $this->translatable->setLocale($this->locale); foreach ($translations as $language => $translation) { $this->$translation->expects($this->exactly(2))->method(getLocale) ->will($this->returnValue($language)); $this->translatable->addTranslation($this->$translation); } $this->locale->expects($this->exactly(1)) ->method(getDefaultLocale) ->will($this->returnValue($defaultLocale)); $this->locale->expects($this->exactly(1)) ->method(getLocale) ->will($this->returnValue($locale)); $result = $this->translatable->getTranslation(); $this->assertEquals($this->$translationExpected, $result);}
  51. 51. Non resta che soddisfare i test scrivendo del codice funzionante :-)
  52. 52. configuriamo i servizi//src/Acme/CoreBundle/Resources/config/services.ymlservices: acme_core.locale: class: AcmeCoreBundleServiceLocale arguments: [%locale%] acme_core.listener.locale: class: AcmeCoreBundleServiceLocaleListener arguments: ["@acme.core.locale"] tags: - { name: kernel.event_listener, event: kernel.request } acme_core.listener.entity: class: AcmeCoreBundleServiceEntityListener arguments: ["@acme.core.locale"] tags: - { name: doctrine.event_listener, event: postLoad }
  53. 53. configuriamo i servizi//src/Acme/CoreBundle/Resources/config/services.ymlservices: acme_core.locale: class: AcmeCoreBundleServiceLocale arguments: [%locale%] acme_core.listener.locale: class: AcmeCoreBundleServiceLocaleListener arguments: ["@acme.core.locale"] tags: - { name: kernel.event_listener, event: kernel.request } acme_core.listener.entity: class: AcmeCoreBundleServiceEntityListener arguments: ["@acme.core.locale"] tags: - { name: doctrine.event_listener, event: postLoad }
  54. 54. class Article extends Translatable{ /** * @ORMOneToMany * (targetEntity="ArticleI18n", mappedBy="translatable") */ protected $translations; public function getTitle() { return $this->getTranslation()->getTitle(); }}
  55. 55. Una volta testata anche lintegrazione e verificato che la richiesta del cliente è statasoddisfatta, abbiamo prodotto il valore richiesto e ci possiamo fermare.
  56. 56. Nuovo cliente, stessa richiesta
  57. 57. Prendiamo il codice e lo isoliamo PUGX/I18nBundle
  58. 58. AcmePlusPUGX I18nBundle Listener LocaleListener.php EntityListener.php Locale Locale.php LocaleInterface.php Model Translatable.php TranslatableInterface.php TranslatingInterface.php Tests
  59. 59. Il grosso è fatto, ora dovremo:- esporre una configurazione semantica- gestire le dipendenze (composer) e la configurazione dei test- aggiungere la documentazione, la licenza ecc.
  60. 60. AcmePlusPUGX I18nBundle DependencyInjection Configuration.php PUGXI18nExtension.php […] Tests bootstrap.php PUGXI18nBundle.php composer.json phpunit.xml.dist
  61. 61. namespace PUGXI18nBundle;use SymfonyComponentHttpKernelBundleBundle;use PUGXI18nBundleDependencyInjectionPUGXI18nExtension;class PUGXI18nBundle extends Bundle{ public function getContainerExtension() { return new PUGXI18nExtension(); }}
  62. 62. namespace PUGXI18nBundleDependencyInjection;use SymfonyComponentConfigDefinitionBuilderTreeBuilder;use SymfonyComponentConfigDefinitionConfigurationInterface;class Configuration implements ConfigurationInterface{ public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root(pugx_i18n); return $treeBuilder; }}
  63. 63. namespace PUGXI18nBundleDependencyInjection;use SymfonyComponentDependencyInjectionContainerBuilder;use SymfonyComponentConfigFileLocator;use SymfonyComponentHttpKernelDependencyInjectionExtension;use SymfonyComponentDependencyInjectionLoaderYamlFileLoader;class PUGXI18nExtension implements Extension{ public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); $path = __DIR__./../Resources/config $loader = new YamlFileLoader($container, new FileLocator($path)); $loader->load(services.yml); } public function getAlias() { return pugx_i18n; }}
  64. 64. Composergestisce le dipendenze
  65. 65. //src/PUGX/I18nBundle/composer.json{ "name": "pugx/i18n-bundle", "type": "symfony-bundle", "description": "Manage i18n", "keywords": ["symfony2, i18n, translation"], "license": "MIT", "authors": [ { "name":"Leonardo Proietti", "email":"leonardo.proietti@gmail.com" } ], "minimum-stability": "dev", "require": { "php": ">=5.3.2", "symfony/framework-bundle": "2.1.*", "doctrine/orm": ">=2.2.3,<2.4-dev", "symfony/yaml": "2.1.*" }, "autoload": { "psr-0": { "PUGXI18nBundle": "" } }, "target-dir": "PUGX/I18nBundle"
  66. 66. //src/PUGX/I18nBundle/Tests/bootstrap.phpif (!is_file($autoloadFile = __DIR__./../vendor/autoload.php)) { throw new LogicException(Could not find autoload.php);}require $autoloadFile;
  67. 67. //src/PUGX/I18nBundle/phpunit.xml.dist<?xml version="1.0" encoding="UTF-8"?><phpunit bootstrap="./Tests/bootstrap.php"> <testsuites> <testsuite name="PUGXI18nBundle test suite"> <directory suffix="Test.php">./Tests</directory> </testsuite> </testsuites> <filter> <whitelist> <directory>./</directory> <exclude> <directory>./Resources</directory> <directory>./Tests</directory> </exclude> </whitelist> </filter></phpunit>
  68. 68. Servizio rivolto alla comunità open source perla continuos integration di progetti pubblicati su github https://travis-ci.org/
  69. 69. //src/PUGX/I18nBundle/.travis.ymllanguage: phpphp: - 5.3 - 5.4before_script: - curl -s http://getcomposer.org/installer | php - php composer.phar install –dev
  70. 70. Ora basta creare un repository su github eavremo dato il nostro piccolo contributo alla comunità open source.
  71. 71. Creditssymfony.com/it/doc/2.0/book/internals.htmlsymfony.com/it/doc/2.0/cookbook/bundles/best_practices.htmlsymfony.com/it/doc/2.0/cookbook/bundles/extension.htmlsviluppoagile.itPUG Roma
  72. 72. GRAZIE :-)

×