Doctrine orm ZF2和x2B;Doctrine2+;DoctrineObject对象:多通关系的空值

Doctrine orm ZF2和x2B;Doctrine2+;DoctrineObject对象:多通关系的空值,doctrine-orm,zend-framework2,zend-form,Doctrine Orm,Zend Framework2,Zend Form,对于项目上的类别结构,我正在构建一个实体。对于add表单,我使用DoctrineObject或。当$parent有一个值时,这可以正常工作,但是当没有父项时,它会给我一个错误,因为没有id来选择父项。在这种情况下,父属性的值应为null 我会创建一个过滤器来实现这一点。此过滤器已执行,但水合器似乎没有得到我希望它执行的操作 有人知道怎么解决这个问题吗 我的实体: use Gedmo\Mapping\Annotation as Gedmo; use Doctrine\ORM\Mapping as

对于项目上的类别结构,我正在构建一个实体。对于add表单,我使用DoctrineObject或。当$parent有一个值时,这可以正常工作,但是当没有父项时,它会给我一个错误,因为没有id来选择父项。在这种情况下,父属性的值应为null

我会创建一个过滤器来实现这一点。此过滤器已执行,但水合器似乎没有得到我希望它执行的操作

有人知道怎么解决这个问题吗

我的实体:
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
use Flex\Entity\Entity;

/**
 * @Gedmo\Tree(type="materializedPath")
 * @ORM\Table(name="categories")
 * @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\MaterializedPathRepository")
 */
class Category extends Entity
{
    /**
     * @ORM\OneToMany(mappedBy="parent", targetEntity="FlexCategories\Entity\Category")
     */
    protected $children;

    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue
     */
    protected $id;

    /**
     * @Gedmo\TreeLevel
     * @ORM\Column(nullable=true, type="integer")
     */
    protected $level;

    /**
     * @ORM\Column(length=64, type="string")
     */
    protected $name;

    /**
     * @Gedmo\TreeParent
     * @ORM\ManyToOne(inversedBy="children", targetEntity="FlexCategories\Entity\Category")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(onDelete="SET NULL", referencedColumnName="id")
     * })
     */
    protected $parent;

    /**
     * @Gedmo\TreePath(appendId=false, endsWithSeparator=false, separator="/", startsWithSeparator=true)
     * @ORM\Column(length=255, nullable=true, type="string", unique=true)
     */
    protected $path;

    /**
     * @Gedmo\Slug(fields={"name"}, unique=false)
     * @Gedmo\TreePathSource
     * @ORM\Column(length=64)
     */
    protected $slug;

    public function setId($value)
    {
        $this->id = $value;
    }    
    public function setName($value)
    {
        $this->name = $value;
    }
    public function setParent($value)
    {
        $this->parent = $value;
    }
}
我的表格:
use DoctrineModule\Stdlib\Hydrator\DoctrineObject;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;

class CategoryForm extends Form implements InputFilterProviderInterface, ServiceManagerAwareInterface
{
    private $_serviceManager;

    public function init()
    {
        // Init hydrator
        $hydrator        = new DoctrineObject($this->_serviceManager->get('doctrine.entitymanager.orm_default'),
                                              'FlexCategories\Entity\Category');

        // Set form basic configurations
        $this->setAttribute('method', 'post')
             ->setHydrator($hydrator);

        // Add parent field
        $this->add(array(
            'name'        => 'parent',
            'type'        => 'Zend\Form\Element\Hidden',
        ));

        // Add name field
        $this->add(array(
            'attributes'  => array(
                'required' => 'required',
            ),
            'name'        => 'name',
            'options'     => array(
                'label'       => 'Name',
            ),
            'type'        => 'Zend\Form\Element\Text',
        ));

        // Add description field
        $this->add(array(
            'name'       => 'description',
            'options'    => array(
                'label'       => 'Description',
            ),
            'type'       => 'Zend\Form\Element\Textarea',
        ));

        // Add CSRF element
        $this->add(array(
            'name'        => 'csrf',
            'type'        => 'Zend\Form\Element\Csrf',
        ));

        // Add submit button
        $this->add(array(
            'attributes'  => array(
                'type'    => 'submit',
                'value'   => 'Save',
            ),
            'name'        => 'submit',
        ));
    }
    public function getInputFilterSpecification()
    {
        return array(
            'description'  => array(
                'filters'     => array(
                    array(
                        'name'    => 'Zend\Filter\StringTrim'
                    ),
                ),
                'required'    => false,
            ),
            'name'         => array(
                'filters'     => array(
                    array(
                        'name'    => 'Zend\Filter\StringTrim'
                    ),
                ),
                'required'    => true,
                'validators'  => array(
                    array(
                        'name'    => 'Flex\Validator\EntityUnique',
                        'options' => array(
                            'entity'    => 'FlexCategories\Entity\Category',
                            'filter'   => array(
                                array('property'     => 'parent',
                                      'value'        => array('_context', 'parent')),
                            ),
                            'property'  => 'name',
                            'serviceLocator'    => $this->_serviceManager,
                        ),
                    ),
                ),
            ),
            'parent'       => array(
                'filters'     => array(
                    array(
                        'name'    => 'Flex\Filter\NullIfEmpty'
                    ),
                ),
                'required'    => false,
            ),
        );
    }

