Add Method not working in a many to many relationship - doctrine-orm

I have this many to many relationship between bus and driver .
This is the bus entity :
/**
* #var ArrayCollection<Driver> The driver of this bus.
* #ORM\ManyToMany(targetEntity="Driver", inversedBy="bus" , cascade={"persist"})
* #ORM\JoinTable(name="bus_driver")
* #ORM\JoinColumn(name="driver_id", referencedColumnName="id")
* */
private $driver;
public function __construct() {
$this->driver = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addDriver($driver) {
$this->driver[] = $driver;
return $this;
}
And this is the driver entity :
/**
* #var ArrayCollection<Bus> The buses of this driver
* #ORM\ManyToMany(targetEntity="Bus", mappedBy="driver")
* #ORM\JoinTable(name="bus_driver")
* #ORM\JoinColumn(name="bus_id", referencedColumnName="id")
*/
private $bus;
public function __construct() {
$this->bus = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addBus($bus) {
$this->bus[] = $bus;
$bus->addDriver($this);
return $this;
}
My problem is that when I add a bus with a driver the relation is persisted but not when I add a driver whih a bus . It works only from the bus side.

please, consider renaming $driver into $drivers, as there is multiple drivers (same for bus -> buses)
and then you should try that:
#ORM\ManyToMany(targetEntity="xxx", cascade={"persist"})
more details: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/working-with-associations.html#transitive-persistence-cascade-operations

Change these (add the null and call it 'drivers'):
use Doctrine\Common\Collections\ArrayCollection;
...
private $drivers = null;
public function __construct() {
$this->drivers = new ArrayCollection();
}
public function addDriver($driver) {
$this->drivers[] = $driver;
return $this;
}
Also, to resolve the problem from the Bus Entity side, you might (but I'm not sure) need this change:
public function addDriver($driver) {
$driver->addBus($this);
$this->drivers[] = $driver;
return $this;
}
Try it, since I have a similar scenario in a ManyToOne relation, and I'm wondering if the above change might work.

The only working scenario was when I had :
A setter for the drivers collection .
An addDriver Method .
A removeDriver Method.
If I remove one of the pervious , addDriver won't even trigger at all .

Related

Symfony4 Doctrine Associations insert

I get xml data from external soap server, parsing data and create Object. Next I want to persist it in database but it does't work.
Company id, I get from external soap and its string unique value like 387sdfh899ohkadkfh8.
Company
/**
* #ORM\Entity
*/
class Company
{
/**
* #ORM\Id()
* #ORM\GeneratedValue(strategy="NONE")
* #ORM\Column(type="string")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Address", mappedBy="company", orphanRemoval=true, cascade={"persist","remove"})
*/
private $addresses;
// ...
}
Address
/**
* #ORM\Entity
*/
class Address
{
// ...
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Company",inversedBy="adresses")
* #ORM\JoinColumn(nullable=false)
*/
private $company;
// ...
}
CompanyController
class CompanyController
{
// ...
$json = $serializer->serialize($data, 'json');
$obj = $serializer->deserialize($json, 'array<App\Entity\Company>', 'json');
// ...
}
Every thing looks as expected. Object was created including two Address objects.
Update - begin
This is structure what I get from deserialize
array:1 [
0 => Company {#524
-id: "0946346d06ffe3f551a80700c2a5c534"
// ..
-addresses: ArrayCollection {#538
-elements: array:2 [
0 => Address {#1017
-id: null
// ...
-company: null
}
1 => Address {#537
-id: null
// ..
-company: null
}
]
}
-status: "Active"
}
]
Update - end
But when I try to store it in database:
CompanyController
class CompanyController
{
// ...
$em= $this->getDoctrine()->getManager();
foreach ($obj as $o) $em->persist($o);
$em->flush();
// ...
}
I'm getting error
inserting address doesn't include id of company. company_id is setting to null
Similar json data, including addresses corresponding to company I'll be getting from client with json format, parsing it via FormType and store to database but I can't manage with :/
How should I insert that related objects in proper way?
Ok, I solved the problem
Company
/**
* #ORM\OneToMany(targetEntity="App\Entity\Address", mappedBy="company", orphanRemoval=true, cascade={"persist","remove"})
* #JMS\Accessor(setter="setAddresses")
*/
private $addresses;
And added method:
/**
* #param ArrayCollection $addresses
* #return Company
*/
public function setAddresses(ArrayCollection $addresses): self
{
if ( !$this->addresses instanceof ArrayCollection ){
$this->addresses = new ArrayCollection();
};
foreach ($addresses as $address){
if (!$this->addresses->contains($address)) {
$this->addresses[] = $address;
$address->setCompany($this);
}
}
return $this;
}
I spend on this issue two days :/ and solution was so easy, #malarzm Thanks for suggestion it helped me a lot.

API Platform - PATCH and ArrayCollection

I'm using API-Platform and faced an issue with updating many-to-many with an empty value.
Here is the small example:
/**
* Many Organizations have Many Followers.
* #ORM\ManyToMany(targetEntity="App\Entity\User\User", inversedBy="organizations")
* #ORM\JoinTable(name="organizations_followers",
* joinColumns={#ORM\JoinColumn(name="organization_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", unique=true)}
* )
*/
protected $followers;
/**
* #return Collection
*/
public function getFollowers(): Collection
{
return $this->followers;
}
/**
* #param array $followers
*/
public function setFollowers(array $followers): void
{
$this->followers = $followers;
}
/**
* Organization constructor.
*/
public function __construct()
{
$this->id = Uuid::uuid4();
$this->followers = new ArrayCollection();
}
So, when I'm trying to delete all followers (PATCH request with empty followers in the relationships field) I always get one undeleted record. What am I doing wrong? Any Ideas?

Relationship between two tables in doctrine. Data dosen't save in database

I have a problem with saving data in Database.I have two tables which are created with Doctrine Entities. My Entities are:
<?php
namespace App\Http\Entities\Cic;
use \Doctrine\ORM\Mapping AS ORM;
use \Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="cic_case_files")
*/
class CicCaseFile {
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="CicCase", inversedBy="cicFiles")
* #ORM\JoinColumn(name="case_id", referencedColumnName="id")
*/
protected $case;
/**
* One Product has One Shipment.
* #ORM\OneToOne(targetEntity="File")
* #ORM\JoinColumn(name="file_id", referencedColumnName="id")
*/
protected $file;
/**
* #ORM\Column(name="case_id", type="integer", nullable=false)
*/
protected $case_id;
/**
* #ORM\Column(name="file_id", type="integer", nullable=false)
*/
protected $file_id;
public function __construct() {
//$this->mailboxes = new ArrayCollection();
}
public function setFileId($fileId){
$this->file_id = $fileId;
}
public function getFileId(){
return $this->file_id;
}
public function setCaseId($caseId){
$this->case_id = $caseId;
}
public function getCaseId(){
return $this->case_id;
}
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getCase(){
return $this->case;
}
public function setCase($case){
$this->case = $case;
}
public function getFile(){
return $this->file;
}
public function setFile($file){
$this->file = $file;
}
}
<?php
namespace App\Http\Entities\Cic;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="\App\Http\Repositories\Cic\FileRepository")
* #ORM\Table(name="files")
*/
class File
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
protected $id;
/**
* #ORM\Column(type="string")
* #var string
*/
protected $path;
/**
* #ORM\Column(type="string")
* #var string
*/
protected $originalName;
/**
* #ORM\Column(type="string")
* #var string
*/
protected $hashName;
/**
* #ORM\Column(type="string")
* #var string
*/
protected $extension;
public function getId(){
return $this->id;
}
public function setId($id){
$this->id = $id;
}
public function getPath(){
return $this->path;
}
public function setPath($path){
$this->path = $path;
}
public function getOriginalName(){
return $this->originalName;
}
public function setOriginalName($originalName){
$this->originalName = $originalName;
}
public function getHashName(){
return $this->hashName;
}
public function setHashName($hashName){
$this->hashName = $hashName;
}
public function getExtension(){
return $this->extension;
}
public function setExtension($extension){
$this->extension = $extension;
}
}
My tables:
files:
cic_case_file:
I'd like to save data in table CicCaseFile by code:
$cicFile = new CicCaseFile();
cicFile->setCaseId($caseId);
$cicFile->setFileId($fileId);
$this->entityManager->persist($cicFile);
$this->entityManager->flush();
Something is wrong with my Entities, but I don't know what. Could someone help me with that? I would be very greatful. Best regards.
This is what I used to follow if I am in your position.
Your File Entity must only have id, path, extension, original_name, hash_name properties and methods, you can internally use any conversion methods
Your CicCaseFile Entity must only have id, case_id, file_id properties and methods, you can internally use any conversion methods
So what must be your approach while inserting the data is, first you need to insert the data for your parent table ie file table then its children table ie cic_case_file table
$em = $this->getDoctrine()->getManager(); /* In Symfony Framework */
$fileObj = new File();
$fileObj->setPath($path);
$fileObj->setExtension($extension);
$fileObj->setOriginalName,($originalName);
$fileObj->setHashName($hashName);
$em->persist($fileObj);
$em->flush();
/* So once you flush here it fileObj will be having the last inserted object which you can use to store in child table */
$cicCaseFileObj = new CicCaseFile();
$cicCaseFileObj->setFile($fileObj); /* $fileObj which was inserted above */
$cicCaseFileObj->setCaseId($caseObj); /* Same as $fileObj as inserted above in case if this is from different table */
$em->persist($cicCaseFileObj);
$em->flush();
if caseId is inserted in different saveMethod then first you need to fetch the object and then give it to the above object as follows
$caseId = 10; /* This you may get from else where */
$caseObj = $em->getRepository('BundleName:Case')->find($caseId);
/* Make sure your case_id is nullable or not */
$cicCaseFileObj = new CicCaseFile();
$cicCaseFileObj->setFile($fileObj);
if($caseObj){
$cicCaseFileObj->setCaseId($caseObj);
}
$em->persist($cicCaseFileObj);
$em->flush();
Happy Coding!

