Todavía no hemos tenido en cuenta una de las características más importantes:
los formularios embebidos. Si se embebe una instancia de CommentForm
dentro
de otro formulario, no se le aplicarán las mejoras realizadas en forms.yml
.
Esto es muy fácil de comprobar mediante las pruebas unitarias:
$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');
La nueva comprobación demuestra que las mejoras no se están aplicando en los formularios embebidos:
Para solucionar este problema es imprescindible crear un gestor de configuración
más avanzado. Este nuevo gestor aplica las mejoras de forms.yml
de forma
modular para tener en cuenta los formularios embebidos. Así que se va a generar
un método específico para cada clase de formulario configurada. Estos nuevos
métodos se van a generar en una nueva clase de tipo worker de nuestro gestor
propio de configuración.
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);
}
}
Durante la generación del código se comprueba si existen determinadas claves en el array de configuración, en vez de realizar la comprobación en tiempo de ejecución. Se trata de un pequelo detalle que permite aumentar el rendimiento de la aplicación.
Nota Como regla general, la lógica que comprueba las condiciones de la configuración se debería realizar en el propio gestor de configuración y no en el código generado. La lógica que comprueba las condiciones en tiempo de ejecución, como por ejemplo el tipo de formulario que se está mejorando, deben realizarse en tiempo de ejecución.
Este código generado se incluye dentro de la definición de una clase y luego se guarda en el directorio de la 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 clase sfFormYamlEnhancer
ahora deriva el trabajo de manipulación de los
formularios a la clase de tipo worker generada, pero debe controlar la
recursión de los formularios embebidos. Para ello es necesario procesar en paralelo
el esquema de los campos del formulario (iterándolo de forma recursiva) y el objeto
del formulario (que incluye los formularios embebidos).
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));
}
}
}
Nota Los campos de los formularios embebidos no se deben modificar después de haber sido embebidos. Los formularios embebidos se guardan en su formulario padre simplemente por razones de procesamiento, ya que no afectan a la forma en la que se muestra el formulario padre.
Ahora que ya está disponible el soporte de los formularios embebidos, las pruebas deberían ejecutarse correctamente. Si ejecutas las pruebas, verás el siguiente resultado: