4

I have two entities where each Product can have oneToMany Aspect entities associated to it.

As the Products table is very large I am using bigint for it's ID, and consequently, I am trying to build a composite key for Aspect to use Product ID and a smallint (which I am trying to increment with Product#aspectsCount). However, I am getting a ContextErrorException:

Notice: Undefined index: aspect

My entities are as below (I originally tried indexBy="id") in the hope of using Aspect's numeric ID but I can't seem to get that working either so used name below to be more consistent with examples I've read online):

Product Entity

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

Aspect Entity

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

 }

By extension, if another table should exist "underneath" Aspect, how would such an association be made? Would Doctrine handle the composite key internally or would I need to do something such as:

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...
}
Bendy
  • 3,506
  • 6
  • 40
  • 71
  • `Notice: Undefined index: aspect` where is that error – Waqar Haider Jan 09 '17 at 08:39
  • I am using FOSRestBundle and this error gets returned in JSON when I request a route that uses the Product entity – Bendy Jan 09 '17 at 08:43
  • Are they your complete entities? if not post them as well And post your FOSBundle code for insertion as well. I may learn something new as well – Waqar Haider Jan 09 '17 at 10:56
  • because i see `$this->setCreateDT(new \Datetime); $this->setUpdateDT(new \Datetime);` and ` $product->setAspectsCount($product->getAspectsCount()+1);` but they are not defined anywhere – Waqar Haider Jan 09 '17 at 10:58
  • I'm 'unfortunately' on vacation so can't get the full code to post - but this is the boiled down code which is giving me the problem. The `getAspectsCount()+1` (and related) code you mention is used to store a counter in each Product just so each new Aspect for an Item has an incremented integer for the ID – Bendy Jan 10 '17 at 02:39

2 Answers2

2

in product Entity change the anotations of $Aspets to this

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

Then you need to update your database schema

G. Mansour
  • 696
  • 6
  • 15
  • I fat-finger flagged this answer instead of awarding my bounty(!). I can't raise a flag to admin now as I have already (accidentally) flagged it. Thank you for solving the problem. Can an admin please fix (and delete this comment if possible)? – Bendy Jan 13 '17 at 19:50
1

I guess the problem occurs when you do $this->aspects[$name] = $aspect; It is an ArrayCollection, not an array and even though in most cases it works like one, you cannot set a name for an item in the collection. Besides ArrayCollections are more complex objects and also allow querying and ordering them. Simply call $this->aspects->add($aspect);

As per your last question, you can define multiple join columns, when specifying a manyToOne relationship, so you would just add definitions for multiple columns, instead of one in the case, you needed to relate to an entity which has a composite key.

Lastly, I would like to note one thing form my personal experience: many features, that are built into programming languages and programs as well, often add unnecessary complexity to the software you are writing. If you have the ability (and most of the time you do) you should shy away from things like composite keys/inheritance/etc... . Often times they prove to provide no real benefit, yet might become a nightmare to work with in the future. There are cases when you cannot do without them, but those are few and far between.

In your case you are already wasting time contemplating how would this composite key thingy work when you need to add relations (also composite keys are a bit less performant when it comes to querying) yet even if you didn't use the composite key (just the regular id on the aspect entity instead), calling the getAspects() method would still provide you with an arraycollection that is ordered by ID(by default. this can be changed), so you would still be able to number the items and the ordering will stay the same. Even if you had to implement something like ability for users to change the order of elements, you could still simply add another order field to the entity, and not spend a second thinking how composite key here is going to interact with the rest of the system. And again, these problems do not disappear, just because you find solutions now, they will also add to the complexity of the program in the future too.

grssn
  • 526
  • 6
  • 8
  • Thank you - very good point. The other answer fixed my immediate problem but I am going to step back and reconsider the composite keys as you suggested – Bendy Jan 13 '17 at 19:54