Symfony2 OneToMany relationship - remove action isn't called - symfony

I'm using Symfony 2.4.6 and I'm trying to use OneToMany relationship to manage images added to a banner.
I did read a lot about the deletion of a child element (setting orphanRemoval, adding 'remove' to cascade) but none of those worked for me. What I've noticed is that remove actions isn't called at all on update.
I have 2 classes, Banner and BannerFile and using collection field type for adding images and it seems to work OK except the delete action.
class Banner
{
/.../
/**
* #ORM\OneToMany(targetEntity="BannerFile", cascade={"persist", "remove"}, mappedBy="banner", orphanRemoval=true)
*/
private $bannerFiles;
/.../
/**
* Remove bannerFiles
*
* #param BannerFile $bannerFiles
*/
public function removeBannerFile(BannerFile $bannerFiles)
{
$this->bannerFiles->removeElement($bannerFiles);
}
}
class BannerFile
{
/.../
/**
* #var integer $banner
*
* #ORM\ManyToOne(fetch="EXTRA_LAZY", inversedBy="bannerFiles", targetEntity="Banner")
* #ORM\JoinColumn(name="banner_id", nullable=false, onDelete="CASCADE", referencedColumnName="id")
*/
private $banner;
/.../
}
My problem is that the removeBannerFile isn't called.
Thanks for any help.

This happens if you use a database table engine type that does not support foreign keys (such as MyISAM).
Change your engine type to InnoDB, then run the following code to update your database schema.
php console doctrine:schema:update

Add cascade to the ManyToOne definition:
class BannerFile
{
/.../
/**
* #var integer $banner
*
* #ORM\ManyToOne(fetch="EXTRA_LAZY", inversedBy="bannerFiles", targetEntity="Banner", cascade={"remove"})
* #ORM\JoinColumn(name="banner_id", nullable=false, onDelete="CASCADE", referencedColumnName="id")
*/
private $banner;
/.../
}

Related

Can't remove entities iterating over collection, but I can remove getting them directly from repository

My News entity is connected with Document entity through joiner NewsDocument entity with some extra fields: News -> NewsDocument <- Document
/**
* #ORM\Entity
* #ORM\Table(name="news_document")
* #UniqueEntity(
* fields={"news", "document"}
* )
*/
class NewsDocument
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Document", inversedBy="news")
* #ORM\JoinColumn(nullable=false)
*/
private $document;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\News", inversedBy="documents")
* #ORM\JoinColumn(nullable=false)
*/
private $news;
/**
* #ORM\Column(type="integer")
*/
private $position;
My News entity
/**
* #ORM\OneToMany(targetEntity="App\Entity\NewsDocument", mappedBy="news", cascade={"persist"})
* #ORM\OrderBy({"position" = "ASC"})
*/
private $documents;
public function getNewsDocuments()
{
return $this->documents;
}
When I tried to remove each NewsEntity iterating over documents field of my News entity, the NewsEntity wasn't removed from DB.
$news = $this->entityManager->getRepository(News::class)->find(26);
foreach($news->getNewsDocuments() as $newsDocument) {
$this->entityManager->remove($newsDocument);
}
$this->entityManager->remove($news);
$this->entityManager->flush();
News was deleted but NewsDocument not.
However the following code is able to remove both entities.
$news = $this->entityManager->getRepository(News::class)->find(26);
$newsDocument = $this->entityManager->getRepository(NewsDocument::class)->find(1);
$this->entityManager->remove($newsDocument);
$this->entityManager->remove($news);
$this->entityManager->flush();
I don't understand why first code isn't able to remove NewsDocument from DB.
I dumped $newsDocument variable in both cases and looked similar to each others.
I probably found a problem. My News entity uses SoftDeletable extension for Doctrine, but the joiner entity doesn't. I guess that extension tries to set a deleted_at flag to the relation, instead of deleting it permanently. This is why if I get joiner entity directly from repository works correctly.

Foreign key and cascade problems on delete Symfony4

