Using DoctrineBehaviors translatable in Symfony 4? - symfony

I'm trying to use the DoctrineBehaviors translatable extension in Symfony 4.Just setup a test following the documentation example:
translatable entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* #ORM\Entity(repositoryClass="App\Repository\FAQRepository")
*/
class FAQ
{
use ORMBehaviors\Translatable\Translatable;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
protected $id;
/.**
* #ORM\Column(type="datetime", nullable=true)
*/
protected $updatedAt;
public function getId(): ?int
{
return $this->id;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(?\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
}
translation entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* #ORM\Entity
*/
class FAQTranslation
{
use ORMBehaviors\Translatable\Translation;
/**
* #ORM\Column(type="text")
*/
protected $question;
/**
* #ORM\Column(type="text")
*/
protected $answer;
/**
* #ORM\Column(type="integer", nullable=true)
*/
protected $category;
public function getQuestion(): ?string
{
return $this->question;
}
public function setQuestion(string $question): self
{
$this->question = $question;
return $this;
}
public function getAnswer(): ?string
{
return $this->answer;
}
public function setAnswer(string $answer): self
{
$this->answer = $answer;
return $this;
}
public function getCategory(): ?int
{
return $this->category;
}
public function setCategory(?int $category): self
{
$this->category = $category;
return $this;
}
}
Testing the translatable entity:
/**
* #Route("/test", name="test")
*/
public function testfaq()
{
$em = $this->getDoctrine()->getManager();
$faq = new FAQ();
$faq->translate('fr')->setQuestion('Quelle est la couleur ?');
$faq->translate('en')->setQuestion('What is the color ?');
$em->persist($faq);
$faq->mergeNewTranslations();
$em->flush();
return $this->render('app/test.html.twig', [
]);
}
A new ID is added in the faq table.
But nothing is persisted in the faqtranslation table.
Bundles.php :
Knp\DoctrineBehaviors\Bundle\DoctrineBehaviorsBundle::class => ['all' => true],
All the documentations I found seem to refer to Symfony 3 or even Symfony 2, is it possible to use DoctrineBehaviors translatable in Symfony 4 ?

I don't know if you found your answer since (I hope you did), but yes you can use KnpLabs/DoctrineBehaviors with Symfony 4. Maybe, you just needed to wait a little longer for an update.

Related

Symfony join query with One-To-Many relation

I am quite new to symfony as in DQL. I have a problem with query, namely I want to compare the ID's between 'term_id' from table 'TermAssign' with 'id' from table Term and then, the one's who are matching are to be rendered on a template. Relation between Term and TermAssign is OneToMany. There is also a table Offer, which has relation OneToMany with table TermAssign.
This is my Term.php:
<?php
namespace App\OfferBundle\Entity;
use App\OfferBundle\Repository\TermRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=TermRepository::class)
* #ORM\Table(name="term")
*/
class Term
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=true, name="term_description")
*/
private $term_description;
/**
* #ORM\OneToMany(targetEntity=TermAssign::class, mappedBy="term")
*/
private $assign;
public function __construct()
{
$this->assign = new ArrayCollection();
}
function getId(): ?int
{
return $this->id;
}
public function getTermDescription(): ?string
{
return $this->term_description;
}
public function setTermDescription(?string $term_description): self
{
$this->term_description = $term_description;
return $this;
}
/**
* #return Collection|TermAssign[]
*/
public function getAssign(): Collection
{
return $this->assign;
}
public function addAssign(TermAssign $assign): self
{
if (!$this->assign->contains($assign)) {
$this->assign[] = $assign;
$assign->setTerm($this);
}
return $this;
}
public function removeAssign(TermAssign $assign): self
{
if ($this->assign->removeElement($assign)) {
// set the owning side to null (unless already changed)
if ($assign->getTerm() === $this) {
$assign->setTerm(null);
}
}
return $this;
}
}
This is Offer.php:
<?php
namespace App\OfferBundle\Entity;
use App\OfferBundle\Repository\OfferRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints\DateTime;
/**
* #ORM\Entity(repositoryClass=OfferRepository::class)
*/
class Offer
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $title;
/**
* #ORM\Column(type="date", name="offer_date")
*/
private $date;
/**
* #ORM\Column(type="string", unique=true)
*/
private $number;
/**
* #ORM\Column(type="string")
*/
private $description;
/**
* #ORM\OneToMany(targetEntity=TermAssign::class, mappedBy="offer")
*/
private $terms;
public function __construct()
{
$this->terms = new ArrayCollection();
}
/**
* Getting offer's id
*/
public function getId(): ?int
{
return $this->id;
}
/**
* getting offer's Title
*/
public function getTitle(): string
{
return $this->title;
}
/**
* Setting offer's name
*/
public function setTitle($title): self
{
$this->title = $title;
return $this;
}
/**
* getting offer's date
*/
public function getDate(): ?\DateTime
{
return $this->date;
}
/**
* Setting offer's date
*/
public function setDate($date): self
{
$this->date = $date;
return $this;
}
/**
* Offer's number
*/
public function getNumber() : string
{
return $this->number;
}
/**
* Setting offer's number
*/
public function setNumber($number): self
{
$this->number = $number;
return $this;
}
/**
* Offer's description
*/
public function getDescription()
{
return $this->description;
}
/**
* Setting offer's description
*/
public function setDescription($description): void
{
$this->description = $description;
}
/**
* #return Collection|TermAssign[]
*/
public function getTerms(): Collection
{
return $this->terms;
}
public function addTerm(TermAssign $term): self
{
if (!$this->terms->contains($term)) {
$this->terms[] = $term;
$term->setOffer($this);
}
return $this;
}
public function removeTerm(TermAssign $term): self
{
if ($this->terms->removeElement($term)) {
// set the owning side to null (unless already changed)
if ($term->getOffer() === $this) {
$term->setOffer(null);
}
}
return $this;
}
}
And this is TermAssign.php:
<?php
namespace App\OfferBundle\Entity;
use App\OfferBundle\Repository\TermAssignRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=TermAssignRepository::class)
*/
class TermAssign
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Offer::class, inversedBy="terms")
*/
private $offer;
/**
* #ORM\ManyToOne(targetEntity=Term::class, inversedBy="assign")
*/
private $term;
public function getId(): ?int
{
return $this->id;
}
public function getOffer(): ?Offer
{
return $this->offer;
}
public function setOffer(?Offer $offer): self
{
$this->offer = $offer;
return $this;
}
public function getTerm(): ?Term
{
return $this->term;
}
public function setTerm(?Term $term): self
{
$this->term = $term;
return $this;
}
}
I did came up with query of this sort:
/**
* #return Term[] Returns an array of Term objects
*/
public function findByIdField():array
{
$em = $this->getEntityManager();
$query = $em->createQuery("SELECT t, a FROM App\OfferBundle\Entity\Term t JOIN t.term a WHERE a.term_id = t.id");
return $query->getResult();
}
But it's of no use.
Also, this is part of my controller where I invoke query to be passed into the template:
OfferController.php
/**
* #Route("/{id}", name="offer_show", methods={"GET"})
*/
public function show(Offer $offer, int $id): Response
{
$termAssigns = $this->getDoctrine()
->getRepository(TermAssign::class)
->findBy(
['offer'=>$id]
);
$terms = $this->getDoctrine()
->getRepository(Term::class)
->findByIdField();
$conditions = $this->getDoctrine()
->getRepository(Condition::class)
->findAll();
return $this->render('offer/show.html.twig', [
'offer' => $offer,
'terms'=> $terms,
'conditions'=>$conditions,
'termAssigns'=>$termAssigns,
]);
}
The question is, what can I do to achieve something like
SELECT description FROM Term.t where 't.id'='ta.term_id'
'ta' is TermAssign table.
That was WAY much less complicated than I have imagined... I didn't have to create a new query, all I had to do was to import a TermAssign repository to show() function located in OfferController.php and use findBy() function to fetch terms from Term table. Solution below:
OfferController.php:
/**
* #Route("/{id}", name="offer_show", methods={"GET"})
*/
public function show(Offer $offer, int $id, TermAssign $terms): Response
{
**$termAssigns = $this->getDoctrine()
->getRepository(TermAssign::class)
->findBy(
['offer'=>$id]
);
$terms = $this->getDoctrine()
->getRepository(Term::class)
->findBy(
['id'=>$terms->getId()]
);**
$conditions = $this->getDoctrine()
->getRepository(Condition::class)
->findAll();
return $this->render('offer/show.html.twig', [
'offer' => $offer,
'terms'=> $terms,
'conditions'=>$conditions,
'termAssigns'=>$termAssigns,
]);
}

