代码之家  ›  专栏  ›  技术社区  ›  rob

Zend3窗体筛选器-ParamConverter无法生成有效的窗体

  •  0
  • rob  · 技术社区  · 6 年前

    我真的很困惑我的窗体过滤器。

    我的测试项目包含两个模型。

    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;
    }
    

    如果有人需要抽象实体

    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);
    }
    

    映射器加载类别fine。伟大的。但是表单无效,因为:

    类cashjournal\model\category的对象无法转换为int

    好的,所以我要删除过滤器,但现在它未能设置条目实体的属性,因为setter需要 Category . 表单错误显示:

    输入的步骤无效

    在symfony我可以创建一个 ParamConverter ,它转换 category_id 有效 类别 实体。

    问题 如何使用过滤器作为参数转换器?

    更新 同样,当我将类别“id”转换为int时,我将从表单中得到错误。

    更新2 我将字段集更改为:

    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 回复  |  直到 6 年前
        1
  •  1
  •   rkeet Aurimas    6 年前

    你查过文件了吗 ObjectSelect ?您似乎缺少一些选项,即使用哪个水合器(EntityManager)和标识属性(ID)。 Have a look here .

    例子:

    $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
        ],
    ]);
    

    要验证所选元素,请添加输入筛选器:

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

    输入筛选器不再需要。一个类别已经存在,并且已经过验证。所以,您应该能够选择它。

    如果您有特殊要求,您只需要额外的过滤器/验证器,例如:“一个类别在条目中只能使用一次”,这样您就需要使用 NoObjectExists 验证器。但这里似乎不是这样。


    根据评论和过去问题进行更新

    我觉得你在做很多事情的时候都过于复杂了。似乎您只是想在加载客户端之前填充一个表单。在(从客户机)接收到一个帖子时,您希望将接收到的数据放入表单中,对其进行验证并存储。对的?

    基于此,请为我在一个项目中拥有的用户找到一个完整的控制器。希望你觉得它有帮助。提供它是因为更新偏离了您最初的问题,这可能会帮助您解决问题。

    我已经删除了一些额外的检查和错误抛出,但除此之外是完全工作方式。

    (请注意,我使用的是自己的抽象控制器,请确保用自己的和/或重新创建和匹配的要求替换它)

    我在这段代码中还添加了其他注释来帮助您

    <?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;
        }
    }
    

    希望有帮助…