symfony2 doctrine2 : how to debug why a collection setter is not called - symfony

I am having an issue described here :
Can't persist symfony collection with double embedded form
https://stackoverflow.com/questions/27188337/symfony-setter-not-called-on-form-collection-type-even-with-by-reference-fals
I can't get a setter collection to get called even with the property by_reference set to true.
Here my question is how can follow the chain of commands conducting symfony/doctrine to call this setUserIngredients of a userIngredients collection ?
Which functions from the vendors files are called to identify the by_reference => false and call the appropriate setter ?
Thanks a lot !

To follow the chain of calls you can hook up a debugger. For example http://phpdbg.com/ or http://xdebug.org/ alternatively if you already know a specific function that gets called then you can temporarily put var_dump(debug_backtrace()) in your vendor code to see how it gets to that point

Related

How to test that EventListener has been called on event in Symfony?

What I have:
I have an EventListener that listens to PreRemove entity event in Symfony.
services:
my_bundle.entity_listener.my_listener:
class: 'MyCompany\MyBundle\MyListener'
public: false
tags:
- { name: doctrine.orm.entity_listener, entity: 'MyCompany\MyBundle\Entity\MyEntity', event: preRemove }
What I want:
I want to have a test (functional/integrational/unit or any other really) that somehow checks that when MyEntity is being removed a particular EventListener is being called.
UPDATE
I don't want to do it in a unit test, the need is to actually check that event dispatcher will really call that particular event listener to that particular event.
UPDATE 2
I thought it was obvious, but it seems that it's not - the solution should NOT modify EventListener or Event.
UPDATE 3
I specified that I do not care what the name of the test is: functional, unit or any other.
UPDATE 4
The solution must guarantee that test will pass in context of any environment. So, if someone extends my bundles and messes with my definitions I should still be able to validate if the EventHandling actually works.
Also, checking the result of handling is not an option because:
EventListener can do absolutely anything - there may be cases where I cannot simply check the result and know for sure that EventListener works.
Someone may handle an Event in almost exactly the same way, so that the "result" is the same, but the "way" is wrong.
A functional test is for testing functionality; it isn't intended to test the implementation of that functionality or the configuration of that implementation.
A test such as the one you propose will be brittle and not very useful.
What you probably want is to test the feature that the event listener implements.
check out this : Write UnitTest for Symfony EventListener
It may be of help, as i had a (sort of) similar issue/question
How about creating a compiler pass and add it to the last stage of container compilation:
$container->addCompilerPass(
new CheckEntityListenerRegistered(),
PassConfig::TYPE_AFTER_REMOVING,
-1000
);
That compiler would get executed last. You could check from that point if your listener is registered properly, and if it is, you could assume it's going to be executed by the Doctrine's Unit of Work.
I think I understand what you want. You want a integration test (to test it on all environment) without modifying the listener, the event dispatcher, etc.
Solution 1
When you work on dev, test or prod symfony load different events dispatchers it use the same interface and behaviour but it is different implementations (I haven't check doctrine one).
So you will have different dispatchers for each environment and you don't want to know what happens inside. Let's call this blackbox.
Action delete -> [ black box -> listener called ] -> listener effect
You don't want to look at the blackbox or touch it in any way? Search for the effect that has the listener on the system. Database, log file, mailer, etc.
Solution 2
If you allow me to hook into the blackbox you I will sorrounder the listener with a proxy and monitor if the listener has been called on the proxy.
Solution 3
Alternative you can use the data collectors from the symfony profiler but you won't probably have that enabled on production.
Maybe you can use the symfony profiler?
see How to Use the Profiler in a Functional Test
in the profiler events section you have two tabs with called / not called Listeners http://whatever.com/app_dev.php/_profiler/352211?panel=events

Symfony2 getters, setters, add and remove

I'm getting really confused by entity properties and what they mean. I understand that get and set allow the application to interact with protected and private properties, but what about add and remove?
When running the command
php app/console doctrine:generate:entities bundle:entity
Sometimes it will generate getters and setters and other times it will generate add and remove properties (and usually a get method as well). I've noticed that it also sometimes depends on the relationship with other entities (i.e. OnetoMany), but not always :-S
Nowhere in the Symfony documentation that I can find is this addressed, and it's causing me to see the message "Neither the property "x" nor one of the methods exist and have public access" way too often. Can anybody provide a succinct explanation of this?
add and remove are used to deal with collections. If for example your Entity (Let's say A) contains a collection of B elements, then the command will provide an addB() and a removeB() public methods to help you add and remove elements from your collection. It'll also provide a getter which returns the whole collection.
The command generates methods based on the type of attributes you're working with (ArrayCollection, string, ...)
For xxxToMany associations, Doctrine will generate a "adder" and a "remover" instead of a plain setter. The idea is to easily add and/or remove a single object from the collection without needing to pass around the entire collection everytime to the setter.
Note though that these generated methods are an implementation detail you are free to revise. If you prefer a single setter method for example, feel free to implement that one yourself.
I personally don't rely on the accessor generation of Doctrine anymore. Doing it manually allows greater control of your entity's API, and is also quite easy in an IDE like Netbeans or PHPStorm.

Insert entity in preUpdate event

I'm trying to persist a History entity whenever a Message gets updated. I have too much going on behind the scenes to post all the code here and for it to make sense, but I've basically tracked the issue down to the UnitOfWork::commit method. There, the UOW first loops through the entityInsertions, and finding nothing, continues on to the entityUpdates. There the UOW's entityInsertions gets updated, but since it's already past that loop, it doesn't pick up that it still needs to persist some entities. Is there any way to force the UOW to "restart" this process? If so, how? I'm using Doctrine 2.4.
Thanks for any help!
This might be the dirtiest solution ever, but what I ended up doing was basically the following...
Create an onFlush event subscriber
Inject the entire container into the subscriber (seeing as injecting only the entity manager will result in a circular reference error)
Loop through the UnitOfWork's scheduledEntityUpdates and scheduledEntityInserts (I wasn't interested in deletes)
Handle each scheduled update or insert which you are interested in (in my case, I marked each entity I was interested in with a LoggableInterface, just to know which entities are loggable)
Handle the relevant object with a handler chain (This was just my own algorithm, yours may not require this. This was set up to handle logging of different LoggableInterface objects in different ways)
Persist the entity (the actual history event) via the entity manager, and do the following:
$classMeta = $this->entityManager->getClassMetadata(get_class($historyEntity));
$this->entityManager->getUnitOfWork()->computeChangeSet($classMeta, $historyEntity);
Profit
Hope this helps somebody!

Possible to use Entity getters in custom repository?

I am in need of grabbing a few fields of an entity, running them through some processing, and returning the processed data. I am wondering if it is possible to call the getters of the Entity for which the custom repository is being built inside that custom repository? The only way I thought of so far seems like it would create an infinite loop by calling the entity's repository, which would include a call to the very custom repository being used as well.
I can write the actual queries, but I figured it it would be cleaner to access the data through existing methods, then why not?
Update: So I built a method in the custom repository that I pass the entity object to that I want to work with. Seems a little weird that I would have to pass the entity to it's own custom repository so that it would know what to act on, but I can't seem to find any other way. So in my Controller right now I am calling the entity, then calling up the repository for the entity, and passing the previously called entity to the repository. Is that right?
$user = $this->get('security.context')->getToken()->getUser();
$user_repository = $this->getDoctrine()->getRepository('AppBundle:User');
$profiles = $user_repository->getAllUsersProfiles($user);
It turns out I had the whole concept of Custom Repositories incorrect. I was using them to store methods that would pull out data relevant to the entity, but only relevant in the sense that the entity was being used to filter something else.
Prior to this epiphany my User Custom Repository had methods like getAllUsersProfiles($user) and getAllUsersCampaigns($user). My previously incorrect understanding was that because the User entity was the thing that was central to filtering the Profiles and Campaigns, that these belonged in the User Custom Repository.
What I have now come to understand is that getAllUserProfiles($user) belongs in the Profiles Custom Repository, and would be more appropriately named getAllByUser($user). Now when I need to use it, I pull up the Profiles Repository and call the appropriate method:
$profile_repository = $this->getDoctrine()->getRepository('AppBundle:Profile');
$all_profiles = $profile_repository->getAllByUser($user);

What's the proper use of $unitOfWork->getScheduledCollectionDeletions() in Doctrine 2 (and Symfony)?

I'm trying to detect changes in a many-to-many relation in an onFlush event.
If new entities are added to the relation or the relation is updated (always keeping an element), I can detect changes using $unitOfWork->getScheduledCollectionUpdates() and then check for getInsertDiff() or getDeleteDiff(). So far so good.
The problem comes when I take all the entities out of the relation: "There were two related entities before but there are NO related entities now."
When the relation is left empty I can access $unitOfWork->getScheduledCollectionDeletions(), but there is no way of knowing which entities were deleted:
getDeleteDiff() for this collections doesn't tell anything.
getSnapshot() doesn't tell me which entities were there before
How should I know which entities were taken out of the many-to-many relation?
I've added a Gist with the full implementation: everything works ok (it may need some optimization) except $uow->getScheduledCollectionDeletions() (line 101)
https://gist.github.com/eillarra/5127606
The cause of this problem is twofold:
1) When the method clear() is called on a Doctrine\ORM\PersistentCollection, it will:
clear its internal collection of entities.
call scheduleCollectionDeletion() on the Doctrine\ORM\UnitOfWork.
take a new snapshot of itself.
Number 2 is the reason your collection shows up in $uow->getScheduledCollectionDeletions() (and not in $uow->getScheduledCollectionUpdates()). Number 3 is the reason why you cannot determine what was in the collection before it was cleared.
2) When using the Symfony2 Form component, specifically the ChoiceType or CollectionType types in combination with the option multiple, that clear() method will get called when all entities should be removed from the collection.
This is due to the MergeDoctrineCollectionListener which is added here:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php#L55
This is done as optimization: It's faster to clear a collection this way, in stead of checking which entities should be removed from it.
I can think of two possible solutions:
1) Create a fork symfony/symfony and implement an option in order to not add the MergeDoctrineCollectionListener. Maybe something like no_clear to prevent the listener from being added. This won't introduce a BC break and would solve your problem because the clear() method of a collection won't get called when all entities should be removed.
2) Redesign your counter: Maybe also listen to the OnLoad event which can count the amount of entities in the collection at the time it's fetched from the db. That way your OnFlush listener can use that number to know how many entities where removed from the collection when it was cleared.
I found that if set 'by_reference' => false, option to EntityType form, then UnitOfWork detect changes of collection.
See difference state in UnitOfWork at OnFlush event:
'by_reference' => false
'by_reference' => true
In case the last item gets removed (like on a form submission), the "getDeleteDiff" sometimes turns out to be empty but in reality, items were there, the solution is to fetch the original data from the database. In my example we use a clone of the collection to achieve it. So the original collection stays untouched and everything still works.
public function onFlush(OnFlushEventArgs $args)
{
$uow = $args->getEntityManager()->getUnitOfWork();
foreach ($uow->getScheduledCollectionDeletions() as $collection) {
/**
* "getDeleteDiff" is not reliable, collection->clear on PersistentCollection also clears the original snapshot
* A reliable way to get removed items is: clone collection, fetch original data
*/
$removedData = $collection->getDeleteDiff();
if (!$removedData) {
$clone = clone $collection;
$clone->setOwner($collection->getOwner(), $collection->getMapping());
// This gets the real data from the database into the clone
$uow->loadCollection($clone);
// The actual removed items!
$removedData = $clone->toArray();
}
}
}
The reason the ->getDeleteDiff() is sometimes empty is because the "onSubmit" function of a form calls the ->clear() function on a PersistentCollection. And by clearing it, the original "snapshot" gets cleared too (for performance reasons I guess). And the "getDeleteDiff" function actually relies on that snapshot, but now it's empty.
There are multiple issues on Github about this problem:
https://github.com/doctrine/orm/issues/2272
https://github.com/doctrine/orm/issues/4173

Resources