Symfony2, creare bundle e valore
         per il cliente

          Leonardo Proietti
              @_leopro_




             Symfony day
        Torino, 5 ottobre 2012
valore per il cliente = benefici / costi
collaborazione
collaborazione
responsabilità
collaborazione
     responsabilità
reciproca soddisfazione
Il processo di sviluppo basato sulla
collaborazione, sulla reciproca soddisfazione e
sul senso di responsabilità permette di ottenere
           un alto valore per il cliente.
bundle = è una cartella con una struttura ben
definita, che può ospitare qualsiasi cosa, dalle
     classi ai controllori e alle risorse web

http://symfony.com/it/doc/current/cookbook/bundles/best_practices.html
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
bundle = una libreria sufficientemente astratta
da essere utilizzata in contesti diversi tra loro
la realizzazione di un bundle non è valore
               per il cliente
Richiesta
un sito web con contenuti aggiornabili
      disponibili in diverse lingue
Analisi
gli strumenti esistenti sono valutati come non
                     adatti
Idea
relazione uno-a-molti, DIC, eventi
Sarà una libreria riutilizzabile?
Sarà una libreria riutilizzabile?
Non è importante perchè l'obiettivo principale è
         produrre valore per il cliente
Ma non vogliamo spaghetti code
Test Driven Development
ovvero sviluppare guidati dai test
Test Driven Development
ovvero sviluppi meglio, vivi meglio
$article->getTitle();

     /it/{article}
  “Il mio articolo”
   /en/{article}
    “My article”
Acme
  CoreBundle
  Listener
  LocaleListener.php
  EntityListener.php
  AnotherListener.php
  Locale
  Locale.php
  LocaleInterface.php
  Model
  Translatable.php
  TranslatableInterface.php
  TranslatingInterface.php
  AnotherModelUtil.php
  Tests
  ...
Acme
  CoreBundle
  Listener
  LocaleListener.php
  EntityListener.php
  AnotherListener.php
  Locale
  Locale.php
  LocaleInterface.php
  Model
  Translatable.php
  TranslatableInterface.php
  TranslatingInterface.php
  AnotherModelUtil.php
  Tests
  ...
interface LocaleInterface
{
   function setLocale($locale);

    function getLocale();

    function getDefaultLocale();
}
use AcmeCoreBundleModelTranslatingInterface;
use AcmeCoreBundleLocaleLocaleInterface;

interface TranslatableInterface
{
   function addTranslation(TranslatingInterface $translation);

    function getTranslations();

    function setLocale(LocaleInterface $locale);
}
use AcmeCoreBundleModelTranslatableInterface;

interface TranslatingInterface
{
    function setTranslatable(TranslatableInterface $translatable);

    function getLocale();

    function setLocale($string);
}
class Article
{
   /**
   * @ORMOneToMany
   * (targetEntity="ArticleI18n", mappedBy="translatable")
   */
   protected $translations;

    public function getTitle()
    {
       return $this->getTranslation()->getTitle();
    }
}
class Article
{
   /**
   * @ORMOneToMany
   * (targetEntity="ArticleI18n", mappedBy="translatable")
   */
   protected $translations;

    public function getTitle()
    {
       return $this->getTranslation()->getTitle();
    }
}
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;
    }
}
Symfony2
kernel.request
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
Doctrine2
postLoad
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
//tranlsations, defaultLocale, locale, tranlsationExpected
public 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'),
     )
}
//tranlsations, defaultLocale, locale, tranlsationExpected
public 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'),
     )
}
//tranlsations, defaultLocale, locale, tranlsationExpected
public 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'),
     )
}
//tranlsations, defaultLocale, locale, tranlsationExpected
public 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'),
     )
}
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);
}
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);
}
Non resta che soddisfare i test scrivendo del
           codice funzionante :-)
configuriamo i servizi
//src/Acme/CoreBundle/Resources/config/services.yml

services:
 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 }
configuriamo i servizi
//src/Acme/CoreBundle/Resources/config/services.yml

services:
 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 }
class Article extends Translatable
{
   /**
   * @ORMOneToMany
   * (targetEntity="ArticleI18n", mappedBy="translatable")
   */
   protected $translations;

    public function getTitle()
    {
       return $this->getTranslation()->getTitle();
    }
}
Una volta testata anche l'integrazione e
  verificato che la richiesta del cliente è stata
soddisfatta, abbiamo prodotto il valore richiesto
             e ci possiamo fermare.
Nuovo cliente, stessa richiesta
Prendiamo il codice e lo isoliamo
       PUGX/I18nBundle
