Embedded form persistence : get or create - symfony

I work with Symfony2 and doctrine. I currently have an entity called Person. This entity is related to some other entities as a One-To-Many relation (as a Many-To-One unidirectional relation). I want each Person entity to be unique (what I have done in my database by using the UniqueConstraint annotation).
To be clear, I will assume that I have two entities called Home and Car which both have a Many-to-One relation to the target entity Person.
Then I am using forms to create or edit my entities Car and Home. In these forms, I display a embedded form to create a Person entity or select one existing. I explain : the first field of my embedded form is the name of the person. As the user type the name of the person, a list of the existing persons is displayed (using JQuery Autocomplete UI) and if the user select one of them the other fields are autocompleted.
The issue is when the user submit the form with an existing person, I caught an Integrity Error (and I know why, because of my Unique Constraint).
One of the first workaround is to add the id field as an hidden input in the embedded form.
But then the user can edit the other fields and corrupt the current entity.
So no.
Another one could be to prevent the persist in the controller if the Person already exist, but as I am using this in many other entities. I will have to duplicate my code, and I do not want to, as the unique constraint is related to the Person entity and not to the Car or Home entity.
So no again.
The workaround that I am working about is to use a PrePersist Listener waiting for a Person entity, but I do not know how to cancel persist (and maybe it is not possible).
I have the following code :
public function prePersist(LifecycleEventArgs $args) {
$entity = $args->getEntity();
if($entity instanceof Personne) {
$em = $args->getEntityManager();
$persons = $em->getRepository('MyBundle:Person')->findBy(array(
// unique fields
));
if(count($persons) > 0) {
// ... ???
}
}
I have tried a $em->detach but it is useless as I am already persisting the entity.
What I want it is just a kind of a "Get Or Create". I explain, there are only two cases :
the Person entity (not persisted) has all the same fields that one existing in the database (excepted the id field), so the Person entity is the one in the database. I have to "replace" it by the one in the database ;
the Person entity (not persisted) is unique in the database, so we create a new one (persist).

Create your own getOrCreate() method and call it inside your listener.
See this post Symfony2 doctrine FindOneOrCreate
Another possibility would be the data transformers. http://symfony.com/doc/current/cookbook/form/data_transformers.html

Related

Doctrine ORM : Joined table inheritance

Related to this previous topic : Database : Account table with Joined Tables Inheritance
I've created a joined tables inheritance in my database (MySQL) :
client_id attributes are both PK and FK.
Now I need to represent it with Doctrine ORM but I can't find a solution of how to make that.
I created, with Symfony commands, the Client entity and for the ClientCompany entity I don't know which relationship to use nor how to use the Client ID as primary key of the CompanyClient entity.
Does anyone know how to do it?
First of all, be really carefull with inheritance, this is a really good feature of doctrine but need to be used with a lot of cautious because of it's counterpart.
For your case, i would advise to not try to put "person" and "company" under the same abstract "client" class for conception reason i already explain in this answer since a company and a person are totally different things: Symfony 6 inheritance Mapping : How to submit a form depends on a clicked radio button?
But i will still answer on how to properly do a join table inheritance :
AbstractClient.php
#[Entity]
#[InheritanceType('JOIN_TABLE')]
#[DiscriminatorColumn(name: 'discr', type: 'string')]
#[DiscriminatorMap(['person' => Person::class, 'company' => Company::class])]
abstract class Client
{
// you do not need clientType since it is hold by the "discr" column
// And if you want to know what type your client is you can do it using
// if($client instanceof Person::class) { do something}
}
Person.php
#[Entity]
class Person extends Client
{
// ...
}
Company.php
#[Entity]
class Company extends Client
{
// ...
}
Take a look at #[InheritanceType('JOIN_TABLE')]
It will create one table for each entity and they will share ids.
If you create a company with id 2, there will be a client with id 2. So a Person with id 2 will never be possible.
But if you use 'SINGLE_TABLE' it will create only one table with all the field of all the entity which will be empty depending on which child you inserted inside.
But again i strongly advise you to not use join table inheritance for your usecase

How Symfony and Doctrine fetches associations and related entities

I cracked my head trying to realize how Symfony and Doctrine fetch associated entities.
Lets imagine: i have several associated entities:
Company (ManyToOne to City)
City (ManyToOne to Region, OneToMany to Company)
Region (ManyToOne to Country, OneToMany to City)
Country (OneToMany to Region)
When i render Company form i create Form Event Listener (on PRE_SET_DATA) that inserts Region and Country selectboxes to this form.
The values in these fields must be set according to associated Region.
I retrieve current Company Region via:
$company = $event->getData();
$city = $company->getCity()
That works good
But when i try this:
$region = $city->getRegion(); // returns NULL
$country = $region->getCountry(); // returns NULL
these methods returns NULL. But in fact all associations exists and Doctrine association mapping is correct. Why?
According to Doctrine documentation: when i call getter for the proxy object (these ovjects are proxies, right?) - Doctrine should fetch insufficient data from database and update the object.
In fact - methods return NULLs.
How can i get any associated entity? (from any association deep level)
I just needed to remove cache dir...
you should try to get it from your controller instead of the Form Class
$em = $this->getDoctrine()->getManager();
$em->initializeObject($obj);//this will initialize the object you need and fetch the real one from the database and not the proxy class Doctrine returns.
It's returning NULL because it has too many levels company->(1)city->(2)region and the doctrine get lost with the proxy class. if this doesn't work, try to make a DLQ query when you have the city.
if you want to understand how a proxy class looks like dump($company); and you will see that it only show the id of city.

How to detect if entity exist in database

I have 2 entities, User and Profile. Profile has in-symfony relation with User, but there is no in-database relation (no foreign key, no cascade) - only simple int column named user_id and nothing more.
Problem is obvious: when i delete user - associated profiles persists, but their user_id points to non-existing user row.
Since I use in-symfony relations when i fetch profile from database it fetches also related user entity. I expected that if there is no row with specific ID, it would just leave null or at least throw an exception or something.
Problem is that symfony creates empty User entity object with only id set. rest of its fields are null.
I know solution would be to create FK, constraints etc ... but I'm not allowed to touch anything in database schema.
How can I manage this problem ? I could even leave those empty object if only i had simple way to determine if they exist in database inside TWIG - so i would know if i can display {{ profile.user.email }} for example.
Fast and dirty solution, as you ask, is to use this test: http://twig.sensiolabs.org/doc/tests/defined.html
But I strongly recommend to rework your entity relations.
Found solution: its fetch: EAGER set to problematic mapping in doctrine.
By default doctrine uses LAZY fetching what results in using Proxy classes generated by doctrine for related entity. That class is almost same as real entity class. Difference is inside getter methods that before returning value performs fetching entity from database.
At this point, when you call getter on such proxy, doctrine tries to find entity in database using its ID, and since it doesn't find anything it throws exception.
When using EAGER fetching doctrine performs fetching of related entities on the same time when it fetches main entity and if it doesn't find it then sets null on relation field.

FOSUserBundle One-To-One mapped Entity not saved

Hi Folks i have a question regarding implementing One-To-One in Entity FosUserBundle.
User Entity Has One To One Mapping With Profile Entity. I have override the basic RegistrationFormType as per shown in the FOSUserBundle's documentation. record is also saved in both table. but mapping entities show me blank data. Please find respected gist file for same.
UserEntity Gist- https://gist.github.com/ajaypatelbardoli/2f0c81cbdf3b0d136785
ProfileEntity Gist - https://gist.github.com/ajaypatelbardoli/fd02025fd338ed90545e
ProfileFormType gist - https://gist.github.com/ajaypatelbardoli/18ef99a3d0bd1198debc
RegistratonFormType Gist - https://gist.github.com/ajaypatelbardoli/09c047425032391c2445
The problem with your implementation is that you do not update the owning side of the bidirectional association. The Doctrine documentation explicitly states:
See how the foreign key is defined on the owning side of the relation, the table Cart.
In your case the owning side is Profile which you can update automatically in setUserId() as folows:
public function setUserId(\XXXX\Bundle\UserBundle\Entity\User $userId = null)
{
$this->userId = $userId;
$userId->setProfile($this);
return $this;
}
You can access the data from both sides of the relation without problems, Doctrine will look up the corresponding entries.

Updating entity with mappings in Doctrine2

I've been doing straight MySQL for a long time and am now trying to do things the ORM way. I have a site where users create designs. The Design table has a field "userId" that maps to User.id. The Design entity has a value, $user, that defines the relationship between entities:
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
It's nice, I can now load a Design entity and get the user's last name with $design->user->lastname.
Now let's say a user creates a new design. I save the entity like:
$design = new Design();
$design->setTitle($_POST['title']);
$design->setUserId($_POST['userId']);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($design);
$em->flush();
When I do this, it fails with the message that userId, which allows no nulls, is null. I thought I might be passing it incorrectly, but when I remove $user from the Design entity it works fine, so I think it's expecting a user object to be passed as well, like:
$design->user = new User($userId)
I don't really want to add a database call just to grab the whole user, when I already know the ID which is all I need. What's the easiest way to handle this?
Hope I have understand your db design that you a user table and design table . Each user may have many designs. right? Then in your entity of design have a field user and its setter(public setUser()) method and a getter method(public getUser()). During the persistance of design entity ,it always expect a user object for User field (not an id) in ORM. In ORM a foreginkey relationship between User and Design it maps through by an object.
So in your case during persistance of design entity
1. Make the user object from userId.
$em = $this->getDoctrine()->getEntityManager();//make your entity manager
$userobj=$em->getRepository('yourBundle:User')->find($userId);
$design = new Design();
$design->setTitle($_POST['title']);
$design->setUser($userobj);
$em->persist($design);
$em->flush();
One Imoprtant thing Make sure that in your design entity that do not have the field userId instead of user. if you have you need to remove the getter and setter method of userid and also edit your yml file or anotation(if you use annotation)

Resources