Working With The Symfony Admin Generator

  • 44,414 views
Uploaded on

Advanced usage of the symfony admin generator.

Advanced usage of the symfony admin generator.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
44,414
On Slideshare
0
From Embeds
0
Number of Embeds
8

Actions

Shares
Downloads
860
Comments
5
Likes
40

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. Working with the admin generator
  • 2. My background John Cleveley Trained as a Systems Engineer with BAE Systems Started my own business in 2006 Created symfony apps for NHS, V&A Museum and Hornby. Currently work at the BBC using their Forge platform (ZF)
  • 3. Objectives Provide information beyond the docs Update on what’s new Suggest a few best practices Real world examples of customising How to get the best from the form framework Utilise the various ways of extending
  • 4. It’s super awesome. Saves huge amount of development time and costs Provides most common admin requirements out of the box Can be extended to provide bespoke needs Fully tested and documented It’s free!
  • 5. What’s new since 1.0? Completely re-written for the form framework Relationships including m2m work without configuration Generator.yml is validated Batch actions (delete) Less reliance on generator.yml (DRY) More templates and actions to override Different configuration for the edit and new form Adds a REST route for your module
  • 6. New PHP configuration file Configure generator via PHP to be more dynamic: lib/newsGeneratorConfiguration.class.php The generated class in the cache lists functions cache/backend/dev/modules/autoNews/lib/BaseNe wsGeneratorConfiguration.class.php You can also mix configuration between the two
  • 7. New helper file Provides html snippets for action links lib/[module]GeneratorHelper.class.php getUrlForAction () linkToDelete () linkToEdit () linkToList () linkToNew () linkToSave () linkToSaveAndAdd () Create custom links with extra javascript etc
  • 8. Your thoughts? PHP! YAML!
  • 9. Is it the right tool? …maybe.
  • 10. Think requirements! Don’t jump to use the admin generator Analyse what’s needed first A bespoke solution may be more appropriate Misusing the admin generator could cause big problems in the future
  • 11. How do we decide? Admin Bespoke Normal CRUD operations Public interface to data Non – technical users Sophisticated sorting need to add data and searching of data Trusted site administrators Ownership of all records The admin site can take you a long way…. …. But be careful it doesn’t become a mess.
  • 12. admin The 10 Commandments
  • 13. 10 Commandments (1-5) 1. Understand the client’s workflow and customise admin to suit 2. Think about security from the start 3. Look through and understand the cached php files 4. Change table_method to reduce db calls 5. Use bespoke Form class for admin if different
  • 14. 10 Commandments (6-10) 6. Keep all form form configuration in the Form Class 7. If you need to make changes to multiple admin modules – create a theme. 8. Think about small screens and target browser 9. Create functional tests – guard against regression 10. Maintain good MVC and decoupling practices
  • 15. What’s the object-oriented way to become wealthy? Inheritance!
  • 16. John’s Top tips!
  • 17. 1. The URL Clients don’t like using: application.com/admin.php Option 1: application.com/admin Option 2: admin.application.com
  • 18. 1. The URL: /admin Modify web/.htaccess RewriteCond %{REQUEST_URI} ^/admin/? RewriteRule ^(.*)$ admin.php [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php [QSA,L] Change all your standard routes – routing.yml homepage: url: /admin param: { module: default, action: index } test: url: /admin/test ...
  • 19. 1. The URL: /admin Change your route collections – routing.yml prefix_path: /admin/module_name Remove script name in urls – settings.yml prod: .settings: no_script_name: true
  • 20. 1. The URL: admin.app Create a new virtual host in httpd.conf <VirtualHost *:80> ServerName admin.application.com DirectoryIndex admin.php DocumentRoot "/path/to/web/folder" <Directory "/path/to/web/folder"> AllowOverride All Allow from All </Directory> </VirtualHost> Tell symfony not to output admin.php in urls Prod .settings: no_script_name: true
  • 21. 2. Dynamic MaxPerPage Add a select box within the _list_header.php js onchange submits to action Add an action to set a user attribute public function executeChangeMaxPage(sfWebRequest $request){ $this->getUser()->setAttribute('maxPage', $request->getParameter('maxPage')); $this->redirect($request->getReferer()); }
  • 22. 2. Dynamic MaxPerPage Override getPagerMaxPerPage() class employeeGeneratorConfiguration extends BaseEmployee { public function getPagerMaxPerPage() { $maxPage = sfContext::getInstance()->getUser() ->getAttribute('maxPage', 10); return $maxPage; } }
  • 23. 3. Adding relations Fewest clicks for common tasks Relevant data placed together Currently unsupported by the generator Symfony provides functions to help sfForm : : mergeForm() sfForm : : embedForm() sfFormDoctrine : : embedRelation()
  • 24. Employee has many phones Employee: Phone: columns: columns: id: id: type: integer(4) type: integer(4) primary: true primary: true autoincrement: true autoincrement: true name: number: type: string(255) type: string(255) notnull: true notnull: true relations: employee_id: Phones: type: integer(4) type: many notnull: true class: Phone type: local: id type: enum foreign: employee_id values: [mobile, home, work] onDelete: CASCADE
  • 25. 3. Adding relations Add ability to edit existing phone numbers from within employee form Embed the ‘Phones’ relation in EmployeeForm::configure class EmployeeForm extends BaseEmployeeForm { public function configure() { $this->embedRelation('Phones'); } }
  • 26. Hide employee_id in PhoneForm::configure() class PhoneForm extends BasePhoneForm { public function configure() { $this->widgetSchema['employee_id'] = new sfWidgetFormInputHidden(); ... No Delete? No Add?
  • 27. There’s a symfony plugin for that! Thanks to ahDoctrineEasyEmbeddedRelationsPlugin by Daniel Lohse
  • 28. 4. Translate admin interface Add chosen culture to settings.yml .all: .settings: i18n: on default_culture: fr ./symfony cc and delete browser cookies
  • 29. 4. Translate admin interface Create a new startrek catalogue Add vulcan XLIFF files to: apps/admin/il8n/ • startrek.vu.xml • startrek_forms.vu.xml
  • 30. 4. Translate admin interface <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis- open.org/committees/xliff/documents/xliff.dtd" > <xliff version="1.0"> <file original="global" source-language="en" target- language="vu_VU" datatype="plaintext"> <header /> <body> <!-- Actions --> <trans-unit> <source>New</source> startrek.vu.xml <target>Uzh</target> </trans-unit> <trans-unit> <source>Edit</source> <target>Ver-tor</target> </trans-unit> ...
  • 31. 4. Translate admin interface Tell admin generator to use alternative catalogue generator: class: sfDoctrineGenerator param: i18n_catalogue: startrek Tell the forms as well – sfFormDoctrine::setup() abstract class BaseFormDoctrine extends sfFormDoctrine { public function setup() { $this->widgetSchema->getFormFormatter() ->setTranslationCatalogue('startrek_forms'); } }
  • 32. 5. Tidy up filters Filters work great – but the default style is a bit off A few CSS tweaks
  • 33. #sf_admin_container #sf_admin_bar { float:none; margin-left: 0px; } #sf_admin_container #sf_admin_bar .sf_admin_filter table tr { clear: none; border: 1px solid #DDD; padding: 0px; } #sf_admin_container #sf_admin_bar .sf_admin_filter table tr td { height: 50px; vertical-align: middle; border: none; } #sf_admin_container #sf_admin_bar .sf_admin_filter table tbody { clear: none; float: left; } #sf_admin_container #sf_admin_bar .sf_admin_filter table tbody tr { float: left; border-right: none; } #sf_admin_container #sf_admin_bar .sf_admin_filter table tfoot { clear: none; float: right; } #sf_admin_container #sf_admin_bar .sf_admin_filter table tfoot tr { float: right; } Thanks to Sebastien
  • 34. What is the definition of programmer? Programmers are machines that turn coffee into code.
  • 35. 6. Timestampable fields Generally don’t need to edit these Simply unset them in form class class NewsForm extends BaseNewsForm { public function configure() { unset($this['created_at'], $this['updated_at']); } }
  • 36. 6. Timestampable fields What if you still need to see the value?
  • 37. 6. Timestampable fields Use sfWidgetFormPlain widget http://trac.symfony-project.org/attachment/ticket/7963/sfWidgetPlain.diff public function configure() { $this->setWidget('created_at', new sfWidgetFormPlain(array('value'=>$this->getObject()->created_at))); unset($this->validatorSchema['created_at']); $this->setWidget('updated_at', new sfWidgetFormPlain(array('value'=>$this->getObject()->updated_at))); unset($this->validatorSchema['updated_at']); ... Thanks to Stephen.Ostrow
  • 38. 7. Pre-filter list
  • 39. 7. Pre-filter list Add an object action to generator.yml list: object_actions: _edit: ~ viewPhones: { label: Phone numbers, action: viewPhones } Set filter atribute in user session class employeeActions extends autoEmployeeActions { public function executeViewPhones($request){ $this->getUser()->setAttribute( 'phone.filters', array('employee_id' => $request->getParameter('id')), 'admin_module' ); $this->redirect($this->generateUrl('phone')); } }
  • 40. 8. Row level ownership Only allow owners of objects access Example presumes: sfGuard plugin is installed Objects have a user_id field fk
  • 41. 8. Row level ownership Secure the list page - moduleActions::buildquery() protected function buildQuery(){ $query = parent::buildQuery(); $query->andWhere( 'user_id = ?', $this->getUser()->getId() ); return $query; } This belongs in the model!!
  • 42. 8. Row level ownership Secure all other actions - moduleActions::preExecute() public function preExecute(){ if($this->getActionName()!= 'new' && $this->getActionName()!= 'index'){ $this->forward404Unless( $this->getUser()->isOwner($this->getRoute()->getObject()) ); } parent::preExecute(); }
  • 43. 8. Row level ownership Add a new method to user class - myUser::isOwner() class myUser extends sfGuardSecurityUser { public function isOwner($obj){ if(is_object($obj)){ if($this->getId() == $obj->getUserId()) return true; } return false; } }
  • 44. 8. Row level ownership What about the user_id field in the form? Don’t want users to change owner Also be careful with injected data
  • 45. 8. Row level ownership We need to remove the widget - Form::configure() public function configure() { unset($this['user_id']); ... Set the user_id manually – Form::doUpdateObject() public function doUpdateObject($values){ $userId = sfContext::getInstance()->getUser()->getId(); $this->getObject()->setUserId($userId); return parent::doUpdateObject($values); } eatmymonkeydust.com
  • 46. 9. Custom filters Find out who has a birthday today Add the new filter name to generator.yml filter: display: [ name, birthday_today ] fields: birthday_today: help: Employees who have a birthday today! Based on info from Tomasz Ducin and dlepage
  • 47. 9. Custom filters Create a new widget in - xxFormFilter::configure() public function configure() { $this->widgetSchema['birthday_today'] = new sfWidgetFormInputCheckbox(); $this->validatorSchema['birthday_today'] = new sfValidatorPass(); } Filter form is now displayed
  • 48. 9. Custom filters Add a add*ColumnQuery to FormFilter class public function addBirthdayTodayColumnQuery($query,$field,$value) { if($value){ $query->andWhere("SUBSTRING(`birthday`, 6, 5) = SUBSTRING(NOW(), 6, 5)"); } return $query; } Now buy the presents!
  • 49. Plugins
  • 50. sfAdminDashPlugin Kevin Bond Joomla style admin Adds a dashboard Configurable admin navigation Replaces the admin css Manually add header component and footer partial to layout
  • 51. sfAdminThemejRollerPlugin Gerald Estadieu Looks stunning jQuery Theme roller system Popup filters Tabs in edit view Completely new admin theme
  • 52. Optimist : The glass is half full. Pessimist : The glass is half empty.. Coder: The glass is twice as big as it needs to be
  • 53. Extending Methods What degree of customisation do you need? Will you need to re-use the functionality?
  • 54. Extending - CSS Define an alternative CSS generator: class: sfDoctrineGenerator param: model_class: News theme: admin non_verbose_templates: true with_show: false singular: ~ plural: ~ route_prefix: news with_doctrine_route: 1 css: funkystyle
  • 55. Extending - Override code Override individual templates and actions Quick and easy Can’t be re-used between modules Can become untidy
  • 56. Extending – Create a theme More work upfront Can be used for multiple modules / projects Much more scope for customising Steep learning curve – PHP in PHP!
  • 57. Extending – Create a theme Create container folder for new theme mkdir -p data/generator/sfDoctrineModule/newtheme Copy the generator files from sfDoctrine plugin cp -r lib/vendor/symfony/lib/plugins/ → sfDoctrinePlugin/data/generator/sfDoctrineModule/admin/* → data/generator/sfDoctrineModule/newtheme/
  • 58. Extending – Create a theme Name of theme Parts – Snippets of code included into cache Skeleton – copied to admin module Templates – generated into cache
  • 59. Extending – Create a theme Change theme name in generator.yml generator: class: sfDoctrineGenerator param: model_class: News theme: newtheme ... Clear cache You’ve made your own theme!
  • 60. Extending – Admin events admin.pre_execute: Notified before any action is executed. admin.build_criteria: Filters the Criteria used for the list view. admin.save_object: Notified just after an object is saved. admin.delete_object: Notified just before an object will be deleted.
  • 61. So, what does… …do?
  • 62. Dashboard Show related models
  • 63. Object history Delete related objects warning
  • 64. Future…. What do you want the admin generator to do? What should the scope of the generator be? Better support for embedded forms? More customisable list view (sfGrid)? Fulltext search in the fields? Saving goes back to list view? Dashboard? Nested sets? Ordering? Inherit from multiple themes?
  • 65. Thanks for listening! Twitter: @jcleveley