Doctrine - Saving associations after deleting parent entity - php

I have two entities with One-To-Many relationship - User and Log.
class User {
/** #OneToMany(targetEntity="Log", mappedBy="user") */
private $logs;
}
class Log {
/**
* #ManyToOne(targetEntity="User", inversedBy="logs")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
}
What I want to achieve is following - Once I delete the User entity, instead of cascade deleting all of that User logs, I want to be able to save those logs and still be able to keep that user_id. I was thinking of creating a new database table old_logs with the same structure as logs table, where all of the data from User entity will be copied to before deleting the User entity, as I believe I can't keep the user_id pointing to non-existing user (with the use of foreign keys).
Is something like this possible through doctrine Entity class?

Related

Nested composite foreign keys as ID in Doctrine

We are developing an online store in Symfony 5 and Doctrine 2 where multiple customers (called participants in this case) can participate in the same order item and share the cost. The following simplified class diagram demonstrates the domain model:
The pure object model works fine in unit tests, but you obviously need to persist the data to a database, which is why we need to introduce IDs.
Order, Product and Participant are entities with their own ID. In an ideal world, OrderItem and OrderItemParticipation would not need their own ID but be identified by the related entities they belong to, meaning their ID would be a composite foreign key.
So, an OrderItem would by identified by the composite key of Order.id and Product.id, which is pretty much exactly the same as given in this example from the Doctrine 2 documentation: https://www.doctrine-project.org/projects/doctrine-orm/en/2.10/tutorials/composite-primary-keys.html#use-case-3-join-table-with-metadata.
Since OrderItemParticipation relates to OrderItem, which uses a composite key itself, it would need to use a nested composite key consisting of Order.id, Product.id and Participant.id.
Unfortunately, Doctrine 2 doesn't seem to be able to work with nested composite keys as ID. I get this error
Column name id referenced for relation from
App\Entity\OrderItemParticipation towards App\Entity\OrderItem does
not exist.
when I try to generate a migration with the following mapping:
/** #ORM\Entity */
class OrderItem {
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Order::class, inversedBy="items")
*/
private Order $order;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Product::class)
*/
private Product $product;
// ...
}
/** #ORM\Entity */
class OrderItemParticipation {
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=OrderItem::class, inversedBy="participations")
*/
private OrderItem $orderItem;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Participant::class)
*/
private Participant $participant;
// ...
}
So it seems that Doctrine is fine with my ID mapping in OrderItem, but it struggles when it gets to OrderItemParticipation. Is there a way to make Doctrine work with the given domain model? Is it maybe just an issue with the auto-generation of the migration, so if I had already manually set up the database, Doctrine might work with the given mapping? Or is the nested composite key ID approach too complicated for Doctrine?

Doctrine & Symfony3 OneToOne relations, inverse side does not exist

I am looking for a clear explanation of how the relations work in Doctrine 2. I've been trying now for days to set up a OneToOne relation in Symfony3, and have read basically every thread and all the documentation, and i simply do not understand. The last few hours i've been bruteforcing my way into a OneToOne relation, with no luck.
Here's the problem i'm dealing with:
My user entity has a Team (which is itself an Entity)
/**
* #ORM\OneToOne(targetEntity="Gluckez\bballBundle\Entity\Team",
mappedBy="Owner")
* #ORM\Column(name="team_id")
*/
private $team;
And My Team has an Owner, which refers to the User:
/**
* #var User $Owner
* #ORM\OneToOne(targetEntity="Application\Sonata\UserBundle\Entity\User",
inversedBy="team_id")
* #ORM\JoinColumn(name="Owner_id", referencedColumnName="id")
*/
protected $Owner;
Now in my User table, i can see the relations, which do point to the right team. but doctrine complains about the inverse side User#team, that does not exist (even though i'm pretty sure i've defined it?)
I've tried countless times to change the inversedby, mappedby, name, referencedcolumnName, as well as dropping and recreating the database and tables. updating the doctrine schema's. i'm out of idea's.
Not a single answer out there, nor the documentation of doctrine points me in the right direction. I realize that there's a lot of threads out there on this, but none of them are clear, or even answered.
Try it like this:
Owner entity
/**
* #ORM\OneToOne(targetEntity="Gluckez\bballBundle\Entity\Team", mappedBy="owner")
*/
private $team;
Team entity
/**
* #ORM\OneToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="team")
*/
protected $owner;
As reference: Association Mapping

Auto database query from User entity in Symfony2

In Symfony2, I have these columns in a user entity:
*
* #ORM\OneToOne(targetEntity="User", inversedBy="lovername")
* #ORM\JoinColumn(name="lover", referencedColumnName="id", onDelete = "SET NULL")
*/
private $lover;
/**
* #ORM\OneToOne(targetEntity="User", mappedBy="lover", cascade={"persist"}, fetch="EXTRA_LAZY")
**/
private $lovername;
The project is a dating website, where a user can send messages only to one user, which he/she chose. The column lover contains the id of user he/she chose to talk to.
It works properly, but it generates an additional SQL query. First Symfony downloads the row about the logged user and then every information about user which 'love' him, even if that information is useless. I need it only when user sends messages.
It is some way to write it/build database better? How do I control auto-generating queries in Symfony2?
I've also faced a similar problem. It seems like LazyLoad doesn't work properly for OneToOne relationships in doctrine. See this question

