Doctrine2 cascade delete doesn't work with Abstract Class - php

In my web application, which is built with Symfony2, contains the following entities:
/**
* #ORM\Entity
* #ORM\Table
*/
class Entity
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id", type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="MappedSuperclass", mappedBy="entity", cascade={"persist", "remove"})
*/
private $mappedSuperclasses;
}
/**
* #ORM\MappedSuperclass
*/
abstract class MappedSuperclass
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id", type="integer")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Entity", inversedBy="mappedSuperclasses")
* #ORM\JoinColumn(name="entity_id", referencedColumnName="id", nullable=false)
*/
protected $entity;
}
/**
* #ORM\Entity
* #ORM\Table(name="table_1")
*/
class Subclass1 extends MappedSuperclass
{
/**
* #ORM\Column(name="unique_member", type="string")
*/
private $uniqueMember;
}
/**
* #ORM\Entity
* #ORM\Table(name="table_2")
*/
class Subclass2 extends MappedSuperclass
{
/**
* #ORM\Column(name="unique_member", type="string")
*/
private $uniqueMember; // This is different from Subclass1
}
I'll explain this a bit. An Entity has a collection of MappedSuperclass. MappedSuperclass is an abstract class which contains some common variables for its subclasses. Subclass1 and Subclass2 are subclasses of MappedSuperclass. When an Entity is removed from database, the items in $mappedSuperclasses should be removed together, that's why the cascade={"persist", "remove"} is set.
However, when I try to delete an Entity, I got the following error:
ContextErrorException: Notice: Undefined index: entity in C:\project\vendor\doctrine\orm\lib\Doctrine\ORM\Persisters\BasicEntityPersister.php line 1753
If I change the targetEntity of Entity::$mappedSuperclasses to Subclass1 or Subclass2, it will work. Is my set up impossible to achieve ON DELETE CASCADE? What am I missing?

I solved this problem by setting the ON DELETE action to database level:
/**
* #ORM\Entity
* #ORM\Table
*/
class Entity
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id", type="integer")
*/
private $id;
}
/**
* #ORM\MappedSuperclass
*/
abstract class MappedSuperclass
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id", type="integer")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Entity")
* #ORM\JoinColumn(name="entity_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
protected $entity;
}
Sources: [1] [2]

Im answering year after the issue was resolved but I had the same problem. Error occurs when I tried to empty count arrayCollection.
So the solution was to check if $this->entity is array and then return its length.

Related

The association X refers to the inverse side field Y which is not defined as association

I have two entities:
Team:
class Teams
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id")
* #ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="team")
*
*/
private $id;
User:
class User implements UserInterface, \Serializable
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Teams", inversedBy="id")
* #ORM\JoinColumn(name="team_id", referencedColumnName="id")
*/
private $team;
One team have many users
And I've got these errors:
The association App\Entity\User#team refers to the inverse side field
App\Entity\Teams#id which is not defined as association.
The association App\Entity\User#team refers to the inverse side field
App\Entity\Teams#id which does not exist.
I don't know where I'm wrong... Anyone know ?
Regards
I'm bit confused of what you want to do.
If you want unidirectional relation from user side then remove
* #ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="team")
this code.
And user should have only
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Teams")
*/
private $teams;
On the other hand if you want bidirectional relation then add property user on teams entity.
It doesn't work for you because the mapping you defined is on $id and it should be on the property
Bidirectional way:
class Teams
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id")
*
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="teams")
* #ORM\JoinColumn(name="team_id", referencedColumnName="id")
*/
private $user;
class User implements UserInterface, \Serializable
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Teams", mappedBy="user")
*/
private $teams;
It's good practice to name entities with singular name so perhaps you'd like to change Teams to Team entity.
Check also http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-one-unidirectional
You want your Team to look like this:
class Team
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\User")
*/
private $users;
This will generate teams of users. If you want your users to be able to on more than one team change your User call to a ManyToMany as well.
class User implements UserInterface, \Serializable
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Team")
*/
private $teams;

No identifier/primary key specified for Abstract Class

I am trying implement this mapping strategy. I defined abstract class with id and other fields. but I keep getting following error:
No identifier/primary key specified for Entity "AppBundle\Entity\AbstractItem". Every Entity must have an identifier/primary key. (500 Internal Server Error)
This is my class. As you can see, unique identifier $id was specified
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"post" = "Post", "group" = "Group"})
*/
class AbstractItem
{
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \DateTime
*/
protected $published;
protected $object;
protected $target;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
}
This abstract class shouldn't be an actual entity in the database.
Try to put #ORM\MappedSuperClass instead of #ORM\Entity
try this please:
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
Instead of this:
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
You need to specify the column id inside comment

doctrine:schema:update not creating OneToOne relations