An entity project can have many personnages, many chapitres, one highConcept for each project. And the user can have many projects. Then, when I want to remove a project I have this error message :
An exception occurred while executing 'DELETE FROM projets WHERE id = ?' with params [2]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (writtle.personnages, CONSTRAINT FK_286738A6C18272 FOREIGN KEY (projet_id) REFERENCES projets (id))
this is my entities:
Personnages Entity
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Chapitre", mappedBy="personnages")
* #ORM\JoinColumn(name="projet_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $chapitres;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Projets", inversedBy="personnages")
* #ORM\JoinColumn(name="projet_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $projet;
Projet entity
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="projets")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Personnages", mappedBy="projet")
*/
private $personnages;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Chapitre", mappedBy="projet", cascade={"remove"})
* #ORM\joinColumn(onDelete="SET NULL")
*/
private $chapitres;
Chapitre entity
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Projets", inversedBy="chapitres")
*/
private $projet;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Personnages", inversedBy="chapitres")
*/
private $personnages;
HighConcept
/**
* #ORM\OneToOne(targetEntity="App\Entity\Projets", cascade={"persist", "remove"})
*/
private $projet;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="highconcepts")
*/
private $user;
User entity
/**
* #ORM\OneToMany(targetEntity="App\Entity\Projets", mappedBy="user")
*/
private $projets;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Highconcept", mappedBy="user")
*/
private $highconcepts;
I don't know how I can relate this, I tried some things like JoinColumn ondelete cascade...
I see some topics, but I think I use it correctly.
Considering that you want to remove all the entities contained in your project I suggest you to use the orphanRemoval option as explained in the Doc.
There is another concept of cascading that is relevant only when removing entities from collections. If an Entity of type A contains references to privately owned Entities B then if the reference from A to B is removed the entity B should also be removed, because it is not used anymore.
making the members of your entity look like:
/**
* #ORM\OneToMany(targetEntity="App\Entity\Personnages", mappedBy="projet", orphanRemoval=true)
*/
private $personnages;

Symfony defining Foreign key

This is my ERD
I need help with how to associate the foreign keys with my entities
from what i know is that it has to something with this kind of code:
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
TL;DR I made entities but now i need to associate the foreign keys (image) but i don't know how.
/**
* #ORM\ManyToOne(targetEntity="klant", mappedBy="Bestellingorder")
*/
private $klanten;
/**
* #ORM\OneToMany(targetEntity="Bestellingorder", inversedBy="klanten")
* #ORM\JoinColumn(name="Bestellingorder_id", referencedColumnName="id")
*/
private $Bestellingorder;
[Creation Error] The annotation #ORM\ManyToOne declared on property TuinBundle\
Entity\Bestellingorder::$klanten does not have a property named "mappedBy". Ava
ilable properties: targetEntity, cascade, fetch, inversedBy
stuck here.
Try with that :
/**
* #ORM\ManyToOne(targetEntity="klant", inversedBy="Bestellingorder")
* #ORM\JoinColumn(name="klant_id", referencedColumnName="id")
*/
private $klanten;
/**
* #ORM\OneToMany(targetEntity="Bestellingorder", mappedBy="klanten")
*/
private $Bestellingorder;
But i don't understand why you have sometimes "klant" or "klanten", didn't make sense for me. What is the name of your entity ? klant or klanten ?

Join-Table with metadata, composite key and one to many relationship

i am building a cart that can take items with specified versions.
I am using Symfony 2.4.3 and Doctrine 2
I have following code for three entities, Cart, CartItem and CartItemVersion.
Cart.php
// ----
/**
* #ORM\OneToMany(targetEntity="CartItem", mappedBy="cart")
*/
private $cartItems;
// ----
CartItem.php
// ----
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Cart")
*/
private $cart;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Item")
*/
private $item;
/**
* #ORM\OneToMany(targetEntity="CartItemVersion", mappedBy="cartItem")
*/
private $cartItemVersions;
// ----
CartItemVersion.php
// ----
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="CartItem")
*/
private $cartItem;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="ItemVersion")
*/
private $itemVersion;
// ----
While updating schema, i got this error.
[Doctrine\ORM\ORMException]
Column name `id` referenced for relation from CartItemVersion towards CartItem does not exist.
Then i gave name to the fields like cartItem_id in CartItemVersion.php and others.
Then updating schema returns,
[Doctrine\DBAL\DBALException]
An exception occurred while executing 'ALTER TABLE cart_item_version ADD CONSTRAINT FK_4D3EA2E02EA80FC1 FOREIGN KEY (cartItem_id) REFERENCES cart_item (cartItemVersion_id)':
SQLSTATE[HY000]: General error: 1005 Can't create table 'symfony.#sql-3d8_12a' (errno: 150)
I have referred Doctrine 2's documentation and followed Use Cases for OrderItem but it seems that this is something because of composite primary keys, but still giving proper names couldn't solve this issue.
Can anyone help?
In your mapping you have a bunch of issues which are corrected as below. You need define the referenced Column name and its own column name; furthermore, for those field which has mappedBy you need to define inversedBy, too.
Cart.php
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="CartItem", mappedBy="cart")
*/
private $cartItems;
CartItem.php
/**
* #ORM\ManyToOne(targetEntity="Cart", inversedBy="cartItems")
* #ORM\JoinColumn(name="cart_id", referencedColumnName="id")
*/
private $cart;
/**
* #ORM\ManyToOne(targetEntity="Item")
* #ORM\JoinColumn(name="item_id", referencedColumnName="id")
*/
private $item;
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="CartItemVersion", mappedBy="cartItem")
*/
private $cartItemVersions;
CartItemVersion.php
/**
* #ORM\ManyToOne(targetEntity="CartItem", inversedBy="cartItemVersions")
* #ORM\JoinColumn(name="cart_item_id", referencedColumnName="id")
*/
private $cartItem;
/**
* #ORM\ManyToOne(targetEntity="ItemVersion")
* #ORM\JoinColumn(name="item_version_id", referencedColumnName="id")
*/
private $itemVersion;
To get more info check Relationship Mapping Metadata Documentation
I think you have to make your entities slowly, step by step.
Begin with unidirectional and then add bidirectional when it is necessary...
By using association mapping documentation.
This answer could be a comment but can't post a comment because of my reputation...