    public function setServiceManager(ServiceManager $serviceManager)
    {
        $this->_serviceManager = $serviceManager;
        $this->init();

        return $this;
    }
}
我的控制器:
use Flex\Controller\AbstractController;
use FlexCategories\Entity\Category;
use FlexCategories\Form\CategoryForm;

class AdminController extends AbstractController
{
    public function addAction()
    {
        // Load form
        $form                = $this->getServiceLocator()->get('FlexCategories\Form\CategoryForm');

        // Create and bind new entity
        $category            = new Category();
        $form->bind($category);

        // Load parent category if present
        $parentId            = $this->params()->fromRoute('id', null);
        if ($parentId !== null)
        {
            if (!is_numeric($parentId))
                throw new \InvalidArgumentException('Invalid parent id specified');

            $entityManager   = $this->getEntityManager();
            $repository      = $entityManager->getRepository('FlexCategories\Entity\Category');
            $parent          = $repository->find($parentId);

            if (!$parent)
                throw new \InvalidArgumentException('Invalid parent id specified');

            $form->get('parent')->setValue($parent->getId());
        }

        // Process request
        $request             = $this->getRequest();
        if ($request->isPost())
        {
            $form->setData($request->getPost());

            if ($form->isValid())
            {                
                $entityManager    = $this->getEntityManager();
                $entityManager->persist($category);
                $entityManager->flush();

                $this->flashMessenger()->addSuccessMessage(sprintf('The category "%1$s" has succesfully been added.', $category->getName()));
                return $this->redirect()->toRoute($this->getEvent()->getRouteMatch()->getMatchedRouteName());
            }
        }

        // Return form
        return array(
            'form'           => $form,
        );
    }
    public function indexAction()
    {
        // Load all categories
        $entityManager        = $this->getEntityManager();
        $repository           = $entityManager->getRepository('FlexCategories\Entity\Category');
        $categories           = $repository->findBy(array(), array('path' => 'asc'));

        return array(
            'categories'      => $categories,
        );
    }
}
我的数据库:

CREATE TABLE `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) DEFAULT NULL,
  `level` int(11) DEFAULT NULL,
  `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `slug` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQ_3AF34668B548B0F` (`path`),
  KEY `IDX_3AF34668727ACA70` (`parent_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


ALTER TABLE `categories`
  ADD CONSTRAINT `FK_3AF34668727ACA70` FOREIGN KEY (`parent_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL;

我可以通过基于“doctrinmodule\Form\ElementObjectSelect”创建一个“HiddenElement”元素并将其用作输入类型来解决此问题。

您需要一个处理“”值的策略。(“”!=null)正如ocramius指出的那样,它可能包含带有“”的主键

您在使用“empty_option”(当前不能将其设置为null)时遇到此问题,表单post将始终传输“”

因此,请为该字段添加一个策略,或将“”转换为null

这可能看起来像:

use Zend\Stdlib\Hydrator\Strategy\DefaultStrategy;

class ForeignKey extends DefaultStrategy
{
    public function hydrate($value)
    {
        if($value == '') {
            return NULL;
        }

        return $value;
    }
}

您需要更改您的表架构,以便在
parent\u id
列中允许
null
值。实际上,我通过添加nullable=thue尝试了这一点,但这并没有什么区别。我应该怎么做?假设您使用的是MySql,并且您的
parent\u id
列的类型为
int(11)
sql将是
ALTER TABLE categories MODIFY parent\u id int(11)默认为空数据库可以将null作为值处理。当我不使用hydrator时,它工作得很好,所以我认为问题在于hydrator或hydrator.Odd的配置,因为我经常使用Gedmo\Tree扩展,唯一一次遇到这个问题是当我忘记在数据库中使列本身为空时。