SlideShare a Scribd company logo
Sonata Project
    AdminBundle
Who am I ?

• Thomas Rabaix
• Speaker at Symfony Live Conferences
• Author of many symfony1 plugins
• lead developer of the sonata project
• Working at Ekino, a french web agency
Talk

• Sonata Project presentation
• Quick Tour
• Under the hood
• Customize / Advanced features
• Conclusion
Sonata Project
• A not so young project
• Based on symfony1 plugins
• Recoded with the best practices of
  Symfony2
• Built on top on very strong and powerful
  framework
Sonata Project

• An ecommerce toolbox
• How :
   • avoiding reinvented the wheel
   • contribution to the community
   • built on top of a strong framework
Sonata’s bundles
• PageBundle : a page manager with block
  as service and strong caching mechanism
• MediaBundle : a media manager on
  steroid, you don’t have to worry about
  managing files or videos
• UserBundle, IntlBundle, etc ...
• AdminBundle : A backend generator
http://sonata-project.org
AdminBundle
   why ?
• No admin generator for Symfony 2.0
• Frustrating by the admin generator provided by
  symfony1

• Admin is not only about Model; but about
  providing a consistent and rich user
  experience for managing data.
Quick Tour




 http://www.flickr.com/photos/38104873@N03/4559985343/
Admin Class

•   An metadata description
    of CRUD operations

•   No code generation

•   Based on Symfony
    services + Sonata Admin
    services
Dashboard



        Actions
       shortcut

       Group +
        Model
Dashboard
Register admin class with the tag “sonata.admin”
  And admin will appears into the dashboard

1.         <services>
2.             <service id="sonata.news.admin.comment" class="%sonata.news.admin.comment.class%">
3.                 <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="comment"/>
4.                 <argument />
5.                 <argument>%sonata.news.admin.comment.entity%</argument>
6.                 <argument>%sonata.news.admin.comment.controller%</argument>
7.             </service>
8.  
9.                 <service id="sonata.news.admin.post" class="%sonata.news.admin.post.class%">
10.                    <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="post"/>
11.                    <argument />
12.                    <argument>%sonata.news.admin.post.entity%</argument>
13.                    <argument>%sonata.news.admin.post.controller%</argument>
14.                </service>
15. 
16.            <service id="sonata.news.admin.tag" class="%sonata.news.admin.tag.class%">
17.                <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="tag"/>
18.                <argument />
19.                <argument>%sonata.news.admin.tag.entity%</argument>
20.                <argument>%sonata.news.admin.tag.controller%</argument>
21.            </service>
22.        </services>
Breadcrumb             List Action
                                          Model
                                          Actions




              Batch
             Actions

                                Filters
List Action




 fields, custom        fields, type
templates, type     detection, based
   detection           on Form
                     Component
List Action
1.     protected function configureDatagridFilters(DatagridMapper $datagridMapper) {
2.                 $datagridMapper
3.  
4.  
5.  
        
        
        
            
            
            
                
                
                
                       ->add('name')
                       ->add('providerReference')
                       ->add('enabled')
                                                    field guesser
6.                     ->add('context')
7.                 ;
8.                 $providers = array();
9.  
10.          foreach($this->pool->getProviderNamesByContext('default') as $name) {
11.              $providers[$name] = $name;
12.          }
13. 
14.                $datagridMapper->add('providerName', 'doctrine_orm_choice', array(
15.                    'field_options'=> array(
16.                        'choices' => $providers,
17. 
18. 
19. 
        
        
        
            
            
            
                
                
                
                           'required' => false,
                           'multiple' => false,
                           'expanded' => false,
                                                                                            custom filter
20.                    ),
21.                    'field_type'=> 'choice',
22.                ));
23.        }


                                                              edit link
24. 
25.        protected function configureListFields(ListMapper $listMapper) {
26.            $listMapper
27.                ->addIdentifier('id')
28.                ->add('image', 'string', array('template' => 'SonataMediaBundle:MediaAdmin:list_image.html.twig'))
29.                ->add('custom', 'string', array('template' => 'SonataMediaBundle:MediaAdmin:list_custom.html.twig'))
30.                ->add('enabled')
31. 
32. 
        
        
                   ->add('_action', 'actions', array(
                       'actions' => array(                                              custom template
33.                        'view' => array(),
34.                        'edit' => array(),
35.                    )
36.                ))
37. 
38. 
        
        
               ;
           }                                                     row’s actions
Edit/Create Form




side menu


                      Field Group
Edit/Create Form


                        Helper message


                                add relation

                              hide fields




save options          delete action
Edit/Create Form
1.          protected function configureFormFields(FormMapper $formMapper)
2.          {
3.              $templates = array();
4.              foreach ($this->cmsManager->getPageManager()->getTemplates() as $code => $template) {
5.                  $templates[$code] = $template->getName();
6.              }

                                                                                             create group
7.  
8.                  $formMapper
9.                      ->with($this->trans('form_page.group_main_label'))
10.                         ->add('name')
11.                         ->add('enabled', null, array('required' => false))
12.                         ->add('position')
13.                         ->add('templateCode', 'choice', array('required' => true, 'choices' => $templates))
14.                         ->add('parent', 'sonata_page_selector', array(
15.                             'page'          => $this->getSubject() ?: null,
16.                             'model_manager' => $this->getModelManager(),
17.                             'class'         => $this->getClass(),
18.  
19.  
         
         
             
             
                 
                 
                                'filter_choice' => array('hierarchy' => 'root'),
                                'required'      => false                                Form Component
20.                         ))
21.                     ->end()
22.                 ;
23.  
24.                 $formMapper
25.                     ->with($this->trans('form_page.group_seo_label'), array('collapsed' => true))
26.                         ->add('metaKeyword', 'textarea', array('required' => false))
27.                         ->add('metaDescription', 'textarea', array('required' => false))
28.                     ->end()

                                                                                                             group options
29.                 ;
30.  
31.                 $formMapper
32.                     ->with($this->trans('form_page.group_advanced_label'), array('collapsed' => true))
33.                         ->add('javascript', null,  array('required' => false))
34.                         ->add('stylesheet', null, array('required' => false))
35.                         ->add('rawHeaders', null, array('required' => false))
36.                     ->end()
37.                 ;
38.  
39.             $formMapper->setHelps(array(
40.                 'name' => $this->trans('help_page_name')
41.  
42.  
         
         
                ));
            }
                                                                               Define help messages
Other Features

