2

I'm stuck on this. Here is what i got:

I got a DocumentEntitiy which is able to have $previosDocuments and $nextDocuments.This relation is bidirectional and both of them are ArrayCollections. But I also need to know some other additional information. This is why I got an DocumentRelation-Entity which holds the additional fields.

Note: I do know that the code below is not correct at all.

This is my code so far:

/**
 * @ORM\Entity
 * @ORM\Table(name="document")
 */
class Document
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

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

    /**
     * @var ArrayCollection $nextDocuments
     * @ORM\OneToMany(targetEntity="App\FrontEndBundle\Entity\DocumentRelation", mappedBy="document", cascade={"persist"})
     */
    protected $nextDocuments;

    /**
     * @var ArrayCollection $prevDocuments
     * @ORM\OneToMany(targetEntity="App\FrontEndBundle\Entity\DocumentRelation", mappedBy="targetDocument", cascade={"persist"})
     */
    protected $prevDocuments;

    /**
     * Document constructor.
     */
    public function __construct()
    {
        $this->prevDocuments = new ArrayCollection();
        $this->nextDocuments = new ArrayCollection();
    }

    /**
     * @param Document $nextDocument
     * @return $this
     */
    public function removeNextDocument($nextDocument)
    {
        if ($this->nextDocuments->contains($nextDocument)) {
            $this->nextDocuments->removeElement($nextDocument);
            $nextDocument->removePrevDocument($this);
        }
        return $this;
    }

    /**
     * @param Document $nextDocument
     * @return $this
     */
    public function addNextDocument($nextDocument)
    {
        if (!$this->nextDocuments->contains($nextDocument)) {
            $this->nextDocuments->add($nextDocument);
            $nextDocument->addPrevDocument($this);
        }
        return $this;
    }

    /**
     * @param Document $prevDocument
     * @return $this
     */
    public function addPrevDocument($prevDocument)
    {
        if (!$this->prevDocuments->contains($prevDocument)) {
            $this->prevDocuments->add($prevDocument);
            $prevDocument->addNextDocument($this);
        }
        return $this;
    }

    /**
     * @param Document $prevDocument
     * @return $this
     */
    public function removePrevDocument($prevDocument)
    {
        if ($this->prevDocuments->contains($prevDocument)) {
            $this->prevDocuments->removeElement($prevDocument);
            $prevDocument->removeNextDocument($this);
        }
        return $this;
    }
    //getter + setter 
}

/**
 * Class DocumentRelation
 * @package App\FrontEndBundle\Entity
 * @ORM\Entity
 * @ORM\Table(name="document_relation")
 */
class DocumentRelation
{

    /**
     * @var Document $targetDocument
     * @ORM\ManyToOne(targetEntity="App\FrontEndBundle\Entity\Document", inversedBy="id")
     * @ORM\JoinColumn(name="target_document_id", referencedColumnName="id")
     */
    protected $targetDocument;

    /**
     * @var Document $document
     * @ORM\ManyToOne(targetEntity="App\FrontEndBundle\Entity\Document", inversedBy="id")
     * @ORM\JoinColumn(name="document_id", referencedColumnName="id")
     */
    protected $document;

    [...] //$id & getter + setter 
}

My problem is, that I don't know how to make them work together. For any idea, help or hint I would be very thankful!

EDIT: What I want a the end is a self-referencing entity with a bidirectional association and some extra fields to hold some additional information about the bidirectional relation.

EDIT 2 @Kuba-Birecki:

public function indexAction()
{
   $this->createDocuments();

   return $this->render('AppFrontEndBundle:Default:index.html.twig');
}

private function createDocuments()
{
    $firstDocument = (new Document())->setName('first');
    $secondDocument = (new Document())->setName('second');

    $firstDocument->addNextDocument($secondDocument);
    $em = $this->getDoctrine()->getManager();
    $em->persist($firstDocument);
    $em->flush();
}

This is the way I try to persist it and this is also how I need to call it.

The exception is bellow

Found entity of type App\FrontEndBundle\Entity\Document on association App\FrontEndBundle\Entity\Document#nextDocuments, but expecting App\FrontEndBundle\Entity\DocumentRelation

Kami Yang
  • 427
  • 5
  • 15

1 Answers1

0

Your annotations look alright, but your business logic doesn't reflect these rules.

$nextDocuments of Document class should contain a collection of DocumentRelation, but in your methods, you try to insert Document objects in there. Same goes for $prevDocuments.

All you need to alter your Document class to work with DocumentRelation objects instead of Document:

class Document
{
    ...

    /**
     * @param DocumentRelation $relation
     * @return $this
     */
    public function removeNextDocument(DocumentRelation $relation)
    {
        if ($this->nextDocuments->contains($relation)) {
            $this->nextDocuments->removeElement($relation);
            $relation->setDocument(null);
        }

        return $this;
    }

    /**
     * @param DocumentRelation $relation
     * @return $this
     */
    public function addNextDocument(DocumentRelation $relation)
    {
        if (!$this->nextDocuments->contains($relation)) {
            $this->nextDocuments->add($relation);
            $relation->setDocument($this);
        }

        return $this;
    }

    /**
     * @param DocumentRelation $relation
     * @return $this
     */
    public function addPrevDocument(DocumentRelation $relation)
    {
        if (!$this->prevDocuments->contains($relation)) {
            $this->prevDocuments->add($relation);
            $relation->setTargetDocument($this);
        }

        return $this;
    }

    /**
     * @param DocumentRelation $relation
     * @return $this
     */
    public function removePrevDocument(DocumentRelation $relation)
    {
        if ($this->prevDocuments->contains($relation)) {
            $this->prevDocuments->removeElement($relation);
            $relation->setTargetDocument(null);
        }

        return $this;
    }
}

Of course, this also means you have to create/fetch these DocumentRelation objects before passing them to these methods.

Then, if for example, you want to check whether $prevDocuments contains a certain Document by passing it directly instead of a DocumentRelation, you'd do it like that:

public function hasPrevDocument(Document $document)
{
    // Iterate over DocumentRelations
    foreach ($this->prevDocuments as $relation) {
        // Check if the relation refers to the wanted document
        if ($relation->getDocument() === $document) {
            return true;
        }
    }

    return false;
}

I hope this helps.

Kuba Birecki
  • 2,926
  • 1
  • 13
  • 16