working with the symfony admin generator

66
Working with the admin generator

Upload: john-cleveley

Post on 08-May-2015

45.684 views

Category:

Technology


3 download

DESCRIPTION

Advanced usage of the symfony admin generator.

TRANSCRIPT

Page 1: Working With The Symfony Admin Generator

Working with the admin generator

Page 2: Working With The Symfony Admin Generator

My background

John Cleveley Trained as a Systems Engineer with BAE SystemsStarted 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)

Page 3: Working With The Symfony Admin Generator
Page 4: Working With The Symfony Admin Generator

Objectives

Provide information beyond the docsUpdate on what’s new Suggest a few best practicesReal world examples of customisingHow to get the best from the form frameworkUtilise the various ways of extending

Page 5: Working With The Symfony Admin Generator

It’s super awesome.

Saves huge amount of development time and costsProvides most common admin requirements out of the boxCan be extended to provide bespoke needsFully tested and documentedIt’s free!

Page 6: Working With The Symfony Admin Generator

What’s new since 1.0?

Completely re-written for the form frameworkRelationships including m2m work without configurationGenerator.yml is validatedBatch actions (delete)Less reliance on generator.yml (DRY)More templates and actions to overrideDifferent configuration for the edit and new formAdds a REST route for your module

Page 7: Working With The Symfony Admin Generator

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/BaseNewsGeneratorConfiguration.class.php

You can also mix configuration between the two

Page 8: Working With The Symfony Admin Generator

New helper file

getUrlForAction ()linkToDelete () linkToEdit ()linkToList () linkToNew ()linkToSave () linkToSaveAndAdd ()

Create custom links with extra javascript etc

lib/[module]GeneratorHelper.class.php

Provides html snippets for action links

Page 9: Working With The Symfony Admin Generator

Your thoughts?

PHP! YAML!

Page 10: Working With The Symfony Admin Generator

Is it the right tool?

…maybe.

Page 11: Working With The Symfony Admin Generator

Think requirements!

Don’t jump to use the admin generatorAnalyse what’s needed firstA bespoke solution may be more appropriateMisusing the admin generator could cause big problems in the future

Page 12: Working With The Symfony Admin Generator

How do we decide?

AdminNormal CRUD operationsNon – technical users need to add dataTrusted site administrators Ownership of all records

BespokePublic interface to data Sophisticated sorting and searching of data

The admin site can take you a long way….…. But be careful it doesn’t become a mess.

Page 13: Working With The Symfony Admin Generator

The 10 Commandmentsadmin

Page 14: Working With The Symfony Admin Generator

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 phpfiles

4. Change table_method to reduce db calls

5. Use bespoke Form class for admin if different

Page 15: Working With The Symfony Admin Generator

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

Page 16: Working With The Symfony Admin Generator

What’s the object-oriented way to become wealthy?

Inheritance!

Page 17: Working With The Symfony Admin Generator

John’s Top tips!

Page 18: Working With The Symfony Admin Generator

1. The URL

Clients don’t like using: application.com/admin.php

Option 1: application.com/admin

Option 2: admin.application.com

Page 19: Working With The Symfony Admin Generator

1. The URL: /admin

RewriteCond %{REQUEST_URI} ^/admin/?

RewriteRule ^(.*)$ admin.php [QSA,L]

RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^(.*)$ index.php [QSA,L]

homepage:

url: /admin

param: { module: default, action: index }

test:

url: /admin/test

...

Change all your standard routes – routing.yml

Modify web/.htaccess

Page 20: Working With The Symfony Admin Generator

1. The URL: /admin

Remove script name in urls – settings.yml

prefix_path: /admin/module_name

prod:.settings:

no_script_name: true

Change your route collections – routing.yml

Page 21: Working With The Symfony Admin Generator

1. The URL: admin.app

Tell symfony not to output admin.php in urls

Prod

.settings:no_script_name: true

Create a new virtual host in httpd.conf<VirtualHost *:80>

ServerName admin.application.comDirectoryIndex admin.phpDocumentRoot "/path/to/web/folder"<Directory "/path/to/web/folder">

AllowOverride AllAllow from All

</Directory></VirtualHost>

Page 22: Working With The Symfony Admin Generator

2. Dynamic MaxPerPage

Add a select box within the _list_header.php

public function executeChangeMaxPage(sfWebRequest $request){

$this->getUser()->setAttribute('maxPage',$request->getParameter('maxPage'));

$this->redirect($request->getReferer());}