• Permissions management
• Flash messages
• Nested Admin
• Command lines utilities
• Translated into more than10 languages
Quick Tour Summary
• Dashboard
• Consistent Interface across bundles
• Easy to configure, but powerful for
  advanced users

• Advanced features
• Inspired from the django admin module
  (user interactions)
Under the hood




   http://www.flickr.com/photos/52251564@N08/5937620090
Admin Class Dependencies

                    Security              Builder




                                                      Sonata Admin Bundle
                                             List
Symfony Framework




                    Translator
                                           Datagrid

                                 Admin      Show
                     Routing
                                  class     Form


                    Validator
                                           Model
                                          Manager
                      Form
Security

• Based on the SecurityHandlerInterface
• 2 built-in implementations
   • NoopSecurityHandler : use the
      Symfony’s firewall
   • AclSecurityHandler : based on ACL
      - Advanced users only
Security
          • Admin Usage
1.     protected function configureFormFields(FormMapper $formMapper)
2.     {
3.         $formMapper
4.             ->with('General')
5.                 ->add('enabled', null, array('required' => false))
6.                 ->add('author', 'sonata_type_model', array(), array('edit' => 'list'))
7.                 ->add('title')
8.             ->end()
9.         ;
10.       
11.        if (!$this->isGranted('CREATE')) {
12.            // do specific code if the user cannot create a new object
13.        }
14.    }




          • Template Usage
                        1. {% if admin.isGranted('CREATE') %}
                        2.    // DO YOUR STUFF
                        3. {% endif %}
Security : ACL
• Required to have a custom external bundle to
  manage permissions and user : see FOS/
  UserBundle and Sonata/UserBundle
• Built on top of a custom MaskBuilder (basic
  roles : List,View, Edit, Create, Delete)

• Command lines :
   • php app/console init:acl
   • php app/console sonata:admin:setup-acl
Routing
          • Definition set from the Admin class
          • Can be tweaked by the configureRoute
        1.         /**
        2.          * @param SonataAdminBundleRouteRouteCollection $collection
        3.          * @return void
        4.          */
        5.         protected function configureRoutes(RouteCollection $collection)
        6.         {
        7.             $collection->add('snapshots');                                Add the id parameter
        8.             $collection->remove('edit');
        9.
        10.          $collection->add('test', $this->getRouterIdParameter().'/test');
        11.      }




          • Template Usage
1. <a href="{{ admin.generateUrl('view', { 'id' : media.id, 'format' : 'reference'}) }}">reference</a>
2. <a href="{{ admin.generateObjectUrl(media, 'view', {'format' : 'reference'}) }}">reference</a>
Admin Model Manager
• Persistency layer abstraction for Admin Bundle.
• All Persistencies actions are done in the Model
  Manager
    • delete, query, pagination, etc..
    • form type manipulation delegation
• For now only Doctrine ORM (Propel and
  Doctrine ODM are work in progress by external
  contributors)
Admin Model Manager

• Some bundle provides custom Model Manager

• You can define your own proxy Admin Model
  Manager to reuse the Bundle Model Manager


• Use case : Sonata Media Bundle (delete action)
Model Manager                              1. class AdminModelManager extends ModelManager {
                                                                           2.     protected $manager;

Sonata Media Bundle                                                        3.  
                                                                           4.     public function __construct($entityManager, $manager) {
                                                                           5.         parent::__construct($entityManager);
  2 Model Managers :                                                       6.         $this->manager = $manager;
     - entity : deal with deletion and so on ...                           7.     }
                                                                           8.  
     - admin : proxy some methods to the entity                            9.     public function delete($object) {
                                                                           10.         $this->manager->delete($object);
                                                                           11.     }
  How to ?                                                                 12. }

    1. create a dedicated Admin Model Manager                              1. class BundleMediaManager extends AbstractMediaManager {  
    2. create a Bundle Model Manager                                       1.      public function delete(MediaInterface $media) {
                                                                           2.         $this->pool
    3. redefine only required methods                                       3.              ->getProvider($media->getProviderName())
    4. define services                                                      4.              ->preRemove($media);
                                                                           5.         $this->em->remove($media);
                                                                           6.         $this->em->flush();

                                    Delete thumbnails
                                                                           7.  
                                                                           8.         $this->pool
                                                                           9.              ->getProvider($media->getProviderName())
                                                                           10.             ->postRemove($media);
                                                                           11.         $this->em->flush();
                                                                           12.     }
                                                                           2. }

   1.              <service id="sonata.media.admin.media" class="SonataMediaBundleEntityBundleMediaManager">
   2.                    <tag name="sonata.admin" manager_type="orm" group="sonata_media" label="media"/>
   3.                    <argument />
   4.                    <argument>%sonata.media.admin.media.entity%</argument>
   5.                    <argument>%sonata.media.admin.media.controller%</argument>
   6.  
   7.                      <call method="setModelManager">
   8.                          <argument type="service" id="sonata.media.admin.media.manager" />
   9.                      </call>
   10.                 </service>
   11.  
   12.                 <service id="sonata.media.admin.media.manager" class="SonataMediaBundleAdminManagerDoctrineModelManager">
   13.                     <argument type="service" id="doctrine.orm.default_entity_manager" />
   14.                     <argument type="service" id="sonata.media.manager.media" />
   15.                 </service>
Translator
• 2 catalogues
   • SonataAdminBundle used to translate
     shared messages
   • messages used to translate current Admin
      1.                   $formMapper
      2.                       ->with($this->trans('form_page.group_main_label'))
      3.                           ->add('name')
      4.                       ->end()
      5.                   ;



• Can be set by updating the translationDomain
  property
Form

