How to not multiply request in symfony when using association table - php

I have a trouble with symfony.
I have three table in my database :
Musicien, Composer and Oeuvre
Composer is an association table between Musicien and Oeuvre.
When I load a page who show a Musicien, it show his Oeuvres.
So I make a join between Musicien and Composer, and between Composer and Oeuvre.
From a Musicien I can get all his composer with one request. But, to show his Oeuvres, I need to loop on his Composers to get all Oeuvres.
It makes one request by get, so if I have n Composer it will makes n request.
It is horribly slow.
I want to make it with one request, but I don't know how to do that, can you help me ?
Maybe if I make the join between Musicien and Oeuvre directly, but I don't know how to do that too.
Thank you.
This is my code :
Musicien:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Musicien
*
* #ORM\Table(name="Musicien", indexes={#ORM\Index(name="IDX_AC6BE67520B77BF2", columns={"Code_Pays"}), #ORM\Index(name="IDX_AC6BE675E1990660", columns={"Code_Genre"}), #ORM\Index(name="IDX_AC6BE675D389A975", columns={"Code_Instrument"})})
* #ORM\Entity
*/
class Musicien
{
/* Some attributes here*/
/**
* #var \Composer
*
* #ORM\OneToMany(targetEntity="Composer", mappedBy="codeMusicien")
*/
private $composer;
public function __construct(){
$this->oeuvres = new ArrayCollection();
}
public function getOeuvres()
{
$oeuvres = new ArrayCollection();
$oeuvres->add($this->composer->getCodeOeuvre());
// foreach($this->composer as $c){
// $oeuvres->add($c->getCodeOeuvre());
// }
return $oeuvres;
}
}
Composer
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Composer
*
* #ORM\Table(name="Composer", indexes={#ORM\Index(name="IDX_6105648EE694D5AB", columns={"Code_Musicien"}), #ORM\Index(name="IDX_6105648ECB48FCBD", columns={"Code_Oeuvre"})})
* #ORM\Entity
*/
class Composer
{
/**
* #var int
*
* #ORM\Column(name="Code_Composer", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $codeComposer;
/**
* #var \Musicien
*
* #ORM\ManyToOne(targetEntity="Musicien", inversedBy="composer")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="Code_Musicien", referencedColumnName="Code_Musicien")
* })
*/
private $codeMusicien;
/**
* #var \Oeuvre
*
* #ORM\ManyToOne(targetEntity="Oeuvre", inversedBy="composer")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="Code_Oeuvre", referencedColumnName="Code_Oeuvre")
* })
*/
private $codeOeuvre;
public function getCodeComposer(): ?int
{
return $this->codeComposer;
}
public function getCodeMusicien(): ?Musicien
{
return $this->codeMusicien;
}
public function setCodeMusicien(?Musicien $codeMusicien): self
{
$this->codeMusicien = $codeMusicien;
return $this;
}
public function getCodeOeuvre(): ?Oeuvre
{
return $this->codeOeuvre;
}
public function setCodeOeuvre(?Oeuvre $codeOeuvre): self
{
$this->codeOeuvre = $codeOeuvre;
return $this;
}
}
Oeuvre
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Oeuvre
*
* #ORM\Table(name="Oeuvre", indexes={#ORM\Index(name="IDX_32522BC898F61075", columns={"Code_Type"})})
* #ORM\Entity
*/
class Oeuvre
{
/**
* Some attributes here
*/
/**
* #var \Composer
*
* #ORM\OneToMany(targetEntity="Composer", mappedBy="codeOeuvre")
* #ORM\JoinTable(name="Composer")
*/
private $composer;
public function __construct(){
$this->musiciens = new ArrayCollection();
}
public function getMusiciens()
{
$musiciens = new ArrayCollection();
foreach($this->composer as $c){
$musiciens->add($c->getCodeMusicien());
}
return $musiciens;
}
}

Related

Doctrine entities reference each other's primary key

