En ocasiones los formularios son tan complejos que incluyen cambios de varios objetos diferentes. Un formulario de registro de usuarios por ejemplo puede contener la información relacionada con un objeto User
y uno o más objetos de tipo Address
. Por suerte, embeber formularios dentro de otros formularios es algo sencillo y completamente natural para el componente de formularios de Symfony.
12.8.1. Embebiendo un solo objeto
Imagina que cada objeto Task
pertenece a un objeto simple de tipo Category
. En primer lugar, define el nuevo objeto Category
:
// src/Acme/TaskBundle/Entity/Category.php
namespace Acme\TaskBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
class Category
{
/**
* @Assert\NotBlank()
*/
public $name;
}
A continuación, añade una nueva propiedad category
a la clase Task
:
// ...
class Task
{
// ...
/**
* @Assert\Type(type="Acme\TaskBundle\Entity\Category")
*/
protected $category;
// ...
public function getCategory()
{
return $this->category;
}
public function setCategory(Category $category = null)
{
$this->category = $category;
}
}
Una vez actualizada esta parte de la aplicación, crea una nueva clase de formulario para que el usuario pueda modificar los objetos de tipo Category
:
// src/Acme/TaskBundle/Form/Type/CategoryType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Category',
));
}
public function getName()
{
return 'category';
}
}
Nuestro objetivo es que los datos del objeto Category
relacionado con el objeto Task
se puedan modificar en el propio formulario con el que se gestiona el objeto Task
. Para ello, añade un campo category
al objeto TaskType
y haz que su tipo sea una instancia de la nueva clase CategoryType
:
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->add('category', new CategoryType());
}
Los campos de CategoryType
ahora se pueden mostrar junto a los de la clase TaskType
. Para activar la validación de CategoryType
, añade la opción cascade_validation
a TaskType
:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Task',
'cascade_validation' => true,
));
}
Ahora muestra los campos de Category
de la misma manera que los campos de Task
:
{# ... #}
<h3>Category</h3>
<div class="category">
{{ form_row(form.category.name) }}
</div>
{{ form_rest(form) }}
{# ... #}
<!-- ... -->
<h3>Category</h3>
<div class="category">
<?php echo $view['form']->row($form['category']['name']) ?>
</div>
<?php echo $view['form']->rest($form) ?>
<!-- ... -->
Cuando el usuario envía el formulario, los datos enviados para los campos de Category
se utilizan para crear una instancia de Category
, que después se asigna como valor del campo category
de la instancia de Task
.
La instancia de Category
se puede acceder, como cualquier otro campo, a través del método $task->getCategory()
y la puedes persistir en la base de datos o utilizarla como quieras.
12.8.2. Embebiendo una colección de formularios
También es posible embeber toda una colección de formularios dentro de otro formulario (por ejemplo para embeber muchos formularios de tipo Product
dentro de otro formulario de tipo Category
). Para ello tienes que utilizar el tipo de campo collection
.
Para más información consulta la referencia del tipo de campo collection.