Utilizzo avanzato delle rotte
di Ryan Weaver
Nel core, il framework delle rotte è la mappa che collega ogni url a una specifica ubicazione interna di un progetto symfony e viceversa. Si possono facilmente creare dei meravigliosi URL che rimangono completamente indipendenti dalla logica dell'applicazione. Grazie ai progressi che ha compiuto nelle sue versioni recenti, ora anche il framework dei form si è molto migliorato.
In questo capitolo si illustra come creare una semplice applicazione web in cui ogni cliente utilizza un sottodominio separato (es. cliente1.miodominio.it
e cliente2.miodominio.it
). Estendendo il framework delle rotte, la cosa diventa abbastanza semplice.
Questo capitolo richiede per il progetto l'utilizzo dell'ORM Doctrine.
Preparazione del progetto: un CMS per molti clienti
In questo progetto, una società immaginaria - Sympal Builder - vuole creare un CMS in modo che i suoi clienti possano costruire siti web come sottodomini di sympalbuilder.com
. In particolare, il cliente XXX può vedere il suo sito su xxx.sympalbuilder.com
e usare l'area admin su xxx.sympalbuilder.com/backend.php
.
Il nome
Sympal
è stato preso in prestito dal content management framework (CMF) Sympal, realizzato da Jonathan Wage con symfony.
Questo progetto ha due requisiti fondamentali:
-
Gli utenti dovrebbero essere in grado di creare pagine e specificare il titolo, il contenuto e l'URL di queste pagine.
-
L'intera applicazione dovrebbe essere costruita all'interno di un progetto symfony che gestisce il frontend e il backend di tutti i siti dei clienti, determinando il cliente e caricando sulla base del sottodominio i dati corretti.
Per creare questa applicazione, il server dovrà essere impostato per indirizzare tutti i sottodomini
*.sympalbuilder.com
alla stessa document root, la cartella web del progetto symfony.
Lo schema e i dati
Il database del progetto è formato dagli oggetti Client
e Page
. Ciascun Client
rappresenta un sito del sottodominio ed è costituito da molti oggetti Page
.
---
# config/doctrine/schema.yml
Client:
columns:
name: string(255)
subdomain: string(50)
indexes:
subdomain_index:
fields: [subdomain]
type: unique
Page:
columns:
title: string(255)
slug: string(255)
content: clob
client_id: integer
relations:
Client:
alias: Client
foreignAlias: Pages
onDelete: CASCADE
indexes:
slug_index:
fields: [slug, client_id]
type: unique
Anche se gli indici su ciascuna tabella non sono necessari, sono una buona idea, perché l'applicazione farà frequenti query su queste colonne.
Per portare in vita il progetto, inserire i seguenti dati di test nel file data/fixtures/fixtures.yml
:
---
# data/fixtures/fixtures.yml
Client:
client_pete:
name: Pete's Pet Shop
subdomain: pete
client_pub:
name: City Pub and Grill
subdomain: citypub
Page:
page_pete_location_hours:
title: Location and Hours | Pete's Pet Shop
content: We're open Mon - Sat, 8 am - 7pm
slug: location
Client: client_pete
page_pub_menu:
title: City Pub And Grill | Menu
content: Our menu consists of fish, Steak, salads, and more.
slug: menu
Client: client_pub
I dati di test inseriscono due siti web, ciascuno con una pagina. L'URL completo di ogni pagina è definito sia dalla colonna subdomain
dell'oggetto Client
, che dalla colonna slug
dell'oggetto Page
.
http://pete.sympalbuilder.com/location
http://citypub.sympalbuilder.com/menu
Le rotte
Ogni pagina del sito web Sympal Builder corrisponde direttamente a un oggetto del modello Page
, che definisce il titolo e il contenuto dell'output. Per collegare ogni specifico URL a un oggetto Page
, creare un oggetto rotta di tipo sfDoctrineRoute
che usa il campo slug
. Il seguente codice cercherà automaticamente nel database un oggetto Page
con un campo slug
che corrisponda all'url:
---
# apps/frontend/config/routing.yml
page_show:
url: /
class: sfDoctrineRoute
options:
model: Page
type: object
params:
module: page
action: show
La rotta di cui sopra troverà la corretta corrispondenza per la pagina http://pete.sympalbuilder.com/location
con l'oggetto Page
. Purtroppo la rotta sopra potrebbe anche combaciare con l'URL http://pete.sympalbuilder.com/menu
, nel senso che nel menu del ristorante sarà mostrato sito web di Pete! In questo momento la rotta non è a conoscenza dell'importanza dei sottodomini del cliente.
Per rendere funzionale l'applicazione, la rotta deve essere "intelligente". Essa dovrebbe trovare il Page
corretto basandosi sullo slug
e sul client_id, che può essere determinato dalla corrispondenza dell'host (es.
pete.sympalbuilder.com) con la colonna
subdomaindel modello
Client`. Per poterlo fare, si utilizzerà il framework delle rotte, creando una classe di rotte personalizzata.
Prima, però, bisogna chiarire alcuni retroscena sul funzionamento del sistema di routing.
Come funziona il sistema delle rotte
Una "rotta" in symfony è un oggetto di tipo ~sfRoute
~, che fa due importanti lavori:
-
Generare un URL. Per esempio, se si passa al metodo
page_show
un parametroslug
, dovrebbe essere in grado di generare un URL reale (es./location
). -
Trovare la corrispondenza con un URL in arrivo. Dato l'URL di una richiesta in arrivo, ciascuna rotta deve essere in grado di determinare se l'URL "combacia" con i requisiti della rotta.
Le informazioni per le singole rotte sono generalmente configurate all'interno della cartella config di ciascuna applicazione, collocata in app/nomeapplicazione/config/routing.yml
. Ricordiamo che ogni rotta è "un oggetto di tipo sfRoute
". Allora, come fanno queste semplici voci YAML a diventare oggetti di tipo sfRoute
?
Gestione della configurazione della cache per le rotte
Sebbene la maggior parte delle rotte siano definite in un file YAML, ciascuna voce di questo file al momento della richiesta è trasformata in un oggetto reale, tramite un particolare tipo di classe chiamato gestore del configuratore della cache. Il risultato finale è un codice PHP che che rappresenta ogni rotta dell'applicazione. Anche se la specificità di questo processo va oltre lo scopo di questo capitolo, ci spostiamo alla fine, nella versione compilata della rotta page_show
. Il file compilato si trova in cache/nomeapp/nomeamb/config/config_routing.yml.php
per gli specifici applicazione (nomeapp) e ambiente (nomeamb). Qui di seguito c'è una versione ridotta del codice presente nella rotta page_show
:
new sfDoctrineRoute('/:slug', array ( 'module' => 'page', 'action' => 'show', ), array ( 'slug' => '[^/\\.]+', ), array ( 'model' => 'Page', 'type' => 'object', ));
Il nome della classe di ciascuna rotta è definito dalla chiave
class
presente nel filerouting.yml
. Se non è specificata la chiaveclass
, la rotta diventerà per impostazione predefinita una classesfRoute
. Un'altra classe di rotta comune èsfRequestRoute
, che permette allo sviluppatore di creare delle rotte REST. Una lista completa delle opzioni disponibili è presente nella Guida di riferimento a symfony
Soddisfare la richiesta in arrivo per una rotta specifica
Uno dei compiti principali del framework delle rotte è quello di trovare la corrispondenza tra ciascun URL in arrivo e il corretto oggetto per la rotta. La classe ~sfPatternRouting
~ rappresenta il nucleo del motore delle rotte ed è incaricato di questo compito specifico. Nonostante la sua importanza, uno sviluppatore raramente interagirà direttamente con sfPatternRouting
.
Per fare l'abbinamento con il percorso corretto, sfPatternRouting
scorre ogni sfRoute
e "chiede" alla rotta se corrisponde all'url in arrivo. Internamente, questo significa che sfPatternRouting
chiama il metodo ~sfRoute::matchesUrl()
~ su ciascun oggetto della rotta. Questo metodo restituisce false
se la rotta non corrisponde all'url in entrata.
Tuttavia, se il percorso non corrisponde all'URL in entrata, sfRoute::matchesUrl()
non si limita a restituire true
. Invece, il percorso restituisce un array di parametri che sono fusi nell'oggetto richiesto. Per esempio, l'URL http://pete.sympalbuilder.com/location
corrisponde alla rotta page_show
, il cui metodo matchesUrl()
restituisce il seguente array:
array('slug' => 'location')
Queste informazioni vengono poi fuse nell'oggetto della richiesta e questo è il motivo per cui è possibile accedere alle variabili della rotta (es. slug
) dai file delle azioni e da altri posti.
$this->slug = $request->getParameter('slug');
Come si può intuire, sovrascrivere il metodo sfRoute::matchesUrl()
è un ottimo modo per estendere e personalizzare una rotta, per poterci fare quasi qualsiasi cosa.
Creazione di una classe di rotte personalizzata
Al fine di estendere la rotta page_show
per trovare una corrispondenza sulla base del sottodominio degli oggetti Client
, si creerà una nuova classe personalizzata per le rotte. Creare un file chiamato acClientObjectRoute.class.php
e posizionarlo nella cartella lib/routing
del progetto (bisognerà creare questa cartella):
// lib/routing/acClientObjectRoute.class.php class acClientObjectRoute extends sfDoctrineRoute { public function matchesUrl($url, $context = array()) { if (false === $parameters = parent::matchesUrl($url, $context)) { return false; } return $parameters; } }
L'unico altro passo da fare è quello di istruire la rotta page_show
per usare la classe della rotta. In routing.yml
, aggiornare la chiave class
della rotta:
---
# apps/fo/config/routing.yml
page_show:
url: /
class: acClientObjectRoute
options:
model: Page
type: object
params:
module: page
action: show
Finora, acClientObjectRoute
non aggiunge ulteriori funzionalità, ma tutti i pezzi sono a posto. Il metodo matchesUrl()
fa due lavori specifici.
Aggiungere la logica alla rotta personalizzata
Per aggiungere le necessarie funzionalità alla rotta personalizzata, sostituire il contenuto del file acClientObjectRoute.class.php
con il seguente.
class acClientObjectRoute extends sfDoctrineRoute { protected $baseHost = '.sympalbuilder.com'; public function matchesUrl($url, $context = array()) { if (false === $parameters = parent::matchesUrl($url, $context)) { return false; } // restituisce false se baseHost non viene trovato if (strpos($context['host'], $this->baseHost) === false) { return false; } $subdomain = str_replace($this->baseHost, '', $context['host']); $client = Doctrine_Core::getTable('Client') ->findOneBySubdomain($subdomain) ; if (!$client) { return false; } return array_merge(array('client_id' => $client->id), $parameters); } }
La chiamata iniziale a parent::matchesUrl()
è importante, in quanto passa attraverso il normale processo di ricerca corrispondenza delle rotte. In questo esempio, dal momento che l'URL /location
ha corrispondenza con la rotta page_show
, parent::matchesUrl()
restituirebbe un array contenente la corrispondenza del parametro slug
.
array('slug' => 'location')
In altre parole, tutto il lavoro per trovare la corrispondenza delle rotte viene già fatto per noi, il che permette alla parte rimanente del metodo di focalizzarsi sulla corrispondenza in base al corretto sottodominio Client
.
public function matchesUrl($url, $context = array()) { // ... $subdomain = str_replace($this->baseHost, '', $context['host']); $client = Doctrine_Core::getTable('Client') ->findOneBySubdomain($subdomain) ; if (!$client) { return false; } return array_merge(array('client_id' => $client->id), $parameters); }
Eseguendo una semplice sostituzione di stringhe, siamo in grado di isolare la parte sottodominio dell'host e quindi interrogare il database per vedere se uno degli oggetti Client
ha questo sottodominio. Se nessun oggetto cliente corrisponde al sottodominio, allora viene restituito false
, indicando che la richiesta in ingresso non corrisponde alla rotta. In caso contrario, se c'è un oggetto cliente con il sottodominio corrente, viene aggiunto un parametro extra, client_id
nell'array restituito.
L'array
$context
passato amatchesUrl()
è preassegnato con molte informazioni utili sulla richiesta corrente, compresohost
, un booleanois_secure
,request_uri
, il metodo HTTPmethod
e altro.
Come si comporta veramente la rotta personalizzata? Ora la classe acClientObjectRoute
fa le seguenti cose:
-
L'
$url
in entrata corrisponderà solo sehost
contiene un sottodominio appartenente a uno degli oggettiClient
. -
Se c'è corrispondenza con la rotta, verrà restituito un parametro aggiuntivo
client_id
, per l'oggettoClient
con cui è stata trovata corrispondenza e infine fuso nei parametri della richiesta.
Sfruttare la rotta personalizzata
Ora che il parametro client_id
corretto viene restituito da acClientObjectRoute
, abbiamo accesso a esso tramite l'oggetto richiesta. Per esempio, l'azione page/show
potrebbe utilizzare il client_id
per trovare il giusto oggetto Page
:
public function executeShow(sfWebRequest $request) { $this->page = Doctrine_Core::getTable('Page')->findOneBySlugAndClientId( $request->getParameter('slug'), $request->getParameter('client_id') ); $this->forward404Unless($this->page); }
Il metodo
findOneBySlugAndClientId()
è un tipo di finder magico nuovo in Doctrine 1.2 che esegue una ricerca per oggetti basati su più campi.
Ma il framework delle rotte permette una soluzione ancora più elegante. In primo luogo, aggiungere il seguente metodo alla classe acClientObjectRoute
:
protected function getRealVariables() { return array_merge(array('client_id'), parent::getRealVariables()); }
Con questo pezzo finale, l'azione può contare completamente sulla rotta per restituire il giusto oggetto Page
. L'azione page/show
può essere ridotta a una singola linea.
public function executeShow(sfWebRequest $request) { $this->page = $this->getRoute()->getObject(); }
Senza alcun lavoro supplementare, il codice sopra farà la query per un oggetto Page
basata sulle colonne slug
e client_id
. Inoltre, come tutte le rotte di oggetti, l'azione sarà automaticamente inoltrata a una pagina 404 se non verrà trovato l'oggetto corrispondente.
Ma come funziona? Rotte di oggetti come sfDoctrineRoute
, che estendono la classe acClientObjectRoute
, fanno una interrogazione automatica per il relativo oggetto, in base alle variabili nella chiave url
della rotta. Ad esempio, la rotta page_show
, che contiene la variabile :slug
nella sua url
, interroga per l'oggetto Page
attraverso la colonna slug
.
In questa applicazione, tuttavia, la rotta page_show
deve anche interrogare per gli oggetti Page
basati sulla colonna client_id
. Per fare questo, si sovrascrive ~sfObjectRoute::getRealVariables()
~, che è chiamato internamente per determinare le colonne da utilizzare per l'interrogazione dell'oggetto. Con l'aggiunta del campo client_id
a questo array, acClientObjectRoute
interroga sulla base delle colonne slug
e client_id
.
Le rotte di oggetti ignorano automaticamente le variabili che non corrispondono a una vera colonna. Ad esempio, se la chiave URL contiene una variabile
:page
, ma non esiste nessuna colonnapage
sulla relativa tabella, la variabile sarà ignorata.
A questo punto, la classe della rotta personalizzata implementa tutto quello che è stato richiesto, con poco sforzo. Nelle prossime sezioni, si riutilizzerà la nuova rotta per creare un'area amministrativa specifica per il cliente.
Generare la rotta corretta
Resta un piccolo problema con il modo in cui la rotta è generata. Supponiamo di creare un link a una pagina con il seguente codice:
<?php echo link_to('Locations', 'page_show', $page) ?>
Generated url: /location?client_id=1
Come si può vedere, client_id
è stato automaticamente aggiunto alla url. Ciò si verifica perché la rotta tenta di utilizzare tutte le sue variabili disponibili per generare l'url. Poiché la rotta è a conoscenza sia del parametro slug
che del parametro client_id
, quando genera la rotta usa entrambi.
Per risolvere questo problema, aggiungere il seguente metodo alla classe `acClientObjectRoute:
protected function doConvertObjectToArray($object) { $parameters = parent::doConvertObjectToArray($object); unset($parameters['client_id']); return $parameters; }
Quando l'oggetto della rotta è generato, tenta di recuperare tutte le informazioni necessarie chiamando doConvertObjectToArray()
. Per impostazione predefinita, client_id
è restituito nell'array $parameters
. Togliendolo, si impedisce che venga incluso nella URL generata. Ricordiamo che possiamo permettercelo, in quanto l'informazione Client
è contenuta nel sottodominio stesso.
È possibile sovrascrivere interamente il processo
doConvertObjectToArray()
e gestirlo da soli, aggiungendo un metodotoParams()
alla classe del modello. Questo metodo dovrebbe restituire un array dei parametri che si vuole utilizzare durante la generazione della rotta.
Collezioni di rotte
Per terminare l'applicazione Sympal Builder, c'è bisogno di creare uno spazio per l'amministrazione dove ciascun individuo Client
è in grado di gestire le sue Pages
. Per fare questo, aci sarà bisogno di un insieme di azioni che permetta di elencare, creare, aggiornare e cancellare oggetti Page
. Poiché questi tipi di moduli sono abbastanza comuni, symfony può generare automaticamente il modulo. Eseguire il seguente task dalla linea di comando, per generare un modulo pageAdmin
all'interno di un'applicazione chiamata backend
:
$ php symfony doctrine:generate-module backend pageAdmin Page --with-doctrine-route --with-show
Il task sopra genera un modulo con un file di azioni e relativi template, in grado di fare tutte le modifiche necessarie a qualsiasi oggetto Page
. Potrebbero essere apportate molte personalizzazioni al presente CRUD generato, ma non rientrano nello scopo di questo capitolo.
Ache se i task di cui sopra generano il modulo, si ha ancora bisogno di creare una rotta per ciascuna azione. Passando l'opzione --with-doctrine-route
al task, ciascuna azione viena generata per lavorare con un oggetto rotta. Questo diminuisce la quantità di codice di ogni azione. Ad esempio l'azione edit
contiene una semplice linea:
public function executeEdit(sfWebRequest $request) { $this->form = new PageForm($this->getRoute()->getObject()); }
In conclusione, si hanno bisogno delle rotte per le azioni index
, new
, create
, edit
, update
e delete
. Normalmente, la creazione di queste rotte in modalità RESTful richiederebbe notevoli configurazioni nel file routing.yml
.
Per visualizzare queste rotte, usare il task app:routes
, che mostra un riepilogo per ciascuna rotta di una specifica applicazione:
$ php symfony app:routes backend
>> app Current routes for application "backend"
Name Method Pattern
pageAdmin GET /pages
pageAdmin_new GET /pages/new
pageAdmin_create POST /pages
pageAdmin_edit GET /pages/:id/edit
pageAdmin_update PUT /pages/:id
pageAdmin_delete DELETE /pages/:id
pageAdmin_show GET /pages/:id
Sostituire le rotte con una collezione di rotte
Fortunatamente, symfony fornisce un modo molto più semplice per specificare tutte le rotte che appartengono a un tradizionale CRUD. Sostituire l'intero contenuto del file routing.yml
con questa semplice rotta.
---
pageAdmin:
class: sfDoctrineRouteCollection
options:
model: Page
prefix_path: /pages
module: pageAdmin
Ancora una volta, eseguire il task app:routes
per visualizzare tutte le rotte. Come si può vedere, tutte e sette le rotte precedenti esistono ancora.
$ php symfony app:routes backend
>> app Current routes for application "backend"
Name Method Pattern
pageAdmin GET /pages.:sf_format
pageAdmin_new GET /pages/new.:sf_format
pageAdmin_create POST /pages.:sf_format
pageAdmin_edit GET /pages/:id/edit.:sf_format
pageAdmin_update PUT /pages/:id.:sf_format
pageAdmin_delete DELETE /pages/:id.:sf_format
pageAdmin_show GET /pages/:id.:sf_format
Le collezioni di rotte sono un tipo speciale di oggetti di rotte, che internamente rappresentano più di una rotta. La rotta ~sfDoctrineRouteCollection
~ per esempio, genera automaticamente le sette più comuni rotte richieste da un CRUD. Dietro le quinte, sfDoctrineRouteCollection
non sta facendo altro che creare le stesse sette rotte precedentemente specificate nel file routing.yml
. Le collezioni di rotte, fondamentalmente esistono come scorciatoia per creare un gruppo comune di rotte.
Creazione di una collezione personalizzata di rotte
A questo punto, ogni Client
sarà in grado di modificare i suoi oggetti Page
all'interno di una struttura crud attraverso l'URL /pages
. Purtroppo, ogni Client
al momento può vedere e modificare tutti gli oggetti Page
, sia quelli appartenenti, che quelli non appartenenti a Client
. Ad esempio, http://pete.sympalbuilder.com/backend.php/pages
visualizzerà un elenco con entrambe le pagine delle fixture, la pagina location
del Pet Shop di Pete e la pagina menu
del City Pub.
Per risolvere questo problema, si riutilizza la rotta acClientObjectRoute
che era stata creata per il frontend. La classe sfDoctrineRouteCollection
genera un gruppo di oggetti sfDoctrineRoute
. In questa applicazione invece, abbiamo bisogno di generare un gruppo di oggetti acClientObjectRoute
.
Per fare questo, c'è bisogno di utilizzare una classe personalizzata di collezione di rotte. Creare un nuovo file chiamato acClientObjectRouteCollection.class.php
e metterlo nella cartella lib/routing
. Il suo contenuto è incredibilmente semplice:
// lib/routing/acClientObjectRouteCollection.class.php class acClientObjectRouteCollection extends sfObjectRouteCollection { protected $routeClass = 'acClientObjectRoute'; }
La proprietà $routeClass
definisce la classe che sarà usata durante la creazione di ciascuna rotta sottostante. Ora che ciascuna di queste rotte sottostanti è una rotta acClientObjectRoute
, l'implementazione è effettivamente fatta. Ad esempio, http://pete.sympalbuilder.com/backend.php/pages
ora mostrerà solo una pagina: la pagina location
del Pet Shop di Pete. Grazie alla classe di rotte personalizzata, l'azione index restituisce solo oggetti Page
legati al Client
corretto, sulla base del sottodominio della richiesta. Con poche righe di codice, si è creato un intero modulo backend che può tranquillamente essere utilizzato da più clienti.
Un pezzo mancante: creare nuove pagine
Attualmente, nel backend, quando si creano o si modificano oggetti Page
, compare un select box Client
. Invece di consentire agli utenti di scegliere il Client
(che sarebbe un rischio per la sicurezza), è meglio impostare automaticamente il Client
in base al sottodominio corrente della richiesta.
Per prima cosa, aggiornare l'oggetto PageForm
presente in lib/form/PageForm.class.php
.
public function configure() { $this->useFields(array( 'title', 'content', )); }
Ora, come richiesto, il select box non compare più nel form di Page
. Tuttavia, quando vengono creati nuovi oggetti Page
, il client_id
non viene mai impostato. Per risolvere questo problema, impostare manualmente il relativo Client
in entrambe le azioni new
e create
.
public function executeNew(sfWebRequest $request) { $page = new Page(); $page->Client = $this->getRoute()->getClient(); $this->form = new PageForm($page); }
Questo introduce una nuova funzione, getClient()
, che attualmente non esiste nella classe acClientObjectRoute
. Aggiungiamola alla classe, facendo alcune semplici modifiche :
// lib/routing/acClientObjectRoute.class.php class acClientObjectRoute extends sfDoctrineRoute { // ... protected $client = null; public function matchesUrl($url, $context = array()) { // ... $this->client = $client; return array_merge(array('client_id' => $client->id), $parameters); } public function getClient() { return $this->client; } }
Con l'aggiunta di una proprietà alla classe $client
e impostandola nella funzione matchesUrl()
, si può facilmente rendere l'oggetto Client
disponibile alla rotta. La colonna client_id
dei nuovi oggetti Page
, ora verrà automaticamente e correttamente impostata, sulla base del sottodominio dell'host corrente.
Personalizzare una collezione di oggetti di rotte
Utilizzando il framework delle rotte, è stato facilmente risolto il problema della creazione dell'applicazione Sympal Builder. Al crescere della domanda, lo sviluppatore sarà in grado di riutilizzare le rotte personalizzate per altri moduli dell'area di backend (ad esempio in modo che ogni Client
possa gestire le proprie gallerie di foto).
Un altro motivo comune per creare una collezione personalizzata di rotte è quello di aggiungere rotte usate di frequente. Per esempio, supponiamo che un progetto impieghi molti modelli, ciascuno con una colonna is_active
. Nell'area di amministrazione ci deve essere un modo facile per attivare/disattivare il valore is_active
per qualunque particolare oggetto. In primo luogo, modificare acClientObjectRouteCollection
e istruirlo al fine di aggiungere una nuova rotta alla collezione:
// lib/routing/acClientObjectRouteCollection.class.php protected function generateRoutes() { parent::generateRoutes(); if (isset($this->options['with_is_active']) && $this->options['with_is_active']) { $routeName = $this->options['name'].'_toggleActive'; $this->routes[$routeName] = $this->getRouteForToggleActive(); } }
Il metodo ~sfObjectRouteCollection::generateRoutes()
~ è chiamato quando l'oggetto della collezione è istanziato, è responsabile della creazione di tutte le rotte necessarie e viene aggiunto alla proprietà dell'array $routes
della classe. In questo caso, si ferma la creazione effettiva della rotta in un nuovo metodo protetto chiamato getRouteForToggleActive()
:
protected function getRouteForToggleActive() { $url = sprintf( '%s/:%s/toggleActive.:sf_format', $this->options['prefix_path'], $this->options['column'] ); $params = array( 'module' => $this->options['module'], 'action' => 'toggleActive', 'sf_format' => 'html' ); $requirements = array('sf_method' => 'put'); $options = array( 'model' => $this->options['model'], 'type' => 'object', 'method' => $this->options['model_methods']['object'] ); return new $this->routeClass( $url, $params, $requirements, $options ); }
L'unico passo rimanente è quello di impostare la collezione di rotte in routing.yml
. Si noti che generateRoutes()
cerca un'opzione chiamata with_is_active
prima di aggiungere la nuova rotta. L'aggiunta di questa logica, fornisce un ulteriore controllo nel caso in seguito si voglia usare acClientObjectRouteCollection
in qualche parte in cui non si ha bisogno della rotta toggleActive
:
---
# apps/frontend/config/routing.yml
pageAdmin:
class: acClientObjectRouteCollection
options:
model: Page
prefix_path: /pages
module: pageAdmin
with_is_active: true
Verificare nel task app:routes
che sia presente la nuova rotta toggleActive
. L'unica cosa che rimane da fare è creare l'azione che che farà il lavoro effettivo. Dal momento che si vuole usare questa collezione di rotte e la corrispondente azione in vari moduli, creare un nuovo file backendActions.class.php
nella cartella apps/backend/lib/action
(questa cartella è da creare):
# apps/backend/lib/action/backendActions.class.php class backendActions extends sfActions { public function executeToggleActive(sfWebRequest $request) { $obj = $this->getRoute()->getObject(); $obj->is_active = !$obj->is_active; $obj->save(); $this->redirect($this->getModuleName().'/index'); } }
Infine modificare la classe base della classe pageAdminActions
, per estendere questa nuova classe backendActions
.
class pageAdminActions extends backendActions { // ... }
Che cosa è stato fatto? Aggiungendo una rotta alla collezione di rotte e un'azione associata in un file di azioni base, ogni nuovo modulo può automaticamente utilizzare questa semplice funzionalità usando acClientObjectRouteCollection
ed estendendo la classe backendActions
. In questo modo, le funzionalità comuni possono essere facilmente condivise tra molti moduli.
Opzioni su una collezione di rotte
Le collezioni di oggetti di rotte contengono una serie di opzioni che gli permettono di essere altamente personalizzate. In molti casi, uno sviluppatore può usare queste opzioni per configurare la collezione senza avere bisogno di creare una nuova classe di collezioni di rotte. Un elenco dettagliato sulle opzioni delle collezioni di rotte è disponibile nella Guida di riferimento a symfony
Rotte di azioni
Ogni collezione di oggetti di rotte, accetta tre diffferenti opzioni che determinano le esatte rotte generate nella collezione. Senza entrare nei dettagli, la collezione seguente genera tutte e sette le rotte predefinite con una aggiuntiva collezione di rotte e un oggetto di rotte:
---
pageAdmin:
class: acClientObjectRouteCollection
options:
# ...
actions: [list, new, create, edit, update, delete, show]
collection_actions:
indexAlt: [get]
object_actions:
toggle: [put]
Colonna
Per impostazione predefinita, la chiave primaria del modello è utilizzata in tutti gli URL generati e viene usata per interrogare gli oggetti. Questo, naturalmente, può essere facilmente cambiato. Per esempio, il seguente codice utilizza la colonna slug
al posto della chiave primaria:
---
pageAdmin:
class: acClientObjectRouteCollection
options:
# ...
column: slug
Metodi di modelli
Per impostazione predefinita, la rotta recupera tutti gli oggetti collegati per una collezione di rotte e interroga sulla column
specificata, per le rotte dell'oggetto. Se si ha bisogno di sovrascriverle, aggiungere l'opzione model_methods
alla rotta. In questo esempio, i metodi fetchAll()
e findForRoute
hanno bisogno di essere aggiunti alla classe PageTable
. Entrambi i metodi riceveranno un array di parametri richiesta come parametro:
---
pageAdmin:
class: acClientObjectRouteCollection
options:
# ...
model_methods:
list: fetchAll
object: findForRoute
Parametri predefiniti
Infine, supponiamo di avere bisogno di creare uno specifico parametro di richiesta da rendere disponibile nella request per ciascuna rotta nella collezione. Questo si può fare facilmente con l'opzione default_params
:
---
pageAdmin:
class: acClientObjectRouteCollection
options:
# ...
default_params:
foo: bar
Considerazioni finali
Il lavoro basilare del framework delle rotte - trovare le corrispondenze e generare url - si è evoluto in un sistema completamente personalizzabile in grado di gestire le URL più complesse. Prendendo il controllo degli oggetti rotta, la speciale struttura delle URL può essere astratta dalla business logic e tenuta totalmente all'interno della rotta a cui essa appartiene. Il risultato finale è un maggiore controllo, maggiore flessibilità e gestibilità di codice.
インデックス
Document Index
-
Utilizzo avanzato delle rotte
- Preparazione del progetto: un CMS per molti clienti
- Come funziona il sistema delle rotte
- Creazione di una classe di rotte personalizzata
- Collezioni di rotte
- Creazione di una collezione personalizzata di rotte
- Personalizzare una collezione di oggetti di rotte
- Opzioni su una collezione di rotte
- Considerazioni finali
関連ページリスト
Related Pages
Introduzione
Utilizzo avanzato delle rotte
Migliorare la propria produttività
Email
Widget e validatori personalizzati
L'utilizzo avanzato dei form
Estendere la Web Debug Toolbar
Uso avanzato di Doctrine
Sfruttare l'ereditarietà delle tabelle di Doctrine
Symfony all'interno
Windows e symfony
Sviluppare su Facebook
Sfruttare la potenza della linea di comando
Lavorare con la cache della configurazione di symfony
Lavorare con la comunità di symfony
Appendice A - codice JavaScript per sfWidgetFormGMapAddress
A proposito degli autori
Appendice B - Esempio di installazione personalizzata
Appendice C - Licenza
A proposito dei traduttori

日本語ドキュメント
Japanese Documents
2011/01/18 Chapter 17 - Extending Symfony
2011/01/18 The generator.yml Configuration File
2011/01/18 Les tâches
2011/01/18 Emails
2010/11/26 blogチュートリアル(8) ビューの作成
リリース情報
Release Information
- 2.0 : 2.0.15(2011/05/30)
Symfony2日本語ドキュメント - 1.4 : 1.4.18(2012/05/30)
Changelog

