advanced symfony techniques
DESCRIPTION
Go beyond the documentation and explore some of what's possible if you stretch symfony to its limits. We will look at a number of aspects of symfony 1.4 and Doctrine 1.2 and tease out some powerful functionality you may not have expected to find, but will doubtless be able to use. Topics covered will include routing, forms, the config cache and record listeners. If you're comfortable in symfony and wondering what's next, this session is for you.TRANSCRIPT
![Page 1: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/1.jpg)
Advanced symfony TechniquesKris Wallsmith
![Page 2: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/2.jpg)
@kriswallsmith
• Release Manager for symfony 1.3 & 1.4
• On Symfony and Doctrine teams
• Senior Software Engineer at
• 10 years experience with PHP and web development
• Open source evangelist and international speaker
• Hopeless plugin developer…
![Page 3: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/3.jpg)
• DbFinderPlugin
• sfControlPanelPlugin
• sfDoctrineDynamicFormRelationsPlugin
• sfDoctrineMasterSlavePlugin
• sfFeed2Plugin
• sfFormYamlEnhancementsPlugin
• sfGoogleAnalyticsPlugin
• sfGoogleWebsiteOptimizerPlugin
• sfModerationPlugin
• sfPagerNavigationPlugin
• sfPropelActAsPolymorphicBehaviorPlugin
• sfSimpleBlogPlugin
• sfSimpleCMSPlugin
• sfSimpleForumPlugin
• sfSpyPlugin
• sfSslRequirementPlugin
• sfStatsPlugin
• sfTaskExtraPlugin
• sfWebBrowserPlugin
![Page 4: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/4.jpg)
Please see me if you want tohelp with or take overa plugin's maintenance.
Lots to choose from!
![Page 5: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/5.jpg)
#phpmatsuriOctober 2-3, 2010
Tokyo
![Page 6: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/6.jpg)
• Around 90 attendees
• CakePHP, Symfony, & Lithiumwere represented
• Most folks were CakePHP users
• CakePHP documentation wastranslated early, so…
• Please help translate Symfony2 & Doctrine2 documentation!
#phpmatsuri
![Page 7: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/7.jpg)
![Page 8: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/8.jpg)
PSEUDO CODE AHEAD
CAUTION
![Page 9: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/9.jpg)
Host Aware Routing
![Page 10: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/10.jpg)
domain.com
![Page 11: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/11.jpg)
foobar.domain.com
![Page 12: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/12.jpg)
barfoo.domain.com
![Page 13: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/13.jpg)
homepage: url: / param: { module: main, action: indexOrDash }
![Page 14: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/14.jpg)
homepage: url: / param: { module: main, action: indexOrDash }
if (preg_match('/.../', $r->getHost(), $m))
![Page 15: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/15.jpg)
• ->matchesUrl(...)Does the supplied URL match this route?
class sfRoute
GET / HTTP/1.0Host: foobar.domain.com
![Page 16: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/16.jpg)
• ->matchesUrl(...)Does the supplied URL match this route?
• ->matchesParameters(...)Do the supplied parameters match this route?
class sfRoute
url_for('main/dashboard?username=foobar')
Very slow
![Page 17: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/17.jpg)
• ->matchesUrl(...)Does the supplied URL match this route?
• ->matchesParameters(...)Do the supplied parameters match this route?
• ->generate(...)Generate a URL using this route and these parameters.
class sfRoute
url_for('@dashboard?username=foobar')
![Page 18: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/18.jpg)
->matchesUrl(...)
• $urlThe current URI
• $contextAn array of contextual information, including the current host
• Returns false or an array of parameters extracted from the URI
![Page 19: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/19.jpg)
->matchesParameters(...)
• $paramsAn associative array of parameter names and values
• $contextAn array of contextual information, including the current host
• Returns true or false
![Page 20: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/20.jpg)
->generate(...)
• $paramsAn associative array of parameter names and values
• $contextAn array of contextual information, including the current host
• $absoluteWhether to generate an absolute URL
• Returns the generated URL
![Page 21: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/21.jpg)
Process the host string with a second, internal route
![Page 22: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/22.jpg)
public function __construct(...){ list($host, $pattern) = explode('/', $pattern, 2);
$hostRoute = $this->createHostRoute($host, ...);
parent::__construct(...);}
![Page 23: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/23.jpg)
public function matchesUrl($url, $c){ // check parent::matchesUrl() first
$hp = $hostRoute->matchesUrl('/'.$c['host'], $c);
// include host parameters in return}
![Page 24: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/24.jpg)
public function matchesParameters($p, $c){ $hp = $this->extractHostParams($p);
return parent::matchesParameters($p, $c) && $hostRoute->matchesParameters($hp, $c);}
![Page 25: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/25.jpg)
public function generate($p, $c, $abs){ $hp = $this->extractHostParams($p);
// protocol, prefix...
$host = $hostRoute->generate($hp, $c, false); $uri = parent::generate($p, $c, false);
return $protocol.':/'.$host.$prefix.$uri;}
![Page 26: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/26.jpg)
homepage: url: / param: { module: main, action: indexOrDash }
![Page 27: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/27.jpg)
Hardcoded FTL :(homepage: url: domain.com/ class: sfHostAwareRoute param: { module: main, action: index }
dashboard: url: :username.domain.com/ class: sfHostAwareRoute param: { module: main, action: dashboard }
![Page 28: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/28.jpg)
homepage: url: %APP_HOST%/ class: sfHostAwareRoute param: { module: main, action: index }
dashboard: url: :username.%APP_HOST%/ class: sfHostAwareRoute param: { module: main, action: dashboard }
![Page 29: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/29.jpg)
Custom Config Handler
![Page 30: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/30.jpg)
class sfHostAwareRoutingConfigHandler extends sfRoutingConfigHandler{ protected function parse($configFiles) { return array_map( array($this, 'filterRoute'), parent::parse($configFiles) ); }
// ...}
![Page 31: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/31.jpg)
protected function filterRoute($route){ list($class, $args) = $route;
$args[0] = $this->replaceConstants($args[0]);
return array($class, $args);}
Free FTW!
![Page 32: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/32.jpg)
# config_handlers.ymlconfig/routing.yml: class: sfHostAwareRoutingConfigHandler file: %SF_LIB_DIR%/sfHostAwareRout...
![Page 33: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/33.jpg)
sfHostAwareRoutingPluginAdd subdomains to your routing rules.
![Page 34: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/34.jpg)
Graceful POST Authentication
![Page 35: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/35.jpg)
An example…
![Page 36: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/36.jpg)
![Page 37: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/37.jpg)
![Page 38: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/38.jpg)
CENSORED
CENSORED
![Page 39: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/39.jpg)
![Page 40: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/40.jpg)
Where's my blog post!?!
#FAIL
![Page 41: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/41.jpg)
Extend the security filter
![Page 42: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/42.jpg)
class GracefulSecurityFilter extends sfBasicSecurityFilter{ protected function forwardToLoginAction() { // stash the interrupted request $attr->add(array( 'module' => $context->getActionName(), 'action' => $context->getModuleName(), 'method' => $request->getMethod(), 'params' => $requestParams->getAll(), ), 'stash');
parent::forwardToLoginAction(); }}
![Page 43: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/43.jpg)
# filters.ymlsecurity: class: GracefulSecurityFilter
![Page 44: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/44.jpg)
Replay the stashed requestafter login
![Page 45: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/45.jpg)
// called after authenticationprotected function replayStashedRequest(){ if ($s = $attr->removeNamespace('stash')) { $request->setMethod($s['method']);
$params->clear(); $params->add($s['params']);
$this->forward($s['module'], $s['action']); }}
![Page 46: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/46.jpg)
Extra Security
![Page 47: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/47.jpg)
An example…
![Page 48: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/48.jpg)
![Page 49: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/49.jpg)
![Page 50: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/50.jpg)
# security.ymlacceptInvitation: is_secure: true extra_credentials: account: { lifetime: 300 }
![Page 51: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/51.jpg)
Events to the rescue!
![Page 52: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/52.jpg)
controller.change_action
![Page 53: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/53.jpg)
// connect to the event$ed->connect('controller.change_action', $cb)
![Page 54: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/54.jpg)
// check security.yml$action->getSecurityValue('extra_credentials')
![Page 55: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/55.jpg)
// check current user$u->getAttribute('extra_credentials', array())
![Page 56: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/56.jpg)
// remove any expired credentials$now = time();foreach ($creds as $name => $attr){ if ($now > $attr['expires_at']) { unset($creds[$name]); }}
![Page 57: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/57.jpg)
// stash credentials and referer$u->setAttribute('challenge_credentials', ...)$u->setAttribute('challenge_referer', ...)
![Page 58: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/58.jpg)
// forward to challenge form$controller->forward('security', 'challenge')throw new sfStopException();
![Page 59: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/59.jpg)
// add the granted credentials$now = time();foreach ($new as $name => $attr){ $creds[$name] = array( 'expires_at' => $now + $attr['lifetime'], );}$u->setAttribute('extra_credentials', $creds);
![Page 60: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/60.jpg)
// send them on their way$this->redirect($referer);
![Page 61: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/61.jpg)
sfExtraSecurityPluginRe-prompt your users for authentication.
![Page 62: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/62.jpg)
Javascript Compression
![Page 63: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/63.jpg)
<script src="http://domain.com/widget.js"></script>
![Page 64: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/64.jpg)
class jsActions extends sfActions{ public function executeWidget(sfWebRequest $req) { $this->lightbox = $req->hasParameter('lb'); $this->debug = $req->hasParameter('debug'); }}
![Page 65: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/65.jpg)
<?php if ($debug): ?>console.log("embedding mootools");<?php endif; ?>
var e = document.createElement("script");e.src = "<?php echo public_path('js/moo.js', true) ?>";e.async = true;document.body.appendChild(e);
// etc...
![Page 66: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/66.jpg)
Custom View Class
![Page 67: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/67.jpg)
# module.ymlall: view_class: Javascript
JavascriptView
![Page 68: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/68.jpg)
class JavascriptView extends sfPHPView{ public function render() { return $this->compress(parent::render()); }
protected function compress() { // ... }}
![Page 69: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/69.jpg)
$i = tempnam(sys_get_temp_dir(), __CLASS__);$o = tempnam(sys_get_temp_dir(), __CLASS__);
file_put_contents($i, $content);
shell_exec(vsprintf( 'java -jar %s --type js -o %s %s', array_map('escapeshellarg', array($yui, $o, $i))));
return file_get_contents($o);
![Page 70: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/70.jpg)
Standard Caching
![Page 71: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/71.jpg)
# cache.ymlwidget: enabled: true with_layout: true
![Page 72: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/72.jpg)
developer.yahoo.com/yui/compressor/
![Page 73: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/73.jpg)
A Few Apache Tricks
![Page 74: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/74.jpg)
rm web/.htaccess
![Page 75: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/75.jpg)
AllowOverride None
![Page 76: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/76.jpg)
<Directory /path/to/web> Include /path/to/.htaccess</Directory>
![Page 77: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/77.jpg)
Core Assets
![Page 78: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/78.jpg)
Missing Assets #FAIL :(
![Page 79: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/79.jpg)
AliasMatch /sf/(.*) /path/to/symfony/data/web/sf/$1
AliasMatch /sfDoctrinePlugin/(.*) /path/to/sfDoctrinePlugin/web/$1
NameVirtualHost *:80<VirtualHost _default_:80> # ...
![Page 80: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/80.jpg)
Assets Found FTW!
![Page 81: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/81.jpg)
The Dreaded Trailing Slash…
![Page 82: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/82.jpg)
![Page 83: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/83.jpg)
#FAIL
![Page 84: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/84.jpg)
RewriteEngine OnRewriteRule ^(.*)/$ /$1 [R=301,L]
![Page 85: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/85.jpg)
GET /about/ HTTP/1.1Host: domain.com
HTTP/1.1 301 Moved PermanentlyLocation: http://domain.com/about
![Page 86: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/86.jpg)
Embedded Forms
![Page 87: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/87.jpg)
Person
One book has many authors,one author has many books.
Book
Authors
![Page 88: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/88.jpg)
Book: columns: title: string(255) relations: authors: { class: Person, refClass: BookAuthor }BookAuthor: columns: book_id: integer author_id: integer relations: book: { local: book_id } author: { class: Person, local: author_id }Person: columns: name: string(255)
![Page 89: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/89.jpg)
![Page 90: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/90.jpg)
// embed related forms$this->embedRelation('authors');unset($this['authors_list']);
![Page 91: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/91.jpg)
![Page 92: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/92.jpg)
![Page 93: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/93.jpg)
![Page 94: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/94.jpg)
![Page 95: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/95.jpg)
![Page 96: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/96.jpg)
![Page 97: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/97.jpg)
// embed related forms dynamically!$this->embedDynamicRelation('authors');
![Page 98: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/98.jpg)
form.method_not_found
form.filter_values
![Page 99: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/99.jpg)
// called when a form is configuredpublic function embedDynamicRelation($name){ $rel = $table->getRelation($name); $this->rels[] = $rel;
$this->doEmbed($name, $obj->get($rel->getAlias()));}
![Page 100: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/100.jpg)
// called when a form is boundpublic function filterValues(sfEvent $event, $values){ foreach ($this->rels as $rel) { $name = $rel->getName(); $this->doEmbed($name, $values[$name]); }
$obj->addListener(new DeleteListener($form));}
![Page 101: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/101.jpg)
$parent = new BaseForm();foreach ($values as $i => $value) { if (is_object($value)) { // create form with object } elseif ($value['id']) { // find previously embedded form } else { // create a new form }
$parent->embedForm($i, $child);}
$form->embedForm($rel->getName(), $parent);
![Page 102: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/102.jpg)
// extract existing objects from embedded forms// and compare to the current object collectionpublic function preSave(Doctrine_Event $event){ foreach ($coll as $i => $obj) { $pos = array_search($obj, $existing, true); if (false === $pos) $coll->remove($i);
if ($column['notnull']) $obj->delete(); }}
![Page 103: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/103.jpg)
sfDoctrineDynamicFormRelationsPluginCommon sense embedded forms.
![Page 104: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/104.jpg)
Questions?
• Host aware routing
• Graceful POST authentication
• Extra security
• Javascript compression
• Apache tricks
• Embedded forms
![Page 105: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/105.jpg)
OpenSky is Hiring!http://engineering.shopopensky.com
Please contact me if you're interested.
![Page 106: Advanced symfony Techniques](https://reader031.vdocuments.net/reader031/viewer/2022020101/54b750164a79592b5b8b45d2/html5/thumbnails/106.jpg)
Thank you!