SlideShare a Scribd company logo
1 of 116
Download to read offline
How Kris Writes Symfony Apps
       @kriswallsmith • February 9, 2013
About Me
@kriswallsmith.net

•   Born, raised, & live in Portland

•   10+ years of experience

•   Lead Architect at OpenSky

•   Open source fanboy
brewcycleportland.com
assetic
Buzz
Spork
Go big or go home.
Getting Started
composer create-project 
    symfony/framework-standard-edition 
    opti-grab/ 2.2.x-dev
-   "doctrine/orm": "~2.2,>=2.2.3",
-   "doctrine/doctrine-bundle": "1.2.*",
+   "doctrine/mongodb-odm-bundle": "3.0.*",
+   "jms/serializer-bundle": "1.0.*",
./app/console generate:bundle 
    --namespace=OptiGrab/Bundle/MainBundle
assetic:
    debug:             %kernel.debug%
    use_controller:    false
    bundles:           [ MainBundle ]
    filters:
         cssrewrite:   ~
         uglifyjs2:    { compress: true, mangle: true }
         uglifycss:    ~
jms_di_extra:
    locations:
        bundles:
            - MainBundle
public function registerContainerConfiguration(LoaderInterface $loader)
{
    $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');

    // load local_*.yml or local.yml
    if (
         file_exists($file = __DIR__.'/config/local_'.$this->getEnvironment().'.yml')
         ||
         file_exists($file = __DIR__.'/config/local.yml')
    ) {
         $loader->load($file);
    }
}
MongoDB
Treat your model like a princess.
She gets her own wing
   of the palace…
doctrine_mongodb:
    auto_generate_hydrator_classes: %kernel.debug%
    auto_generate_proxy_classes:      %kernel.debug%
    connections: { default: ~ }
    document_managers:
        default:
            connection: default
            database:     optiGrab
            mappings:
                 model:
                     type:    annotation
                     dir:     %src_dir%/OptiGrab/Model
                     prefix: OptiGrabModel
                     alias: Model
// repo for src/OptiGrab/Model/Widget.php
$repo = $this->dm->getRepository('Model:User');
…doesn't do any work…
use OptiGrabBundleMainBundleCanonicalizer;

public function setUsername($username)
{
    $this->username = $username;

    $canonicalizer = Canonicalizer::instance();
    $this->usernameCanonical = $canonicalizer->canonicalize($username);
}
use OptiGrabBundleMainBundleCanonicalizer;

public function setUsername($username, Canonicalizer $canonicalizer)
{
    $this->username = $username;
    $this->usernameCanonical = $canonicalizer->canonicalize($username);
}
…and is unaware of the work
  being done around her.
public function setUsername($username)
{
    // a listener will update the
    // canonical username
    $this->username = $username;
}
No query builders
outside of repositories
class WidgetRepository extends DocumentRepository
{
    public function findByUser(User $user)
    {
        return $this->createQueryBuilder()
            ->field('userId')->equals($user->getId())
            ->getQuery()
            ->execute();
    }

    public function updateDenormalizedUsernames(User $user)
    {
        $this->createQueryBuilder()
            ->update()
            ->multiple()
            ->field('userId')->equals($user->getId())
            ->field('userName')->set($user->getUsername())
            ->getQuery()
            ->execute();
    }
}
Eager id creation
public function __construct()
{
    $this->id = (string) new MongoId();
}
public function __construct()
{
    $this->id = (string) new MongoId();
    $this->createdAt = new DateTime();
    $this->widgets = new ArrayCollection();
}
Remember your
clone constructor
$foo = new Foo();
$bar = clone $foo;
public function __clone()
{
    $this->id = (string) new MongoId();
    $this->createdAt = new DateTime();
    $this->widgets = new ArrayCollection(
        $this->widgets->toArray()
    );
}
public function __construct()
{
    $this->id = (string) new MongoId();
    $this->createdAt = new DateTime();
    $this->widgets = new ArrayCollection();
}

