El código mostrado anteriormente recorre, para cada variable que se pasa a la
plantilla, todas las clases de formulario configuradas en forms.yml
. Aunque
es una solución que funciona bien, penaliza mucho el rendimiento de la aplicación
cuando se pasan muchos formularios a la plantilla o si se dispone de una lista
muy larga de formularios configurados en el archivo YAML. Se trata de una buena
oportunidad para crear un gestor propio de configuración que permita optimizar
este proceso.
La mayor parte de la magia de los gestores de configuración se produce internamente. La cache de configuración se encarga de la lógica de la cache antes de ejecutar el gestor de configuración apropiado, por lo que nos podemos centrar exclusivamente en generar el código necesario para aplicar la configuración YAML.
Todos los gestores de configuración deben implementar los siguientes dos métodos:
static public function getConfiguration(array $configFiles)
public function execute($configFiles)
Al primer método, ::getConfiguration()
, se le pasa un array de rutas de
archivos, para que los procese y junte sus contenidos en un único gran archivo.
En la sfSimpleYamlConfigHandler
utilizada anteriormente este método sólo
utiliza una línea:
static public function getConfiguration(array $configFiles)
{
return self::parseYamls($configFiles);
}
La clase sfSimpleYamlConfigHandler
extiende la clase abstracta sfYamlConfigHandler
,
que incluye varios métodos útiles para procesar los archivos de configuración
YAML:
::parseYamls($configFiles)
::parseYaml($configFile)
::flattenConfiguration($config)
::flattenConfigurationWithEnvironment($config)
Los dos primeros métodos implementan la configuración en cascada. de Symfony. El segundo implementa el mecanismo de configuración basada en entornos.
El método ::getConfiguration()
de nuestro gestor de configuración necesita un
método propio para unir toda la configuración en un único archivo. Crea un
método llamado ::applyInheritance()
para encapsular toda esta lógica:
// lib/config/sfFormYamlEnhancementsConfigHandler.class.php
class sfFormYamlEnhancementsConfigHandler extends sfYamlConfigHandler
{
public function execute($configFiles)
{
$config = self::getConfiguration($configFiles);
// compile data
$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;
}
}
Ahora ya se dispone de un array cuyos valores se han unido mediante la herencia
de clases. De esta forma se evita tener que filtrar la configuración mediante
una instrucción instanceof
para cada objeto de formulario. Además, esta
unión de archivos se realiza en el gestor de configuración, por lo que sólo se
realiza una vez y después se guarda en la cache.
Aplicando una lógica muy sencilla, ya es posible aplicar este array a un objeto de formulario:
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)
{
// ...
}
}
}
Antes de volver a ejecutar el script de las pruebas, se añade una comprobación para la nueva lógica de herencia de clases.
# config/forms.yml
# ...
BaseForm:
body:
errors:
min_length: A base min_length message
required: A base required message
A continuación se comprueba mediante las pruebas unitarias que se está aplicando
un mensaje de tipo required
al formulario y que también se está aplicando ese
mensaje a todos los formularios que heredan de un formulario padre, aunque ellos
mismos no tengan configurado ese mensaje.
$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');
Ejecuta de nuevo las pruebas unitarias para comprobar que el sistema de mejora de formularios sigue funcionando correctamente.