Doctrine - OneToOne Unidirectional vs OneToOne Bidirectional

I joust started playing around with Doctrine ORM library, and Im learning about all associations between tables.
So Im stuck with differences in Unidirectional and Bidirectional relation.
As I get it, Unidirectional relation has primary key only on one side, and that side is owning side right?
And Bidirectional relation have primary key in both tables and therefore you can have relation from both sides, and set constrains on both sides.
Now, Im reading through Doctrine documentation about relations and there you have:
Unidirectional and Bidirectional associations.
But they produce the same SQL, and the same tables with the same primary key-s and constrains. So I dont really see any difference in those two. And both examples have primary key on one side.
As I get it the true Bidirectional relation should have primary keys in both tables pointing to the other table right? And with given example on Doctrine documentation that is not the case. Both examples give the same result and are the same.
So what I did, is this, lets say I have User and Card Entity, and want relation to be OneToOne Bidirectional.
/**
* #Entity
* #Table(name="users")
*/
class User
{
/**
* #Id
* #GeneratedValue
* #Column(type="bigint")
*/
protected $id;
/**
* #OneToOne(targetEntity="Card", mappedBy="User")
* #JoinColumn(name="card_id", referencedColumnName="id")
*/
protected $card;
/**
* #Column(name="user_name", type="string")
*/
protected $userName;
/**
* #Column(name="user_pass", type="string")
*/
protected $userPass;
}
/**
* #Entity
* #Table(name="cards")
*/
class Card
{
/**
* #Id
* #GeneratedValue
* #Column(type="bigint")
*/
protected $id;
/**
* #OneToOne(targetEntity="User", inversedBy="Card")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #Column(name="post_title", type="string")
*/
protected $cardType;
}
The difference here is I wrote #JoinColumn in both objects/entities. And in Doctrine example there is only one.
Now I would get what I think is Bidirectional relation. If i look at EER diagram, I can see one line pointing from user to card, and the other from card to user.
So basicly did I get this right?
Is the Doctrine documentation wrong? :D
How would Bidirectional OneToOne relation look in EER diagram?
Thanks!
The only difference is in the PHP class interface, i.e. in the presence or absence of the property that points back to the owner (e.g. the $customer property in the mentioned Doctrine example). In other words Doctrine just needs to know whether it should take care about a single property ($shipping) or two properties ($cart and $customer). There is no other difference. Therefore, the SQL code is the same (because one foreign key is sufficient for representing any 1:N relationship) and there would no difference in EER diagram neither (because in EER you typically do not solve such PHP-related implementation details).
Unidirectional and bidirectional have nothing to do with the background algorithm how to create these connections in the database layer.
All they talk about is how the connections can be used. In an unidirectional relationship you can access the target only from one site. An bidirectional relationship allows the connection to be called from two (both) sides.
So in an unidir. rel. model_a can get to model_b, but model_b cant get to model_a (without extra work).
If you now use a bidir. rel both models can access each other without problems
In doctrine terms, a unidirectional relationship defines a
$modelA->getModelB() method, but not a $modelB->getModelA() method, whereas a bidirectional relationship defines both methods (or accessors, however you want to call them)
in an uml diagram it would look like this:
unidirectional
modelA --X------> modelB
bidirectional
modelA <--------> modelB

Is it possible to get the ID of entity after persist and before Flush in Doctrine2?

I have the User Login Registration Page.
Now on the same Page i have one more Forms i.e UserInterests.
Now i have the PostPersist function which creates new UserProfile after User is Persisted
Now UserProfile is linked with User ID and UserInterests is linked with UserProfile ID
Now the client wants UserInterests on same User page but i have problem that UserProfile is not yet created. Now how can persist them. Is there any way
I don't think you can get the ID before flushing.
You could create an association between the models, that way Doctrine will take care of the id's when saving and you can retrieve your UserInterests with something like:
$user->getProfile()->getInterests();
So you would have your User model with a property that holds your UserProfile:
/**
* #OneToOne(targetEntity="UserProfile")
* #JoinColumn(name="profile_id", referencedColumnName="id")
**/
private $profile;
and your UserProfile class should have a property to hold the UserInterests model.
/**
* #OneToOne(targetEntity="UserInterests")
* #JoinColumn(name="interests_id", referencedColumnName="id")
**/
private $interests;
You can now create an empty $userProfile model (to link the others together, the actual filling can be done in your postPersist function) and a $userInterests model, associate them by
$interests = new UserInterests();
// create an empty UserProfile, and fill it in your PostPersist function,
// that way it can already be used to link the User and UserInterests
$profile = new UserProfile();
$profile->setInterests($interests);
$user->setProfile($profile);
Now Doctrine will fill in the ids when persisting and you don't need to worry about them.
More information here

Categories