How to remove usage of cascade={"remove","persist"} from entities in Symfony2? - symfony

I am working on a project where there I am forced to use cascade={"remove","persist"} because of the problem described here.
Reading through the documentation, to quote:
Even though automatic cascading is convenient it should be used with care. Do not blindly apply cascade=all to all associations as it will unnecessarily degrade the performance of your application. For each cascade operation that gets activated Doctrine also applies that operation to the association, be it single or collection valued.
And I see that the same can be fixed if I use
$em->persist($entity);
In my persistence services, which I am already calling. However, doctrine doesn't work as expected. Here are my entities.
Entity/Employee.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
/**
* #ORM\Entity
* #ORM\Table(uniqueConstraints={#ORM\UniqueConstraint(
* name="UNIQ_EMPLOYEE_ID_NAME_ADDRESS_STATE_CITY_COUNTRY",
* columns={"id","name","city","state","country"}
* )})
*/
class Employee
{
/**
* #ORM\Id
* #ORM\Column(type="integer", options={"unsigned":true})
* #ORM\GeneratedValue(strategy="NONE")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $name;
/**
* #ORM\Column(type="text", nullable=false)
*/
private $address;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $city;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $state;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $country;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Department", inversedBy="employee", cascade={"remove","persist"})
* #ORM\JoinColumn(name="department_id", referencedColumnName="id", nullable=false)
*/
private $department;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Transfer", mappedBy="employee", cascade={"remove","persist"})
*/
private $transfer;
}
?>
Entity/Department.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Selectable;
/**
* #ORM\Entity
* #ORM\Table(uniqueConstraints={#ORM\UniqueConstraint(
* name="UNIQ_DEPARTMENT_ID_NAME",
* columns={"id","name"}
* )})
*/
class Department
{
/**
* #ORM\Id
* #ORM\Column(type="integer", options={"unsigned":true})
* #ORM\GeneratedValue(strategy="NONE")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=false)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Employee", mappedBy="department", cascade={"remove","persist"})
*/
private $employee;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Transfer", mappedBy="department", cascade={"remove","persist"})
*/
private $transfer;
}
?>
Entity/Transfer.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Selectable;
/**
* #ORM\Entity
* #ORM\Table(uniqueConstraints={#ORM\UniqueConstraint(
* name="UNIQ_TRANSFER_ID_DEPARTMENT_EMPLOYEE_START_END",
* columns={"id","name"}
* )})
*/
class Transfer
{
/**
* #ORM\Id
* #ORM\Column(type="integer", options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="date", length=255, nullable=false)
*/
private $start;
/**
* #ORM\Column(type="date", length=255, nullable=false)
*/
private $end;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Employee", inversedBy="attendance", cascade={"persist","remove"})
* #ORM\JoinColumn(name="employee_id", referencedColumnName="id", nullable=false)
*/
private $employee;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Department", inversedBy="attendance", cascade={"persist","remove"})
* #ORM\JoinColumn(name="department_id", referencedColumnName="id", nullable=false)
*/
private $department;
}
?>
UPDATE 1:
Now, I have another problem. Since my GeneratorValue strategy for Employee and Department are NONE, I have problem with duplicate record error. I am trying to use PreFlushEventArgs to remove the entity before persisting if the record exists in database. But I wonder if it should be that complex?
Thanks for your help in advance.

It seems you cascade persist operation from Employee to Department and Transfer. But they do cascade the persist operation to the Employee Entity too.
It means when you do
$em->persist($an_employee);
You are stuck in a persist loop.
In my opinion, the cascade persist should be only in one way, i.e. only on the Employee entity.
Also if you choose to do this, add your Department to the Employee, and not the opposite :
$an_employee->addDepartment($a_department);
$an_employee->addTransfer($a_transfer);
That way, when you persist an employee, its Departments and Transfer should be persisted too

Related

Symfony / Doctrine materializedPath issues

I have followed the docs:
https://github.com/Atlantic18/DoctrineExtensions/blob/v2.4.x/doc/tree.md#materialized-path
I am getting the following error:
An exception occurred while executing 'UPDATE task SET path = ?, task_id = ?, lvl = ? WHERE id = ?' with params ["-1", "-1", 0, 1]:
SQLSTATE[22003]: Numeric value out of range: 1264 Out of range value for column 'task_id' at row 1
Here is my entity:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\MaterializedPathRepository")
* #Gedmo\Tree(type="materializedPath")
*/
class Task
{
/**
* #ORM\Id
* #ORM\Column(type="integer", options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=128, nullable=false)
*/
private $title;
/**
* #ORM\Column(type="integer", nullable=true)
* #Gedmo\TreeLevel
*/
private $lvl;
/**
* #ORM\Column(type="integer", nullable=true)
* #Gedmo\TreeLeft
*/
private $lft;
/**
* #ORM\Column(type="integer", nullable=true)
* #Gedmo\TreeRight
*/
private $rgt;
/**
* #ORM\Column(type="string", nullable=true)
* #Gedmo\TreePath(separator=".", startsWithSeparator=false, endsWithSeparator=false)
*/
private $path;
/**
* #ORM\Column(type="string", nullable=true)
* #Gedmo\TreePathSource
*/
private $source;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Task", mappedBy="parent")
* #ORM\OrderBy({"lft":"ASC"})
*/
private $children;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Task")
* #ORM\JoinColumn(name="task_id", referencedColumnName="id", onDelete="CASCADE")
* #Gedmo\TreeRoot
*/
private $root;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Task", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
* #Gedmo\TreeParent
*/
private $parent;
}
What am I not understanding or what am I missing about the configuration for materialized path?
Why are the node ID's coming up as negative also??? Very odd...

