Last active
August 29, 2015 14:10
-
-
Save Taluu/2b07700c99edcd8ef8aa to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\TagBundle\Entity; | |
use Symfony\Component\Validator\Constraints as Assert; | |
use Doctrine\ORM\Mapping as ORM, | |
Doctrine\Common\Collections\ArrayCollection; | |
/** | |
* Tag Entity | |
* | |
* @ORM\Entity(repositoryClass="Acme\TagBundle\Entity\TagRepository") | |
* @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="hash_idx", columns={"hash"})}) | |
*/ | |
class Tag | |
{ | |
/** | |
* @ORM\Id | |
* @ORM\Column(type="integer") | |
* @ORM\GeneratedValue(strategy="AUTO") | |
*/ | |
private $id; | |
/** | |
* @Assert\NotNull | |
* @Assert\NotBlank | |
* | |
* @ORM\Column(type="string", nullable=false) | |
*/ | |
private $name; | |
/** | |
* @var TagRelation[] | |
* | |
* @ORM\OneToMany(targetEntity="Acme\TagBundle\Entity\TagRelation", mappedBy="tag", cascade={"persist", "merge", "remove"}, orphanRemoval=true) | |
*/ | |
private $relations; | |
/** @var TaggableInterface[] */ | |
private $assignees; | |
public function __construct($name = null) | |
{ | |
$this->name = $name; | |
$this->assignees = new ArrayCollection; | |
$this->relations = new ArrayCollection; | |
} | |
public function getId() | |
{ | |
return $this->id; | |
} | |
/** @return string */ | |
public function getName() | |
{ | |
return $this->name; | |
} | |
public function setName($name) | |
{ | |
$this->name = $name; | |
return $this; | |
} | |
public function getAssignees() | |
{ | |
if (null === $this->assignees) { | |
$this->assignees = new ArrayCollection; | |
} | |
return $this->assignees; | |
} | |
public function hasAssignment(TaggableInterface $object) | |
{ | |
return $this->getAssignees()->contains($object); | |
} | |
public function unassign(TaggableInterface $object) | |
{ | |
if ($this->hasAssignment($object)) { | |
$this->getAssignees()->removeElement($object); | |
} | |
return $this; | |
} | |
public function assign(TaggableInterface $object) | |
{ | |
if (!$this->hasAssignment($object)) { | |
$this->getAssignees()->add($object); | |
} | |
return $this; | |
} | |
public function getRelations() | |
{ | |
return $this->relations; | |
} | |
public function addRelation(TagRelation $relation) | |
{ | |
$this->getRelations()->add($relation); | |
return $this; | |
} | |
public function removeRelation(TagRelation $relation) | |
{ | |
$this->getRelations()->removeElement($relation); | |
return $this; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\TagBundle\Entity; | |
use OutOfBoundsException; | |
/** | |
* Interface saying that an object can have tags | |
* | |
* Should be used with the TagTrait | |
*/ | |
interface TaggableInterface | |
{ | |
/** @return integer */ | |
public function getId(); | |
/** @return Tag[] */ | |
public function getTags(); | |
/** Set the collection of tags of this object */ | |
public function setTags($tags); | |
/** Add a tag into this object */ | |
public function addTag(Tag $tag); | |
/** Remove the tag from this object */ | |
public function removeTag(Tag $tag); | |
/** | |
* Checks if the tag $name is assigned to this object | |
* | |
* @param string $name Tag's name | |
* @return boolean true if it exists, false otherwise | |
*/ | |
public function hasTag($name); | |
/** | |
* Get the tag named $name | |
* | |
* @param string $name Tag's name | |
* | |
* @return Tag | |
* @throws OutOfBoundsException If the tag is not affiliated to this object | |
*/ | |
public function getTag($name); | |
/** | |
* Checks if the tags are synchronized with this object | |
* | |
* This checks that all tags elements in the tags collection is a Tag | |
* If there is something else, it is considered as not synchronized quite yet | |
* | |
* @return boolean | |
*/ | |
public function areTagsSynchronized(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\TagBundle\Entity; | |
use Doctrine\ORM\Mapping as ORM, | |
Doctrine\ORM\Proxy\Proxy; | |
/** | |
* Tag Relation Class | |
* | |
* @ORM\Entity | |
* @ORM\Table(indexes={@ORM\Index(name="object_idx", columns={"object_class", "object_id"})}) | |
*/ | |
class TagRelation | |
{ | |
/** | |
* @ORM\Id | |
* @ORM\Column(type="string", name="object_class") | |
*/ | |
private $objectClass; | |
/** | |
* @ORM\Id | |
* @ORM\Column(type="integer", name="object_id") | |
*/ | |
private $objectId; | |
/** | |
* @var Tag | |
* | |
* @ORM\Id | |
* @ORM\ManyToOne(targetEntity="Acme\TagBundle\Entity\Tag", inversedBy="relations") | |
*/ | |
private $tag; | |
public function __construct(Tag $tag, TaggableInterface $object) | |
{ | |
$this->tag = $tag; | |
$this->setRelatedObject($object); | |
} | |
/** @var Tag */ | |
public function getTag() | |
{ | |
return $this->tag; | |
} | |
public function setRelatedObject(TaggableInterface $object) | |
{ | |
$this->objectId = $object->getId(); | |
$this->objectClass = $object instanceof Proxy | |
? get_parent_class($object) | |
: get_class($object); | |
return $this; | |
} | |
/** | |
* Checks if the given object matches what is in this relation | |
* | |
* @return boolean true if it is the same object, false otherwise | |
*/ | |
public function matches(TaggableInterface $object) { | |
$class = $object instanceof Proxy | |
? get_parent_class($object) | |
: get_class($object); | |
return $object->getId() === $this->objectId && $class === $this->objectClass; | |
} | |
/** | |
* Get objectClass | |
* | |
* @return string | |
*/ | |
public function getObjectClass() | |
{ | |
return $this->objectClass; | |
} | |
/** | |
* Get objectId | |
* | |
* @return integer | |
*/ | |
public function getObjectId() | |
{ | |
return $this->objectId; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\TagBundle\Entity; | |
use Traversable, | |
OutOfBoundsException, | |
InvalidArgumentException; | |
use Doctrine\Common\Collections\ArrayCollection; | |
/** | |
* Trait that implements the gist of the tags | |
* | |
* BEWARE : You _must_ define a `tags` property in the class you're using this trait ! | |
*/ | |
trait TagTrait | |
{ | |
/** {@inheritDoc} */ | |
public function getTags() | |
{ | |
if (null === $this->tags) { | |
$this->tags = new ArrayCollection; | |
} | |
return $this->tags; | |
} | |
/** {@inheritDoc} */ | |
public function setTags($tags) | |
{ | |
if (!$tags instanceof Traversable && !is_array($tags)) { | |
throw new InvalidArgumentException('Traversable or array expected, had ' . (is_object($tags) ? get_class($tags) : gettype($tags))); | |
} | |
$this->tags = new ArrayCollection; | |
foreach ($tags as $tag) { | |
$this->addTag($tag); | |
} | |
return $this; | |
} | |
/** {@inheritDoc} */ | |
public function addTag(Tag $tag) | |
{ | |
if (!$tag->hasAssignment($this)) { | |
$tag->assign($this); | |
} | |
/** | |
* Check if the given Tag already exists in the object's tags collection. | |
* We don't use `ArrayCollection::contains()` because it's not be able to | |
* compare the empty object from a FormType and a complete Tag. | |
*/ | |
if (!$this->getTags()->exists(function ($key, $element) use ($tag) { return $tag->getHash() === $element->getHash(); })) { | |
$this->getTags()->add($tag); | |
} | |
return $this; | |
} | |
/** {@inheritDoc} */ | |
public function removeTag(Tag $tag) | |
{ | |
if ($tag->hasAssignment($this)) { | |
$tag->unassign($this); | |
} | |
$this->getTags()->removeElement($tag); | |
return $this; | |
} | |
/** {@inheritDoc} */ | |
public function hasTag($hash) | |
{ | |
foreach ($this->getTags() as $tag) { | |
if ($hash !== $tag->getHash()) { | |
continue; | |
} | |
return true; | |
} | |
return false; | |
} | |
/** {@inheritDoc} */ | |
public function getTag($hash) | |
{ | |
if (!$this->getTags()->exists(function ($key, Tag $tag) use ($hash) { return $hash === $tag->getHash(); })) { | |
throw new OutOfBoundsException(sprintf('The tag "%s" is not assigned to this object ("%s":%d)', $hash, get_class($this), $this->getId())); | |
} | |
return $this->getTags()->filter(function (Tag $tag) use ($hash) { return $hash === $tag->getHash(); })->first(); | |
} | |
/** {@inheritDoc} */ | |
public function areTagsSynchronized() | |
{ | |
if ($this->getTags()->isEmpty()) { | |
return false; | |
} | |
return !$this->getTags()->exists(function ($key, $tag) { return !$tag instanceof Tag; }); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment