Symfony join query with One-To-Many relation - symfony

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,
]);
}

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();

Semantical Error line 0, col 97 near 'prix WHERE prix': Error: Class App\Entity\Produit has no association named prix

I try to join two entities with Symfony QueryBuilder. I've created a function findAllQuery in order to filter a research, but I get the error that my entity doesn't have any association. I saw already some similar questions but I can't resolve the problem.
Thanks for help !
Publication entity
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=PublicationRepository::class)
*/
class Publication
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="date", nullable=true)
*/
private $datePublication;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="publications")
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity=Produit::class, inversedBy="publications")
*/
private $produit;
public function getId(): ?int
{
return $this->id;
}
public function getDatePublication(): ?\DateTimeInterface
{
return $this->datePublication;
}
public function setDatePublication(?\DateTimeInterface $datePublication): self
{
$this->datePublication = $datePublication;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function getProduit(): ?Produit
{
return $this->produit;
}
public function setProduit(?Produit $produit): self
{
$this->produit = $produit;
return $this;
}
}
Produit Entity
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ProduitRepository::class)
*/
class Produit
{
public function hydrate(array $init)
{
foreach ($init as $key => $value) {
$method = "set" . ucfirst($key);
if (method_exists($this, $method)) {
$this->$method($value);
}
}
}
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $nom;
/**
* #ORM\Column(type="string", length=255)
*/
private $description;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $vendu;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $photo;
/**
* #ORM\ManyToOne(targetEntity=Commande::class, inversedBy="produits")
*/
private $commande;
/**
* #ORM\ManyToOne(targetEntity=Type::class, inversedBy="produit")
*/
private $type;
/**
* #ORM\OneToOne(targetEntity=Publication::class, mappedBy="produit", cascade={"persist", "remove"})
*/
private $publication;
/**
* #ORM\Column(type="float")
*/
private $prix;
/**
* #ORM\OneToMany(targetEntity=Publication::class, mappedBy="produit")
*/
private $publications;
public function __construct()
{
$this->publications = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getVendu(): ?bool
{
return $this->vendu;
}
public function setVendu(?bool $vendu): self
{
$this->vendu = $vendu;
return $this;
}
public function getPhoto(): ?string
{
return $this->photo;
}
public function setPhoto(?string $photo): self
{
$this->photo = $photo;
return $this;
}
public function getCommande(): ?Commande
{
return $this->commande;
}
public function setCommande(?Commande $commande): self
{
$this->commande = $commande;
return $this;
}
public function getType(): ?Type
{
return $this->type;
}
public function setType(?Type $type): self
{
$this->type = $type;
return $this;
}
public function getPrix(): ?float
{
return $this->prix;
}
public function setPrix(float $prix): self
{
$this->prix = $prix;
return $this;
}
/**
* #return Collection|Publication[]
*/
public function getPublications(): Collection
{
return $this->publications;
}
public function addPublication(Publication $publication): self
{
if (!$this->publications->contains($publication)) {
$this->publications[] = $publication;
$publication->setProduit($this);
}
return $this;
}
public function removePublication(Publication $publication): self
{
if ($this->publications->removeElement($publication)) {
// set the owning side to null (unless already changed)
if ($publication->getProduit() === $this) {
$publication->setProduit(null);
}
}
return $this;
}
}
Publication Repository
<?php
namespace App\Repository;
use App\Entity\Publication;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use App\Entity\ProduitSearch;
use App\Entity\Produit;
/**
* #method Publication|null find($id, $lockMode = null, $lockVersion = null)
* #method Publication|null findOneBy(array $criteria, array $orderBy = null)
* #method Publication[] findAll()
* #method Publication[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PublicationRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Publication::class);
}
public function findAllQuery (ProduitSearch $search){
if ($search->getMaxPrix()){
return $this
->createQueryBuilder('publication')
->select('produit')
->from('App\Entity\Produit','produit')
->join('produit.prix','prix')
->andWhere('prix <= :maxPrix')
->setParameter('maxPrix', $search->getMaxPrix())
->getQuery()
->getResult();
}
return $this->findAll();
}
Your query is not right. You can only join on relations and not on fields. I guess you try to get publications where the price of any produit is lower than maxPrix.
return $this
->createQueryBuilder('publication')
->join('publication.produit','produit')
->andWhere('produit.prix <= :maxPrix')
->setParameter('maxPrix', $search->getMaxPrix())
->getQuery()
->getResult();

ManyToMany error : Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given

I have a problem with type ManyToMany relations entities.
I learn symfony i make maybe something wrong.
I tried many ways (Without foreach ...) but i fall always on errors.
These entities were created whit the CLI.
Thanks for your replys and sorry for my bad english
My entities :
Clients
<?php
namespace App\Entity;
use App\Repository\ClientsRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ClientsRepository::class)
*/
class Clients
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $nom;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $prenom;
/**
* #ORM\Column(type="string", length=255)
*/
private $mail;
/**
* #ORM\Column(type="datetime")
*/
private $created_at;
/**
* #ORM\Column(type="boolean")
*/
private $actif;
/**
* #ORM\ManyToMany(targetEntity=Services::class, mappedBy="clients")
*/
private $services;
public function __construct()
{
$this->services = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getPrenom(): ?string
{
return $this->prenom;
}
public function setPrenom(?string $prenom): self
{
$this->prenom = $prenom;
return $this;
}
public function getMail(): ?string
{
return $this->mail;
}
public function setMail(string $mail): self
{
$this->mail = $mail;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->created_at;
}
public function setCreatedAt(\DateTimeInterface $created_at): self
{
$this->created_at = $created_at;
return $this;
}
public function getActif(): ?bool
{
return $this->actif;
}
public function setActif(bool $actif): self
{
$this->actif = $actif;
return $this;
}
/**
* #return Collection|Services[]
*/
public function getServices(): Collection
{
return $this->services;
}
public function addService(Services $service): self
{
if (!$this->services->contains($service)) {
$this->services[] = $service;
$service->setClients($this);
}
return $this;
}
public function removeService(Services $service): self
{
if ($this->services->contains($service)) {
$this->services->removeElement($service);
// set the owning side to null (unless already changed)
if ($service->getClients() === $this) {
$service->setClients(null);
}
}
return $this;
}
}
Services
<?php
namespace App\Entity;
use App\Repository\ServicesRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ServicesRepository::class)
*/
class Services
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $nom;
/**
* #ORM\Column(type="float")
*/
private $prix;
/**
* #ORM\ManyToMany(targetEntity=Clients::class, inversedBy="services")
*/
private $clients;
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getPrix(): ?float
{
return $this->prix;
}
public function setPrix(float $prix): self
{
$this->prix = $prix;
return $this;
}
public function getClients(): ?Clients
{
return $this->clients;
}
public function setClients(?Clients $clients): self
{
$this->clients = $clients;
return $this;
}
}
Controller
[...]
$entityManager = $this->getDoctrine()->getManager();
foreach($servicesClient as $srv){
$service = $this->getDoctrine()->getRepository(Services::class)->find($srv);
$service->setClients($client);
$client->addService($service);
$entityManager->persist($client);
$entityManager->persist($service);
$entityManager->flush();
}
[...]
Your Service::clients property is marked with #ManyToMany annotation, which means it should be typed #var Collection|Clients[] but both getter and setter returns/set a nullable object ?Clients $clients.
No way the bin/console make:entity did that.
You should use the same system as your Clients::services property, i.e using three methods getClients(), addClients() and removeClients() without setClients(). Besides, you need a constructor starting the $clients property: $this->clients = new ArrayCollection();.
Or maybe you want to use #OneToMany and #ManyToOne annotations.
Note that an entity classname should be singular.

