Как встраивать формы

Зачастую вам может захотеться построить форму, которая будет включать поля из многих разных объектов. Например, форма регистрации может содержать данные, принадлежащие объекту User, а также многие объекты Address. К счастью, это просто и природно с помощью компонента формы.

Встраивание одного объекта

Представьте, что каждый Task принадлежит простому объекту Category. Начните, конечно же, с создания объекта Category:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// src/Entity/Category.php
namespace App\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class Category
{
    /**
     * @Assert\NotBlank()
     */
    public $name;
}

Далее, добавьте новое своейство category к классу Task:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ...

class Task
{
    // ...

    /**
     * @Assert\Type(type="App\Entity\Category")
     * @Assert\Valid()
     */
    protected $category;

    // ...

    public function getCategory()
    {
        return $this->category;
    }

    public function setCategory(Category $category = null)
    {
        $this->category = $category;
    }
}

Tip

Ограничение Valid было добавлено к свойству category. Это создаёт каскадное включение валидации в соответствующую сущность. Если вы опустите это ограничение, то дочерняя сущность не будет валидирована.

Теперь, когда ваше приложение было обновлено так, чтобы отображать новые требование, создайте класс формы так, чтобы объект Category можно было изменять пользователю:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/Form/CategoryType.php
namespace App\Form;

use App\Entity\Category;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CategoryType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name');
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Category::class,
        ));
    }
}

Конечная цель - позволить Category класса Task подвергаться изменениям прямо внутри формы задачи. Чтобы достигнуть этого, добавьте поле category к объекту TaskType, тип которого - экземпляр нового класса CategoryType:

1
2
3
4
5
6
7
8
9
use Symfony\Component\Form\FormBuilderInterface;
use App\Form\CategoryType;

public function buildForm(FormBuilderInterface $builder, array $options)
{
    // ...

    $builder->add('category', CategoryType::class);
}

Поля из``CategoryType`` теперь могут быть отбражены на ряду с полями из класса TaskType.

Отобразите поля Category таким же образом, как оргиниальные поля Task:

  • Twig
    1
    2
    3
    4
    5
    6
    7
    8
    {# ... #}
    
    <h3>Category</h3>
    <div class="category">
        {{ form_row(form.category.name) }}
    </div>
    
    {# ... #}
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    <!-- ... -->
    
    <h3>Category</h3>
    <div class="category">
        <?php echo $view['form']->row($form['category']['name']) ?>
    </div>
    
    <!-- ... -->
    

Когда пользователь отправляет форму, отправленные данные для полей Category используются для создания сущности Category, которая потом устанавливается в поле category экземпляра Task.

К экземпляр Category``можно получить доступ через ``$task->getCategory() и его можно сохранить в БД или использовать так, как вам это нужно.

Встраивание коллекции форм

Вы также можете встроить коллекцию форм в одну форму (представьте форму Category со многими подформами Product). Это делается путём использования типа поля collection.

Чтобы узнать больше, смотрите статью How to Embed a Collection of Forms и справочник CollectionType.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.