Symfony2 app/console not generating properties or schema updates for Entity Relationships/Associations

I am reading and following along in code what is written in the Symfony2 book on using Database and Doctrine (http://symfony.com/doc/2.0/book/doctrine.html). I have reached the "Entity Relationships/Associations" section but the framework does not seem to be doing what it is meant to be doing. I have added the protected $category field to the Product entity and added the $products field to the Category entity. My Product and Category entities are as below:
Product:
<?php
namespace mydomain\mywebsiteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Product
*
* #ORM\Table()
* #ORM\Entity
*/
class Product
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
/*
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
/**
* Set description
*
* #param string $description
* #return Product
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
Category:
<?php
namespace mydomain\mywebsiteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use \Doctrine\Common\Collections\ArrayCollection;
/**
* Category
*
* #ORM\Table()
* #ORM\Entity
*/
class Category
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
/*
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
protected $products;
public function __construct(){
$this->products = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set description
*
* #param string $description
* #return Category
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
}
According to the documentation, if i now execute
$ php app/console doctrine:generate:entities mydomain
the framework should generate the getters/setters for the new category field in Product and for the new products field in Category.
HOWEVER when i run the command it supposedly updates the entities but it does not add the properties. I have compared with the backup(~) files and there are no differences. If i add another field (e.g. description2) and add doctrine annotations for persistence to it then it generates the properties. I ignored this at first and manually added the properties for the mapping fields and then executed:
$php app/console doctrine:schema:update --force
for it to add the new association columns.
HOWEVER once again it told me that the metadata and schema were upto date.
I have deleted the app/cache/dev folder and allowed the system to recreate it but it has made no difference.
Can anyone see why the framework is not behaving as described in the documentation??
Thanks
You have forgotten one star here:
/*
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
it must be
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
UPDATE: After trying different things with absolutely no success i ended up deleting the entire bundle and associated database and starting from scratch again. Second time around things are generated correctly and the database schema is being updated correctly. Such flaky behavior is EXTREMELY POOR of the framework and as mentioned in the comment above is the reason that as a developer i am moving away from Grails. Now i find that symfony2 has the same sort of problems.
When i use a framework i should not need to always keep in the back of my mind whether something is not working because the framework is buggy. This is quite unacceptable for such a mainstream framework and it would seem i am not the only person that has come across such kind of problems. The framework developers should definitely address such issues either by (preferably) resolving them or providing some means of understanding why the framework fails on random occasions.
Based on what I found the issue is that you can't have two types of definitions..in the book the entity create comand for category also creates a yml configoration so the annotations failed. You must use either annotations or yml or xml or php. Once I removed the yml config and recreated the tables with annotations it worked..be careful and don't use the comnad for the category createion..you will still though get an error that the description is mandatory field :)
I had the exact same issue and I solved it like this:
Delete the "doctrine"-Folder containing the yml-files with the (in your case redundant!) configuration for the entities. Do this ONLY on your test-system for educational purposes.
Some background-information (maybe someone with more experience than me - probably almost everybody here ;o)) can add to this:
Doctrine preferes YML-Schema configuration over annotations in the entity-class (/** #ORM ... */)
when working through the book you might have created a blog-entity with YML-Schema configuration a few chapters in before chapter 8 - maybe you played around a little and this YML-Schema is in the same bundle than your chapter 8 exercise
consequently: Doctrine thinks you want to use YML but it finds only configuation for "anotherEntity" but not for product and category
OR: you run a few Doctrine commands for testing and choose once (by mistake?) YML and voilĂ : all further annotation chances will be ignored because i.e. a product.orm.yml exists
Hope that helped. I just started chapter 10 ;-)
When it comes to generating getters and setters Symfony is just using the ReflectionClass to look if the methods already exist.
It doesn't look what properties are written in the annotation.
Concerning the schema update problem I don't have another solution then resetting the database and creating it from scratch.
I faced this problems a few times but never really found a good solution, it seems Symfony doesn't differ between some properties, which results in not finding any updates.
I don't have a framework by hand now, to look it up. Maybe you can try to find out what schema:update does exactly thus finding the error.

Resources