SlideShare una empresa de Scribd logo
1 de 73
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 :-)

Más contenido relacionado

La actualidad más candente

Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Kacper Gunia
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of LithiumNate Abele
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkG Woo
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDAleix Vergés
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksNate Abele
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsRoss Tuck
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowKacper Gunia
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationSamuel ROZE
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of LithiumNate Abele
 
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Colin O'Dell
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationKirill Chebunin
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201Fabien Potencier
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionNate Abele
 

La actualidad más candente (20)

Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of Lithium
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php framework
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and Hobgoblins
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers Cracow
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony application
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of Lithium
 
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 Application
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
 

Similar a Symfony2, creare bundle e valore per il cliente

Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Jakub Zalas
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsMichael Peacock
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownpartsBastian Feder
 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitinternational PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitsmueller_sandsmedia
 
Symfony internals [english]
Symfony internals [english]Symfony internals [english]
Symfony internals [english]Raul Fraile
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using CodeceptionJeroen van Dijk
 
Filesystem abstractions and msg queue sergeev - symfony camp 2018
Filesystem abstractions and msg queue   sergeev - symfony camp 2018Filesystem abstractions and msg queue   sergeev - symfony camp 2018
Filesystem abstractions and msg queue sergeev - symfony camp 2018Юлия Коваленко
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Elena Kolevska
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsBastian Feder
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium AppsNate Abele
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Developmentjsmith92
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceIvan Chepurnyi
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony TechniquesKris Wallsmith
 
Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)Fabien Potencier
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 WorldFabien Potencier
 
Practical PHP 5.3
Practical PHP 5.3Practical PHP 5.3
Practical PHP 5.3Nate Abele
 

Similar a Symfony2, creare bundle e valore per il cliente (20)

Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitinternational PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
 
Symfony internals [english]
Symfony internals [english]Symfony internals [english]
Symfony internals [english]
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
Filesystem abstractions and msg queue sergeev - symfony camp 2018
Filesystem abstractions and msg queue   sergeev - symfony camp 2018Filesystem abstractions and msg queue   sergeev - symfony camp 2018
Filesystem abstractions and msg queue sergeev - symfony camp 2018
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
 
Oops in php
Oops in phpOops in php
Oops in php
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)Beyond symfony 1.2 (Symfony Camp 2008)
Beyond symfony 1.2 (Symfony Camp 2008)
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Event Sourcing with php
Event Sourcing with phpEvent Sourcing with php
Event Sourcing with php
 
Practical PHP 5.3
Practical PHP 5.3Practical PHP 5.3
Practical PHP 5.3
 

Último

A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 

Último (20)

A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 

Symfony2, creare bundle e valore per il cliente

  • 1. Symfony2, creare bundle e valore per il cliente Leonardo Proietti @_leopro_ Symfony day Torino, 5 ottobre 2012
  • 2.
  • 3. valore per il cliente = benefici / costi
  • 6. collaborazione responsabilità reciproca soddisfazione
  • 7. Il processo di sviluppo 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 = 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
  • 10. bundle = una libreria sufficientemente astratta da essere utilizzata in contesti diversi tra loro
  • 11. la realizzazione di un bundle non è valore per il cliente
  • 12. Richiesta un sito web con contenuti aggiornabili disponibili in diverse lingue
  • 13. Analisi gli strumenti esistenti sono valutati come non adatti
  • 15. Sarà una libreria riutilizzabile?
  • 16. Sarà una libreria riutilizzabile? Non è importante perchè l'obiettivo principale è produrre valore per il cliente
  • 17. Ma non vogliamo spaghetti code
  • 18. Test Driven Development ovvero sviluppare guidati dai test
  • 19. Test Driven Development ovvero sviluppi 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; interface TranslatableInterface { 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; } }
  • 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); }
  • 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 che soddisfare 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 extends Translatable { /** * @ORMOneToMany * (targetEntity="ArticleI18n", mappedBy="translatable") */ protected $translations; public function getTitle() { return $this->getTranslation()->getTitle(); } }
  • 56. Una volta testata anche l'integrazione e verificato che la richiesta del cliente è stata soddisfatta, abbiamo prodotto il valore richiesto e ci possiamo fermare.
  • 58. Prendiamo il codice e 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; use PUGXI18nBundleDependencyInjectionPUGXI18nExtension; class PUGXI18nBundle extends Bundle { public function getContainerExtension() { return new PUGXI18nExtension(); } }
  • 63. namespace PUGXI18nBundleDependencyInjection; use SymfonyComponentConfigDefinitionBuilderTreeBuilder; use SymfonyComponentConfigDefinitionConfigurationInterface; class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('pugx_i18n'); return $treeBuilder; } }
  • 64. 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'; } }
  • 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"?> <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>
  • 69. Servizio rivolto alla comunità 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 creare un repository su github e avremo dato il nostro piccolo contributo alla comunità open source.