I have an Account entity which contains a collection of Group entities. In addition, each Account must have a default Group. How can one persist the entities? When attempting to do so, $manager->flush(); complains with a Not null violation for the entity that was persisted first.
Account:
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Bridge\Doctrine\IdGenerator\UuidV4Generator;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
*/
class Account
{
/**
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UuidV4Generator::class)
*/
private $id;
/**
* #ORM\OneToOne(targetEntity=Group::class, cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false, unique=true)
*/
private $defaultGroup;
/**
* #ORM\OneToMany(targetEntity=Group::class, mappedBy="account")
*/
private $groups;
public function __construct()
{
$this->groups = new ArrayCollection();
$this->defaultGroup = new Group();
}
// Typical getters and setters
}
Group:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
*/
class Group
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Account::class, inversedBy="groups")
* #ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $account;
// Typical getters and setters
}
Your approach is not possible because Group::$account is already mapped to the one-to-many Account::$groups property. Doctrine does not know what to do when you persist a Group via Account::$defaultGroup, as this is not the property Group::$account is inversing.
You will have to enforce this Account default group invariant yourself programmatically. There's multiple approaches to this, but I suggest using the existing Account::$groups mapping in combination with the Symfony Collection validation constraint. Validating that an Account should have at least 1 Group assigned to it, which will be the default one.
Here's an example:
Because the entity name Group was giving me syntax problems in the SQL dialect I was using I renamed it to Team.
The Account implementation:
<?php declare(strict_types = 1);
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
*/
class Account
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* #ORM\OneToMany(
* targetEntity="App\Entity\Team",
* mappedBy="account",
* cascade={"persist", "remove"},
* )
* #Assert\Count(
* min=1
* )
*/
private Collection $teams;
public function __construct()
{
$this->teams = new ArrayCollection();
$this->addTeam(new Team());
}
public function getId(): ?int
{
return $this->id;
}
public function getDefaultTeam(): ?Team
{
$team = $this->teams->first();
return $team instanceof Team ? $team : null;
}
public function getTeams(): Collection
{
return $this->teams;
}
public function setTeams(array $teams): void
{
$this->teams->clear();
foreach ($teams as $team) {
if ($team instanceof Team) {
$this->addTeam($team);
}
}
}
public function addTeam(Team $team): void
{
if (false === $this->teams->contains($team)) {
$this->teams->add($team);
$team->setAccount($this);
}
}
public function removeTeam(Team $team): void
{
if (true === $this->teams->contains($team)) {
$this->teams->removeElement($team);
}
}
}
The Team implementation:
<?php declare(strict_types = 1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Team
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* #ORM\ManyToOne(
* targetEntity="App\Entity\Account",
* inversedBy="teams",
* )
* #ORM\JoinColumn(
* name="account_id",
* referencedColumnName="id",
* nullable=false
* )
*/
private ?Account $account = null;
public function getId(): ?int
{
return $this->id;
}
public function getAccount(): ?Account
{
return $this->account;
}
public function setAccount(Account $account): void
{
$this->account = $account;
}
}

Filter an array collection relation