Doctrine - validation with associated entities

I have the following associations with Doctrine:
Charge:
id
amount
adjustmentItems
Adjustment
id
date
adjustmentItems
AdjustmentItem
id
adjustment
charge
amount
There are charges, and there are adjustments. Each adjustment is made up of adjustmentItems, which are adjustments to one or more charges.
When adding a new adjustment, I am adding adjustments, and the associated items, via deserialization. Ie:
$adjustment =
["date" => "2020-12-14",
"items" => [
["charge" => 84, "amount" => 600],
["charge" => 85, "amount" => 200],
]
];
Everything works well, except I validate the charges using Assert/Valid on the AdjustmentItem::charge.
In the charge validation, I check to make sure the sum of all the adjustments does not exceed the charge amount.
However, Charge::getAdjustmentItems() does not show the just created adjustmentItems (even though the adjustmentItem shows to charge, and persisting everything works as expected).
$adjustment->getAdjustmentItems()->first()->getCharge()->getAdjustmentItems()->toArray()
is:
[]
How can I get the Charge to "see" the items from deserialization before persist for validation?
SOURCE:
<?php
namespace App\Entity\TestFin;
use App\Repository\TestFin\ChargeRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* #ORM\Entity(repositoryClass=ChargeRepository::class)
* #ORM\Table(name="`test_financials_charge`")
*/
class Charge
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $amount;
/**
* #ORM\OneToMany(targetEntity=AdjustmentItem::class, mappedBy="charge")
*/
private $adjustmentItems;
public function __construct()
{
$this->adjustmentItems = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getAmount(): ?int
{
return $this->amount;
}
public function setAmount(int $amount): self
{
$this->amount = $amount;
return $this;
}
/**
* #return Collection|AdjustmentItem[]
*/
public function getAdjustmentItems(): Collection
{
return $this->adjustmentItems;
}
public function addAdjustmentItem(AdjustmentItem $adjustmentItem): self
{
if (!$this->adjustmentItems->contains($adjustmentItem)) {
$this->adjustmentItems[] = $adjustmentItem;
$adjustmentItem->setCharge($this);
}
return $this;
}
public function removeAdjustmentItem(AdjustmentItem $adjustmentItem): self
{
if ($this->adjustmentItems->contains($adjustmentItem)) {
$this->adjustmentItems->removeElement($adjustmentItem);
// set the owning side to null (unless already changed)
if ($adjustmentItem->getCharge() === $this) {
$adjustmentItem->setCharge(null);
}
}
return $this;
}
/**
* #Assert\Callback
*/
public function validateBalance(ExecutionContextInterface $context, $payload)
{
dd(count($this->adjustmentItems->toArray()));
if (1) {
$context->buildViolation('Charge balance (after adjustments) must be greater or equal to $0.')
->atPath('invoice')
->addViolation();
}
}
}
<?php
namespace App\Entity\TestFin;
use App\Repository\TestFin\AdjustmentRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass=AdjustmentRepository::class)
* #ORM\Table(name="`test_financials_adjustment`")
*/
class Adjustment
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $note;
/**
* #ORM\OneToMany(targetEntity=AdjustmentItem::class, mappedBy="adjustment", orphanRemoval=true)
* #Assert\Valid
*/
private $adjustmentItems;
public function __construct()
{
$this->adjustmentItems = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNote(): ?string
{
return $this->note;
}
public function setNote(?string $note): self
{
$this->note = $note;
return $this;
}
/**
* #return Collection|AdjustmentItem[]
*/
public function getAdjustmentItems(): Collection
{
return $this->adjustmentItems;
}
public function addAdjustmentItem(AdjustmentItem $adjustmentItem): self
{
if (!$this->adjustmentItems->contains($adjustmentItem)) {
$this->adjustmentItems[] = $adjustmentItem;
$adjustmentItem->setAdjustment($this);
}
return $this;
}
public function removeAdjustmentItem(AdjustmentItem $adjustmentItem): self
{
if ($this->adjustmentItems->contains($adjustmentItem)) {
$this->adjustmentItems->removeElement($adjustmentItem);
// set the owning side to null (unless already changed)
if ($adjustmentItem->getAdjustment() === $this) {
$adjustmentItem->setAdjustment(null);
}
}
return $this;
}
}
<?php
namespace App\Entity\TestFin;
use App\Repository\TestFin\AdjustmentItemRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass=AdjustmentItemRepository::class)
* #ORM\Table(name="`test_financials_adjustment_item`")
*/
class AdjustmentItem
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Adjustment::class, inversedBy="adjustmentItems")
* #ORM\JoinColumn(nullable=false)
*/
private $adjustment;
/**
* #ORM\ManyToOne(targetEntity=Charge::class, inversedBy="adjustmentItems")
* #ORM\JoinColumn(nullable=false)
* #Assert\Valid
*/
private $charge;
public function getId(): ?int
{
return $this->id;
}
public function getAdjustment(): ?Adjustment
{
return $this->adjustment;
}
public function setAdjustment(?Adjustment $adjustment): self
{
$this->adjustment = $adjustment;
return $this;
}
public function getCharge(): ?Charge
{
return $this->charge;
}
public function setCharge(?Charge $charge): self
{
$this->charge = $charge;
return $this;
}
}
$json = json_encode([
"note" => "bla",
"adjustmentItems" => [
["charge" => 1],
],
]);
$credit = $this->_deserializeJson($json, Adjustment::class);
//dd($credit);
//dd($credit->getAdjustmentItems()->first()->getCharge()->getAdjustmentItems()->toArray());
$this->_validate($credit);
Calling Charge::addAdjustmentItem in AdjustmentItem::setCharge solved the issue for me.
Anyone know why this is needed?
public function setCharge(?Charge $charge): self
{
$this->charge = $charge;
// This is needed so that the Assert/Callback in Charge knows about this adjustmentItem.
$charge->addAdjustmentItem($this);
return $this;
}