doctrine2 foreign key gets not created

I have two simple entities connected to each other with a OneToMany association.
/**
* #ORM\Entity(repositoryClass="Shopware\CustomModels\JoeKaffeeAbo\Client")
* #ORM\Table(name="joe_kaffee_abo_client")
*/
class Client extends ModelEntity
{
public function __construct() {
$this->abos = new ArrayCollection();
}
/**
* #ORM\OneToMany(targetEntity="Shopware\CustomModels\JoeKaffeeAbo\Abo", mappedBy="client", cascade= "all")
* #var \Doctrine\Common\Collections\ArrayCollection
*/
protected $abos;
public function addAbo($abo)
{
//$this->abos[] = $abo;
$this->abos->add($abo);
}
The second one is Abo:
/**
* #ORM\Entity(repositoryClass="Shopware\CustomModels\JoeKaffeeAbo\Abo")
* #ORM\Table(name="joe_kaffee_abo_abos")
*/
class Abo extends ModelEntity
{
/**
* #ORM\ManyToOne(targetEntity="Shopware\CustomModels\JoeKaffeeAbo\Client",inversedBy="abos")
* #ORM\JoinColumn(name="clientId", referencedColumnName="id")
*/
protected $client;
}
Somehow the join column gets not filled - it seems that doctrine ignores the association I set up. And if I call $client->addAbo($abo) there gets not relation created...and I cant receive the added abos via a getter function which returns the abo array collection.

Doctrine 2 Many To Many follower relationship not working

I have followed the example here doctrine 2 documentation and made the entity
<?php
namespace Account\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Zend\Filter\Null;
/**
* #ORM\Entity
* #ORM\Table(name="accounts")
*/
class Account
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
* #ORM\Column(length=11)
*/
private $id;
// ......
/**
* #ORM\ManyToMany(targetEntity="Account\Entity\Account", mappedBy="following")
*/
private $followers;
/**
* #ORM\ManyToMany(targetEntity="Account\Entity\Account", inversedBy="followers")
* #ORM\JoinTable(name="followers",
* joinColumns={#ORM\JoinColumn(name="account_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="follower_id", referencedColumnName="id")}
* )
*/
private $following;
public function __construct(){
$this->followers = new ArrayCollection();
$this->following = new ArrayCollection();
}
/**
* #param mixed $followers
*/
public function setFollowers($followers)
{
$this->followers[] = $followers;
}
/**
* #return mixed
*/
public function getFollowers()
{
return $this->followers;
}
public function addFollowers($followers){
foreach($followers as $follower)
$this->followers->add($follower);
}
public function removeFollowers($followers){
$this->followers->removeElement($followers);
}
/**
* #param mixed $following
*/
public function setFollowing($following)
{
$this->following[] = $following;
}
/**
* #return mixed
*/
public function getFollowing()
{
return $this->following;
}
public function addFollowing($followers){
foreach($followers as $follower)
$this->following->add($follower);
}
public function removeFollowing($followers){
$this->following->removeElement($followers);
}
/**
* #param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
}
So I have 2 accounts (ids 1 and 2) and made it so that 1 follows (is friend to) 2.
The column is something like
user_id follower_id
2 1
By using the following code, I'm not getting any results as I should
$user = $this->entityManager()->getRepository('Account/Entity/Account')->find(1);
$followers = $user->getFollowers();
var_dump($followers);
It returns something like:
object(Doctrine\ORM\PersistentCollection)#357 (9) { ["snapshot":"Doctrine\ORM\PersistentCollection":private]=> array(0) { } ["owner":"Doctrine\ORM\PersistentCollection":private]=> NULL ["association":"Doctrine\ORM\PersistentCollection":private]=> NULL ["em":"Doctrine\ORM\PersistentCollection":private]=> NULL ["backRefFieldName":"Doctrine\ORM\PersistentCollection":private]=> NULL ["typeClass":"Doctrine\ORM\PersistentCollection":private]=> NULL ["isDirty":"Doctrine\ORM\PersistentCollection":private]=> bool(false) ["initialized":"Doctrine\ORM\PersistentCollection":private]=> bool(false) ["coll":"Doctrine\ORM\PersistentCollection":private]=> object(Doctrine\Common\Collections\ArrayCollection)#358 (1) { ["_elements":"Doctrine\Common\Collections\ArrayCollection":private]=> array(0) { } } }
The same happens if I use getFollowing and all the combinations I've tried. Am I missing something? I mean it's pretty much like the documentation code, please help me out!
I'm using Zend Framework 2, if that's of any help.
All associations are LAZY by default, which means it is populated when you first access it. PersistentCollection actually is an iterator and a var_dump will not trigger iteration, that's why you see _intialized property set to false and the count of _elements is 0.
You can use getArrayCopy or simply iterate through the collection.
var_dump($followers->getArrayCopy());
or:
foreach ($followers as $follower) {
var_dump($follower);
}