AcmePlus
PUGX
  I18nBundle
  Listener
  LocaleListener.php
  EntityListener.php
  Locale
  Locale.php
  LocaleInterface.php
  Model
  Translatable.php
  TranslatableInterface.php
  TranslatingInterface.php
  Tests
Il grosso è fatto, ora dovremo:

- esporre una configurazione semantica

- gestire le dipendenze (composer) e la configurazione dei test

- aggiungere la documentazione, la licenza ecc.
AcmePlus
PUGX
  I18nBundle
  DependencyInjection
  Configuration.php
  PUGXI18nExtension.php
  […]
  Tests
  bootstrap.php
  PUGXI18nBundle.php
  composer.json
  phpunit.xml.dist
namespace PUGXI18nBundle;

use SymfonyComponentHttpKernelBundleBundle;
use PUGXI18nBundleDependencyInjectionPUGXI18nExtension;

class PUGXI18nBundle extends Bundle
{
    public function getContainerExtension()
    {
      return new PUGXI18nExtension();
    }
}
namespace PUGXI18nBundleDependencyInjection;

use SymfonyComponentConfigDefinitionBuilderTreeBuilder;
use SymfonyComponentConfigDefinitionConfigurationInterface;

class Configuration implements ConfigurationInterface
{
    public function getConfigTreeBuilder()
    {
      $treeBuilder = new TreeBuilder();
      $rootNode = $treeBuilder->root('pugx_i18n');

        return $treeBuilder;
    }
}
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';
    }
}
Composer
gestisce le dipendenze
//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"
//src/PUGX/I18nBundle/Tests/bootstrap.php

if (!is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
      throw new LogicException('Could not find autoload.php');
}

require $autoloadFile;
//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>
Servizio rivolto alla comunità open source per
la continuos integration di progetti pubblicati su
                       github

                 https://travis-ci.org/
//src/PUGX/I18nBundle/.travis.yml

language: php

php:
   - 5.3
   - 5.4

before_script:
    - curl -s http://getcomposer.org/installer | php
    - php composer.phar install –dev
Ora basta creare un repository su github e
avremo dato il nostro piccolo contributo alla
         comunità open source.
Credits


symfony.com/it/doc/2.0/book/internals.html

symfony.com/it/doc/2.0/cookbook/bundles/best_practices.html

symfony.com/it/doc/2.0/cookbook/bundles/extension.html

sviluppoagile.it

PUG Roma
GRAZIE :-)

