ContextErrorException:将复合键用于与 Symfony 3 实体的学说关联时的未定义索引

ContextErrorException: Undefined Index when using composite key for doctrine associations with Symfony 3 entities

我有两个实体,每个 Product 可以有一个关联的 Aspect 个实体。

由于产品 table 非常大,我使用 bigint 作为它的 ID,因此,我正在尝试为 Aspect[=36= 构建一个复合键] 使用产品 ID 和 smallint(我试图用 Product#aspectsCount 递增)。但是,我收到 ContextErrorException:

Notice: Undefined index: aspect

我的实体如下(我最初尝试 indexBy="id") 希望使用 Aspect 的数字 ID 但我似乎也无法正常工作所以在下面使用 name 以与我在网上阅读的示例更加一致):

产品实体

class Product
{
    /**
     * @ORM\Column(type="bigint", options={"unsigned"=true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\OneToMany(targetEntity="Aspect", mappedBy="product", cascade={"all"}, indexBy="name")
     */
    private $aspects;

    /**
     * @ORM\Column(name="aspectsCount", type="smallint", options={"unsigned"=true}, nullable=false)
     */
    private $aspectsCount;

    public function __construct()
    {
        $this->aspects = new ArrayCollection();

        $this->setCreateDT(new \Datetime);
        $this->setUpdateDT(new \Datetime);
        $this->aspectsCount = 0;
    }


    /**
     * Add aspect
     *
     * @param \AppBundle\Entity\Aspect $aspect
     *
     * @return product
     */
    public function addAspect($name)
    {
        $aspect = new Aspect($this, $name);
        $this->aspects[$name] = $aspect;

        return $this;
    }

    /**
     * Remove aspect
     *
     * @param \AppBundle\Entity\Aspect $aspect
     */
    public function removeAspect(\AppBundle\Entity\Aspect $aspect)
    {
        $this->aspects->removeElement($aspect);
        $this->setAspectsCount($this->aspectsCount-1);
    }
}

方面实体

class Aspect
{
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Product", inversedBy="aspects") 
     * @ORM\JoinColumn(name="product_id", referencedColumnName="id")
     */
    private $product;

    /**
     * @ORM\Id
     * @ORM\Column(type="smallint", options={"unsigned"=true}, nullable=false)
     */
    private $id;

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

    public function __construct($product, $name)
    {
        $product->setAspectsCount($product->getAspectsCount()+1);

        $this->product = $product;
        $this->id = $product->getAspectsCount();
        $this->name = $name;
    }

 }

推而广之,如果另一个table应该存在"underneath" Aspect,如何建立这样的关联? Doctrine 会在内部处理复合键还是我需要做一些事情,例如:

class Aspect_subtype
{
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Product")   
     * @ORM\JoinColumn(name="product_id", referencedColumnName="id")
     */
    private $product;

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Aspect")    
     * @ORM\JoinColumn(name="aspect_id", referencedColumnName="id")
     */
    private $aspect;

    /**
     * @ORM\Id
     * @ORM\Column(type="smallint", options={"unsigned"=true}, nullable=false)
     */
    private $id;

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

// etc...
}

我想当您执行 $this->aspects[$name] = $aspect; 时会出现问题; 它是一个 ArrayCollection,而不是一个数组,尽管在大多数情况下它像一个数组一样工作,但您不能为集合中的项目设置名称。除了 ArrayCollections 之外,还有更复杂的对象,还允许查询和排序它们。只需调用 $this->aspects->add($aspect);

根据您的最后一个问题,您可以在指定多对一关系时定义多个连接列,因此您只需为多个列添加定义,而不是一个,在这种情况下,您需要关联一个实体,该实体具有复合键。

最后,我想根据我的个人经验指出一件事:编程语言和程序中内置的许多功能通常会给您编写的软件增加不必要的复杂性。如果你有能力(而且大多数时候你有能力),你应该回避复合 keys/inheritance/etc... 之类的东西。很多时候,它们被证明没有提供真正的好处,但可能会成为未来工作的噩梦。有些情况下你离不开它们,但这种情况很少见。

在你的情况下,你已经在浪费时间考虑当你需要添加关系时这个复合键如何工作(复合键在查询时性能稍差),即使你没有使用复合键(只是方面实体上的常规 ID),调用 getAspects() 方法仍会为您提供一个按 ID 排序的数组集合(默认情况下。这可以更改),因此您仍然可以对项目进行编号,顺序将保持不变。即使您必须实现类似让用户更改元素顺序的功能,您仍然可以简单地向实体添加另一个订单字段,而不用花一秒钟思考这里的组合键将如何与系统的其余部分交互.而且,这些问题并没有消失,只是因为你现在找到了解决方案,它们也会在未来增加程序的复杂性。

在产品实体中将 $Aspects 的注释更改为此

     /**
     *@ORM\ManyToOne(targetEntity="Aspect")
     *@ORM\JoinColumn(name="Client", referencedColumnName="product_id",onDelete="CASCADE")
     */
    private $aspects;

然后您需要更新数据库架构