Your SlideShare is downloading. ×

sfDay Cologne - Sonata Admin Bundle

6,173
views

Published on

Published in: Technology, Business

1 Comment
10 Likes
Statistics
Notes
No Downloads
Views
Total Views
6,173
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
0
Comments
1
Likes
10
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 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
  • 7. http://sonata-project.org
  • 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.
  • 10. Quick Tour http://www.flickr.com/photos/38104873@N03/4559985343/
  • 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. DashboardRegister admin class with the tag “sonata.admin” And admin will appears into the dashboard1.     <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, typetemplates, type detection, based detection on Form Component
  • 16. List Action1.     protected function configureDatagridFilters(DatagridMapper $datagridMapper) {2.         $datagridMapper3.  4.  5.                         ->add(name)     ->add(providerReference)     ->add(enabled) field guesser6.             ->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 filter20.            ),21.            field_type=> choice,22.        ));23.    } edit link24. 25.    protected function configureListFields(ListMapper $listMapper) {26.        $listMapper27.            ->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 template33.                    view => array(),34.                    edit => array(),35.                )36.            ))37. 38.          ; } row’s actions
  • 17. Edit/Create Formside menu Field Group
  • 18. Edit/Create Form Helper message add relation hide fieldssave options delete action
  • 19. Edit/Create Form1.     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 group7.  8.         $formMapper9.             ->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 Component20.                 ))21.             ->end()22.         ;23.  24.         $formMapper25.             ->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 options29.         ;30.  31.         $formMapper32.             ->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 ListSymfony 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 Usage1.     protected function configureFormFields(FormMapper $formMapper)2.     {3.         $formMapper4.             ->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 object13.        }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 Usage1. <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. Validator1.   /**2.      * @param SonataAdminBundleValidatorErrorElement $errorElement3.      * @param $object4.      * @return void5.      */6.     public function validate(ErrorElement $errorElement, $object)7.     {8.         $errorElement9.             ->with(name)10.                ->assertMaxLength(array(limit => 32))11.            ->end()12.        ; symfony constraint13.       14.        if ($object->getFoo()) {15.            $errorElement16.                ->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. Menu1. class PostAdmin extends Admin2. {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.}
  • 40. customizehttp://www.flickr.com/photos/7552532@N07/449769140/
  • 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 definition1.    protected $formTheme = array(2.        SonataAdminBundle:Form:form_admin_fields.html.twig,3.        SonataNewsBundle:Form:form.html.twig4.    ); • Block definition1.{% block sonata_user_admin_user_credentialsExpired_text_widget %}2.   PUT HERE THE WIDGET ...3.{% endblock %} • Et voila!
  • 48. Custom List Template • Field definition1.->add(custom, string, array(template => SonataMediaBundle:MediaAdmin:list_custom.html.twig)) • Template1.{% 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 Controller1. namespace SonataMediaBundleController;2.  3. use SonataAdminBundleControllerCRUDController as Controller; Grant check4. 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 Template15.            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        => create21.            ));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 Admin1. class CommentAdmin extends Admin2. {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.         $formMapper12.             ->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 the19.  20.     protected function configureListFields(ListMapper $listMapper)21.     {22.         $listMapper23.             ->addIdentifier(name) current admin is nested or not24.             ->add(getStatusCode, text, array(label => status_code, sortable => status))25.         ;26.  27.         if (!$this->isChild()) {28.             $listMapper->add(post);29.         }30.  31.         $listMapper32.             ->add(email)33.             ->add(url)34.             ->add(message);35.     }36. }
  • 53. Nested AdminComment 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 toexplain the admin configuration
  • 57. sonata:admin:explain
  • 58. conclusion http://www.flickr.com/photos/rv-bordeaux/5909437215/
  • 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
  • 62. Questions http://www.flickr.com/photos/colinkinner/2200500024/