Status with three choices - symfony

I want to make a choice dropdown form with three choices:
-favored
-intended
-verified
so i could'nt use a boolean for this.
I have no idea how to set my annotations for my status field in the entity. Any help?
/**
* #var boolean
*
* #ORM\Column(name="status", type="boolean")
*/
private $status;

I don't know if I have really understood your question but in fact the boolean field type for symfony2/doctrine2 is a tinyint(1) in SQL database. So you can put integer values from -128 to 127.
Generally for my entities I use this "rule" :
<?php
class MyEntity
{
const STATUS_FAVORED = 1;
const STATUS_INTENTED = 2;
const STATUS_VERIFIED = 3;
/**
* #var integer
*
* #ORM\Column(name="status", type="boolean")
*/
private $status;
public function __construct()
{
$this->status = self::STATUS_FAVORED;
}
/**
* For ur form by example
*/
public static function getStatusForChoiceFormField()
{
return array(
self::STATUS_FAVORED => 'favored',
self::STATUS_INTENTED => 'intented',
self::STATUS_VERIFIED => 'verified'
);
}
}
?>
See u !

Related

findBy() foreignKey is not working

There is a relationship between the rubric and brand table (Many rubric to One brand).
Here is my code
$dbChoices = $this->getConfigurationPool()
->getContainer()
->get('Doctrine')
->getManager()
->getRepository('RibambelMainBundle:RubricMenu')
->findBy(array('brand_id' => 2));
Everytime this part is run, I got the following error:
Unrecognized field: brand_id
The brand_id column is found in the rubric table.
Anyone who can help me with this or show me another way of doing it?
class RubricMenu
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Title", type="string", length=255)
*/
private $title;
/**
* #var int
*
* #ORM\Column(name="position", type="integer")
*/
private $position;
/**
* #ORM\ManyToOne(targetEntity="Brand")
* #ORM\JoinColumn(name="brand_id", nullable=false)
*/
private $brand;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $title
*
* #return RubricMenu
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set position
*
* #param integer $position
*
* #return RubricMenu
*/
public function setPosition($position)
{
$this->position = $position;
return $this;
}
/**
* Get position
*
* #return int
*/
public function getPosition()
{
return $this->position;
}
/**
* Set brand
*
* #param \Ribambel\MainBundle\Entity\Brand $brand
*
* #return RubricMenu
*/
public function setBrand(\Ribambel\MainBundle\Entity\Brand $brand = null)
{
$this->brand = $brand;
return $this;
}
/**
* Get brand
*
* #return \Ribambel\MainBundle\Entity\Brand
*/
public function getBrand()
{
return $this->brand;
}
}
Searching by a foreign key simply does not make sense from an object point of view. brand_id is not a field of your RubricMenu entity, brand is.
If you have a Brand entity, then you can use findBy (or the magic method findByBrand) like this: (with $myBrand an instance of Brand)
$repository->findBy(['brand' => $myBrand]);
// OR
$repository->findByBrand($myBrand);
If you simply have the id of your target entity, you can use the IDENTITY DQL function but in that case you cannot use findBy directly as it expects a valid field name. Thus, you will need to add a method in your RubricMenu repository. Your method could look like this:
public function findByBrandId($brandId)
{
$qb = $this->createQueryBuilder('rm');
$qb->where('IDENTITY(rm.brand) = :brandId')
->setParameter('brandId', $brandId);
return $qb->getQuery()->getResult();
}
On a side note, as pointed out by #l.g.karolos's answer, doing
$repository->findBy(['brand' => $brandId]);
// OR
$repository->findByBrand($brandId);
will also work as the findBy method internals will be able to resolve that the given parameter is an ID of a Brand entity, thus it will produce the same query as if you had given the corresponding Brand instance.
Based on your entity you should do the following.
$dbChoices = $this->getConfigurationPool()
->getContainer()
->get('Doctrine')
->getManager()
->getRepository('RibambelMainBundle:RubricMenu')
->findBy(array('brand' => 2));
i thing you can do:
$dbChoices = $this->getConfigurationPool()
->getContainer()
->get('Doctrine')
->getManager()
->getRepository('RibambelMainBundle:RubricMenu')
->findBy(array('brand' => $brand));
if you have $brand object (...\Entity\Brand)
Example on https://www.wanadev.fr/56-comment-realiser-de-belles-requetes-sql-avec-doctrine/ :
$user = …;
$category = …;
$em = $this->container->get("doctrine.orm.default_entity_manager");
$entities = $em->getRepository(MyClass::class)->findBy([
"user" => $user,
"category" => $category,
]);

