Here's the deal, i'm trying to update an Entity, but i'd like to compare the state of my entity before the update with the state of my entity after the update.
I try to compare the number of Users in my entity.
For example, if i add a User, i want my code to know that, for example, i had 4 Users before update, and 5 after (and get that User for further use).
After reading different topics, i've tried doing like that in my preUpdate($object){} method
$em = this->getConfigurationPool()->getContainer()->get('doctrine')->getManager();
$original = $em->getUnitOfWork()->getOriginalEntityData($object);
But both
var_dump(count($object->getUsers()));
var_dump(count($original['users']));
Give the same value, and, according to my example, the value is 5 in both case (so the value after the update).
Is there a way i can save the old_state of my entity in a var? What am i doing wrong?
EDIT:
It's not the preUpdate function of Doctrine, but the preUpdate function of SonataAdmin, don't know if they're the same.
Please try this in the preUpdate function:
$em = $this->getModelManager()->getEntityManager($this->getClass());
$original = $em->getUnitOfWork()->getOriginalEntityData($object);
Related
I have a little problem and I can't solve it.
I have a controller, which accept 4 variables using AJAX and I need to insert data in first table and then get ID value from first table and insert it to 2 with 2 additional parameters.
So, my app structure is:
1) Keywords Table with fields keywordId, KeywordVal and Page ID (getted from AJAX)
2) Translations table with fields keywordID (get from Keywords.keywordId), langCode and translation (AJAX)
3) Controller which get data from ajax, proceed it and insert into table.
So, my question is next: how can I configure my EventListener? This listener must be run after flush() method and insret data into Translations table.
Why not you create it all in the controller, because to do all in a EventListener you must in some way to pass the values to the listener.
If you do it in your controller yo can to persist first the first entity with the needed parameters and in after persist the second entity related to de previusly created entity.
Something like:
$em = $this->getDoctrine()->getManager();
$keyword = new Keyword($param1, $param2);
$em->persist($keyword);
$keywordTranslation = new KeywordTranslation($keyword, $param3, $param4);
$em->persist($keywordTranslation);
$em->flush();
I think is so much easy to do
What you seem to need is a Doctrine Listener. Here is the documentation to create a Doctrine listener. You might want to use the preFlush event in my opinion. Be careful, this event is fired for every flush, not only for Keywords, so you've got to check first it is a Keyword before creating Translations.
EDIT: Nevermind, just noticed this does not answer to your question. However, I feel your model could be improved, because you should not have to flush several times to insert a single set of data. Theoretically, you should have a OneToMany relationship between Keywords and Translations, and Doctrine would manage alone to link the two entities with their id at insertion.
Using Doctrine 2 and Symfony 2.7 I want to use an automated count for a column in my db.
Example:
So when I update a report, I want to add the user (which is the parent of the report by a OneToMany relation) to the leaderboards with the column completed set to 1 (setCompleted). When the user was already on the leaderboards, I want to find him and add 1 to the completed tasks value.
if (!$lb) {
$new = New Leaderboard();
$new->setUsers($user)
->setCompleted('1');
$em->persist($new);
} else {
$update = $em->getRepository('AppBundle:Leaderboard')->findBy(array('user' => $user));
$update->setCompleted('2');
}
So basically I want to automate the $update->setCompleted('2'); so that it takes the current value and adds one to that and then flush that to the database.
I hope this makes any sense? No sure how to explain it or search for it online...
You could use an onFlush listener to watch your Report entity for updates and increment/create the leaderboard entry depending on the state of the Report.
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/events.html#onflush
Requires a bit of (careful) reading, but it will do what you want.
Here's a couple of questions I answered on more or less the same subject with examples that should get you started pretty quick
Track field changes on Doctrine entity
Persisting other entities inside preUpdate of Doctrine Entity Listener
The basic flow you want is to capture updates to Report (the simpler second example under my answer shows this being done for an entity). Then based on the state of Report, you create a new Leaderboard entity and attach it to Report, OR update the existing Leaderboard entity for that report.
Note it's important to not flush the entity, just add it to the UOW like so
$this->getEntityManager()->persist($entity);
$metaData = $this->getEntityManager()->getClassMetadata($className);
$this->getUnitOfWork()->computeChangeSet($metaData, $entity);
Hope that helps!
I have a Entity in my database (say Member) which has many relationships with other tables (6 relationships to be exact). Some of them I don't want mapped with the ORM (I mean linked to this Entity) because they may have many records (like MemberAccessLogs for example) and some other load many other entities.
Now I want this Member Entity to have an isDeletable method so I can disable exclude button in administration page.
If I where to do this the traditional way, I would have to declare the associations with all the other tables in the entity class, including MemberAccessLogs and I would put the method in it so I could test if these associations are empty.
But AFAIU, I would have to make a fetch (or at least a count) to the association's tables in order check for empty.
Another way would be to fetch the Members I want shown and then make a separate query to check for empty with a low cost exists(select * from table limit 1) in these sub-tables and then populate the isDeletable method in Member programmatically before pass it to Twig.
But I found this solution cumbersome. Anyone has a better way to do this ?
Just for the record: Some people may think this is "premature optimization". I maintain (contrary to some), that you should think ahead when you are programming and don't this this is bad. But I really think this isn't the place to discuss it. Please let's focus on the question asked ok ? :)
Edit
To easily prove that limit 1 is increadibly faster than count, I did a small test in a table in my database that has more than 20 million lines. Here are the results:
select count(*) from loga [20 million+ table]
20678473
1 row(s) fetched - 27023ms
select exists(select null from loga limit 1)
true
1 row(s) fetched - 2ms
I guess 13511,5 times faster is conclusive enough. :D
Extra lazy
You could look into extra-lazy associations.
Basically you map all associations as you normally would, and add fetch="EXTRA_LAZY":
/**
* #Entity
*/
class CmsGroup
{
/**
* #ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
*/
public $users;
}
Now Doctrine will not load the complete collection into memory the first time it's accessed, but performs specialized queries to load the parts you actually need at that moment.
So $users->count() (or count($users)) on the collection would trigger a simple count-query in stead of loading the complete collection into memory.
PostLoad
You could use an postLoad event to determine if such an entity is deletable. This postLoad event is called after an entity is constructed by the EntityManager, so when the entity is loaded.
Add an unmapped property ($isDeletable) to the entity that stores whether the entity can be deleted or not.
Create an entity listener that listens to the postLoad event. The listener can have the EntityManager, DBAL Connection, or anything else injected. With that dependency you could perform whatever query you want and use the result to set $isDeletable.
The result is a single additional query when the entity is loaded, after which the entity "knows" whether it's deletable or not.
An example of using the postLoad event can found in a Cookbook entry on the Strategy Pattern
Do note that when the conditions that determine whether it's deletable or not change, the value of $isDeletable could become incorrect. To resolve this issue, you could keep track of those conditions:
Keep track
Add a mapped property ($isDeletable) to the entity that stores whether the entity can be deleted or not. It would probably start with true.
When something is added to an association which would mean that the entity is no longer deletable, set $isDeletable to false.
When something is removed from an association that which would mean that the entity is deletable again, set $isDeletable to true.
In other words: with every change you keep track of whether the entity is deletable or not.
This way you won't need any additional queries at all.
There's a Cookbook entry on aggregate fields that explains this concept very well.
I have 3 Entities Users, UserProfile and Staffs
The User profile is linked to Users table through user id
The Staff table is linked to userprofile using the userprofileid
Admin create the user profile and generate the registration no, username and password.
I want add a user record to the users table then add the user profile and then add the profile id to the staff table.
I want to persist three entities sequentialy
I tried to create an instance for the Users like
$this->userent->setUsername('xxx');
$this->em->persist($this->userent);
$this->em->flush();
then:
$this->profileent->setFirstname('xxx');
$this->em->persist($this->profileent);
$this->em->flush();
Basically a form is shared among three entities and I want to insert into three tables sequentially,
Updated
Apart from users entity i have a usertype entity linked to users...i want to persist only the foreign key. i have
setUserType(Usertype $userType) method an instance of the user_type entity in users
when i do
$this->userent = new Users();
$this->userent->setUserType($this->em->getRepository('\Campus\Entity\Usertype')->findByUserType("admin"))
i get the error
Argument 1 passed to Campus\Entity\Users::setUserType() must be an instance of Campus\Entity\Usertype, array given
if i pass the value of the array which is an instance of Usertype
i get an error saying need an array for the ArrayCollection..help please!!!
Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given, called in D:\xampp\htdocs\zend\library\Doctrine\ORM\UnitOfWork.php on line 406 defined in D:\xampp\htdocs\zend\library\Doctrine\Common\Collections\ArrayCollection.php on line 46
Think less about the database, and more about your objects. That's the whole point of doctrine.
You want something like this:
<?php
// create some entities
$user = new Entity\User();
$user->setUsername('userman');
$profile = new Entity\UserProfile();
$profile->setFirstname('joe');
$profile->setLastname('smith');
$staff = new Entity\Staff();
$staff->setSomething('value-for-something');
// associate those entities together
$profile->setStaff($staff);
$user->setProfile($profile);
// assuming you have set up cascade={"persist"} on your associations
$this->em->persist($user);
// if you haven't set up cascade={"persist"}, you will need to call persist on each entity:
// $this->em->persist($profile);
// $this->em->persist($staff);
$em->flush();
So, the basic idea is you build up your objects, get them into Doctrine's Unit-of-Work (by calling persist() and maybe having some cascades set up), then write them all to the database in a single transaction by calling flush()
You can persist each entity, and then flush() one time at the end.
If you have relations between your entities, you can check the following annotation
cascade={"persist"}
"Cascades persist operations to the associated entities."
Have a look at the documentation here : http://docs.doctrine-project.org/en/2.0.x/reference/working-with-associations.html#transitive-persistence-cascade-operations
Is there a way to have something like this in doctrine:
class Entity {
/**
* #Column(name="related_entity_id")
*/
private $relatedEntityId;
/**
* #ManyToOne(targetEntity="RelatedEntitiy")
* #JoinColumn(name="related_entity_id", referencedColumnName="id")
*/
private $relatedEntity;
}
What I want to do I do something like this:
call Entity::setRelatedEntityId($someId), and persist the entity,
and have the entity return the related entity by calling Entity::getRelatedEntity().
The related entity is selected from a table which will be strictly limited and it will never dynamically grow at runtime, so there is a finite number of related entity ids.
At the time of creating a new Entity, I'd like to set the related entity id, but without having to fetch the whole related entity from the database.
As far as I could test this, it does not work, because if I set the relatedEntityId but not the relatedEntity, Doctrine automatically sets the related_entity_id column to null, since basically no relationship has been established.
I've tried to do something like this also:
remove the relatedEntityId property, and use
Entity::setRelatedEntity(new RelatedEntity($relEntId))
the constructor of the RelatedEntity will set the id, but not other values.
I do not want to persist the RelatedEntity (it's values are already set in the DB for the given $relEntId), but this time Doctrine signals an error at flush, because it has an unpersisted entity.
Basically, what I want to do is create a relationship without knowing anyhing but the Id of the related entity. If there is some other way this can be done, please share.
Thanks in advance
EDIT:
I've found a workaround. Since the RelatedEntities will be a limited set of immutable objects, I've done the following:
use the entityManager to find all RelatedEntities;
inject the list to the object that will be creating new Entities
when creating a new Entity, select one of the RelatedEntities from the list as its RelatedEntity
I'll leave the question open for a day or two, just in case somebody comes up with something better.
Use the entity proxy:
Entity::setRelatedEntity($entityManager->getReference('RelatedEntity', $relEntId))
I don't think this is supposed to work like how you described :)
The entity you add must be a Doctrine managed object, so that means you have to load it yourself first using the find() family of methods.
Based on my experience with Doctrine 2 further elaborated here http://ssmusoke.wordpress.com/2012/03/25/doctrine2-day-3-proxies-associations-relationships/
My approach is as follows:
a) Have only the $relatedEntity property
b) Add a getRelatedEntityId() function which returns the id value from $relatedEntity
c) Add a setRelatedEntityId() which sets the $relatedEntity object - you may need to load it from the database, saves you from polluting other layers when u only have the id of the related entity
d) Add getRelatedEntity() and setRelatedEntity() functions
BOTTOM LINE: You cannot have a property for the foreign key column and the mapped property as Doctrine gets confused