When I run the following command: php app/console doctrine:schema:update --force --dump-sql
It returns this message: Nothing to update - your database is already in sync with the current entity metadata.
I have two Entities, User and UserProfile.
User.php
/**
* #ORM\Table(name="users", options={"charset":"utf8mb4", "collate":"utf8mb4_unicode_ci"})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\OneToOne(targetEntity="UserProfile", inversedBy="user_id")
*/
private $id;
...
UserProfile.php
/**
* #ORM\Table(name="users_profiles", options={"charset":"utf8mb4", "collate":"utf8mb4_unicode_ci"})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserProfileRepository")
*/
class UserProfile
{
/**
* #ORM\Id
* #ORM\Column(name="user_id", type="integer")
* #ORM\OneToOne(targetEntity="User", mappedBy="id")
*/
private $userId;
...
I've already tried clearing the cache.
UPDATE #1
User.php
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\OneToOne(targetEntity="UserProfile", mappedBy="userId")
*/
private $id;
UserProfile.php
/**
* #ORM\Id
* #ORM\Column(name="user_id", type="integer")
* #ORM\OneToOne(targetEntity="User", inversedBy="id")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $userId;
You have a couple of options here, if you want a bi-direction association, meaning you want to be able to access the other entity from either of them then you will want to use another property( NOT the $id) to accomplish this. Something like:
/**
* #ORM\Table(name="users", options={"charset":"utf8mb4", "collate":"utf8mb4_unicode_ci"})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="UserProfile", mappedBy="userId")
*/
private $profile;
...
}
Then in UserProfile.php
/**
* #ORM\Table(name="users_profiles", options={"charset":"utf8mb4", "collate":"utf8mb4_unicode_ci"})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserProfileRepository")
*/
class UserProfile
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="profile")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
...
}
This will allow you to do $userProfile->getUser() and also $user->getProfile().
If you want to use the userId as your primary key for the UserProfile table, you can it just involves more work to set that up and you have to explicitly set it in your application. Easier just to reference it on another field.
Unidirectional
User.php
/**
* #ORM\Table(name="users", options={"charset":"utf8mb4", "collate":"utf8mb4_unicode_ci"})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
}
UserProfile.php
/**
* #ORM\Table(name="users_profiles", options={"charset":"utf8mb4", "collate":"utf8mb4_unicode_ci"})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserProfileRepository")
*/
class UserProfile
{
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="profile")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
...
}
This will only allow you to do $userProfile->getUser(). Likely not what you are looking for. The other unidirectional solution is from the user object towards the UserProfile, like this:
/**
* #ORM\Table(name="users", options={"charset":"utf8mb4", "collate":"utf8mb4_unicode_ci"})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="UserProfile", mappedBy="id")
*/
private $profile;
...
}
Then in UserProfile.php
/**
* #ORM\Table(name="users_profiles", options={"charset":"utf8mb4", "collate":"utf8mb4_unicode_ci"})
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserProfileRepository")
*/
class UserProfile
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
}
This way you can do $user->getProfile().
inversedBy refers to the property, not the database field.
Change inversedBy="user_id" to inversedBy="userId"
You should also use JoinColumn, not Column, and referencedColumnName as per the documentation:
http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-one-unidirectional

OneToMany ManyToOne DoctrineORM Error

I'm newbie with PHP. I started work with symfony but i have this problem
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #param \Doctrine\Common\Collections\Collection $carList
* #ORM\OneToMany(targetEntity="AppBundle\CarBundle\Entity\Car", mappedBy="name", cascade={"persist"})
*/
private $carList;
//getters and setters
}
*
* #ORM\Entity(repositoryClass="AppBundle\CarBundle\Repository\Entity\CarRepository")
* #ORM\Table(name="car")
*/
class Car
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
* #ORM\ManyToOne(targetEntity="AppBundle\UserBundle\Entity\User" , inversedBy="carList")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $name;
//getters and setters
}
The stacktrace says:
Symfony\Component\Debug\Exception\ContextErrorException: Notice: Undefined index: name
at n/a
and when i run php bin/console doctrine:schema:validate
[Mapping] FAIL - The entity-class 'AppBundle\UserBundle\Entity\User'
mapping is invalid:
* The association AppBundle\UserBundle\Entity\User#carList refers to the owning side field AppBundle\CarBundle\Entity\Car#name which is not
defined as association, but as field.
*The association AppBundle\UserBundle\Entity\User#carList refers to the owning side field Appbundle\CarBundle\Entity\Car#name which does
not exist
I have no idea whats going on, can you help me?
You are mixing up association names with column names. When you create an association you don't need to manually add the columns for that association, doctrine will work that out for you.
This code (in the Car class) says that the $name field is a normal text column in the car table, which of course is wrong
* #ORM\Column(name="name",type="string", length=100)
What you're describing is that one user can own many cars, and many cars can belong to one user. I'd then call the associations owner and cars, but you are of course free to call them whatever you want. Note that you do not need to define the join columns.
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #param \Doctrine\Common\Collections\Collection $cars
* #ORM\OneToMany(targetEntity="AppBundle\CarBundle\Entity\Car", mappedBy="owner", cascade={"persist"})
*/
private $cars;
public function __construct()
{
$this->cars = new \Doctrine\Common\Collections\ArrayCollection();
}
//getters and setters
}
 
/**
*
* #ORM\Entity(repositoryClass="AppBundle\CarBundle\Repository\Entity\CarRepository")
* #ORM\Table(name="car")
*/
class Car
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\UserBundle\Entity\User" , inversedBy="cars")
*/
private $owner;
//getters and setters
}
Read more: Doctrine association mapping
Hope it makes sense :)

Symfony2 / Doctrine2 - Get rows not present in other side of relation

I've got 2 entites, with its setters and getters:
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="SRC\PurchaseBundle\Entity\Purchase", inversedBy="users")
* #ORM\JoinTable(name="user_purchases",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="purchase_id", referencedColumnName="id")}
* )
*/
protected $purchases;
...
}
/**
* #ORM\Entity
* #ORM\Table(name="purchases")
*/
class Purchase
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var users
*
* #ORM\ManyToMany(targetEntity="SRC\UserBundle\Entity\User", mappedBy="purchases")
*/
protected $users;
...
}
I'd need to select all missing purchases for the user.
I've already achieved this by getting all existing purchases and checking, one by one, they doesn't exist in $user->getPurchases().
Does exist a better way to get this results?
Is there a way so I could create a method inside USER entity called getMissingPurchases()?
Yes, you are missing
#ORM\JoinColumn(name="{name_of_column}", referencedColumnName="{name_of_fk_column}")

Categories