collection empty in symfony when collecting many to many datas

I have two entities Template and Zone in Symfony connected by a many to many relationship.
I'm trying to get datas from one of theses so i'm using the following command
$templates = $this->getDoctrine()
->getRepository(Template::class)
->findAll();
I succeed to get my templates datas but the champ "zones" display an empty collection.
Have you got an idea about why I don't get the zones attached to the template.
Here are my code :
<?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\TemplateRepository")
*/
class Template
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* Many Users have Many Groups.
* #ORM\ManyToMany(targetEntity="Zone", inversedBy="templates")
* #ORM\JoinTable(name="templates_zones")
*/
private $zones;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $zonesNumber;
/**
* #ORM\Column(type="string", length=1)
*/
private $format;
/**
* #ORM\Column(type="integer")
*/
private $level;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $background;
/**
* Template constructor.
* #param $zones
*/
public function __construct()
{
$this->zones = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getZonesNumber(): ?int
{
return $this->zonesNumber;
}
public function setZonesNumber(?int $zonesNumber): self
{
$this->zonesNumber = $zonesNumber;
return $this;
}
public function getFormat(): ?string
{
return $this->format;
}
public function setFormat(string $format): self
{
$this->format = $format;
return $this;
}
public function getLevel(): ?int
{
return $this->level;
}
public function setLevel(int $level): self
{
$this->level = $level;
return $this;
}
public function getBackground(): ?string
{
return $this->background;
}
public function setBackground(?string $background): self
{
$this->background = $background;
return $this;
}
/**
* #return Collection|Zone[]
*/
public function getZones(): Collection
{
return $this->zones;
}
public function addZone(Zone $zone): self
{
if (!$this->zones->contains($zone)) {
$this->zones[] = $zone;
}
return $this;
}
public function removeZone(Zone $zone): self
{
if ($this->zones->contains($zone)) {
$this->zones->removeElement($zone);
}
return $this;
}
}
And the zone 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\ZonesRepository")
*/
class Zone
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* Many Groups have Many Users.
* #ORM\ManyToMany(targetEntity="Template", mappedBy="zones")
*/
private $templates;
/**
* Zone constructor.
* #param $templates
*/
public function __construct()
{
$this->templates = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|Template[]
*/
public function getTemplates(): Collection
{
return $this->templates;
}
public function addTemplate(Template $template): self
{
if (!$this->templates->contains($template)) {
$this->templates[] = $template;
$template->addZone($this);
}
return $this;
}
public function removeTemplate(Template $template): self
{
if ($this->templates->contains($template)) {
$this->templates->removeElement($template);
$template->removeZone($this);
}
return $this;
}
}