Symfony4 FOSRestBundle try to persist ManyToMany SQL syntax error

I get an SQL syntax error:
An exception occurred while executing 'INSERT INTO group (name) VALUES (?)' with params [\"Entwickler\"]:\n\nSQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'group (name) VALUES ('Entwickler')' at line 1
while I try to persist an entity with a ManyToMany relation.
Scenario
I have an entity called folder and an entity called group. There is a ManyToMany relationship between these entities, because one group can have multiple folders, and one folder can have multiple groups.
There is already one folder with id 1. Now I want to create a group and want to add the folder with id 1 to this group.
Group Entity
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\GroupRepository")
*/
class Group
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Folder", inversedBy="groups")
* #ORM\JoinTable(
* name="group_folder",
* joinColumns={
* #ORM\JoinColumn(name="group_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="folder_id", referencedColumnName="id")
* }
* )
*/
private $folder;
public function __construct()
{
$this->folder = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|Folder[]
*/
public function getFolder(): Collection
{
return $this->folder;
}
public function addFolder(Folder $folder): self
{
if (!$this->folder->contains($folder)) {
$this->folder[] = $folder;
}
return $this;
}
public function removeFolder(Folder $folder): self
{
if ($this->folder->contains($folder)) {
$this->folder->removeElement($folder);
}
return $this;
}
}
Folder Entity
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="App\Repository\FolderRepository")
*/
class Folder
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=45)
* #Assert\NotBlank()
*/
private $name;
/**
* #var Secret[]
* #ORM\OneToMany(targetEntity="App\Entity\Secret", mappedBy="folder")
*/
private $secrets;
/**
* #var Folder[]
*
* #ORM\OneToMany(targetEntity="Folder", mappedBy="parent")
*/
private $children;
/**
* #var Folder
*
* #ORM\ManyToOne(targetEntity="Folder", inversedBy="children")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
private $parent;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Group", mappedBy="folder")
*/
private $groups;
public function __construct()
{
$this->children = new ArrayCollection();
$this->secrets = new ArrayCollection();
$this->groups = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return Secret[]|ArrayCollection
*/
public function getSecrets()
{
return $this->secrets;
}
/**
* #return Folder[]|ArrayCollection
*/
public function getChildren()
{
return $this->children;
}
/**
* #param Folder $children
*/
public function addChildren($children)
{
$this->children[] = $children;
$children->setParent($this);
}
/**
* #return Folder
*/
public function getParent()
{
return $this->parent;
}
/**
* #param Folder $parent
*/
public function setParent($parent)
{
$this->parent = $parent;
}
/**
* #return Collection|Group[]
*/
public function getGroups(): Collection
{
return $this->groups;
}
public function addGroup(Group $group): self
{
if (!$this->groups->contains($group)) {
$this->groups[] = $group;
$group->addFolder($this);
}
return $this;
}
public function removeGroup(Group $group): self
{
if ($this->groups->contains($group)) {
$this->groups->removeElement($group);
$group->removeFolder($this);
}
return $this;
}
}
GroupType Form
<?php
namespace App\Form;
use App\Entity\Folder;
use App\Entity\Group;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GroupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('folder', EntityType::class, [
'class' => Folder::class,
'multiple' => true
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Group::class,
]);
}
}
Controller Action which I send the POST Request to
/**
* #param Request $request
* #return View
*
* #Rest\Post("/api/groups", name="api_group_create")
*/
public function CreateGroupAction(Request $request)
{
$group = new Group();
$form = $this->createForm(GroupType::class, $group);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($group);
$em->flush();
return View::create($group, 201);
}
return View::create($form, 400);
}
The request body I send to /api/groups
{
"group": {
"name": "Entwickler",
"folder": [
1
]
}
}
I tried many diffenrent ways to solve that problem, but nothing works as i expected. That drives me nuts for hours now.
Thanks to Mathieu Dormeval, that was the solution.
I altered the group entity definition from:
/**
* #ORM\Entity(repositoryClass="App\Repository\GroupRepository")
*/
class Group
To:
/**
* #ORM\Entity(repositoryClass="App\Repository\GroupRepository")
* #ORM\Table(name="groups")
*/
class Group

