0

I have to deal with a model which may contain a mistake. A Tag system which have to deal with many type of objects. Let's have an example of desired use. Tags entity is a Tag (id, name), Question entity deal with an object which have to use Tag system in a ManyToMany $tags. TagsLink is the registry of all Tags linked to many objects (ie : Question). Question::$tags is a collection of Tags and it contains all Tags linked to any object id_element = Question::$id. This relation can not be filtered with TagsLink::$type_element.

So, my question, is it possible to add a value in the Question::$tags association which filters the collection by id_element AND type_element ? Is it possible to use a fake column to deal with pseudo composite key (id_element, type_element) ? In the association, can I had a restriction with type_element = "entity class name" on @JoinTable ?

So entities definitions :

class Tags {
  /**
   * Tag primary key
   * @ORM\Id;
   * @ORM\Column(type="integer");
   * @ORM\GeneratedValue(strategy="AUTO");
   */
   protected $id;
}


class TagsLink {
  /**
   * Element type (page, widget, etc ...)
   * @ORM\Id
   * @ORM\Column(type="string")
   */
   protected $type_element;

  /**
   * Element foreign key
   * @ORM\Id
   * @ORM\Column(type="integer")
   */
   protected $id_element;
}



class Question {
  /**
   * @ORM\Id
   * @ORM\Column(type="integer");
   * @ORM\GeneratedValue(strategy="AUTO")
   */
   public $id;

  /**
   * Tags linked
   * @ORM\ManyToMany(targetEntity="Application\Entity\Tags", cascade={"persist","remove"})
   * @ORM\JoinTable(name="tags_link",
   *      joinColumns={@ORM\JoinColumn(name="id_element", referencedColumnName="id")},
   *      inverseJoinColumns={@ORM\JoinColumn(name="id_tag", referencedColumnName="id")}
   * );
   * @var ArrayCollection
   */
   protected $tags;
}

Hope I can have some pieces of lights in this cloudy problem...

EDIT : Solution with trait

<?php
/**
 * Fonctionnalité de Tag.
 * Attribuable à une entité quelconque des modèles utilisés.
 *
 * @author lolallalol
 */

namespace Application\DoctrineExtension;

use Application\Entity\Tags;
use Application\Entity\TagsLink;

use Doctrine\ORM\Mapping as ORM;

trait Tagable {

  /**
   * Type de ressource (TagsLink.type_element)
   * @var string
   */
  private $_type;

  /**
   * Collection de tags
   * @var ArrayCollection
   */
  private $_tags;

  /**
   * Filtre les tags rattachés au bon type de ressource
   * Bricollage de l'association avec clé composée sans persistance
   * @ORM\PostLoad
   */
  public function postLoadHandler($item) {
    $o = $item->getObject();
    $class = new \ReflectionClass(get_class($o));
    $type = $class->getConstant('TAG_TYPE');
    $this->setTagType($type);
    $assocs = $item->getEntityManager()->getRepository('Application\Entity\TagsLink')->findByTypeAndId($this->getTagType(), $o->id);
    $tags = array();
    $ids = array();
    if (count($assocs) > 0) {
      foreach ($assocs as $l) {
        $ids[] = $l->id_tag;
      }    
      $tags = $item->getEntityManager()->getRepository('Application\Entity\Tags')->findById($ids);
    }
    $this->_tags = $tags;
  }

  /**
   * Attribution du type de ressource
   * @param string $type
   */
  public function setTagType($type) {
    $this->_type = $type;
  }

  /**
   * Retourne le type de ressource taggué
   * @return string
   * @throws Exception Mauvaise implémentation du Trait Tagable
   */
  public function getTagType() {
    if (!$this->_type) {
      $cname = get_class(parent);
      $class = new \ReflectionClass($cname);
      $type = $class->getStaticPropertyValue('TAG_TYPE');
      if (!$type) {
        throw new Exception('Tagable object "'.$cname.'" whithout const TAG_NAME');
      }
      $this->setTagType($type);
    }
    return $this->_type;
  }


  /**
   * Ajout d'un Tag a une ressource
   * @param Tag $tag Tag à ajouter à la collection
   * @return TagsLink
   */
  public function addTag(Tags $tag) {
    $tagL = new TagsLink();
    $tagL->id_element = parent::id;
    $tagL->type_element = $this->getTagType();
    $tagL->id_tag = $tag->id;
    $this->em->persist($tagL);
    $this->em->flush();
    $this->_tags[] = $tagL;
    return $tagL;
  }

  /**
   * Supprime un tag associé
   * @param Tag $tag Tag à supprimer de la collection
   * @return boolean
   */
  public function removeTag(Tag $tag) {
    $r = false;
    $tagL = new TagsLink();
    $tagL->id_element = parent::id;
    $tagL->type_element = $this->getTagType();
    $tagL->id_tag = $tag->id;    
    if ($this->_tags->contains($tagL)) {
      $r = true;
      $this->_tags->removeElement($tagL);
      $this->em->remove($tagL);
      $this->em->flush();
    }
    return $r;
  }

  /**
   * Retourne la collection des Tags associés à l'objet
   * @return ArrayCollection
   */
  public function getTags() {
    return $this->_tags;
  }

}
  • I'm trying to get this work by using a trait. Seems to be to complicated... – lolallalol Jul 25 '14 at 14:04
  • Can you revised your question as it isn't really clear what you want, you first try to sketch a scenario and after that you ask a few questions. You want a **M:M** on **Tags:Question**, but you do use the fields of `TagsLink` so I've no clue what you want to achieve. – Kwido Jul 25 '14 at 14:23
  • I found a solution with a trait. TagsLink is the association of Tags and objects. In my example, a kind of object associated is Question. To deal with those kind of objects, I added a column TagsLink.type_element. – lolallalol Jul 25 '14 at 14:39
  • Okay, can you post your answer here, so other people can use your example if they encounter the same problem. Don't forget to mark your answer as the solution ;) – Kwido Jul 25 '14 at 14:41

0 Answers0