Symfony2, creare bundle e valore per il cliente

  • 1.
    Symfony2, creare bundlee valore per il cliente Leonardo Proietti @_leopro_ Symfony day Torino, 5 ottobre 2012
  • 3.
    valore per ilcliente = benefici / costi
  • 4.
  • 5.
  • 6.
    collaborazione responsabilità reciproca soddisfazione
  • 7.
    Il processo disviluppo basato sulla collaborazione, sulla reciproca soddisfazione e sul senso di responsabilità permette di ottenere un alto valore per il cliente.
  • 8.
    bundle = èuna cartella con una struttura ben definita, che può ospitare qualsiasi cosa, dalle classi ai controllori e alle risorse web http://symfony.com/it/doc/current/cookbook/bundles/best_practices.html
  • 9.
    bundle = unospazio dei nomi di PHP ovvero un namespace, con con una classe Bundle http://symfony.com/it/doc/current/cookbook/bundles/best_practices.html
  • 10.
    bundle = unalibreria sufficientemente astratta da essere utilizzata in contesti diversi tra loro
  • 11.
    la realizzazione diun bundle non è valore per il cliente
  • 12.
    Richiesta un sito webcon contenuti aggiornabili disponibili in diverse lingue
  • 13.
    Analisi gli strumenti esistentisono valutati come non adatti
  • 14.
  • 15.
    Sarà una libreriariutilizzabile?
  • 16.
    Sarà una libreriariutilizzabile? Non è importante perchè l'obiettivo principale è produrre valore per il cliente
  • 17.
    Ma non vogliamospaghetti code
  • 18.
    Test Driven Development ovverosviluppare guidati dai test
  • 19.
    Test Driven Development ovverosviluppi meglio, vivi meglio
  • 20.
    $article->getTitle(); /it/{article} “Il mio articolo” /en/{article} “My article”
  • 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.
    Acme CoreBundle Listener LocaleListener.php EntityListener.php AnotherListener.php Locale Locale.php LocaleInterface.php Model Translatable.php TranslatableInterface.php TranslatingInterface.php AnotherModelUtil.php Tests ...
  • 23.
    interface LocaleInterface { function setLocale($locale); function getLocale(); function getDefaultLocale(); }
  • 24.
    use AcmeCoreBundleModelTranslatingInterface; use AcmeCoreBundleLocaleLocaleInterface; interfaceTranslatableInterface { function addTranslation(TranslatingInterface $translation); function getTranslations(); function setLocale(LocaleInterface $locale); }
  • 25.
    use AcmeCoreBundleModelTranslatableInterface; interface TranslatingInterface { function setTranslatable(TranslatableInterface $translatable); function getLocale(); function setLocale($string); }
  • 26.
    class Article { /** * @ORMOneToMany * (targetEntity="ArticleI18n", mappedBy="translatable") */ protected $translations; public function getTitle() { return $this->getTranslation()->getTitle(); } }
  • 27.
    class Article { /** * @ORMOneToMany * (targetEntity="ArticleI18n", mappedBy="translatable") */ protected $translations; public function getTitle() { return $this->getTranslation()->getTitle(); } }
  • 28.
    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; } }
  • 29.
  • 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.
    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.
    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.
    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.
    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.
    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.
    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.
    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.
    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); }
  • 39.
  • 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.
    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.
    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.
    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.
    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.
    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); }
  • 46.
    //tranlsations, defaultLocale, locale,tranlsationExpected public 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.
    //tranlsations, defaultLocale, locale,tranlsationExpected public 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.
    //tranlsations, defaultLocale, locale,tranlsationExpected public 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.
    //tranlsations, defaultLocale, locale,tranlsationExpected public 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'), ) }
  • 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.
    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); }
  • 52.
    Non resta chesoddisfare i test scrivendo del codice funzionante :-)
  • 53.
    configuriamo i servizi //src/Acme/CoreBundle/Resources/config/services.yml services: 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.
    configuriamo i servizi //src/Acme/CoreBundle/Resources/config/services.yml services: 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 }
  • 55.
    class Article extendsTranslatable { /** * @ORMOneToMany * (targetEntity="ArticleI18n", mappedBy="translatable") */ protected $translations; public function getTitle() { return $this->getTranslation()->getTitle(); } }
  • 56.
    Una volta testataanche l'integrazione e verificato che la richiesta del cliente è stata soddisfatta, abbiamo prodotto il valore richiesto e ci possiamo fermare.
  • 57.
  • 58.
    Prendiamo il codicee lo isoliamo PUGX/I18nBundle
  • 59.
    AcmePlus PUGX I18nBundle Listener LocaleListener.php EntityListener.php Locale Locale.php LocaleInterface.php Model Translatable.php TranslatableInterface.php TranslatingInterface.php Tests
  • 60.
    Il grosso èfatto, ora dovremo: - esporre una configurazione semantica - gestire le dipendenze (composer) e la configurazione dei test - aggiungere la documentazione, la licenza ecc.
  • 61.
    AcmePlus PUGX I18nBundle DependencyInjection Configuration.php PUGXI18nExtension.php […] Tests bootstrap.php PUGXI18nBundle.php composer.json phpunit.xml.dist
  • 62.
    namespace PUGXI18nBundle; use SymfonyComponentHttpKernelBundleBundle; usePUGXI18nBundleDependencyInjectionPUGXI18nExtension; class PUGXI18nBundle extends Bundle { public function getContainerExtension() { return new PUGXI18nExtension(); } }
  • 63.
    namespace PUGXI18nBundleDependencyInjection; use SymfonyComponentConfigDefinitionBuilderTreeBuilder; useSymfonyComponentConfigDefinitionConfigurationInterface; class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('pugx_i18n'); return $treeBuilder; } }
  • 64.
    namespace PUGXI18nBundleDependencyInjection; use SymfonyComponentDependencyInjectionContainerBuilder; useSymfonyComponentConfigFileLocator; 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'; } }
  • 65.
  • 66.
    //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"
  • 67.
    //src/PUGX/I18nBundle/Tests/bootstrap.php if (!is_file($autoloadFile =__DIR__.'/../vendor/autoload.php')) { throw new LogicException('Could not find autoload.php'); } require $autoloadFile;
  • 68.
    //src/PUGX/I18nBundle/phpunit.xml.dist <?xml version="1.0" encoding="UTF-8"?> <phpunitbootstrap="./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>
  • 69.
    Servizio rivolto allacomunità open source per la continuos integration di progetti pubblicati su github https://travis-ci.org/
  • 70.
    //src/PUGX/I18nBundle/.travis.yml language: php php: - 5.3 - 5.4 before_script: - curl -s http://getcomposer.org/installer | php - php composer.phar install –dev
  • 71.
    Ora basta creareun repository su github e avremo dato il nostro piccolo contributo alla comunità open source.
  • 72.
  • 73.