new entity not detected

I created a new entity in an entity folder where I already have some other used entity. However this new entity is not detected by doctrine:mapping:info nor doctrine:schema:validate
It appears the file is simply not taken into account (if I write an error inside symfony is executed without issue).
I was thinking about a VM system issue but then I tried to create other new files (such as a new YML,a new symfony form) and it works...
I also cleared the cache:clear and doctrine:cache:all options
here is the class:
<?php
namespace NRtworks\BusinessDimensionBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use JsonSerializable;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Table(name="test")
*/
class test implements JsonSerializable
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=50)
*/
protected $name;
/**
* #ORM\Column(type="string", length=100)
*/
protected $description;
/**
* Constructor
*/
public function __construct($id = NULL)
{
$this->id = $id;
$this->name = "Chart Of Accounts";
$this->description = "Default chart of accounts";
}
//this method returns an array with default values
public function getDefaultObject()
{
$result = Array();
$result['id'] = $this->id;
$result['name'] = $this->name;
$result['description'] = $this->code;
return $result;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
/**
* Set name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*/
public function getName()
{
return $this->name;
}
/**
* Set code
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* Get description
*/
public function getDescription()
{
return $this->description;
}
public function jsonSerialize()
{
return array(
'id' => $this->id,
'name' => $this->name,
'description' => $this->description
);
}
}
?>
where could this come from ?
You need to define #ORM\Entity annotation for your class:
/**
* #ORM\Entity
* #ORM\Table(name="test")
*/
class test implements JsonSerializable
{

symfony2: how to write the methods for a many to many relationship?

I've created these two models below (Author and Book) associated through a many to many relationship. Then I have generated the crud methods.
The problem: when I try to create or save an author using the new or edit form, it doesn't save the books.
namespace Prueba\FrontendBundle\Entity;
use \Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #ORM\Entity()
* #ORM\Table(name="author")
*/
class Author
{
/** #ORM\Id #ORM\GeneratedValue #ORM\Column(type="integer") **/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $name;
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="authors")
* #ORM\JoinTable(name="author_book")
*/
protected $books;
public function __construct()
{
$this->books = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getBooks()
{
return $this->books;
}
public function addBook($book)
{
$this->books[] = $book;
}
public function setBooks($books)
{
$this->books = $books;
}
public function __toString()
{
return $this->name;
}
}
--------------
namespace Prueba\FrontendBundle\Entity;
use \Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #ORM\Entity()
* #ORM\Table(name="book")
*/
class Book
{
/** #ORM\Id #ORM\GeneratedValue #ORM\Column(type="integer") **/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $title;
/**
* #ORM\ManyToMany(targetEntity="Author", inversedBy="books")
* #ORM\JoinTable(name="author_book")
*/
protected $authors;
public function __construct()
{
$this->authors = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
//die("fadsfadsff");
$this->title = $title;
}
public function getAuthors()
{
//die();
return $this->authors;
}
public function addAuthor($authors)
{
die();//Here is not entering after editing a book!!!!! why????
$this->authors = $authors;
}
public function setAuthors($authors)
{
die();//Here is not entering after editing a book!!!!! why????
$this->authors = $authors;
}
public function __toString()
{
return $this->title;
}
}
Not sure if that's the cause of your problem, but you specified inversedBy in both the entities.
It should be:
class Author
{
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="authors")
*/
protected $books;
}
Because an author owns the books.
And
class Book
{
/**
* #ORM\ManyToMany(targetEntity="Author", mappedBy="books")
*/
protected $authors;
}
Because a book belongs to an author.
Cascaded persistence is not automatic, and you haven't specified any cascade rules
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="authors", cascade={"persist"})
* #ORM\JoinTable(name="author_book")
*/
protected $books;
I've not used cascading with ManyToMany associations myself, but it should work

Resources