• Originally built on the the first implementation
• Too bad ..... major refactoring 3 months later
   • Some admin features has been removed or still
      broken :(
   • But the new form implementation is pretty
      awesome .... once you know how to use it.
Form Mapper

• Interact with the FormMapper
• Proxy class between the Admin Class and the
  Symfony Form Component
• Act as the Symfony FormBuilder component
• You can use your custom field types
Core types


                                   Form Type                                                Model Manager Types

                                                                                                Form Types



• AdminType : used to embedded form from
  another Admin class
• CollectionType : use by one-to-many association
• ModelType : select choice (like EntityType)
• ModelReferenceType : handle an model id
• ImmutableArrayType : specify a form type per
  array element
  1.                   $formMapper->add('settings', 'sonata_type_immutable_array', array(
  2.                       'keys' => array(
  3.                           array('layout', 'textarea', array()),
  4.                           array('action', 'text', array()),
  5.                           array('parameters', 'text', array()),
  6.                       )
  7.                   ));
Validator


• Assert rules can be defined in the validation.
  [xml|yml] files (validator component)
  nothing new ...




• Conditional Validation
Conditional Validation

• Not mandatory, but allow to add inline validation
  a runtime.
    • ex : check only if a value is set
• Validate method inside the admin class
• Interact with a new ErrorElement object
• The ErrorElement is just a validator service based
  on the Validator Component
Validator
1.     /**
2.        * @param SonataAdminBundleValidatorErrorElement $errorElement
3.        * @param $object
4.        * @return void
5.        */
6.       public function validate(ErrorElement $errorElement, $object)
7.       {
8.           $errorElement
9.               ->with('name')
10.                  ->assertMaxLength(array('limit' => 32))
11.              ->end()
12.          ;

                                                      symfony constraint
13.         
14.          if ($object->getFoo()) {
15.              $errorElement
16.                  ->with('test')
17.                      ->addViolation('my_message')
18.                  ->end()
19.              ;
20.          }
21.      }                                                  custom error
Menu

• Based on KnpMenu lib

• Used for Breadcrumb and Side Menu

• No magic for sidemenu, need to code your
  own menu per admin (if required)
Menu

1. class PostAdmin extends Admin
2. {
3.     protected function configureSideMenu(MenuItemInterface $menu, $action, Admin $childAdmin = null)
4.     {
5.         if (!$childAdmin && !in_array($action, array('edit'))) {
6.             return;
7.         }
8.  
9.         $admin = $this->isChild() ? $this->getParent() : $this;
10. 
11.        $id = $admin->getRequest()->get('id');
12. 
13.        $menu->addChild(
14.            $this->trans('view_post'),
15.            array('uri' => $admin->generateUrl('edit', array('id' => $id)))
16.        );
17. 
18.        $menu->addChild(
19.            $this->trans('link_view_comment'),
20.            array('uri' => $admin->generateUrl('sonata.news.admin.comment.list', array('id' => $id)))
21.        );
22.    }
23.}
customize




http://www.flickr.com/photos/7552532@N07/449769140/
Form : one-to-many
• Type : sonata_type_collection (CollectionType)
• Form Option
       by_reference => false
• Sonata Options
     • edit : standard / inline
     • inline : table | list
     • position : field name (if exists)
Form : many-to-many


• Type : sonata_type_model (ModelType)
• Sonata Options
     • no options
Form : many-to-one
• Type : sonata_type_model (ModelType)
• Form Options :
     • expanded : true|false
• Sonata Options
     • edit : standard (select box) / list (popup)
     • link_parameters : add extra link parameters to
        the link
Form : many-to-one

• Type : sonata_type_admin (AdminType)
• Embed an admin form for an entity into the current
  admin
• Sonata Options
     • no option!
Form Theme


• Based on the form theme mechanism
• Define a getFormTheme()
   • a set of form templates
   • allows to customize an admin form
Form Theme
•   Just define a custom block with the correct name ...

•   How to know the block name ... ?




•   Provide a custom `block_name` option

•   or use a defined built-in pattern :
    admin_service_id_[type]_[widget|label|errors|row]
    admin_service_id_[fieldName]_[widget|label|errors|row]
Form block
  •       Theme definition

1.        protected $formTheme = array(
2.            'SonataAdminBundle:Form:form_admin_fields.html.twig',
3.            'SonataNewsBundle:Form:form.html.twig'
4.        );


  •       Block definition

1.{% block sonata_user_admin_user_credentialsExpired_text_widget %}
2.   PUT HERE THE WIDGET ...
3.{% endblock %}



  •       Et voila!
Custom List Template
 •   Field definition
1.->add('custom', 'string', array('template' =>
  'SonataMediaBundle:MediaAdmin:list_custom.html.twig'))

 •   Template
1.{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
2. 
3.{% block field %}
4.    <div>
5.        <strong>{{ object.name }}</strong> <br />
6.        {{ object.providerName|trans({}, 'SonataMediaBundle') }}:
  {{ object.width }}x{{ object.height }} <br />
7.    </div>
8.{% endblock %}


 •   Et voila!
CRUD Controller
• Use the admin class to generate required
  objects
• contains create, edit, update, delete and
  batch actions
• can be extended to change some logic
• use case : Sonata Media Bundle
Custom CRUD Controller
1. namespace SonataMediaBundleController;
2.  
3. use SonataAdminBundleControllerCRUDController as Controller; Grant check
4. use SymfonyComponentSecurityCoreExceptionAccessDeniedException;
5.  
6. class MediaAdminController extends Controller {
7.     public function createAction() {
8.         if (false === $this->admin->isGranted('CREATE')) {
9.             throw new AccessDeniedException();
10.        }
11. 
12.        $parameters = $this->admin->getPersistentParameters();
13. 
14.        if (!$parameters['provider']) {
                                                         Custom Template
15.            return $this-
   >render('SonataMediaBundle:MediaAdmin:select_provider.html.twig', array(
16.                'providers'     => $this->get('sonata.media.pool')
17.                                         ->getProvidersByContext($this->get('request')-
   >get('context', 'default')),
18.                'base_template' => $this->getBaseTemplate(),
19.                'admin'         => $this->admin,
20.                'action'        => 'create'
21.            ));
22.        }
23. 
24.        return parent::createAction();
25.    }
26.}

                                   Parent action
Nested Admin

• clean url : /admin/sonata/news/post/1/comment/list
• reuse other admin definition
• autofilter with the targeted elements
• only work on one level
• You don’t need to know routing name, as long as
  you use the admin class
Nested Admin
1. class CommentAdmin extends Admin
2. {
3.     protected $parentAssociationMapping = 'post';
4.  
5.     protected function configureFormFields(FormMapper $formMapper)
6.     {
7.         if(!$this->isChild()) {
8.             $formMapper->add('post', 'sonata_type_model', array(), array('edit' => 'list'));
9.         }
10.  
11.         $formMapper
12.             ->add('name')
13.             ->add('email')
14.             ->add('url', null, array('required' => false))
15.             ->add('message')
16.             ->add('status', 'choice', array('choices' => Comment::getStatusList(), 'expanded' => true, 'multiple' => false))
17.         ;
18.     }                                                                                     Display custom
                                                                                                field if the
19.  
20.     protected function configureListFields(ListMapper $listMapper)
21.     {
22.         $listMapper
23.             ->addIdentifier('name')                                                      current admin is
                                                                                              nested or not
24.             ->add('getStatusCode', 'text', array('label' => 'status_code', 'sortable' => 'status'))
25.         ;
26.  
27.         if (!$this->isChild()) {
28.             $listMapper->add('post');
29.         }
30.  
31.         $listMapper
32.             ->add('email')
33.             ->add('url')
34.             ->add('message');
35.     }
36. }
Nested Admin




Comment Admin   Nested Comment Admin
Admin Extension

• Allow to add extra feature or redefine some field to
  the admin
• Good entry point if you extends some entities
• Add extension must implement the
  AdminExtensionInterface and define as
  service with the sonata.admin.extension
Debug
•   Check out external configurations

      •   validator configuration

      •   doctrine schema definition

      •   use firebug to check Ajax errors (missing toString method or
          type hinting)

•   Get information from the Admin command tools

      •   sonata:admin:list

      •   sonata:admin:explain
sonata:admin:list




 service name can be used to
explain the admin configuration
sonata:admin:explain
conclusion




 http://www.flickr.com/photos/rv-bordeaux/5909437215/
small numbers
•   First commit in
    november 2010

•   650 commits

•   49 Contributors!

•   14 Translations



           Thanks!
What’s next ?
• Version 1
   • Stabilize the AdminBundle
   • Add more tests... : 56 tests, 145
      assertions
• Version 1.1
   • Add missing features from the original
      form factoring
    • Add more layer persistency
      (contribution)
Resources


• http://sonata-project.org
• irc : #symfony
• twitter : @sonataproject
• symfony google groups
Questions




 http://www.flickr.com/photos/colinkinner/2200500024/

More Related Content

What's hot

Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
Samuel 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 patterns
Samuel ROZE
 
Twig tips and tricks
Twig tips and tricksTwig tips and tricks
Twig tips and tricks
Javier Eguiluz
 
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
Leonardo Proietti
 
First Steps in Drupal Code Driven Development
First Steps in Drupal Code Driven DevelopmentFirst Steps in Drupal Code Driven Development
First Steps in Drupal Code Driven Development
Nuvole
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010Fabien Potencier
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
Konstantin Kudryashov
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
Hugo Hamon
 
Apostrophe
ApostropheApostrophe
Apostrophe
tompunk
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
Vic Metcalfe
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
Jorn Oomen
 
Keeping It Small with Slim
Keeping It Small with SlimKeeping It Small with Slim
Keeping It Small with Slim
Raven Tools
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 
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
Aleix Vergés
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
Mariusz Kozłowski
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
Lars Jankowfsky
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
Andréia Bohner
 

What's hot (20)

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
 
Twig tips and tricks
Twig tips and tricksTwig tips and tricks
Twig tips and tricks
 
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
 
First Steps in Drupal Code Driven Development
First Steps in Drupal Code Driven DevelopmentFirst Steps in Drupal Code Driven Development
First Steps in Drupal Code Driven Development
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Apostrophe
ApostropheApostrophe
Apostrophe
 
Symfony2 - OSIDays 2010
Symfony2 - OSIDays 2010Symfony2 - OSIDays 2010
Symfony2 - OSIDays 2010
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Keeping It Small with Slim
Keeping It Small with SlimKeeping It Small with Slim
Keeping It Small with Slim
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
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 tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
PhpBB meets Symfony2
PhpBB meets Symfony2PhpBB meets Symfony2
PhpBB meets Symfony2
 

Viewers also liked

Easy backends with Symfony2 and the Sonata Admin bundle
Easy backends with Symfony2 and the Sonata Admin bundleEasy backends with Symfony2 and the Sonata Admin bundle
Easy backends with Symfony2 and the Sonata Admin bundle
phpassionate
 
Back-end with SonataAdminBundle (and Symfony2, of course...)
Back-end with SonataAdminBundle (and Symfony2, of course...)Back-end with SonataAdminBundle (and Symfony2, of course...)
Back-end with SonataAdminBundle (and Symfony2, of course...)
Andrea Delfino
 
Have you played this Symfony? Why Symfony is great choice for Web development
Have you played this Symfony? Why Symfony is great choice for Web developmentHave you played this Symfony? Why Symfony is great choice for Web development
Have you played this Symfony? Why Symfony is great choice for Web development
Mike Taylor
 
Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)
Javier Eguiluz
 
8 Simple Ways to Hack Your Joomla
8 Simple Ways to Hack Your Joomla8 Simple Ways to Hack Your Joomla
8 Simple Ways to Hack Your Joomla
SiteGround.com
 
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years laterSymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
Haehnchen
 
Symfony Best Practices
Symfony Best PracticesSymfony Best Practices
Symfony Best Practices
Baptiste Donaux
 
30 Symfony Best Practices
30 Symfony Best Practices30 Symfony Best Practices
30 Symfony Best Practices
Nicolas Perriault
 
(株)農業情報設計社の取り組みついて
(株)農業情報設計社の取り組みついて(株)農業情報設計社の取り組みついて
(株)農業情報設計社の取り組みついて
Shinya TODORI
 
Semester 7 - Industrial Training (Final Presentation)
Semester 7 - Industrial Training (Final Presentation) Semester 7 - Industrial Training (Final Presentation)
Semester 7 - Industrial Training (Final Presentation)
Nur Aisyah Khor
 
IoTt時代のERPに求められる条件とは2017Mar.
IoTt時代のERPに求められる条件とは2017Mar.IoTt時代のERPに求められる条件とは2017Mar.
IoTt時代のERPに求められる条件とは2017Mar.
Keiichiro Nabeno
 
Industrial training presentation
Industrial training presentationIndustrial training presentation
Industrial training presentation
isamhasbi
 
Contoh Presentation Latihan Industri (PIS)
Contoh Presentation Latihan Industri (PIS)Contoh Presentation Latihan Industri (PIS)
Contoh Presentation Latihan Industri (PIS)
Syafwan Laili
 
Webの仕組みとプログラミング言語
Webの仕組みとプログラミング言語Webの仕組みとプログラミング言語
Webの仕組みとプログラミング言語
Yossy Taka
 
Website people-our-workplace
Website people-our-workplaceWebsite people-our-workplace
Website people-our-workplace
Sonata Software
 
MySQLerの7つ道具 plus
MySQLerの7つ道具 plusMySQLerの7つ道具 plus
MySQLerの7つ道具 plus
yoku0825
 
Io tプラットフォーム主要ベンダ相関図2017mar.
Io tプラットフォーム主要ベンダ相関図2017mar.Io tプラットフォーム主要ベンダ相関図2017mar.
Io tプラットフォーム主要ベンダ相関図2017mar.
Keiichiro Nabeno
 
Contoh Laporan Latihan Industri (FULL)
Contoh Laporan Latihan Industri (FULL)Contoh Laporan Latihan Industri (FULL)
Contoh Laporan Latihan Industri (FULL)
Rizalshah Zulkifli
 
Social Media Workshop at Jordan University
Social Media Workshop at Jordan UniversitySocial Media Workshop at Jordan University
Social Media Workshop at Jordan University
DigiArabs
 

Viewers also liked (20)

Easy backends with Symfony2 and the Sonata Admin bundle
Easy backends with Symfony2 and the Sonata Admin bundleEasy backends with Symfony2 and the Sonata Admin bundle
Easy backends with Symfony2 and the Sonata Admin bundle
 
Back-end with SonataAdminBundle (and Symfony2, of course...)
Back-end with SonataAdminBundle (and Symfony2, of course...)Back-end with SonataAdminBundle (and Symfony2, of course...)
Back-end with SonataAdminBundle (and Symfony2, of course...)
 
Have you played this Symfony? Why Symfony is great choice for Web development
Have you played this Symfony? Why Symfony is great choice for Web developmentHave you played this Symfony? Why Symfony is great choice for Web development
Have you played this Symfony? Why Symfony is great choice for Web development
 
Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)Mastering Twig (DrupalCon Barcelona 2015)
Mastering Twig (DrupalCon Barcelona 2015)
 
