Doctrine2 ORM Ignore relations in Merge - symfony

I'm implementing a REST Api with FOSRestBundle and I have encountered a problem with the modification of existing entity (PUT)
I have a class Student with has a ManyToOne relation
/**
* #JMS\MaxDepth(2)
* #ORM\ManyToOne(targetEntity="ClassRoom", inversedBy="students")
* #ORM\JoinColumn(name="classroom_id", referencedColumnName="id")
*/
protected $classRoom;
When performing a PUT action I only receive the value attributes since i do not want to let the user modify the relations via a put request.
This is a received data example.
{
"id": 3,
"name": "pelayo",
"second_name": "ramon",
"last_name": "fernandez",
"birthday": "1983-08-15T00:00:00+0200"
}
Data gets deserialized with JMS serializer wich sets the $classRoom attribute to null since it didn't find it in the received data.
When performing a merge
$student2 = $this->get('doctrine')->getManager()->merge($student);
If the student2 gets persisted the current relation with classRoom gets erased from the database since the merge sets the relation to null.
This behavior can be dodged by retrieving the current classRoom and setting it to the deserialized entity by hand before the merge, but this is ugly.
Is there any way to tell doctrine to ignore an attribute in a merge from the detached one and make it always use the stored value?

Merge is not the only solution.
The JMSSerializerBundle includes an object constructor for Doctrine entities. When you enable this constructor, the deserialized entities are managed entities that can be persisted(with $em->persist($student)). The only attributes modified on the deserialized entity are the ones mentioned in the JSON from the request.
Here is how you can enable it.

Related

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.

How to log an entity that has collections?

I want to log all changes of an entity. I looked into Loggable doctrine extension as provided by the StofDoctrineExtensionsBundle.
I got it working for fields that store simple data, e.g. string and integers. But my entity also has ManyToMany relationship to another entity, e.g. Tags.
I am getting this error:
InvalidMappingException: Cannot versioned [tags] as it is collection in object - Hn\AssetDbBundle\Entity\Asset
Is there a way to log an entity with its relationships? I don't mind switching to another bundle.
Currently no bundles/extensions have this functionality out of the box. One option would be to implement it yourself. This can be done by making use of Doctrine Listeners. Particularly you need to listen to postUpdate and postPersist events - these happen when entity is updated and created and store your Tags there.
Another option is to get rid of ManyToMany relationship. For this create an intermediate entity AssetTag that would have OneToMany relationship to both Asset and Tag. After this is done, you can use EntityAudit Doctrine Extension, which supports this type of relationships.

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)

GAE Datastore - workaround for updating entity?

Assume:
class Contacts(db.Model):
first_name = StringProperty()
last_name = StringProperty()
phone_number = PhoneNumberProperty()
new_contact = Contacts(first_name="Homer", last_name="Simpson", phone_number = 1234566)
new_contact.put()
I am new to GAE Datastore, but per GAE Datastore Docs (see below if interested), i can't modify a single property of the entity (eg, phone_number). I have to re-declare all properties, and then put() the new entity. Omitting previously-declared properties from the new entity results in them being declared as None. Is there a workaround for this -- such as manually overriding the key-name generation function -- so the entity maintains the same key?
#
from GAE Datastore Docs:
To update an existing entity, modify the attributes of the object, then call the put() method. The object data overwrites the existing entity. The entire object is sent to the datastore with every call to put(). Note: The datastore API does not distinguish between creating a new entity and updating an existing entity. If the object's key represents an entity that exists, calling its put() method overwrites the entity.
that's not true. You need to have a way to get the contact you want and you can update just that. Using the keyname is only one way to do it. If you know the ID of filter a query to only get one entity, you can update a field from it and the put() to update it.
You could have something like:
query = Contact.all().filter('first_name', 'john').filter('last_name', 'doe')
for contact in query:
contact.phone_number = 498340594834
contact.put()
Note that that code would update any contacts with that name to that phone number. If there is more than one with that name, both are updated. Using a keyname can prevent that but you have to create more complex keys since only the first and last name might colide.

Resources