Symfony 2 - Entity is not updated

I am working with form aimed at uploading the file and updating the database in Symfony2. I want to manually set value of book_id field and not to allow user to change it in the form. Thus in my controller before using doctrine to persist document I am calling:
$documents->setBookId('1');
Unluckilly I get error which indicates that the doctrine does not recognise the above hard coded value input.
An exception occurred while executing 'INSERT INTO Documents (book_id, marker, document_date, link, notes) VALUES (?, ?, ?, ?, ?)' with params [null, "fdd", "2015-04-04", null, "test"]:
To my mind this may be connected with the fact that book_id field is related to Books. Therefore probably I should use setBook function instead. Could you please advice how to do this properly?
My controler file looks like this:
/**
* This code is aimed at checking if the book is chosen and therefore whether any further works may be carried out
*/
$session = new Session();
if(!$session->get("App_Books_Chosen_Lp")) return new RedirectResponse($this->generateUrl('app_listbooks'));
// Authorization goes here
$documents = new Documents();
$form = $this->createForm(new DocumentsType(), $documents);
$form->add('save', 'submit', array('label' => 'Dodaj dokument'));
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$documents->upload();
$documents->setBookId('1');
$em->persist($documents);
$em->flush();
}
return $this->render('AppBundle:Documents:adddocuments.html.twig', array('form' => $form->createView()));
Document class:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Entity
* #ORM\Table(name="Documents")
* #ORM\HasLifecycleCallbacks
*/
class Documents
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Books", inversedBy="documents")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $book;
/**
* #ORM\Column(type="integer")
*/
protected $book_id;
/**
* #ORM\Column(type="string", length=220)
*/
protected $marker;
/**
* #ORM\Column(type="date", length=220)
*/
protected $document_date;
/**
* #ORM\Column(type="string", length=220)
* #Assert\File(maxSize="6000000")
*/
protected $link;
/**
* #ORM\Column(type="text")
*/
protected $notes;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set book_id
*
* #param integer $bookId
* #return Documents
*/
public function setBookId($bookId)
{
$this->book_id = $bookId;
return $this;
}
/**
* Get book_id
*
* #return integer
*/
public function getBookId()
{
return $this->book_id;
}
/**
* Set marker
*
* #param string $marker
* #return Documents
*/
public function setMarker($marker)
{
$this->marker = $marker;
return $this;
}
/**
* Get marker
*
* #return string
*/
public function getMarker()
{
return $this->marker;
}
/**
* Set document_date
*
* #param \DateTime $documentDate
* #return Documents
*/
public function setDocumentDate($documentDate)
{
$this->document_date = $documentDate;
return $this;
}
/**
* Get document_date
*
* #return \DateTime
*/
public function getDocumentDate()
{
return $this->document_date;
}
/**
* Set link
*
* #param string $link
* #return Documents
*/
public function setLink($link)
{
$this->link = $link;
return $this;
}
/**
* Get link
*
* #return string
*/
public function getLink()
{
return $this->link;
}
/**
* Set notes
*
* #param string $notes
* #return Documents
*/
public function setNotes($notes)
{
$this->notes = $notes;
return $this;
}
/**
* Get notes
*
* #return string
*/
public function getNotes()
{
return $this->notes;
}
/**
* Set book
*
* #param \AppBundle\Entity\Books $book
* #return Documents
*/
public function setBook(\AppBundle\Entity\Books $book = null)
{
$this->book = $book;
return $this;
}
/**
* Get book
*
* #return \AppBundle\Entity\Books
*/
public function getBook()
{
return $this->book;
}
/*
* ### FILE UPLOAD PROCESS ###
*/
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
public function getAbsolutePath()
{
return null === $this->path
? null
: $this->getUploadRootDir().'/'.$this->path;
}
public function getWebPath()
{
return null === $this->path
? null
: $this->getUploadDir().'/'.$this->path;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded
// documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw up
// when displaying uploaded doc/image in the view.
return 'uploads/documents';
}
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->getFile()->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->path = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->file = null;
}
}
Okay, first since you're using ManyToOne relation, you don't actually need another property refering to the book - book_id. You can remove that and leave book only.
Then in your controller you have to query the database for that Book and set the that object your Document.
You can do it like this:
$bookId = 1; // Following your example, let's say tou already know the book ID.
$book = $em->getReference('AppBundle:Books', $bookId);
// Check if we actually found a record and then set it to Documents
// Looking at your entity mapping, your reference to Book can not be null,
// but doing an extra check never hurts, since this is just an example.
if( $book ) {
$documents->setBook($book);
}
-Update-
If you want to directly insert the bookID, then what is the purpose of having ManyToOne reference in your entity? Eventually you're going to have to start using doctrine's relations and objects properly. Also, the cool thing about getReference method is that you are getting a reference to an entity, without having to load the entity from the database - you get the so called Proxy objects.
The method EntityManager#getReference($entityName, $identifier) lets you obtain a reference to an entity for which the identifier is known, without loading that entity from the database. This is useful, for example, as a performance enhancement, when you want to establish an association to an entity for which you have the identifier
You can read further about this here.