Symfony Components
Symfony ComponentsSymfony Components
Symfony Components
 
8 Simple Ways to Hack Your Joomla
8 Simple Ways to Hack Your Joomla8 Simple Ways to Hack Your Joomla
8 Simple Ways to Hack Your Joomla
 
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years laterSymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
 
Symfony Best Practices
Symfony Best PracticesSymfony Best Practices
Symfony Best Practices
 
30 Symfony Best Practices
30 Symfony Best Practices30 Symfony Best Practices
30 Symfony Best Practices
 
(株)農業情報設計社の取り組みついて
(株)農業情報設計社の取り組みついて(株)農業情報設計社の取り組みついて
(株)農業情報設計社の取り組みついて
 
Semester 7 - Industrial Training (Final Presentation)
Semester 7 - Industrial Training (Final Presentation) Semester 7 - Industrial Training (Final Presentation)
Semester 7 - Industrial Training (Final Presentation)
 
IoTt時代のERPに求められる条件とは2017Mar.
IoTt時代のERPに求められる条件とは2017Mar.IoTt時代のERPに求められる条件とは2017Mar.
IoTt時代のERPに求められる条件とは2017Mar.
 
Industrial training presentation
Industrial training presentationIndustrial training presentation
Industrial training presentation
 
Contoh Presentation Latihan Industri (PIS)
Contoh Presentation Latihan Industri (PIS)Contoh Presentation Latihan Industri (PIS)
Contoh Presentation Latihan Industri (PIS)
 
Webの仕組みとプログラミング言語
Webの仕組みとプログラミング言語Webの仕組みとプログラミング言語
Webの仕組みとプログラミング言語
 
Website people-our-workplace
Website people-our-workplaceWebsite people-our-workplace
Website people-our-workplace
 
MySQLerの7つ道具 plus
MySQLerの7つ道具 plusMySQLerの7つ道具 plus
MySQLerの7つ道具 plus
 
Io tプラットフォーム主要ベンダ相関図2017mar.
Io tプラットフォーム主要ベンダ相関図2017mar.Io tプラットフォーム主要ベンダ相関図2017mar.
Io tプラットフォーム主要ベンダ相関図2017mar.
 
Contoh Laporan Latihan Industri (FULL)
Contoh Laporan Latihan Industri (FULL)Contoh Laporan Latihan Industri (FULL)
Contoh Laporan Latihan Industri (FULL)
 
Social Media Workshop at Jordan University
Social Media Workshop at Jordan UniversitySocial Media Workshop at Jordan University
Social Media Workshop at Jordan University
 

Similar to sfDay Cologne - Sonata Admin Bundle

ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 Developer
Gary Hockin
 
Magento Dependency Injection
Magento Dependency InjectionMagento Dependency Injection
Magento Dependency InjectionAnton Kril
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
Lukas Smith
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Optimization in django orm
Optimization in django ormOptimization in django orm
Optimization in django orm
Denys Levchenko
 
WordCamp Praga 2015
WordCamp Praga 2015WordCamp Praga 2015
WordCamp Praga 2015
Tomasz Dziuda
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin Generator
John Cleveley
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
Richard Leland
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
Ryunosuke SATO
 
Digital Mayflower - Data Pilgrimage with the Drupal Migrate Module
Digital Mayflower - Data Pilgrimage with the Drupal Migrate ModuleDigital Mayflower - Data Pilgrimage with the Drupal Migrate Module
Digital Mayflower - Data Pilgrimage with the Drupal Migrate Module
Erich Beyrent
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
Jonathan Wage
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
Rebecca Murphey
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
Nishan Subedi
 
Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Luka Zakrajšek
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf Conference
 
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
jQuery UI Widgets, Drag and Drop, Drupal 7 JavascriptjQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
Darren Mothersele
 

Similar to sfDay Cologne - Sonata Admin Bundle (20)

ZF2 for the ZF1 Developer
ZF2 for the ZF1 DeveloperZF2 for the ZF1 Developer
ZF2 for the ZF1 Developer
 
Magento Dependency Injection
Magento Dependency InjectionMagento Dependency Injection
Magento Dependency Injection
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Optimization in django orm
Optimization in django ormOptimization in django orm
Optimization in django orm
 
WordCamp Praga 2015
WordCamp Praga 2015WordCamp Praga 2015
WordCamp Praga 2015
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin Generator
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Digital Mayflower - Data Pilgrimage with the Drupal Migrate Module
Digital Mayflower - Data Pilgrimage with the Drupal Migrate ModuleDigital Mayflower - Data Pilgrimage with the Drupal Migrate Module
Digital Mayflower - Data Pilgrimage with the Drupal Migrate Module
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
jQuery UI Widgets, Drag and Drop, Drupal 7 JavascriptjQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
 

Recently uploaded

UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
Elena Simperl
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
Ana-Maria Mihalceanu
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Inflectra
 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
