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
Related
Hey guys i have an error (i think that i do a really idiot error but i don't see were) trying to waste time i ask you the question. The error is that my foreign keys is incorrectly formed. Cannot be more precise ... Thx to all people who will try to answer or search :p
My UserInterestEntity :
/**
* UserInterest
*
* #ORM\Table(name="user_interest", indexes {#ORM\Index(name="userInterest_category_id_fk", columns={"category_id"})})
* #ORM\Entity
*/
class UserInterest
{
/**
* #var \AppBundle\Entity\Category
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
* })
*/
private $category;
}
My Category Entity :
/**
* UserInterest
*
* #ORM\Table(name="Category", uniqueConstraints {#ORM\UniqueConstraint(name="category_id_uindex", columns={"id"})})
* #ORM\Entity
*/
class Category
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\OneToOne(targetEntity="UserInterest", inversedBy="Category")
*/
protected $id;
The error is that when i do : php bin/console doctrine:schema:update --force tat tells me : Foreign key constraint is incorrectly formed
The relation must not be mapped on the ID but on a separate property. On the category class you can create an userIntrests property defining the userIntrests that have this category. In example:
class Category
{
/**
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="UserInterest", mappedBy="category")
*/
protected $intrests;
}
class UserIntrest
{
/**
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="intrests")
*/
protected $category;
}
Use this for reference https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/association-mapping.html#one-to-many-bidirectional
I'm getting a foreign constraint violation when trying to cascade delete entities having one-to-one and one-to-many bidirectional relations.
Here are my four entities related this way : the "User" object may have zero or one "Contact". Contact may have zero or one "Address" and zero or many "Telephone". But "Contact" must be related to a "User" entity, as well as "Address" and "Telephon"e with a "Contact" entity.
My aim is, when I delete a "User" all the child objects are cascade deleted too ("Contact", "Address" and "Telephone"). However, when I delete a child object, I just want its reference id in the parent entity to be set to NULL.
I've tried several ways including the options onDelete="CASCADE" and onDelete=NULL but I still get the foreign constraint violation error.
User entity
class User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* Contact
*
* #ORM\OneToOne(targetEntity="Contact", mappedBy="user", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $contact;
...
}
Contact entity
class Contact
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* Address
*
* #ORM\OneToOne(targetEntity="Address", mappedBy="contact", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $address;
/**
* Telephones
*
* #ORM\OneToMany(targetEntity="Telephone", mappedBy="contact", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $telephones;
/**
* User
*
* #ORM\OneToOne(targetEntity="User", inversedBy="contact", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
private $user;
/**
* Constructeur
*/
public function __construct()
{
$this->telephones = new ArrayCollection();
}
...
}
Address entity
class Address
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="Contact", inversedBy="address", cascade={"persist"})
* #ORM\JoinColumn(name="contact_id", referencedColumnName="id", nullable=false)
*/
private $contact;
...
}
Telephone entity
class Telephone
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Contact", inversedBy="telephones", cascade={"persist"})
* #ORM\JoinColumn(name="contact_id", referencedColumnName="id", nullable=false)
*/
private $contact;
...
}
Try this configuration. I have only made changes to the #ORM\JoinColumn annotations by adding onDelete="CASCADE". This uses the built in database cascading so you will need to update your schema.
I have also removed some extraneous #ORM\JoinColumn(nullable=true) annotations on non owning sides of relations. These had no effect and were only misleading.
User entity
class User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* Contact
*
* #ORM\OneToOne(targetEntity="Contact", mappedBy="user", cascade={"persist", "remove"})
*/
private $contact;
...
}
Contact entity
class Contact
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* Address
*
* #ORM\OneToOne(targetEntity="Address", mappedBy="contact", cascade={"persist", "remove"})
*/
private $address;
/**
* Telephones
*
* #ORM\OneToMany(targetEntity="Telephone", mappedBy="contact", cascade={"persist", "remove"})
*/
private $telephones;
/**
* User
*
* #ORM\OneToOne(targetEntity="User", inversedBy="contact", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
private $user;
/**
* Constructeur
*/
public function __construct()
{
$this->telephones = new ArrayCollection();
}
...
}
Address entity
class Address
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="Contact", inversedBy="address", cascade={"persist"})
* #ORM\JoinColumn(name="contact_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
private $contact;
...
}
Telephone entity
class Telephone
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Contact", inversedBy="telephones", cascade={"persist"})
* #ORM\JoinColumn(name="contact_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
private $contact;
...
}
$user->setContact(null);
$contact->setUser(null);
$em->remove($user);
$em->remove($contact);
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.
I have two entity called PictureTag and Tag, here's the relationship:
/**
* #ORM\Entity
* #ORM\Table(name="picture_tag")
* #ORM\HasLifecycleCallbacks()
*/
class PictureTag
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\OneToOne(targetEntity="App\MainBundle\Entity\Tag", inversedBy="id")
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id", nullable=false)
*/
private $tag;
}
/**
* #ORM\Entity
* #ORM\Table(name="tag")
* #ORM\HasLifecycleCallbacks()
*/
class Tag
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\ManyToOne(targetEntity="App\MainBundle\Entity\PictureTag", inversedBy="tag")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #ORM\Column(name="tag", type="string", nullable=true)
*/
private $tag;
}
Basically I wanted the tag table to contain all the unique tags, so there are no duplicate in the tag. And i wanted picturetag to have a joincolumn that points to the id of the tag. So here's my code in the controller:
foreach ($image->tags as $tag) {
$existingTag = $em->getRepository('AppMainBundle:InstagramTag')->findOneByTag($tag);
$instaPictureTag = new PictureTag();
if ($existingTag) {
$instaPictureTag->setTag($existingTag);
} else {
$instagramTag = new Tag();
$instagramTag->setTag($tag);
$em->persist($instagramTag);
$instaPictureTag->setTag($instagramTag);
}
$instaPictureTag->setPicture($instaShopPicture);
$em->persist($instaPictureTag);
}
So the relationship is basically:
One picture is going to have many tags. And one tag will belong to many pictures.
Picture tag here is the intermediary table.
Basically I am doing a check if the tag already exists in the tag table, if it is then I set the picturetag to be associated with this tag, if not then create one. However doing so generates the following error:
Fatal error: Call to a member function setValue() on a non-object in /Users/MyName/Sites/App/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 2625
Any idea why this is?
As I understand, you want implement OneToMany bidirectional relation between PictureTag and Tag.
Please try:
/**
* #ORM\Entity
* #ORM\Table(name="picture_tag")
* #ORM\HasLifecycleCallbacks()
*/
class PictureTag
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="DynamicSolutions\Bundle\ProsAndConsBundle\Entity\Tag", inversedBy="pictureTags")
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id", nullable=false)
*/
private $tag;
}
/**
* #ORM\Entity
* #ORM\Table(name="tag")
* #ORM\HasLifecycleCallbacks()
*/
class Tag
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="PictureTag", mappedBy="tag")
*/
private $pictureTags;
/**
* #var string
* #ORM\Column(name="tag", type="string", nullable=true)
*/
private $tag;
}
I have region entity and regionEmploymentDetails, regionEducationDetails, regionPowerDetails entities which hold regions employment, education and power details. Entities are kept separate because they hold different data. Below are classes,
class Region
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*#ORM\OneToOne(targetEntity="RegionEducationDetails")
#ORM\JoinColumn(name="region_id", referencedColumnName="id")
**/
private $education_details;
/**
*#ORM\OneToOne(targetEntity="RegionEmploymentDetails")
#ORM\JoinColumn(name="region_id", referencedColumnName="id")
**/
private $employment_details;
/**
*#ORM\OneToOne(targetEntity="RegionPowerDetails")
#ORM\JoinColumn(name="region_id", referencedColumnName="id")
**/
private $power_details;
}
class RegionEducationDetails
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="region_id", type="integer", length=20)
*/
private $region_id;
}
class RegionEmploymentDetails
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="region_id", type="integer")
*/
private $region_id;
}
class RegionPowerDetails
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="region_id", type="integer")
*/
private $region_id;
}
The problem is when I generate schema using command
php app/console doctrine:schema:update --force
It generate database tables but relates Region Entity with Only RegionPowserDetails Entity and does not relate other entities.
It seems it considers only the last mapping and so relates only with the last entity. If I remove Power entity relation it relates with Entity which is before power i.e Employment.
Am I doing anything wrong ? Is there any solution to this problem ?
The region class should be,
class Region {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private $education_details_id;
/**
*#ORM\OneToOne(targetEntity="RegionEducationDetails")
#ORM\JoinColumn(name="education_details_id", referencedColumnName="id")
**/
private $education_details;
private $employment_details_id;
/**
*#ORM\OneToOne(targetEntity="RegionEmploymentDetails")
#ORM\JoinColumn(name="employment_details_id", referencedColumnName="id")
**/
private $employment_details;
private $power_details_id;
/**
*#ORM\OneToOne(targetEntity="RegionPowerDetails")
#ORM\JoinColumn(name="power_details_id", referencedColumnName="id")
**/
private $power_details;
}