Collection Prototype and Doctrine Persistance ManyToOne Relation

Context : I am building my little TodoList bundle (which is a good exercice to go deep progressively with Symfony2), the difficulty comes with recursivity : each Task can has children and parent, so I used Gedmo Tree.
I have a collection of tasks each having a sub collection of children, children collection has prototype enabled so I can display a new sub task form when clicking "add sub task".
I wanted the default name of the subtask to be "New Sub Task" instead of "New Task" set in Task constructor, so I figured out how to pass a custom instance for the prototype and took some care for preventing infinite loop.
So I am almost done and my new task is added with the name I set when saving...
Problem : I am not able to persist the parent task to the new sub task, the new task persist the name well, but not the parentId, I probably forgot somewhere with Doctrine, here is some relevant parts :
// Entity Task
/**
* #Gedmo\Tree(type="nested")
* #ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\Table(name="task")
*/
class Task {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
*/
protected $created;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank(message="Name must be not empty")
*/
protected $name = 'New Task';
//....
/**
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #Gedmo\TreeRoot
* #ORM\Column(name="root", type="integer", nullable=true)
*/
private $root;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Task", inversedBy="children")
* #ORM\JoinColumn(name="parentId", referencedColumnName="id", onDelete="SET NULL")
*/
protected $parent = null;//
/**
* #ORM\Column(type="integer", nullable=true)
*/
protected $parentId = null;
/**
* #Assert\Valid()
* #ORM\OneToMany(targetEntity="Task", mappedBy="parent", cascade={"persist", "remove"})
* #ORM\OrderBy({"status" = "ASC", "created" = "DESC"})
*/
private $children;
public function __construct(){
$this->children = new ArrayCollection();
}
/**
* Set parentId
*
* #param integer $parentId
* #return Task
*/
public function setParentId($parentId){
$this->parentId = $parentId;
return $this;
}
/**
* Get parentId
*
* #return integer
*/
public function getParentId(){
return $this->parentId;
}
/**
* Set parent
*
* #param \Dmidz\TodoBundle\Entity\Task $parent
* #return Task
*/
public function setParent(\Dmidz\TodoBundle\Entity\Task $parent = null){
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* #return \Dmidz\TodoBundle\Entity\Task
*/
public function getParent(){
return $this->parent;
}
/**
* Add children
*
* #param \Dmidz\TodoBundle\Entity\Task $child
* #return Task
*/
public function addChild(\Dmidz\TodoBundle\Entity\Task $child){
$this->children[] = $child;
return $this;
}
/**
* Remove child
*
* #param \Dmidz\TodoBundle\Entity\Task $child
*/
public function removeChild(\Dmidz\TodoBundle\Entity\Task $child){
$this->children->removeElement($child);
}
}
// TaskType
class TaskType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options){
$builder
->add('name', null, ['label' => false])
->add('notes', null, ['label' => 'Notes'])
->add('status', 'hidden')
->add('parentId', 'hidden')
;
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($builder){
$record = $event->getData();
$form = $event->getForm();
if(!$record || $record->getId() === null){// if prototype
$form->add('minutesEstimated', null, ['label' => 'Durée', 'attr'=>['title'=>'Durée estimée en minutes']]);
}elseif($record && ($children = $record->getChildren())) {
// this is where I am able to customize the prototype default values
$protoTask = new Task();
$protoTask->setName('New Sub Task');
// here I am loosely trying to set the parentId I want
// so the prototype form input has the right value
// BUT it goes aways when INSERT in mysql, the value is NULL
$protoTask->setParentId($record->getId());
$form->add('sub', 'collection', [// warn don't name the field 'children' or it will conflict
'property_path' => 'children',
'type' => new TaskType(),
'allow_add' => true,
'by_reference' => false,
// this option comes from a form type extension
// allowing customizing prototype default values
// extension code : https://gist.github.com/jumika/e2f0a5b3d4faf277307a
'prototype_data' => $protoTask
]);
}
});
}
public function setDefaultOptions(OptionsResolverInterface $resolver){
$resolver->setDefaults([
'data_class' => 'Dmidz\TodoBundle\Entity\Task',
'label' => false,
]);
}
public function getParent(){ return 'form';}
}
// my controller
/**
* #Route("/")
* #Template("DmidzTodoBundle:Task:index.html.twig")
*/
public function indexAction(Request $request){
$this->request = $request;
$repo = $this->doctrine->getRepository('DmidzTodoBundle:Task');
$em = $this->doctrine->getManager();
//__ list of root tasks (parent null)
$query = $repo->createQueryBuilder('p')
->select(['p','FIELD(p.status, :progress, :wait, :done) AS HIDDEN field'])
->addOrderBy('field','ASC')
->addOrderBy('p.id','DESC')
->andWhere('p.parent IS NULL')
->setParameters([
'progress' => Task::STATUS_PROGRESS,
'wait' => Task::STATUS_WAIT,
'done' => Task::STATUS_DONE
])
->setMaxResults(20)
->getQuery();
$tasks = $query->getResult();
//__ form building : collection of tasks
$formList = $this->formFactory->createNamed('list_task', 'form', [
'records' => $tasks
])
->add('records', 'collection', [
'type'=>new TaskType(),
'label'=>false,
'required'=>false,
'by_reference' => false,
])
;
//__ form submission
if ($request->isMethod('POST')) {
$formList->handleRequest($request);
if($formList->isValid()){
// persist tasks
// I thought persisting root tasks will persist their children relation
foreach($tasks as $task){
$em->persist($task);
}
$em->flush();
return new RedirectResponse($this->router->generate('dmidz_todo_task_index'));
}
}
return [
'formList' => $formList->createView(),
];
}
As mentionned in the comments in TaskType, the form prototype of the new sub task has the right value for parentId which is posted, BUT the value is gone and NULL on INSERT in db (looking at the doctrine log).
So do you think it is the right way of doing, and then what thing I forgot for persisting correctly the parent task of the new sub task ?
On your child setting you should set the parent when adding, like so..
/**
* Add children
*
* #param \Dmidz\TodoBundle\Entity\Task $children
* #return Task
*/
public function addChild(\Dmidz\TodoBundle\Entity\Task $children){
$this->children->add($children);
$children->setParent($this);
return $this;
}
/**
* Remove children
*
* #param \Dmidz\TodoBundle\Entity\Task $children
*/
public function removeChild(\Dmidz\TodoBundle\Entity\Task $children){
$this->children->removeElement($children);
$children->setParent(null);
}
When your prototype adds and deletes a row it calls addChild and removeChild but it doesn't call the setParent in the associated child.
This way any child that is added or removed/deleted get automatically set in the process.
Also you could change the $children to $child as it makes grammatical sense and it's really bugging me because I am a child(ren).
It seems weird to me that you try using the parentId field as a simple column, whereas it is a relation column. Theoretically, you should not:
$task->getParentId(); //fetching a DB column's value
but instead:
$task->getParent()->getId(); //walking through relations to find an object's attribute
However, if you really need this feature to avoid loading the full parent object and just get its ID, your setParentId method should be transparent (although, as mentionned, I'm not sure using the same DB field is valid):
public function setParent(Task $t = null) {
$this->parent = $t;
$this->parentId = null === $t ? null : $t->getId();
return $this;
}
Back to your issue: in the TaskType class, you should call:
$protoTask->setParent($record);
instead of:
$protoTask->setParentId($record->getId());
The reason:
you tell Doctrine parentId is a relation field (in the $parent attribute declaration), therefore Doctrine expects an object of the proper type
you also tell Doctrine to map this relation field directly to an attribute (the $parentId attribute declaration), I'm neither convinced this is valid, nor convinced this is good practice, but I guess you did some research before going for this structure
you set $parentId, but $parent has not been set (i.e. null), so Doctrine must erase the $parentId value with the $parent value: your code is proof that Doctrine handles attributes first, then computes relations ;)
Keep in mind Doctrine is an Object Relational Mapper, not a simple query helper: mapper is what it does (mapping persistence layer with your code), relational is how it does it (one-to-many and the like), object is what it does it on (therefore not directly using IDs).
Hope this helps!