Paul Groth
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
Dorra BARTAGUIZ
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
RTTS
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Ramesh Iyer
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Jeffrey Haguewood
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 

Recently uploaded (20)

UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 

sfDay Cologne - Sonata Admin Bundle

  • 1. Sonata Project AdminBundle
  • 2. Who am I ? • Thomas Rabaix • Speaker at Symfony Live Conferences • Author of many symfony1 plugins • lead developer of the sonata project • Working at Ekino, a french web agency
  • 3. Talk • Sonata Project presentation • Quick Tour • Under the hood • Customize / Advanced features • Conclusion
  • 4. Sonata Project • A not so young project • Based on symfony1 plugins • Recoded with the best practices of Symfony2 • Built on top on very strong and powerful framework
  • 5. Sonata Project • An ecommerce toolbox • How : • avoiding reinvented the wheel • contribution to the community • built on top of a strong framework
  • 6. Sonata’s bundles • PageBundle : a page manager with block as service and strong caching mechanism • MediaBundle : a media manager on steroid, you don’t have to worry about managing files or videos • UserBundle, IntlBundle, etc ... • AdminBundle : A backend generator
  • 8. AdminBundle why ?
  • 9. • No admin generator for Symfony 2.0 • Frustrating by the admin generator provided by symfony1 • Admin is not only about Model; but about providing a consistent and rich user experience for managing data.
  • 11. Admin Class • An metadata description of CRUD operations • No code generation • Based on Symfony services + Sonata Admin services
  • 12. Dashboard Actions shortcut Group + Model
  • 13. Dashboard Register admin class with the tag “sonata.admin” And admin will appears into the dashboard 1.     <services> 2.         <service id="sonata.news.admin.comment" class="%sonata.news.admin.comment.class%"> 3.             <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="comment"/> 4.             <argument /> 5.             <argument>%sonata.news.admin.comment.entity%</argument> 6.             <argument>%sonata.news.admin.comment.controller%</argument> 7.         </service> 8.   9.         <service id="sonata.news.admin.post" class="%sonata.news.admin.post.class%"> 10.            <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="post"/> 11.            <argument /> 12.            <argument>%sonata.news.admin.post.entity%</argument> 13.            <argument>%sonata.news.admin.post.controller%</argument> 14.        </service> 15.  16.        <service id="sonata.news.admin.tag" class="%sonata.news.admin.tag.class%"> 17.            <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="tag"/> 18.            <argument /> 19.            <argument>%sonata.news.admin.tag.entity%</argument> 20.            <argument>%sonata.news.admin.tag.controller%</argument> 21.        </service> 22.    </services>
  • 14. Breadcrumb List Action Model Actions Batch Actions Filters
  • 15. List Action fields, custom fields, type templates, type detection, based detection on Form Component
  • 16. List Action 1.     protected function configureDatagridFilters(DatagridMapper $datagridMapper) { 2.         $datagridMapper 3.   4.   5.                         ->add('name')     ->add('providerReference')     ->add('enabled') field guesser 6.             ->add('context') 7.         ; 8.         $providers = array(); 9.   10.        foreach($this->pool->getProviderNamesByContext('default') as $name) { 11.            $providers[$name] = $name; 12.        } 13.  14.        $datagridMapper->add('providerName', 'doctrine_orm_choice', array( 15.            'field_options'=> array( 16.                'choices' => $providers, 17.  18.  19.                            'required' => false,         'multiple' => false,         'expanded' => false, custom filter 20.            ), 21.            'field_type'=> 'choice', 22.        )); 23.    } edit link 24.  25.    protected function configureListFields(ListMapper $listMapper) { 26.        $listMapper 27.            ->addIdentifier('id') 28.            ->add('image', 'string', array('template' => 'SonataMediaBundle:MediaAdmin:list_image.html.twig')) 29.            ->add('custom', 'string', array('template' => 'SonataMediaBundle:MediaAdmin:list_custom.html.twig')) 30.            ->add('enabled') 31.  32.              ->add('_action', 'actions', array(             'actions' => array( custom template 33.                    'view' => array(), 34.                    'edit' => array(), 35.                ) 36.            )) 37.  38.          ; } row’s actions
  • 18. Edit/Create Form Helper message add relation hide fields save options delete action
  • 19. Edit/Create Form 1.     protected function configureFormFields(FormMapper $formMapper) 2.     { 3.         $templates = array(); 4.         foreach ($this->cmsManager->getPageManager()->getTemplates() as $code => $template) { 5.             $templates[$code] = $template->getName(); 6.         } create group 7.   8.         $formMapper 9.             ->with($this->trans('form_page.group_main_label')) 10.                 ->add('name') 11.                 ->add('enabled', null, array('required' => false)) 12.                 ->add('position') 13.                 ->add('templateCode', 'choice', array('required' => true, 'choices' => $templates)) 14.                 ->add('parent', 'sonata_page_selector', array( 15.                     'page'          => $this->getSubject() ?: null, 16.                     'model_manager' => $this->getModelManager(), 17.                     'class'         => $this->getClass(), 18.   19.                           'filter_choice' => array('hierarchy' => 'root'),             'required'      => false Form Component 20.                 )) 21.             ->end() 22.         ; 23.   24.         $formMapper 25.             ->with($this->trans('form_page.group_seo_label'), array('collapsed' => true)) 26.                 ->add('metaKeyword', 'textarea', array('required' => false)) 27.                 ->add('metaDescription', 'textarea', array('required' => false)) 28.             ->end() group options 29.         ; 30.   31.         $formMapper 32.             ->with($this->trans('form_page.group_advanced_label'), array('collapsed' => true)) 33.                 ->add('javascript', null,  array('required' => false)) 34.                 ->add('stylesheet', null, array('required' => false)) 35.                 ->add('rawHeaders', null, array('required' => false)) 36.             ->end() 37.         ; 38.   39.         $formMapper->setHelps(array( 40.             'name' => $this->trans('help_page_name') 41.   42.           )); } Define help messages
  • 20. Other Features • Permissions management • Flash messages • Nested Admin • Command lines utilities • Translated into more than10 languages
  • 21. Quick Tour Summary • Dashboard • Consistent Interface across bundles • Easy to configure, but powerful for advanced users • Advanced features • Inspired from the django admin module (user interactions)
  • 22. Under the hood http://www.flickr.com/photos/52251564@N08/5937620090
  • 23. Admin Class Dependencies Security Builder Sonata Admin Bundle List Symfony Framework Translator Datagrid Admin Show Routing class Form Validator Model Manager Form
  • 24. Security • Based on the SecurityHandlerInterface • 2 built-in implementations • NoopSecurityHandler : use the Symfony’s firewall • AclSecurityHandler : based on ACL - Advanced users only
  • 25. Security • Admin Usage 1.     protected function configureFormFields(FormMapper $formMapper) 2.     { 3.         $formMapper 4.             ->with('General') 5.                 ->add('enabled', null, array('required' => false)) 6.                 ->add('author', 'sonata_type_model', array(), array('edit' => 'list')) 7.                 ->add('title') 8.             ->end() 9.         ; 10.        11.        if (!$this->isGranted('CREATE')) { 12.            // do specific code if the user cannot create a new object 13.        } 14.    } • Template Usage 1. {% if admin.isGranted('CREATE') %} 2.    // DO YOUR STUFF 3. {% endif %}
  • 26. Security : ACL • Required to have a custom external bundle to manage permissions and user : see FOS/ UserBundle and Sonata/UserBundle • Built on top of a custom MaskBuilder (basic roles : List,View, Edit, Create, Delete) • Command lines : • php app/console init:acl • php app/console sonata:admin:setup-acl
  • 27. Routing • Definition set from the Admin class • Can be tweaked by the configureRoute 1.     /** 2.      * @param SonataAdminBundleRouteRouteCollection $collection 3.      * @return void 4.      */ 5.     protected function configureRoutes(RouteCollection $collection) 6.     { 7.         $collection->add('snapshots'); Add the id parameter 8.         $collection->remove('edit'); 9. 10.        $collection->add('test', $this->getRouterIdParameter().'/test'); 11.    } • Template Usage 1. <a href="{{ admin.generateUrl('view', { 'id' : media.id, 'format' : 'reference'}) }}">reference</a> 2. <a href="{{ admin.generateObjectUrl(media, 'view', {'format' : 'reference'}) }}">reference</a>
  • 28. Admin Model Manager • Persistency layer abstraction for Admin Bundle. • All Persistencies actions are done in the Model Manager • delete, query, pagination, etc.. • form type manipulation delegation • For now only Doctrine ORM (Propel and Doctrine ODM are work in progress by external contributors)
  • 29. Admin Model Manager • Some bundle provides custom Model Manager • You can define your own proxy Admin Model Manager to reuse the Bundle Model Manager • Use case : Sonata Media Bundle (delete action)
  • 30. Model Manager 1. class AdminModelManager extends ModelManager { 2.     protected $manager; Sonata Media Bundle 3.   4.     public function __construct($entityManager, $manager) { 5.         parent::__construct($entityManager); 2 Model Managers : 6.         $this->manager = $manager; - entity : deal with deletion and so on ... 7.     } 8.   - admin : proxy some methods to the entity 9.     public function delete($object) { 10.         $this->manager->delete($object); 11.     } How to ? 12. } 1. create a dedicated Admin Model Manager 1. class BundleMediaManager extends AbstractMediaManager {   2. create a Bundle Model Manager 1. public function delete(MediaInterface $media) { 2.         $this->pool 3. redefine only required methods 3. ->getProvider($media->getProviderName()) 4. define services 4. ->preRemove($media); 5.         $this->em->remove($media); 6.         $this->em->flush(); Delete thumbnails 7.   8.         $this->pool 9. ->getProvider($media->getProviderName()) 10. ->postRemove($media); 11.         $this->em->flush(); 12.     } 2. } 1.       <service id="sonata.media.admin.media" class="SonataMediaBundleEntityBundleMediaManager"> 2.             <tag name="sonata.admin" manager_type="orm" group="sonata_media" label="media"/> 3.             <argument /> 4.             <argument>%sonata.media.admin.media.entity%</argument> 5.             <argument>%sonata.media.admin.media.controller%</argument> 6.   7.             <call method="setModelManager"> 8.                 <argument type="service" id="sonata.media.admin.media.manager" /> 9.             </call> 10.         </service> 11.   12.         <service id="sonata.media.admin.media.manager" class="SonataMediaBundleAdminManagerDoctrineModelManager"> 13.             <argument type="service" id="doctrine.orm.default_entity_manager" /> 14.             <argument type="service" id="sonata.media.manager.media" /> 15.         </service>
  • 31. Translator • 2 catalogues • SonataAdminBundle used to translate shared messages • messages used to translate current Admin 1.         $formMapper 2.             ->with($this->trans('form_page.group_main_label')) 3.                 ->add('name') 4.             ->end() 5.         ; • Can be set by updating the translationDomain property
  • 32. Form • Originally built on the the first implementation • Too bad ..... major refactoring 3 months later • Some admin features has been removed or still broken :( • But the new form implementation is pretty awesome .... once you know how to use it.
  • 33. Form Mapper • Interact with the FormMapper • Proxy class between the Admin Class and the Symfony Form Component • Act as the Symfony FormBuilder component • You can use your custom field types
  • 34. Core types Form Type Model Manager Types Form Types • AdminType : used to embedded form from another Admin class • CollectionType : use by one-to-many association • ModelType : select choice (like EntityType) • ModelReferenceType : handle an model id • ImmutableArrayType : specify a form type per array element 1.         $formMapper->add('settings', 'sonata_type_immutable_array', array( 2.             'keys' => array( 3.                 array('layout', 'textarea', array()), 4.                 array('action', 'text', array()), 5.                 array('parameters', 'text', array()), 6.             ) 7.         ));
  • 35. Validator • Assert rules can be defined in the validation. [xml|yml] files (validator component) nothing new ... • Conditional Validation
  • 36. Conditional Validation • Not mandatory, but allow to add inline validation a runtime. • ex : check only if a value is set • Validate method inside the admin class • Interact with a new ErrorElement object • The ErrorElement is just a validator service based on the Validator Component
  • 37. Validator 1.   /** 2.      * @param SonataAdminBundleValidatorErrorElement $errorElement 3.      * @param $object 4.      * @return void 5.      */ 6.     public function validate(ErrorElement $errorElement, $object) 7.     { 8.         $errorElement 9.             ->with('name') 10.                ->assertMaxLength(array('limit' => 32)) 11.            ->end() 12.        ; symfony constraint 13.        14.        if ($object->getFoo()) { 15.            $errorElement 16.                ->with('test') 17.                    ->addViolation('my_message') 18.                ->end() 19.            ; 20.        } 21.    } custom error
  • 38. Menu • Based on KnpMenu lib • Used for Breadcrumb and Side Menu • No magic for sidemenu, need to code your own menu per admin (if required)
  • 39. Menu 1. class PostAdmin extends Admin 2. { 3.     protected function configureSideMenu(MenuItemInterface $menu, $action, Admin $childAdmin = null) 4.     { 5.         if (!$childAdmin && !in_array($action, array('edit'))) { 6.             return; 7.         } 8.   9.         $admin = $this->isChild() ? $this->getParent() : $this; 10.  11.        $id = $admin->getRequest()->get('id'); 12.  13.        $menu->addChild( 14.            $this->trans('view_post'), 15.            array('uri' => $admin->generateUrl('edit', array('id' => $id))) 16.        ); 17.  18.        $menu->addChild( 19.            $this->trans('link_view_comment'), 20.            array('uri' => $admin->generateUrl('sonata.news.admin.comment.list', array('id' => $id))) 21.        ); 22.    } 23.}
  • 41. Form : one-to-many • Type : sonata_type_collection (CollectionType) • Form Option by_reference => false • Sonata Options • edit : standard / inline • inline : table | list • position : field name (if exists)
  • 42. Form : many-to-many • Type : sonata_type_model (ModelType) • Sonata Options • no options
  • 43. Form : many-to-one • Type : sonata_type_model (ModelType) • Form Options : • expanded : true|false • Sonata Options • edit : standard (select box) / list (popup) • link_parameters : add extra link parameters to the link
  • 44. Form : many-to-one • Type : sonata_type_admin (AdminType) • Embed an admin form for an entity into the current admin • Sonata Options • no option!
  • 45. Form Theme • Based on the form theme mechanism • Define a getFormTheme() • a set of form templates • allows to customize an admin form
  • 46. Form Theme • Just define a custom block with the correct name ... • How to know the block name ... ? • Provide a custom `block_name` option • or use a defined built-in pattern : admin_service_id_[type]_[widget|label|errors|row] admin_service_id_[fieldName]_[widget|label|errors|row]
  • 47. Form block • Theme definition 1.    protected $formTheme = array( 2.        'SonataAdminBundle:Form:form_admin_fields.html.twig', 3.        'SonataNewsBundle:Form:form.html.twig' 4.    ); • Block definition 1.{% block sonata_user_admin_user_credentialsExpired_text_widget %} 2.   PUT HERE THE WIDGET ... 3.{% endblock %} • Et voila!
  • 48. Custom List Template • Field definition 1.->add('custom', 'string', array('template' => 'SonataMediaBundle:MediaAdmin:list_custom.html.twig')) • Template 1.{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %} 2.  3.{% block field %} 4.    <div> 5.        <strong>{{ object.name }}</strong> <br /> 6.        {{ object.providerName|trans({}, 'SonataMediaBundle') }}: {{ object.width }}x{{ object.height }} <br /> 7.    </div> 8.{% endblock %} • Et voila!
  • 49. CRUD Controller • Use the admin class to generate required objects • contains create, edit, update, delete and batch actions • can be extended to change some logic • use case : Sonata Media Bundle
  • 50. Custom CRUD Controller 1. namespace SonataMediaBundleController; 2.   3. use SonataAdminBundleControllerCRUDController as Controller; Grant check 4. use SymfonyComponentSecurityCoreExceptionAccessDeniedException; 5.   6. class MediaAdminController extends Controller { 7.     public function createAction() { 8.         if (false === $this->admin->isGranted('CREATE')) { 9.             throw new AccessDeniedException(); 10.        } 11.  12.        $parameters = $this->admin->getPersistentParameters(); 13.  14.        if (!$parameters['provider']) { Custom Template 15.            return $this- >render('SonataMediaBundle:MediaAdmin:select_provider.html.twig', array( 16.                'providers'     => $this->get('sonata.media.pool') 17. ->getProvidersByContext($this->get('request')- >get('context', 'default')), 18.                'base_template' => $this->getBaseTemplate(), 19.                'admin'         => $this->admin, 20.                'action'        => 'create' 21.            )); 22.        } 23.  24.        return parent::createAction(); 25.    } 26.} Parent action
  • 51. Nested Admin • clean url : /admin/sonata/news/post/1/comment/list • reuse other admin definition • autofilter with the targeted elements • only work on one level • You don’t need to know routing name, as long as you use the admin class
  • 52. Nested Admin 1. class CommentAdmin extends Admin 2. { 3.     protected $parentAssociationMapping = 'post'; 4.   5.     protected function configureFormFields(FormMapper $formMapper) 6.     { 7.         if(!$this->isChild()) { 8.             $formMapper->add('post', 'sonata_type_model', array(), array('edit' => 'list')); 9.         } 10.   11.         $formMapper 12.             ->add('name') 13.             ->add('email') 14.             ->add('url', null, array('required' => false)) 15.             ->add('message') 16.             ->add('status', 'choice', array('choices' => Comment::getStatusList(), 'expanded' => true, 'multiple' => false)) 17.         ; 18.     } Display custom field if the 19.   20.     protected function configureListFields(ListMapper $listMapper) 21.     { 22.         $listMapper 23.             ->addIdentifier('name') current admin is nested or not 24.             ->add('getStatusCode', 'text', array('label' => 'status_code', 'sortable' => 'status')) 25.         ; 26.   27.         if (!$this->isChild()) { 28.             $listMapper->add('post'); 29.         } 30.   31.         $listMapper 32.             ->add('email') 33.             ->add('url') 34.             ->add('message'); 35.     } 36. }
  • 53. Nested Admin Comment Admin Nested Comment Admin
  • 54. Admin Extension • Allow to add extra feature or redefine some field to the admin • Good entry point if you extends some entities • Add extension must implement the AdminExtensionInterface and define as service with the sonata.admin.extension
  • 55. Debug • Check out external configurations • validator configuration • doctrine schema definition • use firebug to check Ajax errors (missing toString method or type hinting) • Get information from the Admin command tools • sonata:admin:list • sonata:admin:explain
  • 56. sonata:admin:list service name can be used to explain the admin configuration
  • 59. small numbers • First commit in november 2010 • 650 commits • 49 Contributors! • 14 Translations Thanks!
  • 60. What’s next ? • Version 1 • Stabilize the AdminBundle • Add more tests... : 56 tests, 145 assertions • Version 1.1 • Add missing features from the original form factoring • Add more layer persistency (contribution)
  • 61. Resources • http://sonata-project.org • irc : #symfony • twitter : @sonataproject • symfony google groups