Symfony2 doctrine ManyToMany associations mapping invalid?

I have two entities relating to this issue, and I want a join table to be able to cross reference values in each table.
Here is an explanation:
Entity ContainerType.php:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\Containers;
/**
* ContainerType
*
* #ORM\Table(name="container_type")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class ContainerType
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="type", type="string", length=255)
*/
private $type;
/**
* #var \DateTime
*
* #ORM\Column(name="date_added", type="datetime")
*/
private $dateAdded;
/**
* #var \DateTime
*
* #ORM\Column(name="date_modified", type="datetime", nullable=true)
*/
private $dateModified;
/**
* #ORM\ManyToMany(targetEntity="Containers", inversedBy="type")
* #ORM\JoinTable(name="container_type_containers")
**/
private $container;
public function __construct()
{
$this->container = new ArrayCollection();
}
And entity Containers.php:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\ContainerType;
/**
* Containers
*
* #ORM\Table(name="containers")
* #ORM\Entity
*/
class Containers
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="number", type="string", length=255)
*/
private $number;
/**
* #ORM\ManyToMany(targetEntity="Containers", mappedBy="container")
*/
private $type;
public function __construct()
{
$this->type = new ArrayCollection();
}
And although the schema update works without problems, when I do a doctrine:schema:validate I get the following fail:
[Mapping] FAIL - The entity-class 'AppBundle\Entity\Containers' mapping is invalid:
* The association AppBundle\Entity\Containers#type refers to the owning side field AppBundle\Entity\Containers#container which does not exist.
But the $container field DOES exist in ContainerType so I do not understand why it's trying to reference a field called container in the Containers entity?
Can anyone shed any light on this?
Thank you
Michael
I think this code should works fine for you :)
ContainerType.php
/**
* #ORM\ManyToMany(targetEntity="Containers", inversedBy="containersType")
* #ORM\JoinTable(name="container_type_containers",
* joinColumns={#ORM\JoinColumn(name="container_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="container_type_id", referencedColumnName="id")})
*/
protected $containers;
Containers.php
/**
* #ORM\ManyToMany(targetEntity="ContainerType", mappedBy="containers")
*/
protected $containersType;
I have just realized my error!
I had used the wrong target Entity in the Containers.php file, I should have used ContainerType as the target, instead I had Containers which is why it was trying to find the field in the wrong table!

OneToMany/ManyToOne Bidirectional not working

Hi everyone and thanks for help,
I'm currently dealing with a problem while I want to make an OneToMany/ManyToOne Bidirectional Relationship with Doctrine2 (& Symfony2.5.6).
Here are my two classes : Voiture , and Voiture modeles
namespace Esprit\ParcBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Voiture
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $serie;
/**
* #ORM\Column(type="date")
*/
private $dateM;
/**
* #ORM\Column(type="string")
*/
private $marque;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="ModeleVoiture")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $id_m;
and :
<?php
namespace Esprit\ParcBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class ModeleVoiture
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
*#ORM\Column(type="string")
*/
private $libelle;
/**
* #ORM\Column(type="string")
*/
private $pays;
when i want to phpmyadmin to check if the foreign key relationis added or no, i can't find it
thank you for your help.
You need to remove #ORM\Column(type="integer") from private $id_m;

not able to view foreign key constrain in database

i am using symfony 2 and doctrine to create enitity class with a foreign key
here is my entity class , now the issue is when i check the table fos_user in mysql database , i don't see travel_access_token and travel_id as a foreign key and even if i change the value of travel_id in fos_user table database is not raising any issues.
i have one to one unidirectional relation as only one user can have one token only
<?php
namespace Travel\HomeBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #ORM\Column(name="facebook_id", type="string", length=255, nullable=true) */
protected $facebook_id;
/** #ORM\Column(name="facebook_access_token", type="string", length=255, nullable=true) */
protected $facebook_access_token;
/**
#ORM\Column(name="travel_id", type="string", length=255, nullable=true)
#ORM\OneToOne(targetEntity="Travel\HomeBundle\Entity\Client")
#ORM\JoinColumn(name="travel_id", referencedColumnName="id")
*/
protected $travel_id;
/**
#ORM\Column(name="travel_access_token", type="string", length=255, nullable=true)
#ORM\OneToOne(targetEntity="Travel\HomeBundle\Entity\Client")
#ORM\JoinColumn(name="travel_access_token", referencedColumnName="secret")
*/
protected $travel_access_token;
public function __construct()
{
parent::__construct();
// your own logic
}
}
?>
As "targetEntity" try use just the names of entities. Like this:
#ORM\OneToOne(targetEntity="Client")