Add an action to set a user attribute

js onchangesubmits to action

Page 23: Working With The Symfony Admin Generator

2. Dynamic MaxPerPage

Override getPagerMaxPerPage()

class employeeGeneratorConfiguration extends BaseEmployee{

public function getPagerMaxPerPage(){

$maxPage = sfContext::getInstance()->getUser()->getAttribute('maxPage', 10);

return $maxPage;}

}

Page 24: Working With The Symfony Admin Generator

3. Adding relations

Fewest clicks for common tasksRelevant data placed togetherCurrently unsupported by the generatorSymfony provides functions to help

sfForm : : mergeForm()sfForm : : embedForm()sfFormDoctrine : : embedRelation()

Page 25: Working With The Symfony Admin Generator

Employee has many phonesEmployee:

columns:

id:

type: integer(4)

primary: true

autoincrement: true

name:

type: string(255)

notnull: true

relations:

Phones:

type: many

class: Phone

local: id

foreign: employee_id

onDelete: CASCADE

Phone:

columns:

id:

type: integer(4)

primary: true

autoincrement: true

number:

type: string(255)

notnull: true

employee_id:

type: integer(4)

notnull: true

type:

type: enum

values: [mobile, home, work]

Page 26: Working With The Symfony Admin Generator

Embed the ‘Phones’ relation in EmployeeForm::configure

class EmployeeForm extends BaseEmployeeForm{

public function configure(){$this->embedRelation('Phones');

}}

Add ability to edit existing phone numbers from within employee form

3. Adding relations

Page 27: Working With The Symfony Admin Generator

class PhoneForm extends BasePhoneForm{

public function configure(){

$this->widgetSchema['employee_id'] =new sfWidgetFormInputHidden();

...

Hide employee_id in PhoneForm::configure()

No Delete?No Add?

Page 28: Working With The Symfony Admin Generator

There’s a symfony plugin for that!Thanks to ahDoctrineEasyEmbeddedRelationsPlugin by Daniel Lohse

Page 29: Working With The Symfony Admin Generator

4. Translate admin interface

.all:

.settings:

i18n: on

default_culture: fr

Add chosen culture to settings.yml

./symfony cc and delete browser cookies

Page 30: Working With The Symfony Admin Generator

4. Translate admin interface

Create a new startrek catalogue

Add vulcan XLIFF files to:apps/admin/il8n/• startrek.vu.xml• startrek_forms.vu.xml

Page 31: Working With The Symfony Admin Generator

<?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><target>Uzh</target>

</trans-unit><trans-unit><source>Edit</source><target>Ver-tor</target>

</trans-unit>...

startrek.vu.xml

4. Translate admin interface

Page 32: Working With The Symfony Admin Generator

4. Translate admin interfaceTell admin generator to use alternative catalogue

generator: class: sfDoctrineGeneratorparam:

i18n_catalogue: startrek

Tell the forms as well – sfFormDoctrine::setup()

abstract class BaseFormDoctrine extends sfFormDoctrine{

public function setup(){$this->widgetSchema->getFormFormatter()

->setTranslationCatalogue('startrek_forms');}

}

Page 33: Working With The Symfony Admin Generator

5. Tidy up filters

Filters work great – but the default style is a bit off

A few CSS tweaks

Page 34: Working With The Symfony Admin Generator

#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

Page 35: Working With The Symfony Admin Generator

What is the definition of programmer?

Programmers are machines that turn coffee into code.

Page 36: Working With The Symfony Admin Generator

6. Timestampable fields

Simply unset them in form class

class NewsForm extends BaseNewsForm{

public function configure(){unset($this['created_at'], $this['updated_at']);

}}

Generally don’t need to edit these

Page 37: Working With The Symfony Admin Generator

6. Timestampable fields

What if you still need to see the value?

Page 38: Working With The Symfony Admin Generator

6. Timestampable fieldsUse sfWidgetFormPlain widget

http://trac.symfony-project.org/attachment/ticket/7963/sfWidgetPlain.diff

Thanks to Stephen.Ostrow

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']);...

Page 39: Working With The Symfony Admin Generator

7. Pre-filter list

Page 40: Working With The Symfony Admin Generator

7. Pre-filter list Add an object action to generator.yml

Set filter atribute in user session

list:object_actions:_edit: ~viewPhones: { label: Phone numbers, action: viewPhones }

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'));}

}

Page 41: Working With The Symfony Admin Generator

8. Row level ownership

Only allow owners of objects access

Example presumes: sfGuard plugin is installedObjects have a user_id field fk

Page 42: Working With The Symfony Admin Generator

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!!

Page 43: Working With The Symfony Admin Generator

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();}

