Фильтр форм Zend3 - ParamConverter не может сгенерировать правильную форму - PullRequest
0 голосов
/ 20 ноября 2018

Я действительно запутался в своем фильтре форм.

Мой тестовый проект содержит 2 модели.

class Category extends AbstractEntity 
{
    use Nameable; // just property name and getter and setter

    /**
     * @var boolean
     * @ORM\Column(name="issue", type="boolean")
     */
    private $issue;

    /**
     * @var Collection|ArrayCollection|Entry[]
     *
     * @ORM\OneToMany(targetEntity="CashJournal\Model\Entry", mappedBy="category", fetch="EAGER", orphanRemoval=true, cascade={"persist", "remove"})
     */
    private $entries;
}

запись

class Entry extends AbstractEntity
{
    use Nameable;

    /**
     * @var null|float
     *
     * @ORM\Column(name="amount", type="decimal")
     */
    private $amount;

    /**
     * @var null|Category
     *
     * @ORM\ManyToOne(targetEntity="CashJournal\Model\Category", inversedBy="entries", fetch="EAGER")
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false)
     */
    protected $category;

    /**
     * @var null|DateTime
     *
     * @ORM\Column(name="date_of_entry", type="datetime")
     */
    private $dateOfEntry;
}

И если кто-тотребуется AbstractEntity

abstract class AbstractEntity implements EntityInterface
{
    /**
     * @var int
     * @ORM\Id
     * @ORM\Column(name="id", type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;
}

Каждая категория может иметь много записей.Я использую Doctrine для этого отношения.И это прекрасно работает.

У меня есть форма на основе этого набора полей:

$this->add([
    'name' => 'id',
    'type' => Hidden::class
]);

$this->add([
    'name' => 'name',
    'type' => Text::class,
    'options' => [
        'label' => 'Name'
    ]
]);

$this->add([
    'name' => 'amount',
    'type' => Number::class,
    'options' => [
        'label' => 'Summe'
    ]
]);

$this->add([
    'name' => 'date_of_entry',
    'type' => Date::class,
    'options' => [
        'label' => 'Datum'
    ]
]);

$this->add([
    'name' => 'category',
    'type' => ObjectSelect::class,
    'options' => [
        'target_class' => Category::class,
    ]
]);

Так что моя форма отображает раскрывающийся список с моими категориями.Да, хорошо.

Чтобы загрузить категорию для моей сущности входа, я использую фильтр.

$this->add([
    'name' => 'category',
    'required' => true,
    'filters' => [
        [
            'name' => Callback::class,
            'options' => [
                'callback' => [$this, 'loadCategory']
            ]
        ]
    ]
]);

И обратный вызов:

public function loadCategory(string $categoryId)
{
    return $this->mapper->find($categoryId);
}

Маппер загружает категорию отлично,отличный.Но форма недействительна, потому что:

Объект класса CashJournal \ Model \ Category не может быть преобразован в int

Хорошо, поэтому я удаляю фильтр, нотеперь он не смог установить атрибуты Entry Entity, потому что установщик нуждается в Category.Ошибка формы гласит:

Ввод неверный шаг

В Symfony я могу создать ParamConverter, который преобразует category_id в действительный Category Entity.

Вопрос Как я могу использовать фильтр в качестве моего ParamConver?

Обновление Также, когда я приведу category_id к int, я получу ошибку из формы.

Обновление 2 Я изменил свой FieldSet на:

class EntryFieldSet extends Fieldset implements ObjectManagerAwareInterface
{
    use ObjectManagerTrait;

    /**
     * {@inheritDoc}
     */
    public function init()
    {
        $this->add([
            'name' => 'id',
            'type' => Hidden::class
        ]);

        $this->add([
            'name' => 'name',
            'type' => Text::class,
            'options' => [
                'label' => 'Name'
            ]
        ]);

        $this->add([
            'name' => 'amount',
            'type' => Number::class,
            'options' => [
                'label' => 'Summe'
            ]
        ]);

        $this->add([
            'name' => 'date_of_entry',
            'type' => Date::class,
            'options' => [
                'label' => 'Datum'
            ]
        ]);

        $this->add([
            'name' => 'category',
            'required' => false,
            'type' => ObjectSelect::class,
            'options' => [
                'target_class' => Category::class,
                'object_manager' => $this->getObjectManager(),
                'property'       => 'id',
                'display_empty_item' => true,
                'empty_item_label'   => '---',
                'label_generator' => function ($targetEntity) {
                    return $targetEntity->getName();
                },
            ]
        ]);

        parent::init();
    }
} 

Но это будет завершено с сообщением об ошибке:

Entry :: setDateOfEntry () должен быть экземпляром DateTime, строка должна иметь значение

