I have a table "cms_objects" // Object.php - that stores all object info
I have another table "cms_media" // Media.php - that stores all media info
An object can have many media items (post with lots of different images)
In Object.php
/**
* #ORM\OneToMany(targetEntity="Media", mappedBy="Object")
*/
private $cms_media;
In Media.php
/**
* #ORM\ManyToOne(targetEntity="Object", inversedBy="cms_media")
* #ORM\JoinColumn(name="object_id", referencedColumnName="id")
*
* #Annotation\Exclude()
*/
private $object;
When I run: php public/index.php orm:validate-schema - I get:
[Mapping] FAIL - The entity-class 'Application\Entity\Cms\Media' mapping is invalid:
* The mappings Application\Entity\Cms\Media#object and Application\Entity\Cms\Object#cms_media are inconsistent with each other.
[Mapping] FAIL - The entity-class 'Application\Entity\Cms\Object' mapping is invalid:
* The association Application\Entity\Cms\Object#cms_media refers to the owning side field Application\Entity\Cms\Media#Object which does not exist.
Ideally, I need to be able to create a ZF2 form with element: 'media' or 'cms_media' but I haven't been able to validate it yet...
You can try to use FQCN inside the annotations. Instead of
/**
* #ORM\OneToMany(targetEntity="Media", mappedBy="Object")
*/
try
/**
* #ORM\OneToMany(targetEntity="Application\Entity\Cms\Media", mappedBy="Object")
*/
in both entities.
Also i would like to recommend using camelCased entity properties instead of underscored_ones. Hydration process of the entities with underscored properties using DoctrineObject hydrator is problematic. You can find more details here.
BEWARE - Using unnecessary bi-directional associations increases your object graph and domain model complexity. Best practice is avoiding bi-directional associations if possible.
For this case, you can rewrite the same mapping using uni-directional relation between Post (Object) and Media entities if you don't need reverse access from Media to Post like
$media->getPost()
For example Application/Entity/Cms/Post.php :
/** #ORM\Entity **/
class Post
{
/**
* One to many, unidirectional
* #ORM\ManyToMany(targetEntity="Application\Entity\Cms\Media")
* #ORM\JoinTable(name="post_to_media",
* joinColumns={#ORM\JoinColumn(name="post_id", referencedColumnName="id")},
* inverseJoinColumns={
* #ORM\JoinColumn(name="media_id", referencedColumnName="id",unique=true)
* })
**/
private $media;
public function __construct()
{
$this->media = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
and Application/Entity/Cms/Media.php :
/** #ORM\Entity **/
class Media
{
// No need to know about post
}
Related
Am working on a simple setup with two tables with a one to many (and inversed) relationship. The two corresponding entities are:
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ArticlesRepository::class)
* #ORM\Table(name="articles")
*/
class Article
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(name="unique_id", type="integer")
*/
protected int $idx;
/**
* The inverse side
* #ORM\OneToMany(targetEntity="Comment", mappedBy="article")
* #ORM\Column(name="uid", type="integer", nullable=true)
*/
protected $id;
public function __construct()
{
$this->id=new ArrayCollection();
}
}
and
use Doctrine\ORM\Mapping as ORM;
/**
* ORM\Entity(repositoryClass="CommentRepository::class")
* ORM\Table(name="comments")
*/
class Comment
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(name="uid", type="integer")
*/
protected $idx;
/**
* The owning side
* #ORM\ManyToOne(targetEntity="Article", inversedBy="uid")
* #ORM\JoinColumn(name="article", referencedColumnName="uid")
*/
protected $article;
protected $content;
}
Attempting to persist a new blank Article
$article=new Article();
$em->persist($article);
$em->flush();
results in the following exception
An exception occurred while executing 'INSERT INTO articles (uid) VALUES (?)' with params [{}]: PHP Warning: Object of class Doctrine\Common\Collections\ArrayCollection could not be converted to float in ...\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\Mysqli\MysqliStatement.php line 164
I went searching for answers and came across suggestions close to but don't touch on this subject. One had to do with removing the type indication of the column. So I did and it gets stuck with the following exception
An exception occurred while executing 'INSERT INTO articles (uid) VALUES (?)' with params [{}]: Cannot add or update a child row: a foreign key constraint fails...
When type is removed, the field defaults to type string which makes no difference, since Doctrine still attempts to store an empty array in an integer field (see the values in the exception above).
Question: How can I get the setup to work properly and persist all objects correctly?
It turns out that the #ORM\OneToMany annotation in the Article entity should be used on a non-existent database field. In other words, property $id should not be an actual field in the database table of the entity.
For eg I have entities like User,Item, Image. User has many items. Item has many images.
Which delete option should I choose, cascade={'remove'} or onDelete=Cascade ?
Also I have life cycle callbacks on Image. I know the difference between above mentioned cascade options. I was wondering if I used onDelete=cascade option, on deleting a User object, will the life cycle callback like PostRemove() be called ?
Here are my entities:
//User.php
class User {
/**
* #ORM\OneToMany(targetEntity="Item", mappedBy="user", onDelete="CASCADE")
*/
private $items;
}
//Item.php
class Item {
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User", inversedBy="items")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="ItemImage", mappedBy="item",onDelete="CASCADE")
*/
protected $images;
}
//ItemImage.php
class ItemImage {
/* Setters and getter **/
/**
* #var Items
*
* #ORM\ManyToOne(targetEntity="Item", inversedBy="images")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="item_id", referencedColumnName="id")
* })
*/
private $item;
/**
* #ORM\PostRemove()
*/
public function removeUpload() {
unlink($this->getUploadDir() . '/' . $this->imageName);
}
}
My question is when a user is deleted, will all the items associated with user and images related to the items be deleted ? I also want the PostRemove() callback of Image entity be called when User is deleted ? Which option should I use, onDelete="cascade" or cascade={'remove'} for such cases?
onDelete='CASCADE' will add an SQL level ON DELETE CASCADE. So yes, the images will be deleted from the table. This is very efficient because the database handles the deletes.
To have the listener called cascade={'remove'} is necessary. This makes Doctrine fetch the object's graph, which is slower.
Both options may be specified at the same time. In this case Doctrine will act as described in the last paragraph, but DELETEs not coming from Doctrine will cascade correctly, too; no listeners will be called in this case obviously.
I would tend to specify cascade={'remove'} only in this case to avoid accidental DELETEs without the listener being called (if there is no cleanup task for unreferenced files).
Details can be found in the Doctrine documentation.
I'm trying to detach an object from entity_manager in doctrine2 in order to put him in session but it doesn't work. not when there is Association.
Ex:
<?php
namespace Travelyo\CoreBundle\Entity\Order;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="bundles")
* /
class Bundle
{
/**
* #var integer $id
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Order", inversedBy="orders",cascade={"detach","merge","remove"})
* #ORM\JoinColumn(name="order_id", referencedColumnName="id")
*/
private $order;
}
There is no other relation in order.
But when I'm trying to do that
$em = $this->getDoctrine()->getEntityManager();
$bundle = $em->getRepository('TravelyoCoreBundle:Order\Bundle')->find(27);
$em->detach($bundle);
$em->detach($bundle->order);
serialize($bundle);
It's not working, I have all the object dependecies, proxies,...
If I'm doing that before detach
$bundle->setOrder(new Order());
Then the serialize is perfect, I just have the bundle.
What am I missing here ?
Even if your object is detached, it still has references to the doctrine components used to create it.
As a simple advice, I can tell you to simply serialize the object identifier instead. That's much cleaner, and while it will require you to perform a query on the other side (the unserialize part), you will avoid a lot of problems.
Just don't serialize your entities, it's just messy.
Hello I have problem when trying to cascade remove entities in OneToMany relations.
After a few hours of debugging I tried to downgrade the doctrine from the latest 2.1.2 to 2.0.2 and It suddenly starts working.
Imagin two entities Company and Address in relation 1:N.
/**
* #Entity
*/
class Company extends Entity
{
/**
* #var integer
* #id #Column(type="integer")
* #generatedValue
*/
private $id;
/**
* #var Collection
* #OneToMany(targetEntity="Address",mappedBy="company", cascade={"persist","remove"})
*/
private $addresses;
}
/**
* #Entity
*/
class Address extends Entity
{
/**
* #var integer
* #id #Column(type="integer")
* #generatedValue
*/
private $id;
/**
* #var Company
* #ManyToOne(targetEntity="Company", inversedBy="addresses")
* #JoinColumn(name="company_id", referencedColumnName="id",nullable=false)
*/
private $company;
}
when I try to remove the entity Company, I would like the assigned addresses will be removed as well.
$em->remove($company);
$em->flush();
In doctrine 2.1.2 the deletion of addresses is not performed so the integrity constraint fails. In version 2.0.2 there it works perfectly. Wierd thing on it is, if I use EntityAudit extension https://github.com/simplethings/EntityAudit the LogRevisionListener is corretly versioning the addresses entities (set them revtype = DEL) in doctrine 2.1.2 (of course in 2.0.2 as well) but the UnitOfWork is not removing it.
Is there any difference how to handle cascade removing in 2.0.2 and in 2.1.2?
Thank you very much
Try using this on the addresses attribute of your Company Class
#OneToMany(targetEntity="Address",mappedBy="company",
cascade={"persist"}, orphanRemoval=true)
I had the same problem... Relations were added or updated, but not deleted, even if I had cascade: [persist, remove].
I found out that I didn't need the "remove" attribute in "cascade", but I had to add the orphanRemoval: true.
I was going crazy, you made my day!
I have met the same problem and i have solved him with that code :
$em->remove($object);
$em->flush();
$em->remove($user);
$em->flush();
Maybe you can use a findAll on your company for the addresses and remove this with a foreach like that :
// Return all the addresses of the company
$addresses = $em->getRepository(...)->findAllAddressesByCompany($company);
$em->remove($company);
foreach ($address in $addresses)
{
$em->remove($address);
}
That's not a very good method but for now, that's all I've found.
I have an Entity called Game with a related Repository called GameRepository:
/**
* #ORM\Entity(repositoryClass="...\GameRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Game {
/**
* #ORM\prePersist
*/
public function setSlugValue() {
$this->slug = $repo->createUniqueSlugForGame();
}
}
In the prePersist method, I need to ensure that the Game's slug field is unique, which requires a database query. To do the query, I need access to the EntityManager. I can get the EntityManager from inside GameRepository. So: how do I get the GameRespository from a Game?
You actually can get the repository in your entity and only during a lifecycle callback. You are very close to it, all you have to do is to receive the LifecycleEventArgs parameter.
Also see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html
use Doctrine\ORM\Event\LifecycleEventArgs;
/**
* #ORM\Entity(repositoryClass="...\GameRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Game {
/**
* #ORM\prePersist
*/
public function setSlugValue( LifecycleEventArgs $event ) {
$entityManager = $event->getEntityManager();
$repository = $entityManager->getRepository( get_class($this) );
$this->slug = $repository->createUniqueSlugForGame();
}
}
PS. I know this is an old question, but I answered it to help any future googlers.
You don't. Entities in Doctrine 2 are supposed to not know of the entity manager or the repository.
A typical solution to the case you present would be to add a method to the repository (or a service class) which is used to create (or called to store) new instances, and also produces a unique slug value.
you can inject the doctrine entity manager in your entity
(using JMSDiExtraBundle)
and have the repository like this:
/**
* #InjectParams({
* "em" = #Inject("doctrine.orm.entity_manager")
* })
*/
public function setInitialStatus(\Doctrine\ORM\EntityManager $em) {
$obj = $em->getRepository('AcmeSampleBundle:User')->functionInRepository();
//...
}
see this : http://jmsyst.com/bundles/JMSDiExtraBundle/1.1/annotations
In order to keep the logic encapsulated without having to change the way you save the entity, instead of the simple prePersist lifecycle event you will need to look at using the more powerful Doctrine events which can get access to more than just the entity itself.
You should probably look at the DoctrineSluggableBundle or StofDoctrineExtensionsBundle bundles which might do just what you need.