FOSUserBundle One-To-One mapped Entity not saved - symfony

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.

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

Doctrine2 OneToMany with multiple Entities

I have three kinds of Users, every user has some FiscalData and is linked to a UserCredential entry.
Which translates in:
UserX (X=1,2,3) has two FKs referring to FiscalData and
UserCredential tables.
Using Doctrine2, reading the docs http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html, I believe to need the MappedSuperClass pattern.
I have also read the following questions:
Doctrine 2 - One-To-Many with multiple Entities
Many-To-One with multiple target entities
Doctrine2, Symfony2 - oneToOne with multiple entities?
But in the docs is clearly stated that
A mapped superclass cannot be an entity, it is not query-able and persistent relationships defined by a mapped superclass must be unidirectional (with an owning side only). This means that One-To-Many associations are not possible on a mapped superclass at all. Furthermore Many-To-Many associations are only possible if the mapped superclass is only used in exactly one entity at the moment. For further support of inheritance, the single or joined table inheritance features have to be used.
So, how to achieve what I'm trying to achieve, which is a bidirectional relationship between UserX and FiscalData/UserCredential? (so that f.e. via Doctrine I can get a UserCredential and check what kind of profiles it has associated)
Any complete minimal code example showing how to enforce the relationship I'm looking for (and not just the MappedSuperClass inheritance shown in the docs will be highly appreciated.
Use an abstract entity instead of MappedSuperClass. Single-table is usually the way to go unless you're sure you want class/table.
<?php
/**
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({
* "mentor" = "Mentor",
* "protege" = "Protege"
* })
*/
abstract class User { ... }

Embedded form persistence : get or create

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

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)

Primary key of owning side as a join column

NOTE: Topic is lengthy but detailed and may come in handy if you use Doctrine2 and oneToOne relationships.
Recently I came across a problem in Doctrine:
I created User and UserData objects with oneToOne bidirectional relationship:
User:
...
oneToOne:
userdata:
targetEntity: UserData
mappedBy: user
UserData:
...
oneToOne:
user:
targetEntity: User
inversedBy: userdata
So UserData was the owning side with user_id column in it:
user: id, ...
userdata: id, user_id, ...
This created a problem, where every time you fetch a User object (Single user, collection of user or collection of other object with user joined on it) Doctrine would lazy load a UserObject for each User.
Issue described here:
How to prevent Doctrine from lazy loading one to one relationsip?
http://groups.google.com/group/doctrine-user/browse_thread/thread/7e421a2b189f0ea7
https://github.com/doctrine/doctrine2/issues/4389
Proposed solution described here:
https://github.com/doctrine/doctrine2/issues/2364
So there are 3 ways around this:
Wait and see if proposed solution is addressed in Doctrine and fixed in future releases (may not happen)
Manually left join UserData to User in every query (still waste of resources, dont need UserData)
Switch inverse side and make User the owning side.
I decided to go with #3. So my schema relationship now looks like this:
User:
...
oneToOne:
userdata:
targetEntity: UserData
inversedBy: user
UserData:
...
oneToOne:
user:
targetEntity: User
mappedBy: userdata
This means that my tables now look like this:
user: id, userdata_id, ...
userdata: id, ...
I decided that instead of having Userdata.id autoincremented, I'll set it manually and match it with user.id. This means that UserData.id will always match user.id.
Question Can I use user.id (a primary autoincremented key) as joinColum instead of userdata_id since they will always have the same value? Do you see any potential issues with this way of doing things?
Any other tips or opinions about this issue is greatly welcomed and appreciated.
You could also force partial objects, to get rid off lazy-loading:
use Doctrine\ORM\Query;
//...
$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
This is a known issue for OneToOne associations. There is a github discussion about this that is worth reading. A solution (pull request) was proposed and rejected.
Recommendation
Your question suggests the same solution proposed by the contributors to Doctrine: change the owning side of the relationship.
Other Options Explored
I had a similar problem with an entity called Version that had a OneToOne bidirectional relationship with Settings. Every time I queried Version (say for 10 specific version records), Doctrine would do additional queries for the joined Settings (as if it was Lazy Loading these entities). This happened, even though I did not reference Settings anywhere, e.g. $Version->getSettings()->getSomeProperty().
Manual JOIN
The only "solution" (hack) that works for me is to manually included a JOIN for this Settings entity every time I did a query on Version. But since I don't need the extra entity (in this case), that would still be a single extra unnecessary query, every time I query this table in different ways.
Extra Lazy
Based on other suggestions, I thought that if I explicitly specified this relationship as "extra lazy" it would work, e.g.
/**
* #ManyToMany(targetEntity="Settings", mappedBy="version", fetch="EXTRA_LAZY")
*/
But this doesn't affect hydration. See the Doctrine Docs for more details about what EXTRA_LAZY does.
Hydration Type: HYDRATE_ARRAY
What helped in my case (when I didn't need an entity), was to specify that I wanted to fetch as an array (rather than object).
$query = $queryBuilder->getQuery();
$query->getResult(Query:HYDRATE_ARRAY);
This returns an array, and as a result it doesn't lazy load the OneToOne associations. However, in other contexts where I need the entity object, I had to explicitly JOIN the entity (despite not wanting it).
My workaround was to turn the OneToOne relationship into a ManyToOne (with an associated ArrayCollection), with a custom getter and setter which only sets and returns the 0-th element of the collection:
/**
* Set pref
*
* #param \MyBundle\Entity\Pref $pref
* #return Employee
*/
public function setPref(\MyBundle\Entity\Pref $pref = null) {
$this->pref[0] = $pref;
return $this;
}
/**
* Get pref
*
* #return \MyBundle\Entity\Pref
*/
public function getPref() {
return $this->pref[0];
}
These methods can (seemingly) be used the same as the ones created by a OneToOne relationship.
doctrine:generate:entities still creates the normal "add" and "remove" methods, but they can be ignored.
Note: I only recently started using these and currently only to read from the database. I don't know of any side effects from this workaround. I'll update this answer if I notice any.
This is admittedly an ugly hack. I hope Doctrine fixes this soon so I can go back to using proper OneToOne relationships.
I came across this same problem and remember that the symblog tutorial gave an example of how to reduce the lazy loading by explicitly add left joins on the tables that you do not need. It seems strange to include tables on a join when you do not even want that data at all, but this way you will reduce all of those extra queries down to 1 and it does run faster.
Search for lazy loading - about 1/5 of the way down
http://tutorial.symblog.co.uk/docs/customising-the-view-more-with-twig.html
To fix this for the user/userdata issue try adding this to the user repository and use to whenever you need to get all users even if you do not want userdata. It can be further enhanced by selecting partial:
->select('partial p.{user_id,name,}')
public function getAll($limit = 500) {
$qb = $this->createQueryBuilder('u')
->select('u', 'd')
->leftJoin('p.userdata', 'd')
if (false === is_null($limit))
$qb->setMaxResults($limit);
return $qb->getQuery()->getResult();
}

Resources