di Kris Wallsmith
Uno dei miei obiettivi personali, come sviluppatore di symfony, è quello di snellire il più possibile il flusso di lavoro su un determinato progetto. Anche se io posso conoscere sia dentro che fuori la base del nostro codice, questa non è una aspettativa ragionevole per tutto il team. Fortunatamente symfony fornisce meccanismi per isolare e centralizzare le funzionalità all'interno di un progetto, rendendo facile per gli altri fare delle modifiche con il minimo della fatica.
Le stringhe nei form
Un eccellente esempio di questo è il framework dei form di symfony. Il framework dei form è un componente potente di symfony, che dà un grande controllo sui form, spostando la loro visualizzazione e convalida, in oggetti PHP. Questa è una manna per gli sviluppatori di applicazioni, perché significa che si possono incapsulare logiche complesse in una sola classe di form, ed estenderla / riutilizzarla in posti diversi.
Tuttavia, dal punto di vista di chi sviluppa la grafica, questa astrazione su come visualizzare un form, potrebbe essere problematica. Dare un'occhiata al seguente form:
La classe che configura il form, assomiglia a questa:
// lib/form/CommentForm.class.php class CommentForm extends BaseForm { public function configure() { $this->setWidget('body', new sfWidgetFormTextarea()); $this->setValidator('body', new sfValidatorString(array( 'min_length' => 12, ))); } }
Il form è quindi visualizzato in un template PHP tipo questo:
<!-- apps/frontend/modules/main/templates/indexSuccess.php --> <form action="#" method="post"> <ul> <li> <?php echo $form['body']->renderLabel() ?> <?php echo $form['body'] ?> <?php echo $form['body']->renderError() ?> </li> </ul> <p><button type="submit">Post your comment now</button></p> </form>
Chi sviluppa il template ha un po 'di controllo su come questo form viene visualizzato. Può cambiare le etichette predefinite in modo che siano un po' più amichevoli:
<?php echo $form['body']->renderLabel('Please enter your comment') ?>
Può aggiungere una classe ai campi in input:
<?php echo $form['body']->render(array('class' => 'comment')) ?>
Queste modifiche sono facili e intuitive. Ma se c'è la necessità di modificare un messaggio di errore?
Il metodo ->renderError()
non accetta parametri, quindi l'unica possibilità
per chi sviluppa i template è quella di aprire il file con la classe del
form, trovare il codice che crea il validatore in questione e modificare il suo
costruttore, in modo che il nuovo messaggio di errore sia associato con
l'appropriato codice di errore.
Nel nostro esempio, chi sviluppa il template avrebbe dovuto apportare la seguente modifica:
// prima $this->setValidator('body', new sfValidatorString(array( 'min_length' => 12, ))); // dopo $this->setValidator('body', new sfValidatorString(array( 'min_length' => 12, ), array( 'min_length' => 'You haven't written enough', )));
C'è un problema? Oops, si! Si è usato un apostrofo dentro una stringa racchiusa da un apice singolo. Naturalmente noi non avremmo mai fatto un errore simile, ma che cosa si può dire a chi sviluppa il template e si perde dentro alla classe di un form?
In tutta serietà, ci si può aspettare che chi sviluppa i template conosca abbastanza bene il framework dei form di symfony, da individuare esattamente dove è definito un messaggio di errore? Ci si aspetta che qualcuno che lavora sulla vista conosca la signature per il costruttore di un validatore?
Si può essere tutti d'accordo sul fatto che la risposta a queste domande sia no. Chi sviluppatori i template fa un sacco di lavoro prezioso, ma è semplicemente irragionevole aspettarsi che qualcuno che non scrive codice per applicazioni, imparare il funzionamento interno del framework dei form di symfony.
YAML: Una soluzione
Per semplificare il processo di modifica delle stringhe di un form, si andrà ad aggiungere un livello di configurazione YAML che migliora ogni oggetto dei form quando viene passato alla vista. Il file di configurazione sarà simile a questo:
# config/forms.yml CommentForm: body: label: Inserisci il tuo commento attributes: { class: comment } errors: min_length: Non hai scritto abbastanza
Questo metodo è molto più semplice! La configurazione si spiega da sola e il problema dell'apostrofo che si è incontrato in precedenza, ora è scomparso. Bene, procediamo!
Filtraggio delle variabili del template
La prima sfida è quella di trovare un punto di aggancio in symfony, che
permetta di filtrare ogni variabile del form passata al template, attraverso
questa configurazione. Per fare questo, si usa l'evento template.filter_parameters
,
che è chiamato dal nucleo di symfony, appena prima di visualizzare un template o
il partial di un template.
// lib/form/sfFormYamlEnhancer.class.php class sfFormYamlEnhancer { public function connect(sfEventDispatcher $dispatcher) { $dispatcher->connect('template.filter_parameters', array($this, 'filterParameters')); } public function filterParameters(sfEvent $event, $parameters) { foreach ($parameters as $name => $param) { if ($param instanceof sfForm && !$param->getOption('is_enhanced')) { $this->enhance($param); $param->setOption('is_enhanced', true); } } return $parameters; } public function enhance(sfForm $form) { // ... } }
note
Si noti che questo codice controlla una opzione is_enhanced
su ogni oggetto
del form, prima di migliorarlo. Questo serve a prevenire che i form vengano
passati dai template ai partial, per essere migliorati per due volte.
Questa classe di miglioramento ha bisogno di essere collegata nella configurazione dell'applicazione:
// apps/frontend/config/frontendConfiguration.class.php class frontendConfiguration extends sfApplicationConfiguration { public function initialize() { $enhancer = new sfFormYamlEnhancer($this->getConfigCache()); $enhancer->connect($this->dispatcher); } }
Ora che si è in grado di isolare le variabili dei form poco prima che vengano passate a un template o a un partial, si ha tutto il necessario per terminare il lavoro. Il compito finale è quello di applicare ciò che è stato configurato nello YAML.
Utilizzare lo YAML
Il modo più semplice per utilizzare questa configurazione YAML in ogni form è caricarla in un array e ciclare attraverso ciascuna configurazione:
public function enhance(sfForm $form) { $config = sfYaml::load(sfConfig::get('sf_config_dir').'/forms.yml'); foreach ($config as $class => $fieldConfigs) { if ($form instanceof $class) { foreach ($fieldConfigs as $fieldName => $fieldConfig) { if (isset($form[$fieldName])) { if (isset($fieldConfig['label'])) { $form->getWidget($fieldName)->setLabel($fieldConfig['label']); } if (isset($fieldConfig['attributes'])) { $form->getWidget($fieldName)->setAttributes(array_merge( $form->getWidget($fieldName)->getAttributes(), $fieldConfig['attributes'] )); } if (isset($fieldConfig['errors'])) { foreach ($fieldConfig['errors'] as $code => $msg) { $form->getValidator($fieldName)->setMessage($code, $msg); } } } } } } }
Ci sono una serie di problemi con questa implementazione. In primo luogo, il
file YAML viene letto dal file system e caricato in sfYaml
ogni volta che un
form viene migliorato. Questo modo di accedere al file system dovrebbe essere
evitato. In secondo luogo, vi sono più livelli di cicli nidificati e una serie
di condizionali che servono solo a rallentare l'applicazione. La soluzione per
entrambi i problemi si trova nella configurazione della cache di symfony.
La configurazione della cache
La configurazione della cache è costituita da un insieme di classi che ottimizzano
l'utilizzo dei file di configurazione YAML, automatizzando la loro traduzione in
codice PHP e scrivendo questo codice nella cartella della cache per l'esecuzione.
Questo meccanismo elimina il sovraccarico necessario per caricare il contenuto
del nostro file di configurazione in sfYaml
, prima di applicare i suoi valori.
Si vuole implementare una configurazione della cache per il nostro form migliorato.
Invece di caricare forms.yml
in sfYaml
, viene chiesta una versione pre-compilata
alla configurazione corrente della cache dell'applicazione.
Per fare questo la classe sfFormYamlEnhancer
avrà bisogno di accedere alla
configurazione corrente della cache dell'applicazione, quindi verrà aggiunta
al costruttore.
class sfFormYamlEnhancer { protected $configCache = null; public function __construct(sfConfigCache $configCache) { $this->configCache = $configCache; $this->configCache->registerConfigHandler('config/forms.yml', 'sfSimpleYamlConfigHandler'); } // ... }
La configurazione della cache ha bisogno di sapere cosa fare quando dall'applicazione
viene richiesto un certo file di configurazione. Per ora è stato detto al
configuratore della cache di usare sfSimpleYamlConfigHandler
per processare
forms.yml
. Questo gestore di configurazione, semplicemente analizza lo YAML
in un array e lo mette in cache come codice PHP.
Con la configurazione della cache messa a posto e un gestore della configurazione
registrato per forms.yml
, si può chiamarlo al posto di sfYaml
:
public function enhance(sfForm $form) { $config = include $this->configCache->checkConfig('config/forms.yml'); // ... }
Questo è molto meglio. Non solo si è eliminato il sovraccarico delle ripetute
analisi dello YAML dopo la prima richiesta, ma si è anche passati all'utilizzo
di include
, che espone questa lettura al bonus della cache nell'op-code.
Copritemi di test!
Prima di andare avanti, verranno scritti alcuni test. Si può iniziare con questo script di base:
// test/unit/form/sfFormYamlEnhancerTest.php include dirname(__FILE__).'/../../bootstrap/unit.php'; $t = new lime_test(3); $configuration = $configuration->getApplicationConfiguration( 'frontend', 'test', true, null, $configuration->getEventDispatcher()); sfToolkit::clearDirectory(sfConfig::get('sf_app_cache_dir')); $enhancer = new sfFormYamlEnhancer($configuration->getConfigCache()); // ->enhance() $t->diag('->enhance()'); $form = new CommentForm(); $form->bind(array('body' => '+1')); $enhancer->enhance($form); $t->like($form['body']->renderLabel(), '/Please enter your comment/', '->enhance() enhances labels'); $t->like($form['body']->render(), '/class="comment"/', '->enhance() enhances widgets'); $t->like($form['body']->renderError(), '/You haven\'t written enough/', '->enhance() enhances error messages');
Eseguendo questo test sull'attuale sfFormYamlEnhancer
, si verifica che tutto
stia funzionando correttamente:
Ora si può fare del refactoring, sapendo che i test ci diranno se si stanno facendo delle modifiche che pregiudicano il corretto funzionamento.
Gestori di configurazione personalizzati
Nel miglioramento di cui sopra, ogni variabile form passata a un template
ciclerà su ogni classe del form, configurata in forms.yml
. Il lavoro viene
eseguito, ma se si passano al template oggetti con più form, o si ha un lungo
elenco di form configurati nello YAML, si inizierà a vedere un impatto nelle
prestazioni. Questa è una buona occasione per scrivere un gestore personalizzato
di configurazione, che possa ottimizzare il processo.
La maggior parte della magia dei gestori di configurazione, avviene dietro le quinte. La cache di configurazione si prende cura della logica di cache prima di eseguire ogni particolare gestore di configurazione, in modo da potersi concentrare solo sulla generazione del codice necessario per applicare la configurazione YAML.
Ogni gestore di configurazione deve implementare i seguenti due metodi:
static public function getConfiguration(array $configFiles)
public function execute($configFiles)
Al primo metodo, ::getConfiguration()
, viene passato un array di percorsi di
file, li analizza e fonde i contenuti in un singolo valore. Nella classe
sfSimpleYamlConfigHandler
usata sopra, questo metodo include solo una linea:
static public function getConfiguration(array $configFiles) { return self::parseYamls($configFiles); }
La classe sfSimpleYamlConfigHandler
estende quella astratta
sfYamlConfigHandler
che comprende una serie di metodi helper per elaborare i
file di configurazione YAML:
::parseYamls($configFiles)
::parseYaml($configFile)
::flattenConfiguration($config)
::flattenConfigurationWithEnvironment($config)
I primi due metodi implementano la configurazione a cascata di symfony. Gli altri due implementano la consapevolezza dell'ambiente.
Il metodo ::getConfiguration()
del nostro gestore di configurazione, avrà
bisogno di un metodo personalizzato per unire le configurazioni basate
sull'ereditarietà delle classi. Creare un metodo ::applyInheritance()
che
incapsuli questa logica:
// lib/config/sfFormYamlEnhancementsConfigHandler.class.php class sfFormYamlEnhancementsConfigHandler extends sfYamlConfigHandler { public function execute($configFiles) { $config = self::getConfiguration($configFiles); // compila i dati $retval = "<?php\n". "// auto-generated by %s\n". "// date: %s\nreturn %s;\n"; $retval = sprintf($retval, __CLASS__, date('Y/m/d H:i:s'), var_export($config, true)); return $retval; } static public function getConfiguration(array $configFiles) { return self::applyInheritance(self::parseYamls($configFiles)); } static public function applyInheritance($config) { $classes = array_keys($config); $merged = array(); foreach ($classes as $class) { if (class_exists($class)) { $merged[$class] = $config[$class]; foreach (array_intersect(class_parents($class), $classes) as $parent) { $merged[$class] = sfToolkit::arrayDeepMerge( $config[$parent], $merged[$class] ); } } } return $merged; } }
Ora si ha un array i cui valori sono stati fusi secondo l'ereditarietà delle
classi. Ciò elimina la necessità di filtrare l'intera configurazione tramite
instanceof
per ogni oggetto del form. In più, questa fusione è fatta nel
gestore di configurazione, in modo che possa avvenire solo una volta e poi
venga memorizzata nella cache.
Ora si può applicare questa fusione di array all'oggetto form, con un po' di semplice logica di ricerca:
class sfFormYamlEnhancer { protected $configCache = null; public function __construct(sfConfigCache $configCache) { $this->configCache = $configCache; $this->configCache->registerConfigHandler('config/forms.yml', 'sfFormYamlEnhancementsConfigHandler'); } // ... public function enhance(sfForm $form) { $config = include $this->configCache->checkConfig('config/forms.yml'); $class = get_class($form); if (isset($config[$class])) { $fieldConfigs = $config[$class]; } else if ($overlap = array_intersect(class_parents($class), array_keys($config))) { $fieldConfigs = $config[current($overlap)]; } else { return; } foreach ($fieldConfigs as $fieldName => $fieldConfig) { // ... } } }
Prima di eseguire nuovamente lo script di test, aggiungiamo l'asserzione per la nuova classe, con la logica di ereditarietà.
# config/forms.yml # ... BaseForm: body: errors: min_length: A base min_length message required: A base required message
Nello script del test, si può verificare che il nuovo messaggio required
venga applicato e verificare che i form figli riceveranno quelli ereditati,
anche se non sono configurati per la classe figlio.
$t = new lime_test(5); // ... $form = new CommentForm(); $form->bind(); $enhancer->enhance($form); $t->like($form['body']->renderError(), '/A base required message/', '->enhance() considers inheritance'); class SpecialCommentForm extends CommentForm { } $form = new SpecialCommentForm(); $form->bind(); $enhancer->enhance($form); $t->like($form['body']->renderLabel(), '/Please enter your comment/', '->enhance() applies parent config');
Eseguire lo script di prova aggiornato, per verificare se il form migliorato sta lavorando come ci si aspetta.
Giocare con l'inclusione dei form
C'è una caratteristica importante nel framework dei form di symfony, che non è
stata ancora considerata: l'inclusione dei form. Se un'istanza di CommentForm
è incorporata in un altro form, i miglioramenti che sono stati fatti su
forms.yml
, non verranno applicati. Questo fatto è abbastanza facile da
verificare con questo script di test:
$t = new lime_test(6); // ... $form = new BaseForm(); $form->embedForm('comment', new CommentForm()); $form->bind(); $enhancer->enhance($form); $t->like($form['comment']['body']->renderLabel(), '/Please enter your comment/', '->enhance() enhances embedded forms');
Questa nuova asserzione dimostra che i form incorporati non sono stati "migliorati":
Mettere a posto questo test, comporta la realizzazione di un gestore di configurazione
più avanzato. Bisogna essere in grado di applicare i miglioramenti configurati
in forms.yml
in un modo più modulare per i form incorporati, quindi si andrà
a generare un metodo potenziato su misura per ciascuna classe configurata di
form. Questi metodi saranno generati dal nostro gestore di configurazione
personalizzato, in una nuova classe "worker".
class sfFormYamlEnhancementsConfigHandler extends sfYamlConfigHandler { // ... protected function getEnhancerCode($fields) { $code = array(); foreach ($fields as $field => $config) { $code[] = sprintf('if (isset($fields[%s]))', var_export($field, true)); $code[] = '{'; if (isset($config['label'])) { $code[] = sprintf(' $fields[%s]->getWidget()->setLabel(%s);', var_export($config['label'], true)); } if (isset($config['attributes'])) { $code[] = ' $fields[%s]->getWidget()->setAttributes(array_merge('; $code[] = ' $fields[%s]->getWidget()->getAttributes(),'; $code[] = ' '.var_export($config['attributes'], true); $code[] = ' ));'; } if (isset($config['errors'])) { $code[] = sprintf(' if ($error = $fields[%s]->getError())', var_export($field, true)); $code[] = ' {'; $code[] = ' $error->getValidator()->setMessages(array_merge('; $code[] = ' $error->getValidator()->getMessages(),'; $code[] = ' '.var_export($config['errors'], true); $code[] = ' ));'; $code[] = ' }'; } $code[] = '}'; } return implode(PHP_EOL.' ', $code); } }
Notare che l'array di configurazione viene controllato per determinare certe chiavi, quando il codice viene generato, piuttosto che in fase di runtime. Questo fornirà un piccolo aumento delle prestazioni.
tip
Come regola generale, la logica che controlla le condizioni della configurazione deve essere eseguita nel gestore di configurazione, non nel codice generato. La logica che controlla le condizioni di esecuzione, come la natura dell'oggetto form "migliorato", deve essere eseguita in fase di runtime.
Il codice generato viene inserito all'interno di una definizione di classe, che viene quindi salvata nella cartella della cache.
class sfFormYamlEnhancementsConfigHandler extends sfYamlConfigHandler { public function execute($configFiles) { $forms = self::getConfiguration($configFiles); $code = array(); $code[] = '<?php'; $code[] = '// auto-generated by '.__CLASS__; $code[] = '// date: '.date('Y/m/d H:is'); $code[] = 'class sfFormYamlEnhancementsWorker'; $code[] = '{'; $code[] = ' static public $enhancable = '.var_export(array_keys($forms), true).';'; foreach ($forms as $class => $fields) { $code[] = ' static public function enhance'.$class.'(sfFormFieldSchema $fields)'; $code[] = ' {'; $code[] = ' '.$this->getEnhancerCode($fields); $code[] = ' }'; } $code[] = '}'; return implode(PHP_EOL, $code); } // ... }
La classe sfFormYamlEnhancer
rinvia alla classe worker generata, per
gestire la manipolazione di oggetti form, ma ora deve tener conto della ricorsione
attraverso l'inclusione dei form. Per fare questo, bisogna processare lo schema
con i campi dei form (che possono essere iterati ricorsivamente) e l'oggetto
form (che comprende i moduli inclusi) in parallelo.
class sfFormYamlEnhancer { // ... public function enhance(sfForm $form) { require_once $this->configCache->checkConfig('config/forms.yml'); $this->doEnhance($form->getFormFieldSchema(), $form); } protected function doEnhance(sfFormFieldSchema $fieldSchema, sfForm $form) { if ($enhancer = $this->getEnhancer(get_class($form))) { call_user_func($enhancer, $fieldSchema); } foreach ($form->getEmbeddedForms() as $name => $form) { if (isset($fieldSchema[$name])) { $this->doEnhance($fieldSchema[$name], $form); } } } public function getEnhancer($class) { if (in_array($class, sfFormYamlEnhancementsWorker::$enhancable)) { return array('sfFormYamlEnhancementsWorker', 'enhance'.$class); } else if ($overlap = array_intersect(class_parents($class), sfFormYamlEnhancementsWorker::$enhancable)) { return array('sfFormYamlEnhancementsWorker', 'enhance'.current($overlap)); } } }
note
I campi degli oggetti di form inclusi, non devono essere modificati dopo che sono stati incorporati. I form inclusi sono memorizzati per l'elaborazione nel form genitore, ma non hanno alcun effetto su come il form padre viene reso.
Con la realizzazione del supporto ai form incorporati, i test ora dovrebbero passare. Eseguire lo script per scoprirlo:
Cosa abbiamo fatto?
Lanciamo alcuni benchmark per essere sicuri di non avere perso tempo. Per
rendere interessanti i risultati, aggiungere alcune classi form a forms.yml
utilizzando un ciclo PHP.
# <?php for ($i = 0; $i < 100; $i++): ?> # Form<?php echo $i ?>: ~ # <?php endfor; ?> #
Creare tutte le classi, eseguendo il seguente frammento di codice:
mkdir($dir = sfConfig::get('sf_lib_dir').'/form/test_fixtures'); for ($i = 0; $i < 100; $i++) { file_put_contents($dir.'/Form'.$i.'.class.php', '<?php class Form'.$i.' extends BaseForm { }'); }
Ora si è pronti per lanciare alcuni benchmarks. Per i risultati che trovate qui sotto, è stato più volte eseguito il seguente comando Apache su un MacBook, finché non si è avuta una deviazione standard inferiore a 2ms.
$ ab -t 60 -n 20 http://localhost/config_cache/web/index.php
Si inizia con un benchmark di base per lanciare l'applicazione senza il
miglioramento collegato al tutto. Commentare sfFormYamlEnhancer
in
frontendConfiguration
e lanciare il benchmark:
Tempo di connessione (ms) min media[+/-sd] mediana max Connessione: 0 0 0.0 0 0 Elaborazione: 62 63 1.5 63 69 Attesa: 62 63 1.5 63 69 Totale: 62 63 1.5 63 69
Ora incollare la prima versione di sfFormYamlEnhancer::enhance()
, che chiama
nella classe direttamente sfYaml
e lancia il benchmark:
Tempo di connessione (ms) min media[+/-sd] mediana max Connessione: 0 0 0.0 0 0 Elaborazione: 87 88 1.6 88 93 Attesa: 87 88 1.6 88 93 Totale: 87 88 1.7 88 94
Si può vedere che si è aggiunta una media di 25ms per ogni richiesta, con un
incremento di quasi il 40%. Ora, annullare la modifica che si è appena fatta a
->enhance()
, in modo che il gestore di configurazione personalizzato sia
ripristinato e lanciare nuovamente il benchmark:
Tempo di connessione (ms) min media[+/-sd] mediana max Connessione: 0 0 0.0 0 0 Elaborazione: 62 63 1.6 63 70 Attesa: 62 63 1.6 63 70 Totale: 62 64 1.6 63 70
Come si può vedere, con la creazione di un gestore di configurazione personalizzato, è stato ridotto il tempo di elaborazione riportandolo al valore di riferimento.
Solo per divertirsi: creare un plugin
Ora che è stato realizzato questo sistema per migliorare gli oggetti dei form con un semplice file YAML di configurazione, perché non inserirlo in un plugin e condividerlo con la comunità? Questa può sembrare una intimidazione per coloro che in passato non hanno pubblicato plugin; vediamo di scacciare ogni possibile paura!
Questo plugin avrà la seguente struttura di file:
sfFormYamlEnhancementsPlugin/ config/ sfFormYamlEnhancementsPluginConfiguration.class.php lib/ config/ sfFormYamlEnhancementsConfigHandler.class.php form/ sfFormYamlEnhancer.class.php test/ unit/ form/ sfFormYamlEnhancerTest.php
C'è bisogno di fare alcune modifiche per facilitare il processo di installazione del plugin. La creazione e il collegamento dell'oggetto "migliorato" può essere incapsulato nella classe di configurazione del plugin:
class sfFormYamlEnhancementsPluginConfiguration extends sfPluginConfiguration { public function initialize() { if ($this->configuration instanceof sfApplicationConfiguration) { $enhancer = new sfFormYamlEnhancer($this->configuration->getConfigCache()); $enhancer->connect($this->dispatcher); } } }
Lo script di test dovrà essere aggiornato per fare riferimento allo script di avvio del progetto:
include dirname(__FILE__).'/../../../../../test/bootstrap/unit.php'; // ...
In ultimo, abilitare il plugin in ProjectConfiguration
:
class ProjectConfiguration extends sfProjectConfiguration { public function setup() { $this->enablePlugins('sfFormYamlEnhancementsPlugin'); } }
Se si vogliono eseguire i test dal plugin, collegarli in
ProjectConfiguration
:
class ProjectConfiguration extends sfProjectConfiguration { // ... public function setupPlugins() { $this->pluginConfigurations['sfFormYamlEnhancementsPlugin']->connectTests(); } }
Ora, quando si chiama qualunque task test:*
, i test vengono eseguiti dal plugin.
Tutte le classi sono collocate nella nuova cartella del plugin, ma c'è un problema: lo script di test si basa sui file che si trovano ancora nel progetto. Questo significa che chiunque possa volere eseguire questi test, non ne sarà in grado, a meno che non abbiano nel loro progetto gli stessi file.
Per risolvere questo problema, c'è bisogno di isolare il codice nella classe
enhancer che chiama la configurazione della cache, in modo che si possa
sovrascriverla nello script di test e utilizzare al suo posto una fixture
forms.yml
.
class sfFormYamlEnhancer { // ... public function enhance(sfForm $form) { $this->loadWorker(); $this->doEnhance($form->getFormFieldSchema(), $form); } public function loadWorker() { require_once $this->configCache->checkConfig('config/forms.yml'); } // ... }
Si può quindi sovrascrivere il metodo ->loadWorker()
nello script di test, per
chiamare direttamente il gestore personalizzato della configurazione. La classe
CommentForm
deve essere spostata nello script di test e il file forms.yml
spostato nella cartella test/fixtures
del plugin.
include dirname(__FILE__).'/../../../../../test/bootstrap/unit.php'; $t = new lime_test(6); class sfFormYamlEnhancerTest extends sfFormYamlEnhancer { public function loadWorker() { if (!class_exists('sfFormYamlEnhancementsWorker', false)) { $configHandler = new sfFormYamlEnhancementsConfigHandler(); $code = $configHandler->execute(array(dirname(__FILE__).'/../../fixtures/forms.yml')); $file = tempnam(sys_get_temp_dir(), 'sfFormYamlEnhancementsWorker'); file_put_contents($file, $code); require $file; } } } class CommentForm extends BaseForm { public function configure() { $this->setWidget('body', new sfWidgetFormTextarea()); $this->setValidator('body', new sfValidatorString(array('min_length' => 12))); } } $configuration = $configuration->getApplicationConfiguration( 'frontend', 'test', true, null, $configuration->getEventDispatcher()); $enhancer = new sfFormYamlEnhancerTest($configuration->getConfigCache()); // ...
Impacchettare il plugin è facile avendo installato sfTaskExtraPlugin
. Basta
lanciare il task plugin:package
e dopo un paio di richieste interattive verrà
creato un package.
$ php symfony plugin:package sfFormYamlEnhancementsPlugin
note
Il codice presente in questo articolo è stato pubblicato come plugin ed è disponibile per il download dal sito con i plugin di symfony:
http://symfony-project.org/plugins/sfFormYamlEnhancementsPlugin
Questo plugin include quello che abbiamo visto qua e molto altro, incluso
il supporto per i file widgets.yml
e validators.yml
, così come
l'integrazione con il task i18n:extract
per internazionalizzare facilmente
i form.
Considerazioni finali
Come si può vedere dal benchmark fatto in questo capitolo, la configurazione della cache di symfony permette di utilizzare la semplicità dei file di configurazione YAML, senza avere in pratica alcun impatto sulle prestazioni.
This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.