Symfony4 FOSRestBundle try to persist ManyToMany SQL syntax error - symfony

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

Related

Symfony: Persisting in a "ManyToMany with extra data" after implementing bridge entity

I am trying to persist a "collection" of objects that are related between them through an entity that is in a ManyToOne relation with two other entities in order to provide a kind of "ManyToMany" relation but with extrafields.
I have followed the only strategy proposed at symfonycasts and also here in stackoverflow by different users which is to create an entity that will contain as properties the two other entities plus one extra field.
Users make reservations to a travel on a given date, but such reservations offer different options depending on the travel type.
I make part of the reservation process using javascript and ajax, but yet the problem I have is that my controller will only persist the data from the last item from the array.
The controller's lines where I am encountering the trouble are these:
$options = $request->request->get('options') ? $request->request->get('options') : [];
$dateId = $request->request->get('dateId');
$em = $this->getDoctrine()->getManager();
$datesRepository = $em->getRepository(Dates::class);
$date = $datesRepository->find($dateId);
$reservation = new Reservation();
$now = new \DateTime();
$reservation->setDateAjout($now);
$reservation->setStatus('initialized');
$reservation->setDate($date);
/** INTRODUCING OPTIONS */
if(count($options) > 0) {
$reservationOptions = new ReservationOptions();
$optionsRepository = $em->getRepository(Options::class);
foreach ( $options as $key => $value ){
if($value > 0) {
$option = $optionsRepository->find($key);
$reservationOptions->setOptions($option);
$reservationOptions->setAmount($value);
$reservation->addReservationOption($reservationOptions);
//This only persist on the last loop
}
}
}
$em->persist($reservation);
$em->flush();
The entity that relates both entities in a for a "ManyToMany + extra fields" relation:
<?php
namespace App\Entity;
use App\Repository\ReservationOptionsRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ReservationOptionsRepository::class)
*/
class ReservationOptions
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(
* targetEntity=Reservation::class,
* inversedBy="reservationOptions",
* cascade={"persist"}
* )
* #ORM\JoinColumn(nullable=false)
*/
private $reservation;
/**
* #ORM\ManyToOne(
* targetEntity=Options::class,
* inversedBy="reservationOptions",
* cascade={"persist"}
* )
* #ORM\JoinColumn(nullable=false)
*/
private $options;
/**
* #ORM\Column(type="integer")
*/
private $amount;
public function getId(): ?int
{
return $this->id;
}
public function getReservation(): ?Reservation
{
return $this->reservation;
}
public function setReservation(?Reservation $reservation): self
{
$this->reservation = $reservation;
return $this;
}
public function getOptions(): ?Options
{
return $this->options;
}
public function setOptions(?Options $options): self
{
$this->options = $options;
return $this;
}
public function getAmount(): ?int
{
return $this->amount;
}
public function setAmount(int $amount): self
{
$this->amount = $amount;
return $this;
}
public function __toString()
{
return $this->id;
}
}
The reservation entity looks like this:
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\ReservationRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=ReservationRepository::class)
*/
class Reservation
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Dates::class, inversedBy="reservations")
*/
private $date;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="reservations")
*/
private $user;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $status;
/**
* #ORM\OneToMany(targetEntity=ReservationOptions::class, mappedBy="reservation", orphanRemoval=true, cascade={"persist"})
*/
private $reservationOptions;
public function __construct()
{
$this->travellers = new ArrayCollection();
$this->reservationOptions = new ArrayCollection();
}
public function __toString():string
{
return $this->getStatus();
}
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(?string $status): self
{
$this->status = $status;
return $this;
}
/**
* #return Collection|ReservationOptions[]
*/
public function getReservationOptions(): Collection
{
return $this->reservationOptions;
}
public function addReservationOption(ReservationOptions $reservationOption): self
{
if (!$this->reservationOptions->contains($reservationOption)) {
$this->reservationOptions[] = $reservationOption;
$reservationOption->setReservation($this);
}
return $this;
}
public function removeReservationOption(ReservationOptions $reservationOption): self
{
if ($this->reservationOptions->removeElement($reservationOption)) {
// set the owning side to null (unless already changed)
if ($reservationOption->getReservation() === $this) {
$reservationOption->setReservation(null);
}
}
return $this;
}
}
The options file looks like this:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* Options
*
* #ORM\Table(name="options")
* #ORM\Entity(repositoryClass="App\Repository\OptionsRepository")
*/
class Options
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="prix", type="decimal", precision=10, scale=2, nullable=false)
*/
private $price;
/**
* #ORM\ManyToOne(targetEntity=Travel::class, inversedBy="options")
*/
private $travel;
/**
* #ORM\OneToMany(targetEntity=OptionsTranslations::class, mappedBy="options", orphanRemoval=true, cascade={"persist"})
*/
private $optionsTranslations;
/**
* #ORM\OneToMany(targetEntity=ReservationOptions::class, mappedBy="options", orphanRemoval=true, cascade={"persist"})
*/
private $reservationOptions;
public function __construct()
{
$this->optionsTranslations = new ArrayCollection();
$this->reservationOptions = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getPrice(): ?string
{
return $this->price;
}
public function setPrice(string $price): self
{
$this->price = $price;
return $this;
}
public function getTravel(): ?Travel
{
return $this->travel;
}
public function setTravel(?Travel $travel): self
{
$this->travel = $travel;
return $this;
}
/**
* #return Collection|OptionsTranslations[]
*/
public function getOptionsTranslations(): Collection
{
return $this->optionsTranslations;
}
public function addOptionsTranslation(OptionsTranslations $optionsTranslation): self
{
if (!$this->optionsTranslations->contains($optionsTranslation)) {
$this->optionsTranslations[] = $optionsTranslation;
$optionsTranslation->setOptions($this);
}
return $this;
}
public function removeOptionsTranslation(OptionsTranslations $optionsTranslation): self
{
if ($this->optionsTranslations->removeElement($optionsTranslation)) {
// set the owning side to null (unless already changed)
if ($optionsTranslation->getOptions() === $this) {
$optionsTranslation->setOptions(null);
}
}
return $this;
}
public function __toString():string
{
return $this->travel->getMainTitle();
}
/**
* #return Collection|ReservationOptions[]
*/
public function getReservationOptions(): Collection
{
return $this->reservationOptions;
}
public function addReservationOption(ReservationOptions $reservationOption): self
{
if (!$this->reservationOptions->contains($reservationOption)) {
$this->reservationOptions[] = $reservationOption;
$reservationOption->setOptions($this);
}
return $this;
}
public function removeReservationOption(ReservationOptions $reservationOption): self
{
if ($this->reservationOptions->removeElement($reservationOption)) {
// set the owning side to null (unless already changed)
if ($reservationOption->getOptions() === $this) {
$reservationOption->setOptions(null);
}
}
return $this;
}
}
You are reusing the same ReservationOptions entity, changing the option and value on each iteration of the loop. So, you end up with only one ReservationOptions having the values from the last item.
You need to create a new ReservationOptions entity for each option/value.
/** INTRODUCING OPTIONS */
if(count($options) > 0) {
$optionsRepository = $em->getRepository(Options::class);
foreach ( $options as $key => $value ){
if($value > 0) {
$reservationOptions = new ReservationOptions();
$option = $optionsRepository->find($key);
$reservationOptions->setOptions($option);
$reservationOptions->setAmount($value);
$reservation->addReservationOption($reservationOptions);
}
}
}
$em->persist($reservation);
$em->flush();

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;
}

Using DoctrineBehaviors translatable in Symfony 4?

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.

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
{

Resources