Page 44: Working With The Symfony Admin Generator

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;

}}

Page 45: Working With The Symfony Admin Generator

8. Row level ownershipWhat about the user_id field in the form?

Don’t want users to change ownerAlso be careful with injected data

Page 46: Working With The Symfony Admin Generator

8. Row level ownershipWe need to remove the widget - Form::configure()

Set the user_id manually – Form::doUpdateObject()

public function configure(){

unset($this['user_id']);...

public function doUpdateObject($values){

$userId = sfContext::getInstance()->getUser()->getId();$this->getObject()->setUserId($userId);

return parent::doUpdateObject($values);}

eatmymonkeydust.com

Page 47: Working With The Symfony Admin Generator

9. Custom filters

Find out who has a birthday today

Based on info from Tomasz Ducin and dlepage

Add the new filter name to generator.yml

filter:display: [ name, birthday_today ]fields:

birthday_today:help: Employees who have a birthday today!

Page 48: Working With The Symfony Admin Generator

9. Custom filtersCreate a new widget in - xxFormFilter::configure()

Filter form is now displayed

public function configure(){$this->widgetSchema['birthday_today'] =new sfWidgetFormInputCheckbox();

$this->validatorSchema['birthday_today'] = new sfValidatorPass();

}

Page 49: Working With The Symfony Admin Generator

9. Custom filtersAdd 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!

Page 50: Working With The Symfony Admin Generator

Plugins

Page 51: Working With The Symfony Admin Generator

sfAdminDashPluginKevin Bond

Joomla style adminAdds a dashboardConfigurable admin navigation

Replaces the admin cssManually add header component and footer partial to layout

Page 52: Working With The Symfony Admin Generator

sfAdminThemejRollerPluginGerald Estadieu

Looks stunningjQueryTheme roller systemPopup filtersTabs in edit view

Completely new admin theme

Page 53: Working With The Symfony Admin Generator

Optimist : The glass is half full. Pessimist : The glass is half empty..

Coder: The glass is twice as big as it needs to be

Page 54: Working With The Symfony Admin Generator

Extending Methods

What degree of customisation do you need?Will you need to re-use the functionality?

Page 55: Working With The Symfony Admin Generator

Extending - CSS

Define an alternative CSS

generator:class: sfDoctrineGeneratorparam:

model_class: Newstheme: adminnon_verbose_templates: truewith_show: falsesingular: ~plural: ~route_prefix: newswith_doctrine_route: 1css: funkystyle

Page 56: Working With The Symfony Admin Generator

Extending - Override code

Override individual templates and actionsQuick and easyCan’t be re-used between modulesCan become untidy

Page 57: Working With The Symfony Admin Generator

Extending – Create a theme

More work upfrontCan be used for multiple modules / projectsMuch more scope for customisingSteep learning curve – PHP in PHP!

Page 58: Working With The Symfony Admin Generator

Extending – Create a theme

Create container folder for new theme

cp -r lib/vendor/symfony/lib/plugins/

→ sfDoctrinePlugin/data/generator/sfDoctrineModule/admin/*

→ data/generator/sfDoctrineModule/newtheme/

mkdir -p data/generator/sfDoctrineModule/newtheme

Copy the generator files from sfDoctrine plugin

Page 59: Working With The Symfony Admin Generator

Extending – Create a theme

Skeleton – copied to admin module

Templates – generated into cache

Parts – Snippets of code included into cache

Name of theme

Page 60: Working With The Symfony Admin Generator

Extending – Create a theme

Change theme name in generator.yml

Clear cache

You’ve made your own theme!

generator:class: sfDoctrineGeneratorparam:

model_class: Newstheme: newtheme

...

Page 61: Working With The Symfony Admin Generator

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.

Page 62: Working With The Symfony Admin Generator

So, what does…

…do?

Page 63: Working With The Symfony Admin Generator

Dashboard

Show related models

Page 64: Working With The Symfony Admin Generator

Object history

Delete related objects warning

Page 65: Working With The Symfony Admin Generator

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?

Page 66: Working With The Symfony Admin Generator

Thanks for listening!Twitter: @jcleveley