public function __clone()
{
    $this->id = (string) new MongoId();
    $this->createdAt = new DateTime();
    $this->widgets = new ArrayCollection(
        $this->widgets->toArray()
    );
}
Only flush from the controller
public function theAction(Widget $widget)
{
    $this->get('widget_twiddler')
         ->skeedaddle($widget);
    $this->flush();
}
Save space on field names
/** @ODMString(name="u") */
private $username;

/** @ODMString(name="uc") @ODMUniqueIndex */
private $usernameCanonical;
public function getUsername()
{
    return $this->username ?: $this->usernameCanonical;
}

public function setUsername($username)
{
    if ($username) {
        $this->usernameCanonical = strtolower($username);
        $this->username = $username === $this->usernameCanonical ? null : $username;
    } else {
        $this->usernameCanonical = null;
        $this->username = null;
    }
}
No proxy objects
/** @ODMReferenceOne(targetDocument="User") */
private $user;
public function getUser()
{
    if ($this->userId && !$this->user) {
        throw new UninitializedReferenceException('user');
    }

    return $this->user;
}
Mapping Layers
What is a mapping layer?
A mapping layer is thin
Thin controller, fat model…
Is Symfony an MVC framework?
Symfony is an HTTP framework
Application Land
  Controller
  HTTP Land
The controller maps from
HTTP-land to application-land.
What about the model?
public function registerAction()
{
    // ...
    $user->sendWelcomeEmail();
    // ...
}
public function registerAction()
{
    // ...
    $mailer->sendWelcomeEmail($user);
    // ...
}
Persistence Land
    Model
Application Land
The model maps from
application-land to persistence-land.
Persistence Land
     Model
Application Land
  Controller
  HTTP Land
Who lives in application land?
Thin controller, thin model…
      Fat service layer!
Application Events
Use lots of them
That happened.
/** @DIObserve("user.username_change") */
public function onUsernameChange(UserEvent $event)
{
    $user = $event->getUser();
    $dm   = $event->getDocumentManager();

    $dm->getRepository('Model:Widget')
       ->updateDenormalizedUsernames($user);
}
Unit of Work
public function onFlush(OnFlushEventArgs $event)
{
    $dm = $event->getDocumentManager();
    $uow = $dm->getUnitOfWork();

    foreach ($uow->getIdentityMap() as $class => $docs) {
        if (self::checkClass('OptiGrabModelUser', $class)) {
            foreach ($docs as $doc) {
                $this->processUserFlush($dm, $doc);
            }
        } elseif (self::checkClass('OptiGrabModelWidget', $class)) {
            foreach ($docs as $doc) {
                $this->processWidgetFlush($dm, $doc);
            }
        }
    }
}
private function processUserFlush(DocumentManager $dm, User $user)
{
    $uow     = $dm->getUnitOfWork();
    $meta    = $dm->getClassMetadata('Model:User');
    $changes = $uow->getDocumentChangeSet($user);

    if (isset($changes['id'][1])) {
        $this->dispatcher->dispatch(UserEvents::CREATE, new UserEvent($dm, $user));
    }

    if (isset($changes['usernameCanonical'][0]) && null !== $changes['usernameCanonical'][0]) {
        $this->dispatcher->dispatch(UserEvents::USERNAME_CHANGE, new UserEvent($dm, $user));
    }

    if ($followedUsers = $meta->getFieldValue($user, 'followedUsers')) {
        foreach ($followedUsers->getInsertDiff() as $otherUser) {
            $this->dispatcher->dispatch(
                UserEvents::FOLLOW_USER,
                new UserUserEvent($dm, $user, $otherUser)
            );
        }

        foreach ($followedUsers->getDeleteDiff() as $otherUser) {
            // ...
        }
    }
}
/** @DIObserve("user.create") */
public function onUserCreate(UserEvent $event)
{
    $user = $event->getUser();

    $activity = new Activity();
    $activity->setActor($user);
    $activity->setVerb('register');
    $activity->setCreatedAt($user->getCreatedAt());

    $this->dm->persist($activity);
}
/** @DIObserve("user.create") */
public function onUserCreate(UserEvent $event)
{
    $dm   = $event->getDocumentManager();
    $user = $event->getUser();

    $widget = new Widget();
    $widget->setUser($user);

    $dm->persist($widget);

    // manually notify the event
    $event->getDispatcher()->dispatch(
        WidgetEvents::CREATE,
        new WidgetEvent($dm, $widget)
    );
}
/** @DIObserve("user.follow_user") */
public function onFollowUser(UserUserEvent $event)
{
    $event->getUser()
          ->getStats()
          ->incrementFollowedUsers(1);
    $event->getOtherUser()
          ->getStats()
          ->incrementFollowers(1);
}
Two event classes per model
• @MainBundleUserEvents: encapsulates event name constants
  such as UserEvents::CREATE and
  UserEvents::CHANGE_USERNAME