Symfony 4 - Delete on cascade doesn't work

I've a Symfony 4 project, and I have these entities :
<?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\GroupeValidateursRepository")
*/
class GroupeValidateurs
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Validateur", inversedBy="groupeValidateurs", cascade={"persist"})
* #ORM\OrderBy({"ordre" = "ASC"})
*/
private $validateurs;
/**
* #ORM\Column(type="string", length=255)
*/
private $nom;
/**
* #ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="groupe")
*/
private $users;
public function __construct()
{
$this->validateurs = new ArrayCollection();
$this->users = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* #return Collection|Validateur[]
*/
public function getValidateurs(): Collection
{
return $this->validateurs;
}
public function addValidateur(Validateur $validateur): self
{
if (!$this->validateurs->contains($validateur)) {
$this->validateurs[] = $validateur;
}
return $this;
}
public function removeValidateur(Validateur $validateur): self
{
if ($this->validateurs->contains($validateur)) {
$this->validateurs->removeElement($validateur);
}
return $this;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getListeNomValidateurs()
{
$liste = [];
foreach ($this->validateurs as $validateur) {
$nom = $validateur->getValidateur()->getFullName();
$liste[] = $nom;
}
return $liste;
}
/**
* #return Collection|User[]
*/
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(User $user): self
{
if (!$this->users->contains($user)) {
$this->users[] = $user;
$user->setGroupe($this);
}
return $this;
}
public function removeUser(User $user): self
{
if ($this->users->contains($user)) {
$this->users->removeElement($user);
// set the owning side to null (unless already changed)
if ($user->getGroupe() === $this) {
$user->setGroupe(null);
}
}
return $this;
}
public function getUsersValidateurs()
{
$users = [];
foreach ($this->validateurs as $validateur) {
$unUserValidateur = $validateur->getValidateur();
$users[] = $unUserValidateur;
}
return $users;
}
}
And
<?php
namespace App\Entity;
use Gedmo\Sortable\Sortable;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Gedmo\Mapping\Annotation\SortablePosition;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass="App\Repository\ValidateurRepository")
*/
class Validateur
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="integer")
* #Gedmo\Mapping\Annotation\SortablePosition
*/
private $ordre;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\GroupeValidateurs", mappedBy="validateurs")
*/
private $groupeValidateurs;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User")
* #ORM\JoinColumn(nullable=false)
*/
private $validateur;
public function __construct()
{
$this->groupeValidateurs = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getOrdre(): ?int
{
return $this->ordre;
}
public function setOrdre(int $ordre): self
{
$this->ordre = $ordre;
return $this;
}
public function getValidateur(): ?User
{
return $this->validateur;
}
public function setValidateur(User $validateur): self
{
$this->validateur = $validateur;
return $this;
}
/**
* #return Collection|GroupeValidateurs[]
*/
public function getGroupeValidateurs(): Collection
{
return $this->groupeValidateurs;
}
public function addGroupeValidateur(GroupeValidateurs $groupeValidateur): self
{
if (!$this->groupeValidateurs->contains($groupeValidateur)) {
$this->groupeValidateurs[] = $groupeValidateur;
$groupeValidateur->addValidateur($this);
}
return $this;
}
public function removeGroupeValidateur(GroupeValidateurs $groupeValidateur): self
{
if ($this->groupeValidateurs->contains($groupeValidateur)) {
$this->groupeValidateurs->removeElement($groupeValidateur);
$groupeValidateur->removeValidateur($this);
}
return $this;
}
}
And, when I manage my groups, like this :
If I remove a "validateur", I would like my "validator" object to be removed from the database. So I tried, in my entity "validator", to add the "cascade = {" remove "}", on the attribute "groupValidators" and "validator", but when I delete a validator of my group, the validator object is not removed from the database
I am a bit late, but for those still coming here be careful with typo :
use cascade = {"remove"} not cascade = {" remove "}
Make sure that the owning side of the relationship cascades the "remove" towards the inverse side.
If you've done that, the example code is still missing any kind of EntityManager::flush() call to persist those pending changes towards the orm.

Resources