Symfony2-Doctrine -- Selecting data with WHERE from DoctrineArray

In an entity I have a field that looks like this:
/**
* #ORM\Column(type="array")
*/
protected $category;
and QueryBuilder
$qb = $this->createQueryBuilder('s');
$qb->select($fields)
->where( 's.category IN (:category)') //////////// <----
->orderBy('s.name', 'ASC')
->setParameter('category', $category_id);
So in database field category is Doctrine2 Array. I want to select records from database with QueryBuilder. My question is, how can i do this, with WHERE clause that will be checking fields from that array ?
A look here may help you
// Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1 (see section above)
public function in($x, $y); // Returns Expr\Func instance
$qb->select($fields)
->where($qb->expr()->in('s.category', $categories))
#Cerad gave you a perfectly valid comment. One of the problem of storing arrays is that you don't have any chance of searching.
See PHP/MySQL - Storing array in database, and Storing arrays in the database. As you can see, it is a terrible practice.
The best way is to simply create a Category entity, and to have a OneToMany relation with that category.
Here is an example of an entity Book that has many categories:
1 Create your category entity:
class Category implements CategoryInterface
{
//.....
/**
* Title of the category
*
* #ORM\Column(type="string", length=100)
*/
protected $title;
/**
* Relation with your book entity for example
*
* #ORM\ManyToOne(targetEntity="Book", inversedBy="categories")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $book;
/**
* Set book
*
* #param BookInterface $book
*/
public function setBook(BookInterface $book)
{
$this->book = $book;
}
/**
* Get book
*
* #return BookInterface
*/
public function getBook()
{
return $this->book;
}
}
2 Your book entity:
use Doctrine\Common\Collections\ArrayCollection;
class Book implements BookInterface
{
/**
* Categories for the books
*
* #ORM\OneToMany(targetEntity="Category", mappedBy="book")
* #var CategoryInterface[]
*/
protected $categories ;
public function __construct()
{
$this->categories = new ArrayCollection();
}
/**
* Add Categories
*
* #param CategoryInterface $category
*/
public function addCategory(CategoryInterface $category)
{
$category->setBook($this);
$this->categories->add($category);
}
/**
* Remove Category
*
* #param CategoryInterface $category
* #return bool
*/
public function removeCategory(CategoryInterface $category)
{
return $this->categories->removeElement($category);
}
/**
* Get Categories
*
* #return Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
/**
* Set Categories
*
* #param ArrayCollection $categories
*/
public function setCategories($categories) {
$this->categories->clear();
foreach ($categories as $category) {
$this->addCategory($category);
}
return $this;
}
3 Your can now search properly.

Doctrine - ManyToMany Self Referencing Association + nested toArray() on child elements

I'm trying to perform a ManyToMany self referencing association in my Symfony 2.1 project by following the Doctrine docs: http://docs.doctrine-project.org/en/latest/reference/association-mapping.html#many-to-many-self-referencing
My use-case is that I'm working on a CMS and I'm adding the ability to have related items of content. For example: I could have a sidebar on a website which would say that this piece of content X is related to Y and Z. Similarly on pages where content Y appears it says that it is related to content item X.
In my tests using this to add a new relation between content items fails because it reaches PHP's maximum nesting level of 100 because it is running toArray() on the current content item and then again on the related content item and so on and so on.
I've seen many similar questions on SO about Many-to-Many Self referential Doctrine associations but none with enough complete code to be able to see how others have managed this. Can anybody help?
My Content entity:
/**
* #ORM\MappedSuperclass
* #ORM\Table(name="content")
* #ORM\Entity(repositoryClass="CMS\Bundle\Common\ContentBundle\Entity\ContentRepository")
* #ORM\InheritanceType("JOINED")
*/
abstract class content implements ContentInterface
{
/**
* #var int $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $title
*
* #ORM\Column(name="title", type="string", length=255)
* #Assert\NotBlank()
*/
private $title;
// Other class properties
/**
* #var array
*
* #ORM\ManyToMany(targetEntity="Content", cascade={"persist"})
* #ORM\JoinTable(name="content_relation",
* joinColumns={#ORM\JoinColumn(name="relation_id", referencedColumnName="id")},
* inverseJoinColumns={
* #ORM\JoinColumn(name="related_content_id", referencedColumnName="id")
* })
**/
private $related;
public function __construct()
{
$this->related = new ArrayCollection();
}
// Other getters & setters for class properties
/**
* #return array
*/
public function getRelated()
{
return $this->related;
}
/**
* #param Content $relation
*/
public function addRelation(Content $relation)
{
$this->related->add($relation);
$this->related->add($this);
}
/**
* #return array
*/
public function toArray()
{
$related = array();
foreach($this->getRelated() as $relatedItem) {
$related[] = $relatedItem->toArray();
}
return array(
'type' => static::getType(),
'id' => $this->id,
'title' => $this->title,
....
'related' => $related
);
}
In my RelationsController for managing the related content data I use it like this:
/**
* Creates a new relation to a content item
*
* #Route("{_locale}/content/{id}/related", name="relation_add")
* #Method("POST")
*/
public function addAction(Request $request, $id)
{
// Validation and error checking
// $entity is loaded by the repository manager doing a find on the passed $id
$entity->addRelation($relation);
$em = $this->getEntityManager();
$em->persist($entity);
$em->persist($relation);
$em->flush();
$response = $relation->toArray();
return new JsonResponse($response, 201);
}
The fix for this was to use the JMSSerializerBundle to encode the entity to JSON instead of using a toArray method and change the addRelation function to:
/**
* #param Content $relation
*/
public function addRelation(Content $relation)
{
$this->related[] = $relation;
if (! $relation->getRelated()->contains($this)) {
$relation->addRelation($this);
}
}

Resources