• @MainBundleEventUserEvent: base event object, accepts
  $dm and $user arguments

• @MainBundleWidgetEvents…
• @MainBundleEventWidgetEvent…
$event = new UserEvent($dm, $user);
$dispatcher->dispatch(UserEvents::CREATE, $event);
Delegate work to clean, concise,
 single-purpose event listeners
Contextual Configuration
Save your future self a headache
# @MainBundle/Resources/config/widget.yml
services:
    widget_twiddler:
        class: OptiGrabBundleMainBundleWidgetTwiddler
        arguments:
            - @event_dispatcher
            - @?logger
/** @DIService("widget_twiddler") */
class Twiddler
{
    /** @DIInjectParams */
    public function __construct(
        EventDispatcherInterface $dispatcher,
        LoggerInterface $logger = null)
    {
        // ...
    }
}
services:
    # aliases for auto-wiring
    container: @service_container
    dm: @doctrine_mongodb.odm.document_manager
    doctrine: @doctrine_mongodb
    dispatcher: @event_dispatcher
    security: @security.context
JMSDiExtraBundle
require.js
<script src="{{ asset('js/lib/require.js') }}"></script>
<script>
require.config({
    baseUrl: "{{ asset('js') }}",
    paths: {
         "jquery": "//ajax.googleapis.com/.../jquery.min",
         "underscore": "lib/underscore",
         "backbone": "lib/backbone"
    },
    shim: {
         "jquery": { exports: "jQuery" },
         "underscore": { exports: "_" },
         "backbone": {
             deps: [ "jquery", "underscore" ],
             exports: "Backbone"
         }
    }
})
require([ "main" ])
</script>
// web/js/model/user.js
define(
    [ "underscore", "backbone" ],
    function(_, Backbone) {
        var tmpl = _.template("<%- first %> <%- last %>")
        return Backbone.Model.extend({
            name: function() {
                return tmpl({
                    first: this.get("first_name"),
                    last: this.get("last_name")
                })
            }
        })
    }
)
{% block head %}
<script>
require(
    [ "view/user", "model/user" ],
    function(UserView, User) {
         var view = new UserView({
             model: new User({{ user|serialize|raw }}),
             el: document.getElementById("user")
         })
    }
)
</script>
{% endblock %}
Dependencies


•   model: backbone, underscore

•   view: backbone, jquery