Multiple JoinColumns in Symfony2 using Doctrine annotations?

Here is the problem:
Class Routing with attributes objectId and objectType. objectId is an int, and objectType is a string. The reason for this was to allow the same table to hold data for different kind of routings. For instance for the routing of Products, Department and Brand. So, the combination of the objectType and the objectId is my JoinColumn.
How do I create such a bidirectional relationship with Doctrine2? I looked at inherited relationships, but none of the concepts seemed to be what I'm looking for.
I could create some views in the database and just have a couple of different Routing entities, but this does not seem the best route.
Here are my entities Department, Product and Brand.
../Entity/Department.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
* #ORM\Table(name="departments")
*/
class Department implements DescribableInterface
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer", length=11)
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="status", type="string", length=1)
*/
private $status;
/**
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
...
../Entity/Product.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="products")
*/
class Product implements DescribableInterface
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer", length=11)
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="status", type="string", length=1)
*/
private $status;
/**
* #ORM\Column(name="product_code", type="string", length=100, nullable=true)
*/
private $productCode = '';
/**
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
...
../Entity/Brand.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="brands")
*/
class Brand
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer", length=11)
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="status", type="string", length=1)
*/
private $status = 'a';
/**
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
...
Each product, brand and department has its own URL that is held in the routing table set by the object_type and object_id where the object_type is simply department, product or brand and the object_id is the unique id of the corresponding product, brand or department.
../Entity/Routing.php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="routing")
*/
class Routing
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer", length=11)
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="object_id", type="integer", length=11)
*/
private $objectId;
/**
* #ORM\Column(name="object_type", type="string", length=100)
*/
private $objectType;
/**
* #ORM\Column(name="url", type="text")
*/
private $url;
...
What I am really struggling with is how do I setup the relationship, so the departments, products and brands can access their URL from the single routing entity.
I have tried adding the relationships to the $objectId, but it doesn't seem to like that. Is it possible to set this up like this?
What I am basically trying to achieve is to get the data object and have the ability to get the URL of an object, for example:
$departments = $em->getRepository("AdamStaceySiteBundle:Department")->findAll();
foreach ($departments as $department)
{
echo ''.$department->getMenuTitle().';
}
Can anyone help?
After further researching I found a man (Dirk Olbertz) in the know who had the same problem.
Information can be found at: Google Groups: Multiple JoinColumns?
I have now implemented this and I will explain how I did it incase it might help anyone else.
The answer to my problem was the use of single table inheritance.
The first thing I needed to do was update the routing entity to use single table inheritance:
../Entity/Routing.php
/**
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="object_type", type="string")
* #ORM\DiscriminatorMap({"product" = "ProductRouting", "department" = "DepartmentRouting", "brand" = "BrandRouting"})
* #ORM\Table(name="routing")
*/
class Routing
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer", length=11)
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
The DiscriminatorColumn allowed me to specify what column would be used to link, which in this case was the object_type field.
The DiscriminatorMap allowed me to specify what object_type will link with what entities.
These entities then had to be created that extended the Routing entity.
../Entity/ProductRouting.php
/**
* #ORM\Entity
*/
class ProductRouting extends Routing
{
/**
* #ORM\ManyToOne(targetEntity="Product")
* #ORM\JoinColumn(name="object_id", referencedColumnName="id")
*/
protected $product;
...
../Entity/DepartmentRouting.php
/**
* #ORM\Entity
*/
class DepartmentRouting extends Routing
{
/**
* #ORM\ManyToOne(targetEntity="Department")
* #ORM\JoinColumn(name="object_id", referencedColumnName="id")
*/
protected $department;
...
../Entity/BrandRouting.php
/**
* #ORM\Entity
*/
class BrandRouting extends Routing
{
/**
* #ORM\ManyToOne(targetEntity="Brand")
* #ORM\JoinColumn(name="object_id", referencedColumnName="id")
*/
protected $brand;
...
Then in each of the Product, Department and Brand entities I needed to add the new $routings.
../Entity/Product.php
...
class Product
{
...
/**
* #ORM\OneToMany(targetEntity="ProductRouting", mappedBy="product", cascade={"all"})
*/
private $routings;
...
../Entity/Department.php
...
class Department
{
...
/**
* #ORM\OneToMany(targetEntity="DepartmentRouting", mappedBy="department", cascade={"all"})
*/
private $routings;
...
../Entity/Brand.php
...
class Brand
{
...
/**
* #ORM\OneToMany(targetEntity="BrandRouting", mappedBy="brand", cascade={"all"})
*/
private $routings;
...
Hope that helps...

Resources