I try to filter the collection returned by a parent Entity base on several conditions that refer to another Entity (User).
Basicaly i want API PLATFORM to return the messages from the current connected User only.
Here is my Entity named File (very simplified for global understanding)
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation\Groups;
use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\DateFilter;
use App\Controller\FileCreatePdfController;
use Ramsey\Uuid\Uuid;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #ApiResource(
*
* normalizationContext={"groups"={"file"},"enable_max_depth"=true},
* denormalizationContext={"groups"={"file-write"},"enable_max_depth"=true},
* attributes={"force_eager"=false},
* )
* #ApiFilter(SearchFilter::class, properties={"status": "exact"})
* #ApiFilter(DateFilter::class, properties={"updatedAt"})
* #ORM\Entity
* #ORM\Table(name="cases")
*/
class File
{
public function __construct()
{
$this->id = Uuid::uuid4();
$this->messages = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
/**
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
*/
private $id;
/**
* #var Collection|FileMessage[]
*
* #Groups({"file"})
* #ORM\OneToMany(targetEntity="App\Entity\FileMessage", mappedBy="file")
*/
private $messages;
/**
* #return Collection|FileMessage[]
*/
public function getMessages(): Collection
{
return $this->messages;
}
/**
* #param FileMessage $message
* #return File
*/
public function addMessage(FileMessage $message): self
{
if (false === $this->messages->contains($message)) {
$this->messages->add($message);
}
return $this;
}
}
My file contains some messages from FileMessage (very simplified for global understanding
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Ramsey\Uuid\Uuid;
/**
* #ApiResource(
* normalizationContext={"groups"={"file-message"}, "enable_max_depth"=true},
* denormalizationContext={"groups"={"file-message-write"}, "enable_max_depth"=true},
* attributes={"force_eager"=false}
* )
* #ORM\Entity
* #ORM\Table(name="`file_messages`")
*/
class FileMessage {
/**
*
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
*/
private $id;
/**
* #var File
*
* #ORM\ManyToOne(targetEntity="File", cascade={"persist"}, inversedBy="messages")
* #Assert\Valid
* #Groups({"file-message", "file-message-write","file"})
*/
private $file;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User", cascade={"persist"}, inversedBy="messages")
* #Assert\Valid
* #Groups({"file-message", "file-message-write","file"})
*/
private $user;
public function __construct()
{
$this->id = Uuid::uuid4();
}
public function getId()
{
return $this->id;
}
public function getFile(): ?File
{
return $this->file;
}
public function setFile(?File $file): self
{
$this->file = $file;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
Each message is posted by a specific user (another entity User ) i don't think it is neccesary to post the content of this entity.
When i fetch a specific file , url/files/[file_id]
All the messages for all users are displayed , i want to hide all message that not refer to the connected User
Solution I have tried so far :
Extensions , I only filter the /files that i retreive not the messages collection
Custom filters , does not seems to be appropriated
Collection filtering , seems perfect only when you filter on current class properties , in my case i need to filter the properties based on another class content (User).
Do I have any solution ? I was thinking maybe to use an event listener
Extensions are the way to go. They give you access to the Doctrine ORM Query Builder, so you can filter both the current resource and its relations by tweaking the WHERE clause of the DQL query.
It's always better to filter the data as early as possible (for performance, among other reasons), so here doing it at the DBMS level is the best option.

Symfony Doctrine ManyToOne association migration No reaction

I have two tables one is entry.php and user.php
I Want to associate $balance of user with the $balance of entry
but when I try php bin/console doctrine:migrations:diff
I get
No changes detected in your mapping information.
this is my user.php
<?php
namespace BankBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Table(name="user")
*
* #ORM\Entity(repositoryClass="BankBundle\Entity\user")
**/
class user
{
/**
* #ORM\Id
* #ORM\Column(name="id",type="integer")
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="entry", mappedBy="balance")
* #var entry[] An ArrayCollection of entry objects.
*/
private $balance;
public function getId()
{
return $this->id;
}
public function getBalance()
{
return $this->balance;
}
}
and my entry.php
<?php
namespace BankBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Table(name="entry")
*
* #ORM\Entity(repositoryClass="BankBundle\Repository\BankRepository")
**/
class entry
{
/**
* #ORM\Id
* #ORM\Column(name="id",type="integer")
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Column(name="amount",type="integer")
*/
private $amount;
/**
* #ORM\Column(name="balance",type="integer")
* #ORM\ManyToOne(targetEntity="user", inversedBy="balance")
*/
private $balance;
/**
* #ORM\Column(name="created_at",type="datetime")
*/
private $created_at;
}

I'm struggling about a not working ManyToMany relationship in Symfony 2 with doctrine as ORM

Hi there and thanks ahead to anybody who will try to help.
I'm pretty new to Symfony and doctrine and I cannot figure out, why my ticket entity is stored without the ManyToMany Realtionship.
I tried very hard, did anything I found for some days but finally I need help.
Here we are
Ticket
<pre>
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use AppBundle\Entity\Computer;
/**
* #ORM\Entity
* #ORM\Table(name="tickets")
*/
class Ticket {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="integer")
*/
protected $creator;
/**
* #ORM\Column(type="integer")
*/
protected $owner;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="tickets")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* #ORM\ManyToOne(targetEntity="Type", inversedBy="tickets")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id")
*/
protected $type;
/**
* #ORM\Column(type="string")
*/
protected $subject;
/**
* #ORM\ManyToMany(targetEntity="Computer", inversedBy="tickets", cascade={"persist"})
* #ORM\JoinTable(name="inventory_computer_join",
* joinColumns={#ORM\JoinColumn(name="ticket_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="computer_id", referencedColumnName="id")}
*)
*/
protected $computer;
[...]
/**
* Add computer
*
* #param \AppBundle\Entity\Computer $computer
*
* #return Ticket
*/
public function addComputer(\AppBundle\Entity\Computer $computer)
{
$this->computers[] = $computer;
return $this;
}
/**
* Remove computer
* #param \AppBundle\Entity\Computer $computer
*/
public function removeComputer(\AppBundle\Entity\Computer $computer)
{
$this->computers->removeElement($computer);
}
/**
* Get computers
* #return \Doctrine\Common\Collections\Collection
*/
public function getComputers()
{
return $this->computers;
}
}
</pre>
Computer
<pre>
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use AppBundle\Entity\Ticket;
/**
* #ORM\Entity
* #ORM\Table(name="inventory_computer")
*/
class Computer
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="Ticket", mappedBy="computer")
*/
protected $tickets;
[...]
/**
* Add ticket
*
* #param \AppBundle\Entity\Ticket $ticket
*
* #return Computer
*/
public function addTicket(\AppBundle\Entity\Ticket $ticket)
{
$this->tickets[] = $ticket;
return $this;
}
</pre>
Controller
<pre>
/**
* #Route("admint/createTicket", name="AdminTicketErstellen")
*/
public function AdminCreateTicketAction(Request $request) {
$ticket = new Ticket;
$form = $this->createForm(TicketType::class, $ticket, array(
'method' => 'GET'
));
$form->handleRequest($request);
if ($form->isSubmitted()) {
$em = $this->getDoctrine()->getManager();
$em->persist($ticket);
$em->flush();
return $this->redirectToRoute('measure_servers');
}
return $this->render( 'sysadmin/pages/AdminCreateTicket.html.twig', array(
'ticket' => $form->createView(),
)
);
}
</pre>
If you want storage ManyToMany try it;
First:
Change protected $commputer; to protected $computers at Ticket.php entity.
Because, you using $this->computers at addComputer() or removeComputer() .
Second: * #ORM\ManyToMany(targetEntity="Ticket", mappedBy="computer") to mappedBy="computers" at Computer.php entity.
You can storage ticket with computer, try it at Controller:
$ticket = new Ticket();
$computer = new Computer();
$ticket->setCreator(4);
$ticket->setOwner(3);
$computer->addTicket($ticket);
$ticket->addComputer($computer);
$em = $this->getDoctrine()->getManager();
$em->persist($computer);
$em->persist($ticket);
$em->flush();

Symfony2 Doctrine OneToMany Error

I have a big understanding problem with OneToMany Relationship - i searched a lot but that didnt help, maybe someone can help me with this.
I have a Event Entity with OneToMany Relation
<?php
namespace #\#\#;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Turmforum\BookingBundle\Entity\Contact;
/**
* Event
*
* #ORM\Table(name="events")
* #ORM\Entity
*/
class Event
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Contact", mappedBy="event")
*/
protected $event_contacts;
/**
*
*/
public function __construct() {
$this->contacts = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getContacts() {
return $this->contacts;
}
public function addContact(Contact $contact) {
$this->contacts[] = $contact;
return $this;
}
And have a Contact Entity with ManyToOne Relation
<?php
namespace #\#\#;
use Doctrine\ORM\Mapping as ORM;
use Turmforum\BookingBundle\Entity\Event;
/**
* Contact
*
* #ORM\Table(name="contacts")
* #ORM\Entity
*/
class Contact
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Event", inversedBy="event_contacts")
* #ORM\JoinColumn(name="event_id", referencedColumnName="id")
*/
protected $event;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getEvent() {
return $this->event;
}
When i try to save my event details over a form i get the error:
Neither the property "event_id" nor one of the methods "getEventId()",
"eventId()", "isEventId()", "hasEventId()", "__get()" exist and have
public access in class "Turmforum\BookingBundle\Entity\Event".
What am i missing?

Categories