I have this class:
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
class Parameter{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Project\Bundle\Entity\Anthropometry", inversedBy="parameter")
* #ORM\JoinColumn(name="anthropometry_id", referencedColumnName="id")
*
*/
protected $anthropometry;
/**
* #ORM\Column(name="data", type="string", length=255, nullable=true)
*/
protected $data;
...
}
and this:
/**
* #ORM\Table(name="anthropometry")
* #ORM\Entity
*/
class Anthropometry {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #ORM\OneToMany(targetEntity="Project\Bundle\Entity\Parameter", mappedBy="anthropometry", cascade={"persist"})
*
*/
protected $parameter;
...
}
In my Controller I am creating a form and validating in the same Action.
To create the form I need instance one Parameter. But not need persist him.
So.. when I call $em->flush I got the error:
A new entity was found through the relationship ...
To solve this I put cascade={"persist"} in annotation:
//Class Anthropometry
...
/**
*
* #ORM\OneToMany(targetEntity="Project\Bundle\Entity\Parameter", mappedBy="anthropometry", cascade={"persist"})
*
*/
protected $parameter;
But now, in my Database, the parameters are being persisted with field 'Data' = NULL
Can I check with prePersist if the field is NULL before persist?
something like this?
//class Parameter
/**
*
* #ORM\prePersist
*/
public function prePersist(){
if($this->getData() == NULL){
return false;
}
}
Thx!
I didn't verify if it works but you could try unsetting the parameter before persisting the Anthropometry (since you don't need to persist parameters):
//class Anthropometry
/**
* #ORM\prePersist
*/
public function prePersist()
{
if(!is_null($this->parameter) && $this->parameter->getData() == null){
$this->parameter = null;
}
}
Related
I have a "many-to-many" relation with an association table. The function calculDesCouts(), from the parent table is calculating the total cost of ingredients, and persist this info in a field. Actually, because of the owning and inversed sides of the relations, I'm not able to cascade persist the parent table when an ingredient price has changed.
What is the proper way to do this? I mean, when a child row is updated, how to trigger the persist callback from the parent table? I know I can do it in my controller, with a loop on all recipes using this ingredient, but I would really like to do it on the ORM layer...
This is my parent entity, with a lifecycle callback on the calculDesCouts :
<?php
/**
* #ORM\Table(name="recette")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Recette
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="\My\AcmeBundle\Entity\RecetteIngredientAssociation", mappedBy="recette", cascade={"persist"})
*/
protected $recette_ingredient_associations;
/**
* #ORM\Column(type="decimal", scale=2, nullable=true)
*/
private $cout_nourriture;
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function calculDesCouts()
{
$this->cout_nourriture = 0;
foreach ($this->recette_ingredient_associations as $ria) {
$this->cout_nourriture += $ria->getIngredient()->getPrix() * $ria->getQuantite();
}
}
}
This is my association entity:
<?php
/**
* #ORM\Table(name="recette_ingredient_association")
* #ORM\Entity
*/
class RecetteIngredientAssociation
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="\My\AcmeBundle\Entity\Recette", inversedBy="recette_ingredient_association")
* #ORM\JoinColumn(name="recette_id", referencedColumnName="id")
*
*/
private $recette;
/**
* #ORM\ManyToOne(targetEntity="\My\AcmeBundle\Entity\Ingredient", inversedBy="recette_ingredient_association")
* #ORM\JoinColumn(name="ingredient_id", referencedColumnName="id")
*/
private $ingredient;
/**
* #ORM\Column(type="decimal", scale=2, nullable=false)
*/
private $quantite;
}
And this is my child entity:
<?php
/**
* #ORM\Table(name="ingredient")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Ingredient
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="decimal", scale=2, nullable=true)
*/
private $prix;
/**
* #ORM\OneToMany(targetEntity="\My\AcmeBundle\Entity\RecetteIngredientAssociation", mappedBy="ingredient")
*/
protected $recette_ingredient_associations;
}
I don't know how to make it on ORM level, but I would do it on Symfony2 layer.
To make this what you need is to create an EventListener which will in case of Ingredient's update call Recette's calculDesCouts.
Like declare a service:
app.ingredient_listener_update:
class: AppBundle\Listener\IngredientPriceListener
tags:
- { name: doctrine.event_listener, event: postUpdate }
and in this AppBundle\Listener\IngredientPriceListener make a method
public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$em = $args->getEntityManager();
if ($entity instanceof Ingredient) {
foreach ($entity->get_recette_ingredient_associations() as $association)
foreach ($association->get_recettes() as $recette) {
$recette->calculDesCouts();
$em->persist($recette);
}
}
}
I have a series of classes with a slightly complicated set of references between the properties of those classes. I am trying to remove an entity and have that remove be cascaded to its children, but I'm running into foreign key constraint errors. Here is an example of my class structure:
<?php
/**
* #ORM\Entity
* #ORM\Table(name="student_tests")
*/
class StudentTest implements IEntityAccess {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="studentTest", cascade{"remove","persist"})
*/
protected $studentTestItems;
/**
* #var Test
* #ORM\ManyToOne(targetEntity="Test", inversedBy="studentTests")
*/
protected $test;
/**
* #var \DateTime
* #ORM\Column(type="datetime", nullable=true)
*/
protected $created;
/**
* #var User
* #ORM\ManyToOne(targetEntity="User", inversedBy="studentTests")
*/
protected $student;
}
//...
<?php
/**
* #ORM\Entity
* #ORM\Table(name="student_test_items")
*/
class StudentTestItem {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
*/
protected $studentTest;
/**
* #var User
* #ORM\ManyToOne(targetEntity="User", inversedBy="studentTestItems", cascade={"persist"})
*/
protected $student;
/**
* #var TestItem
* #ORM\ManyToOne(targetEntity="TestItem", inversedBy="studentTestItems", cascade{"persist"})
*/
protected $testItem;
}
//...
/**
*
* #ORM\Table(name="tests")
* #ORM\Entity
*
* #ORM\HasLifecycleCallbacks
*/
class Test implements IEntityAccess {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var \DateTime
* #ORM\Column(type="datetime", nullable=true)
*/
protected $startDate;
/**
* #var StudentTest[]
* #ORM\OneToMany(targetEntity="StudentTest", mappedBy="test" )
*/
protected $studentTests;
/**
* #var TestItem[]
* #ORM\OneToMany(targetEntity="TestItem", mappedBy="test", cascade={"all"})
*/
protected $items;
}
//...
/**
*
* #ORM\Table(name="test_items")
* #ORM\Entity
*/
abstract class TestItem {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var Test
* #ORM\ManyToOne(targetEntity="Test", inversedBy="items")
*/
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="testItem")
*/
protected $studentTestItems;
}
/**
* This is the primary user object. Used for login and all the other
* good stuff.
*
* #ORM\Table(name="users")
* #ORM\HasLifecycleCallbacks
class User implements AdvancedUserInterface, \Serializable, IEntityAccess
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #var StudentTest[]
* #ORM\OneToMany(targetEntity="StudentTest", mappedBy="student", cascade={"persist", "remove"})
*/
protected $studentTests;
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="student", cascade={"persist", "remove"})
*/
protected $studentTestItems;
}
Let's say I want to delete a student test, and have that delete cascaded to its StudentTestItem children. To do so, I run the following code inside of a controller.
//... blah blah class definition
/**
* Delete a student test
*
* #return \Symfony\Component\HttpFoundation\Response
* #Route("/studenttest/delete", name="student_test_delete")
*/
public function DeleteStudentTestAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$studentTest = $em->getRepository("MyAcmeBundle:StudentTest")->findOneBy(array("id" => 3));
$em->remove($studentTest);
$em->flush();
return $this->redirect($this->generateUrl('student_delete_success'));
}
When I try to run that code, I get the following error message:
An exception occurred while executing 'DELETE FROM student_tests WHERE id = ?' with params [3]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`my_acme_bundle/student_test_items`, CONSTRAINT `FK_71FA2A7F36BB1A1` FOREIGN KEY (`student_test_id`) REFERENCES `student_tests` (`id`))
500 Internal Server Error - DBALException
NOW, if I remove all references to studentTestItems from the classes, i.e. I comment out $studentTestItems from the TestItem and User classes, it deletes fine without that issue. Why is this happening? Does Doctrine keep track of the parent references through associations or something?
Looks like you forgot to add ON DELETE CASCADE to the foreign key constraint. Try changing the following association in class StudentTestItem:
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
*/
protected $studentTest;
To this:
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
* #ORM\JoinColumn(name="student_test_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $studentTest;
Im tying to add communication parts to a rootCommunication in my data-fixture, there is no error, but only just NULL in the database field 'root_communication_id'. Why?
Parts of my Model 'Communication'
/**
* Communication
*
* #ORM\Table(name="communication")
* #ORM\Entity(repositoryClass="Mother\BaseBundle\Entity\Repository\CommunicationRepository")
*/
class Communication
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="message", type="text", nullable=true)
*/
private $message;
/**
* #ORM\ManyToOne(targetEntity="Communication", inversedBy="childrenCommunication", cascade={"persist"})
* #ORM\JoinColumn(name="root_communication_id", referencedColumnName="id", nullable=true)
*
*/
private $rootCommunication;
/**
* #ORM\OneToMany(targetEntity="Communication", mappedBy="rootCommunication")
*
*/
private $childrenCommunication;
}
In a first data-fixture i added three communications to the database, in this secound fixture i add the childrenCommunication to the rootCommunication.
/**
* {#inheritDoc}
*/
public function load( ObjectManager $manager ){
$contentRepo = $this->container->get('doctrine')->getManager()->getRepository('MotherBaseBundle:Communication');
$communication1 = $contentRepo->find( $this->getReference('communication1')->getId() );
$communication1->addChildrenCommunication( $this->getReference('communication2') );
$communication1->addChildrenCommunication( $this->getReference('communication3') );
$manager->persist( $communication1 );
$manager->flush();
}
I assume you are not setting the rootCommunication when you are adding the child.
You should add an auto setter to the add method, like..
public function addChildrenCommunication(CommunicationInterface $communication)
{
if (!$this->childrenCommunication->contains($communication)) {
$this->childrenCommunication->add($communication);
$communication->setRootCommunication($this);
}
return $this;
}
.. and the same for the remove..
this is my entity:
/**
* #ORM\Table(name="Animal")
* #ORM\HasLifecycleCallbacks
*/
class Animal {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var localizedcontent $lctitle
*
* #ORM\ManyToOne(targetEntity="localizedcontent",fetch="EAGER", cascade={"persist"})
* #ORM\JoinColumn(name="lcTitle", referencedColumnName="pkId", nullable=false)
*/
private $lctitle;
/**
* #var localizedcontent $lcdescription
*
* #ORM\ManyToOne(targetEntity="localizedcontent",fetch="EAGER", cascade={"persist"})
* #ORM\JoinColumn(name="lcDescription", referencedColumnName="pkId", nullable=false)
*/
private $lcdescription;
/**
* #ORM\PostLoad
*/
public function postLoad(){
$lct = $this->lctitle;
$lcd = $this->lcdescription;
}
This is my dql:
SELECT a,lct FROM Animal JOIN e.lctitle lct WHERE a.id=:id
When i'm starting xdebug, it tells me that lcdescription is a proxy object and lctitle doesn't exists. I don't know why.
I think the postLoad event is too early because the localizedcontent isn't loaded at this moment, right? Is there an other listener for reading the value of lctitle in relation to the Animal Object?
Thanks
Doctrine always returns proxies. These classes inherit from the entity-classes. It might help if you declare your relations protected instead of private.
/**
* #var localizedcontent $lctitle
*
* #ORM\ManyToOne(targetEntity="localizedcontent",fetch="EAGER", cascade={"persist"})
* #ORM\JoinColumn(name="lcTitle", referencedColumnName="pkId", nullable=false)
*/
protected $lctitle;
or you could write a getter and call this one in your post-load function
public function getLctitle() {
return $this->lctitle;
}
public function getLcdescription() {
return $this->lcdescription;
}
/**
* #ORM\PostLoad
*/
public function postLoad(){
$lct = $this->getLctitle();
$lcd = $this->getLcdescription();
}
I'm trying to learn read/write data to/from database, and I have a huge problem :
My entities looks :
Kategorie:
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="kategorie")
*/
class Kategorie
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idkategorii;
/**
* #ORM\Column(type="string", length=50)
*/
protected $nazwa;
/**
* #ORM\OneToMany(targetEntity="Ogloszenia", mappedBy="ogloszenia")
*/
protected $ogloszenia;
Ogloszenia:
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="ogloszenia")
*/
class Ogloszenia
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string",length=100)
*/
protected $tytul;
/**
* #ORM\Column(type="string", length=120)
*/
protected $tytul_seo; //tytul bez polskich znaków etc.
/**
* #ORM\Column(type="text")
*/
protected $tresc; //tresc ogloszenia
/**
* #ORM\Column(type="string",length=50)
*/
protected $dodal; //imie osoby ktora dodala ogloszenie
/**
* #ORM\Column(type="string", length=50)
*/
protected $kontakt; //nr tel lub mail
/**
* #ORM\ManyToOne(targetEntity="Kategorie", inversedBy="kategoria")
* #ORM\JoinColumn(name="kategoria", referencedColumnName="idkategorii")
*/
protected $kategoria;
Now, in my controller i'm trying to read all values :
public function odczytajAction()
{
$id = 1;
$kategoria = $this->getDoctrine()
->getRepository('FrontendOgloszeniaBundle:Kategorie')
->find($id);
$ogl = $kategoria->getOgloszenia();
foreach($ogl as $o)
{
print_r($o);
}
return new Response('test odczytu');
}
And unfortunately symfony2 gives me an following error
Notice: Undefined index: ogloszenia in /home/sl4sh/public_html/Projekt1/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 1574
500 Internal Server Error - ErrorException
So, please tell me what's wrong with mine code ?
You have invalid mappedBy annotation:
/**
* #ORM\OneToMany(targetEntity="Ogloszenia", mappedBy="kategoria")
*/
protected $ogloszenia;
And also inverse side:
/**
* #ORM\ManyToOne(targetEntity="Kategorie", inversedBy="ogloszenia")
* #ORM\JoinColumn(name="kategoria", referencedColumnName="idkategorii")
*/
protected $kategoria;