En esencia, un formulario web es una colección de campos que se muestran y se
vuelven a enviar al servidor. Igualmente, el objeto sfForm
es básicamente
un array de campos de formulario. sfForm
gestiona el proceso completo,
pero los campos individuales son los responsables de mostrarse y validarse.
En Symfony, cada campo de formulario se define mediante dos objetos diferentes:
- Un widget que muestra el código XHTML del campo de formulario
- Un validador que limpia y valida los datos enviados en ese campo
Nota En Symfony, un widget se define como cualquier objeto cuya única tarea consiste en generar código XHTML. Aunque normalmente se utilizan en los formularios, se puede crear un objeto de tipo widget para generar cualquier tipo de código.
6.6.1. Un formulario es un array
Recuerda que un objeto sfForm
es básicamente un array de campos de
formulario. De forma más precisa, sfForm
incluye un array de widgets y un
array de validadores para todos los campos del formularios. Estos dos arrays,
llamados widgetSchema
y validatorSchema
son propiedades de la clase sfForm
.
Para añadir un nuevo campo al formulario, simplemente se añade el widget del
campo en el array widgetSchema
y el validador del campo en el array
validatorSchema
. El siguiente código por ejemplo añade un campo email
en
el formulario:
public function configure()
{
$this->widgetSchema['email'] = new sfWidgetFormInputText();
$this->validatorSchema['email'] = new sfValidatorEmail();
}
Nota Los arrays widgetSchema
y validatorSchema
en realidad son clases llamadas
sfWidgetFormSchema
y sfValidatorSchema
que implementan la interfaz
ArrayAccess
.
6.6.2. Diseccionando el formulario ProductForm
Como la clase ProductForm
hereda de sfForm
, también incluye todos sus
widgets y validadores en los arrays widgetSchema
y validatorSchema
. A
continuación se muestra cómo se organiza cada array en el objeto ProductForm
final.
widgetSchema => array
(
[id] => sfWidgetFormInputHidden,
[name] => sfWidgetFormInputText,
[price] => sfWidgetFormInputText,
[newPhotos] => array(
[0] => array(
[id] => sfWidgetFormInputHidden,
[filename] => sfWidgetFormInputFile,
[caption] => sfWidgetFormInputText,
),
[1] => array(
[id] => sfWidgetFormInputHidden,
[filename] => sfWidgetFormInputFile,
[caption] => sfWidgetFormInputText,
),
),
)
validatorSchema => array
(
[id] => sfValidatorDoctrineChoice,
[name] => sfValidatorString,
[price] => sfValidatorNumber,
[newPhotos] => array(
[0] => array(
[id] => sfValidatorDoctrineChoice,
[filename] => sfValidatorFile,
[caption] => sfValidatorString,
),
[1] => array(
[id] => sfValidatorDoctrineChoice,
[filename] => sfValidatorFile,
[caption] => sfValidatorString,
),
),
)
Nota Al igual que widgetSchema
y validatorSchema
son realmente objetos que se
comportan como arrays, los arrays anteriores definidos mediante las claves
newPhotos
, 0
y 1
son objetos sfWidgetSchema
y sfValidatorSchema
.
Como era de esperar, los campos básicos (id
, name
y price
) se representan
en el primer nivel de cada array. En los formularios que no embeben otros
formularios, tanto widgetSchema
como validatorSchema
solamente tienen un
nivel, que representa los campos básicos del formulario. Los widgets y validadores
de cualquier formulario embebido se representan como subarrays de widgetSchema
y validatorSchema
. A continuación se explica el método que se encarga de este
proceso.
6.6.3. Entendiendo el método sfForm::embedForm()
Como un formulario está compuesto por un array de widgets y otro de validadores,
embeber un formulario en otro consiste fundamentalmente en añadir los arrays de
widgets y validadores de un formulario dentro de los arrays de widgets y
validadores del formulario principal. Este proceso lo realiza completamente el
método sfForm::embedForm()
. El resultado siempre es la creación de unos arrays
widgetSchema
y validatorSchema
multidimensionales, tal y como se mostró
anteriormente.
A continuación se explica la configuración de ProductPhotoCollectionForm
, que
asocia objetos ProductPhotoForm
individuales consigo mismo. Este formulario
intermedio actúa como un formulario contenedor y facilita la organización de
todos los formularios. En primer lugar, veamos el siguiente código extraído de
ProductPhotoCollectionForm::configure()
:
$form = new ProductPhotoForm($productPhoto);
$this->embedForm($i, $form);
El propio formulario ProductPhotoCollectionForm
comienza como un nuevo objeto
de tipo sfForm
. Por tanto, sus arrays widgetSchema
y validatorSchema
están
vacíos.
widgetSchema => array()
validatorSchema => array()
Por su parte, cada formulario ProductPhotoForm
ya contiene tres campos
(id
, filename
y caption
) y sus correspondientes tres elementos en los
arrays widgetSchema
y validatorSchema
.
widgetSchema => array
(
[id] => sfWidgetFormInputHidden,
[filename] => sfWidgetFormInputFile,
[caption] => sfWidgetFormInputText,
)
validatorSchema => array
(
[id] => sfValidatorDoctrineChoice,
[filename] => sfValidatorFile,
[caption] => sfValidatorString,
)
El método sfForm::embedForm()
simplemente añade los arrays widgetSchema
y validatorSchema
de cada ProductPhotoForm
dentro de los arrays widgetSchema
y validatorSchema
del objeto ProductPhotoCollectionForm
vacío.
Al finalizar, los arrays widgetSchema
y validatorSchema
del formulario
contenedor (ProductPhotoCollectionForm
) son arrays multidimensionales que
contienen los widgets y validadores de los dos formularios ProductPhotoForm
.
widgetSchema => array
(
[0] => array
(
[id] => sfWidgetFormInputHidden,
[filename] => sfWidgetFormInputFile,
[caption] => sfWidgetFormInputText,
),
[1] => array
(
[id] => sfWidgetFormInputHidden,
[filename] => sfWidgetFormInputFile,
[caption] => sfWidgetFormInputText,
),
)
validatorSchema => array
(
[0] => array
(
[id] => sfValidatorDoctrineChoice,
[filename] => sfValidatorFile,
[caption] => sfValidatorString,
),
[1] => array
(
[id] => sfValidatorDoctrineChoice,
[filename] => sfValidatorFile,
[caption] => sfValidatorString,
),
)
En el último paso de este proceso, el formulario contenedor resultante
(ProductPhotoCollectionForm
) se embebe directamente en ProductForm
. Esto
se realiza dentro del método ProductForm::configure()
, que aprovecha todo el
trabajo realizado dentro de ProductPhotoCollectionForm
:
$form = new ProductPhotoCollectionForm(null, array(
'product' => $this->getObject(),
'size' => 2,
));
$this->embedForm('newPhotos', $form);
El código anterior produce la estructura definitiva de los arrays widgetSchema
y validatorSchema
que se mostró anteriormente. En realidad, el método embedForm()
es similar a combinar manualmente los arrays widgetSchema
y validatorSchema
:
$this->widgetSchema['newPhotos'] = $form->getWidgetSchema();
$this->validatorSchema['newPhotos'] = $form->getValidatorSchema();