1 Ответ

0 голосов
/ 21 ноября 2018

Вы проверили документацию для ObjectSelect?Похоже, вам не хватает нескольких опций, а именно, какой гидратор (EntityManager) и определение свойства (id) для использования. Взгляните сюда .

Пример:

$this->add([
    'type'    => ObjectSelect::class,
    'name'    => 'category',                           // Name of property, 'category' in your question
    'options' => [
        'object_manager' => $this->getObjectManager(), // Make sure you provided the EntityManager to this Fieldset/Form
        'target_class'   => Category::class,           // Entity to target
        'property'       => 'id',                      // Identifying property
    ],
]);

Чтобы проверить выбранный элемент, добавьте в ваш InputFilter:

$this->add([
    'name'     => 'category',
    'required' => true,
]);

Больше не требуется для InputFilter.Категория уже существует и как таковая была проверена ранее.Итак, вы должны просто иметь возможность выбрать его.

Вам понадобятся дополнительные фильтры / валидаторы, только если у вас есть особые требования, например: «Категория может использоваться только один раз в записях», поэтому вам необходимо использовать валидатор NoObjectExists.Но, похоже, это не так.


ОБНОВЛЕНИЕ НА ОСНОВЕ КОММЕНТАРИЙ И ПРОШЕДШИХ ВОПРОСОВ

Я думаю, вы слишком усложняете то, что пытаетесь сделать.Кажется, вы хотите просто заполнить форму, прежде чем загружать ее на стороне клиента.Получив POST (от клиента), вы хотите поместить полученные данные в форму, проверить их и сохранить.Правильный?

Исходя из этого, пожалуйста, найдите полный контроллер для пользователя, который у меня есть в одном из моих проектов.Надеюсь, вы найдете это полезным.Предоставление, потому что обновления отклоняются от вашего первоначального вопроса, и это может помочь вам.

Я удалил некоторые дополнительные проверки и выбрасывание ошибок, но в остальном работает полностью.

(Обратите внимание, что я использую свой собственный абстрактный контроллер, обязательно замените его на свои собственные и / или пересоздайте и сопоставьте требования)

Я также разместил дополнительные комментарии на протяжении всего этогокод, который поможет вам

<?php

namespace User\Controller\User;

use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\ORMException;
use Exception;
use Keet\Mvc\Controller\AbstractDoctrineActionController;
use User\Entity\User;
use User\Form\UserForm;
use Zend\Http\Request;
use Zend\Http\Response;

class EditController extends AbstractDoctrineActionController
{
    /**
     * @var UserForm
     */
    protected $userEditForm; // Provide this

    public function __construct(ObjectManager $objectManager, UserForm $userEditForm)
    {
        parent::__construct($objectManager); // Require this in this class or your own abstract class

        $this->setUserEditForm($userEditForm);
    }

    /**
     * @return array|Response
     * @throws ORMException|Exception
     */
    public function editAction()
    {
        $id = $this->params()->fromRoute('id', null);
        // check if id set -> else error/redirect

        /** @var User $entity */
        $entity = $this->getObjectManager()->getRepository(User::class)->find($id);
        // check if entity -> else error/redirect


        /** @var UserForm $form */
        $form = $this->getUserEditForm();  // GET THE FORM
        $form->bind($entity);              // Bind the Entity (object) on the Form

        // Only go into the belof if() on POST, else return Form. Above the data is set on the Form, so good to go (pre-filled with existing data)

        /** @var Request $request */
        $request = $this->getRequest();
        if ($request->isPost()) {
            $form->setData($request->getPost());       // Set received POST data on Form

            if ($form->isValid()) {                    // Validates Form. This also updates the Entity (object) with the received POST data
                /** @var User $user */
                $user = $form->getObject();            // Gets updated Entity (User object)

                $this->getObjectManager()->persist($user); // Persist it

                try {
                    $this->getObjectManager()->flush();    // Store in DB
                } catch (Exception $e) {

                    throw new Exception('Could not save. Error was thrown, details: ', $e->getMessage());
                }

                return $this->redirectToRoute('users/view', ['id' => $user->getId()]);
            }
        }

        // Returns the Form with bound Entity (object). 
        // Print magically in view with `<?= $this->form($form) ?>` (prints whole Form!!!)

        return [
            'form' => $form,
        ];
    }

    /**
     * @return UserForm
     */
    public function getUserEditForm() : UserForm
    {
        return $this->userEditForm;
    }

    /**
     * @param UserForm $userEditForm
     *
     * @return EditController
     */
    public function setUserEditForm(UserForm $userEditForm) : EditController
    {
        $this->userEditForm = $userEditForm;

        return $this;
    }
}

Надеюсь, это поможет ...

...