Troubles with persisting relations - symfony

I ran into a problem when I'm trying to persist User entity with its relations:
class User {
/**
* #OneToMany(targetEntity="AppBundle\Entity\UserDataAttribute", mappedBy="user", cascade={"persist", "remove"})
* #var \Doctrine\Common\Collections\Collection
*/
private $customAttributes;
}
class UserDataAttribute
{
/**
* #ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="customAttributes")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
}
When I specify #JoinColumn as name="user_id" in database goes this query:
INSERT INTO user_data_attr (user_id) VALUES (null);
And when I specify #JoinColumn as name="userId" in database goes this query:
INSERT INTO user_data_attr (userId) VALUES (2323 (the actual value)));
But what I need is:
INSERT INTO user_data_attr (user_id) VALUES (2323);
What do I do wrong?

/**
* #OneToMany(targetEntity="AppBundle\Entity\UserDataAttribute", mappedBy="user", cascade={"persist", "remove"})
*/
private $customAttributes;
/**
* #ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="customAttributes")
*/
private $user;
And check it before:
app/console doctrine:schema:validate for Symfony 2
`app/console doctrine:schema:valid` for Symfony 3

Related

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;

Join columns by referencedColumnName that is not primary key - id

I am rookie in Symfony Doctrine and need some help with Join entities.
Normally Column are joins by primary key ID
/**
* User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="MainBundle\Repository\UserRepository")
* UniqueEntity("email", message="Account with email already exists.")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #var \MainBundle\Entity\PersonDetails
*
* #ORM\ManyToOne(targetEntity="MainBundle\Entity\Person")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="person_details_id", referencedColumnName="id", nullable=true)
* })
*/
private $personDetails = null;
This is ok.
But problem is that I want to Join two columns in Relation OneToOne by id field in User Entity
/**
* User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="MainBundle\Repository\UserRepository")
* UniqueEntity("email", message="Account with email already exists.")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #var \MainBundle\Entity\PersonDetails
*
* #ORM\ManyToOne(targetEntity="MainBundle\Entity\Person")
* #ORM\JoinColumn(name="id", referencedColumnName="user_id", nullable=true)
* })
*/
private $personDetails = null;
When I try to join columns on this way I get error
Missing value for primary key id on MainBundle\Entity\PersonDetails
Is it possible to index other field than id or what I trying to do is impossible?
Thanks guys.
You have mixed up the column-name and the field-name that shall be referenced in your #JoinColumn declaration.
#JoinColumn(name="id", referencedColumnName="user_id")
This way Doctrine looks for a field/property named user_id on your User entity. I guess you want the column in the join-table to be named user_id and the entries being id's of the User entity.
UserDetail
/**
* #ORM\Entity
*/
class UserDetail
{
/**
* #ORM\ManyToOne(
* targetEntity="User",
* inversedBy="details"
* )
* #ORM\JoinColumn(
* name="user_id",
* referencedColumnName="id"
* )
*/
protected $user;
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/** #ORM\Column() */
protected $key;
/** #ORM\Column() */
protected $value;
public function __construct($key, $value)
{
$this->key = $key;
$this->value = $value;
}
User
class User
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\OneToMany(
* targetEntity="UserDetail",
* mappedBy="user",
* cascade={
* "persist",
* "remove",
* "merge"
* },
* orphanRemoval=true
* )
*/
protected $details;
public function __construct()
{
$this->details = new ArrayCollection();
}
public function addDetail(UserDetail $detail)
{
$detail->setUser($this);
$this->details->add($detail);
return $this;
}
Now if you add a detail to your User like this and persist/flush afterwards:
$user->addDetail(new UserDetail('Height', '173cm'));
This will result in a join-colum in the user_detail table that looks like this:
| key | value | user_id |
|---------------|-----------|---------|
| Height | 173cm | 1 |
Citing Doctrine documentation:
It is not possible to use join columns pointing to non-primary keys.
Doctrine will think these are the primary keys and create lazy-loading
proxies with the data, which can lead to unexpected results. Doctrine
can for performance reasons not validate the correctness of this
settings at runtime but only through the Validate Schema command.
I had the same problem, I solved it by performing the mapping only to fields that are primary key. If I needed to get the related entities by other fields, I implemented methods in the Entity repository.

Error when persisting doctrine entity with OneToMany association

New entities in a collection using cascade persist will produce an Exception and rollback the flush() operation. The reason is that the "UserGroupPrivilege" entity has identity through a foreign entity "UserGroup".
But if the "UserGroupPrivilege" has its own identity with auto generated value the code works just fine, and I don't want that I want the identity to be a composite key to enforce validation. here is my code:
Entity UserGroup:
class UserGroup
{
/**
* #var integer
*
* #ORM\Column(type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(type="boolean", nullable=false)
* #Type("integer")
*/
private $active;
/**
* #ORM\OneToMany(targetEntity="UserGroupPrivilege", mappedBy="userGroup", cascade={"persist"})
*/
private $privileges;
Entity UserGroupPrivilege:
class UserGroupPrivilege
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer", nullable=false)
*
*/
private $privilegeId;
/**
* #var UserGroup
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="UserGroup", inversedBy="privileges")
* #ORM\JoinColumn(name="userGroupId", referencedColumnName="id")
*/
private $userGroup;
/**
* #var string
* #ORM\Column(type="string", nullable=false)
*/
private $name;
/**
* #var string
* #ORM\Column(type="string", nullable=false)
*/
private $value;
Controller:
$userGroup = new UserGroup();
$userGroupPrivilege = new UserGroupPrivilege();
userGroupPrivilege->setUserGroup($userGroup)
->setName($arrPrivilege['name'])
->setValue($arrPrivilege['value'])
->setPrivilegeId($arrPrivilege['privilegeId']);
$userGroup->addPrivilege($userGroupPrivilege);
$data = $repo->saveUserGroup($userGroup);
return $data;
Repository:
$em = $this->getEntityManager();
$em->persist($userGroup);
$em->flush();
I get the following error:
Entity of type UserGroupPrivilege has identity through a foreign entity UserGroup, however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before trying to persist 'UserGroupPrivilege'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.
Error message is pretty self explanatory. To relate UserGroupPrivilege to UserGroup, UserGroup must have it's ID set. However, since you've just created both entities it has no id because it hasn't been persisted to database yet.
In your case :
$em = $this->getEntityManager();
$em->persist($userGroup);
$em->persist($userGroupPrivilege);
$em->flush();
Can you "enforce validation" with unique constraint:
/**
* #Entity
* #Table(uniqueConstraints={#UniqueConstraint(name="ugppriv_idx", columns={"priviledgeId", "userGroup"})})
*/
class UserGroupPriviledge
{
...

Symfony2 OneToMany relationship - remove action isn't called

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;
/.../
}

(doctrine2 + symfony2) cascading remove : integrity constraint violation 1451

First, sorry for my poor English...
I got four entities : User, Application, Bundle & Entity. Here are their relations (with cascading persist & remove, see code below) :
User 1-n Application
Application 1-n Bundle
Bundle 1-n Entity
It's working fine. But an User can have two of his entities as default, and I need to access them directly.
So I add on User two fields, entity1 & entity2, with a 1-1 relation. And now my app crashes :
An exception occurred while executing 'DELETE FROM bundle WHERE id = ?' with params {"1":13}:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`misc`.`entity`, CONSTRAINT `FK_E284468F1FAD9D3` FOREIGN KEY (`bundle_id`) REFERENCES `bundle` (`id`))
I tried several things, including those founded in this post, but I wasn't able to fix it.
Any help be welcome, thanks in advance.
EDIT : I need to point out that User->Entity relations are optionnal : User's entity1 & entity2 can be null. The error happens even if they are null both.
Here are my entities definitions :
# User :
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Application", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $applications;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity1_id", referencedColumnName="id")
*/
private $entity1;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity2_id", referencedColumnName="id")
*/
private $entity2;
#Application :
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", mappedBy="application", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $bundles;
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\UserBundle\Entity\User", inversedBy="applications", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
#Bundle :
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Application", inversedBy="bundles", cascade={"persist"})
* #ORM\JoinColumn(name="application_id", referencedColumnName="id")
*/
protected $application;
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Entity", mappedBy="bundle", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $entitys;
#Entity :
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", inversedBy="entitys", cascade={"persist"})
* #ORM\JoinColumn(name="bundle_id", referencedColumnName="id")
*/
protected $bundle;
So, thanks to this French forum, I fixed the problem.
I needed to add nullable=true & onDelete="SET NULL" in #ORM\JoinColumn
Here is the workable configuration, maybe it will help someone :
#User.
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Application", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $applications;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity1_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
private $entity1;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity2_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
private $entity2;
#Application.
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", mappedBy="application", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $bundles;
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\UserBundle\Entity\User", inversedBy="applications", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $user;
#Bundle.
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Application", inversedBy="bundles", cascade={"persist"})
* #ORM\JoinColumn(name="application_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $application;
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Entity", mappedBy="bundle", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $entitys;
#Entity.
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", inversedBy="entitys", cascade={"persist"})
* #ORM\JoinColumn(name="bundle_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $bundle;
Use onDelete="CASCADE" if you are using annotation
/**
* #ORM\ManyToOne(targetEntity="Report", inversedBy="responses")
* #ORM\JoinColumn(name="reportId", referencedColumnName="id",onDelete="CASCADE")
*/
Use onDelete: CASCADE if you are using yml
joinColumn:
name: pid
referencedColumnName: id
onDelete: CASCADE
onDelete="CASCADE" also works fine. But don't forget to run app/console doctrine:schema:update --force before DB level changes will take an effect.
orphanRemoval some times doesn't work because it depends on (gets schedulled in) PersistencCollection. And we might be calling ArrayCollection#removeElement().
Following is a snippet of the PersistencCollection#remove()
if ($this->association !== null &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->owner &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
and ArrayCollection does not do that.

Resources