•   template: model, view
{% javascripts
    "js/lib/jquery.js" "js/lib/underscore.js"
    "js/lib/backbone.js" "js/model/user.js"
    "js/view/user.js"
    filter="?uglifyjs2" output="js/packed/user.js" %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

<script>
var view = new UserView({
    model: new User({{ user|serialize|raw }}),
    el: document.getElementById("user")
})
</script>
Unused dependencies
 naturally slough off
JMSSerializerBundle
{% block head %}
<script>
require(
    [ "view/user", "model/user" ],
    function(UserView, User) {
         var view = new UserView({
             model: new User({{ user|serialize|raw }}),
             el: document.getElementById("user")
         })
    }
)
</script>
{% endblock %}
/** @ExclusionPolicy("ALL") */
class User
{
    private $id;

    /** @Expose */
    private $firstName;

    /** @Expose */
    private $lastName;
}
Miscellaneous
When to create a new bundle
Lots of classes pertaining to
        one feature
{% include 'MainBundle:Account/Widget:sidebar.html.twig' %}
{% include 'AccountBundle:Widget:sidebar.html.twig' %}
Access Control
The Symfony ACL is for
 arbitrary permissions
Encapsulate access logic in
  custom voter classes
/** @DIService(public=false) @DITag("security.voter") */
class WidgetVoter implements VoterInterface
{
    public function supportsAttribute($attribute)
    {
        return 'OWNER' === $attribute;
    }

    public function supportsClass($class)
    {
        return 'OptiGrabModelWidget' === $class
            || is_subclass_of($class, 'OptiGrabModelWidget');
    }

    public function vote(TokenInterface $token, $widget, array $attributes)
    {
        // ...
    }
}
public function vote(TokenInterface $token, $map, array $attributes)
{
    $result = VoterInterface::ACCESS_ABSTAIN;

    if (!$this->supportsClass(get_class($map))) {
        return $result;
    }

    foreach ($attributes as $attribute) {
        if (!$this->supportsAttribute($attribute)) {
            continue;
        }

        $result = VoterInterface::ACCESS_DENIED;
        if ($token->getUser() === $map->getUser()) {
            return VoterInterface::ACCESS_GRANTED;
        }
    }

    return $result;
}
/** @SecureParam(name="widget", permissions="OWNER") */
public function editAction(Widget $widget)
{
    // ...
}
{% if is_granted('OWNER', widget) %}
{# ... #}
{% endif %}
Only mock interfaces
interface FacebookInterface
{
    function getUser();
    function api();
}

/** @DIService("facebook") */
class Facebook extends BaseFacebook implements FacebookInterface
{
    // ...
}
$facebook = $this->getMock('OptiGrabBundleMainBundleFacebookFacebookInterface');
$facebook->expects($this->any())
    ->method('getUser')
    ->will($this->returnValue(123));
Questions?
@kriswallsmith.net



        joind.in/8024


      Thank You!

More Related Content

What's hot

Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingSamuel ROZE
 
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
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form componentSamuel ROZE
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
DOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryDOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryRemy Sharp
 
An Introduction to Jquery
An Introduction to JqueryAn Introduction to Jquery
An Introduction to JqueryPhil Reither
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery ApplicationsRebecca Murphey
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code OrganizationRebecca Murphey
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
amsterdamjs - jQuery 1.5
amsterdamjs - jQuery 1.5amsterdamjs - jQuery 1.5
amsterdamjs - jQuery 1.5mennovanslooten
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuerysergioafp
 
jQuery 1.7 Events
jQuery 1.7 EventsjQuery 1.7 Events
jQuery 1.7 Eventsdmethvin
 
Beyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsBeyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsRebecca Murphey
 
Jqeury ajax plugins
Jqeury ajax pluginsJqeury ajax plugins
Jqeury ajax pluginsInbal Geffen
 
Kick start with j query
Kick start with j queryKick start with j query
Kick start with j queryMd. Ziaul Haq
 

What's hot (20)

Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
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
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
Bacbkone js
Bacbkone jsBacbkone js
Bacbkone js
 
jQuery: Events, Animation, Ajax
jQuery: Events, Animation, AjaxjQuery: Events, Animation, Ajax
jQuery: Events, Animation, Ajax
 
DOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryDOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQuery
 
An Introduction to Jquery
An Introduction to JqueryAn Introduction to Jquery
An Introduction to Jquery
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code Organization
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
amsterdamjs - jQuery 1.5
amsterdamjs - jQuery 1.5amsterdamjs - jQuery 1.5
amsterdamjs - jQuery 1.5
 
Special Events
Special EventsSpecial Events
Special Events
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuery
 
jQuery 1.7 Events
jQuery 1.7 EventsjQuery 1.7 Events
jQuery 1.7 Events
 
Beyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsBeyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS Apps
 
Jqeury ajax plugins
Jqeury ajax pluginsJqeury ajax plugins
Jqeury ajax plugins
 
Dojo Confessions
Dojo ConfessionsDojo Confessions
Dojo Confessions
 
Jquery Fundamentals
Jquery FundamentalsJquery Fundamentals
Jquery Fundamentals
 
Kick start with j query
Kick start with j queryKick start with j query
Kick start with j query
 

Similar to How Kris Writes Symfony Apps

Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleHugo Hamon
 
ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperGary Hockin
 
Drupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency InjectionDrupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency InjectionPhilip Norton
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Leonardo Proietti
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsMichael Peacock
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Jakub Zalas
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016Kacper Gunia
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperJonathan Wage
 
Et si on en finissait avec CRUD ?
Et si on en finissait avec CRUD ?Et si on en finissait avec CRUD ?
Et si on en finissait avec CRUD ?Julien Vinber
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using CodeceptionJeroen van Dijk
 
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
 
Version Control with Puppet
Version Control with PuppetVersion Control with Puppet
Version Control with PuppetPuppet
 
PuppetCamp SEA 1 - Version Control with Puppet
PuppetCamp SEA 1 - Version Control with PuppetPuppetCamp SEA 1 - Version Control with Puppet
PuppetCamp SEA 1 - Version Control with PuppetWalter Heck
 

Similar to How Kris Writes Symfony Apps (20)

Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 Developer
 
BEAR DI
BEAR DIBEAR DI
BEAR DI
 
Drupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency InjectionDrupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency Injection
 
Symfony2 your way
Symfony2   your waySymfony2   your way
Symfony2 your way
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
Phpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friendsPhpne august-2012-symfony-components-friends
Phpne august-2012-symfony-components-friends
 
Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12Symfony components in the wild, PHPNW12
Symfony components in the wild, PHPNW12
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
 
Doctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document MapperDoctrine MongoDB Object Document Mapper
Doctrine MongoDB Object Document Mapper
 
Et si on en finissait avec CRUD ?
Et si on en finissait avec CRUD ?Et si on en finissait avec CRUD ?
Et si on en finissait avec CRUD ?
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
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
 
Version Control with Puppet
Version Control with PuppetVersion Control with Puppet
Version Control with Puppet
 
PuppetCamp SEA 1 - Version Control with Puppet
PuppetCamp SEA 1 - Version Control with PuppetPuppetCamp SEA 1 - Version Control with Puppet
PuppetCamp SEA 1 - Version Control with Puppet
 

More from Kris Wallsmith

Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)Kris Wallsmith
 
Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Kris Wallsmith
 
Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3Kris Wallsmith
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Kris Wallsmith
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony TechniquesKris Wallsmith
 
A Practical Introduction to Symfony2
A Practical Introduction to Symfony2A Practical Introduction to Symfony2
A Practical Introduction to Symfony2Kris Wallsmith
 

More from Kris Wallsmith (11)

The View From Inside
The View From InsideThe View From Inside
The View From Inside
 
Assetic (Zendcon)
Assetic (Zendcon)Assetic (Zendcon)
Assetic (Zendcon)
 
Assetic (OSCON)
Assetic (OSCON)Assetic (OSCON)
Assetic (OSCON)
 
Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)
 
Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)
 
Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
A Practical Introduction to Symfony2
A Practical Introduction to Symfony2A Practical Introduction to Symfony2
A Practical Introduction to Symfony2
 
Symfony 2
Symfony 2Symfony 2
Symfony 2
 
Symfony in the Cloud
Symfony in the CloudSymfony in the Cloud
Symfony in the Cloud
 

Recently uploaded

costume and set research powerpoint presentation
costume and set research powerpoint presentationcostume and set research powerpoint presentation
costume and set research powerpoint presentationphoebematthew05
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsHyundai Motor Group
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Neo4j
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxnull - The Open Security Community
 

Recently uploaded (20)

costume and set research powerpoint presentation
costume and set research powerpoint presentationcostume and set research powerpoint presentation
costume and set research powerpoint presentation
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
 

How Kris Writes Symfony Apps