Upgrade to Pro — share decks privately, control downloads, hide ads and more …

sfDay - Sonata Admin Bundle

006208eb7abd814b79b4dbb4b02d8494?s=47 Thomas R.
October 21, 2011

sfDay - Sonata Admin Bundle

006208eb7abd814b79b4dbb4b02d8494?s=128

Thomas R.

October 21, 2011
Tweet

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. Actions shortcut Group + Model Dashboard

  13. 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> Register admin class with the tag “sonata.admin” And admin will appears into the dashboard
  14. Filters Model Actions List Action Batch Actions Breadcrumb

  15. List Action fields, custom templates, type detection fields, type detection,

    based on Form Component
  16. List Action 1. protected function configureDatagridFilters(DatagridMapper $datagridMapper) { 2. $datagridMapper

    3. ->add('name') 4. ->add('providerReference') 5. ->add('enabled') 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. 'required' => false, 18. 'multiple' => false, 19. 'expanded' => false, 20. ), 21. 'field_type'=> 'choice', 22. )); 23. } 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. ->add('_action', 'actions', array( 32. 'actions' => array( 33. 'view' => array(), 34. 'edit' => array(), 35. ) 36. )) 37. ; 38. } field guesser custom filter edit link custom template row’s actions
  17. Edit/Create Form side menu Field Group

  18. Helper message Edit/Create Form 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. } 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. 'filter_choice' => array('hierarchy' => 'root'), 19. 'required' => false 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() 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. } create group Form Component Define help messages group options
  20. • Permissions management • Flash messages • Nested Admin •

    Command lines utilities • Translated into more than10 languages Other Features
  21. • Dashboard • Consistent Interface across bundles • Easy to

    configure, but powerful for advanced users • Advanced features • Inspired from the django admin module (user interactions) Quick Tour Summary
  22. Under the hood http://www.flickr.com/photos/52251564@N08/5937620090

  23. Admin class Routing Translator Model Manager Symfony Framework Security Validator

    Form Sonata Admin Bundle List Datagrid Show Form Builder Admin Class Dependencies
  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. } 1. {% if admin.isGranted('CREATE') %} 2. // DO YOUR STUFF 3. {% endif %} • Template Usage
  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 \Sonata\AdminBundle\Route\RouteCollection $collection 3. * @return void 4. */ 5. protected function configureRoutes(RouteCollection $collection) 6. { 7. $collection->add('snapshots'); 8. $collection->remove('edit'); 9. 10. $collection->add('test', $this->getRouterIdParameter().'/test'); 11. } Add the id parameter 1. <a href="{{ admin.generateUrl('view', { 'id' : media.id, 'format' : 'reference'}) }}">reference</a> 2. <a href="{{ admin.generateObjectUrl(media, 'view', {'format' : 'reference'}) }}">reference</a> • Template Usage
  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 Sonata Media Bundle 2 Model Managers : -

    entity : deal with deletion and so on ... - admin : proxy some methods to the entity How to ? 1. create a dedicated Admin Model Manager 2. create a Bundle Model Manager 3. redefine only required methods 4. define services 1. class AdminModelManager extends ModelManager { 2. protected $manager; 3. 4. public function __construct($entityManager, $manager) { 5. parent::__construct($entityManager); 6. $this->manager = $manager; 7. } 8. 9. public function delete($object) { 10. $this->manager->delete($object); 11. } 12. } 1. <service id="sonata.media.admin.media" class="Sonata\MediaBundle\Entity\BundleMediaManager"> 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="Sonata\MediaBundle\Admin\Manager\DoctrineModelManager"> 13. <argument type="service" id="doctrine.orm.default_entity_manager" /> 14. <argument type="service" id="sonata.media.manager.media" /> 15. </service> 1. class BundleMediaManager extends AbstractMediaManager { 1. public function delete(MediaInterface $media) { 2. $this->pool 3. ->getProvider($media->getProviderName()) 4. ->preRemove($media); 5. $this->em->remove($media); 6. $this->em->flush(); 7. 8. $this->pool 9. ->getProvider($media->getProviderName()) 10. ->postRemove($media); 11. $this->em->flush(); 12. } 2. } Delete thumbnails
  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. Form Type • 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. )); Core types Model Manager Types Form Types
  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 \Sonata\AdminBundle\Validator\ErrorElement $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. ; 13. 14. if ($object->getFoo()) { 15. $errorElement 16. ->with('test') 17. ->addViolation('my_message') 18. ->end() 19. ; 20. } 21. } symfony constraint 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.}
  40. customize http://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 1.{% block sonata_user_admin_user_credentialsExpired_text_widget %} 2. PUT HERE THE

    WIDGET ... 3.{% endblock %} 1. protected $formTheme = array( 2. 'SonataAdminBundle:Form:form_admin_fields.html.twig', 3. 'SonataNewsBundle:Form:form.html.twig' 4. ); • Theme definition • Block definition • Et voila!
  48. Custom List Template 1.->add('custom', 'string', array('template' => 'SonataMediaBundle:MediaAdmin:list_custom.html.twig')) • Field

    definition • Template • Et voila! 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 %}
  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 Sonata\MediaBundle\Controller; 2. 3. use Sonata\AdminBundle\Controller\CRUDController

    as Controller; 4. use Symfony\Component\Security\Core\Exception\AccessDeniedException; 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']) { 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.} Grant check Custom Template 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. } 19. 20. protected function configureListFields(ListMapper $listMapper) 21. { 22. $listMapper 23. ->addIdentifier('name') 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. } Display custom field if the current admin is nested or not Display custom field if the current admin is nested or not
  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
  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/