How can I schedule an entity for update, manually, when no property is actually changed?
I tried $entityManager->getUnitOfWork()->scheduleForUpdate($entity) but it gave an error in the core, and I have no intetion of debuging Doctrine.
The entity is managed if it matters: $entity = $repository->findOne(1)
I need this so doctrine would call my EventSubscriber on flush().
I've also tried something like $entityManager->getEventManager()->dispatchEvent(\Doctrine\ORM\Events::preUpdate), but then my listener's preUpdate() receives EventArgs instead of PreUpdateEventArgs.
Any help is appreciated!
Method mentioned by Wpigott not working for me (at least in doctrine/orm v2.4.2), instead I'm using this:
$entityManager->getUnitOfWork()->setOriginalEntityProperty(spl_object_hash($entity), 'field_name', '__fake_value__');
Where field_name existent property name.
The solution is a bit hacky, but I was able to achieve this by doing something like the following.
$objectManager->getUnitOfWork()->setOriginalDocumentData($object, array('__fake_field'=>'1'));
This essentially causes Doctrine to think the document has changed from the original, and it computes it as a change which will cause the events to be executed on flush.
The example is for the MongoODM solution, but the same technique should work for ORM like below.
$objectManager->getUnitOfWork()->setOriginalEntityData($object, array('__fake_field'=>'1'));
Even though this question is quite a bit old, I just found a way much more elegant to solve this problem I want to share here using Doctrine ORM 2.6.2:
You can simply tell the ClassMetadataInfo object of your table to just state those fields as dirty that you pass to the propertyChanged function of the UnitOfWork. The important parts here are the setChangeTrackingPolicy and propertyChanged calls:
$unitOfWork = $entityManager->getUnitOfWork();
$classMeta = $entityManager->getClassMetadata('Your\Table\Class');
$ctp = $classMeta->changeTrackingPolicy; # not a function but a variable
# Tell the table class to not automatically calculate changed values but just
# mark those fields as dirty that get passed to propertyChanged function
$classMeta->setChangeTrackingPolicy(ORM\ClassMetadataInfo::CHANGETRACKING_NOTIFY);
# tell the unit of work that an value has changed no matter if the value
# is actually different from the value already persistent
$oldValue = $entity->getValue('fieldName'); # some custom implementation or something
$newValue = $oldValue;
$unitOfWork->propertyChanged($entity, 'fieldName', $oldValue, $newValue);
# field value will be updated regardless of whether its PHP value has actually changed
$entityManager->flush();
# set the change tracking policy back to the previous value to not mess things up
$classMeta->setChangeTrackingPolicy($ctp);
You may also want to have a look at the Doctrine\Common\NotifyPropertyChanged interface.
I hope this will be useful for someone.
What's the goal? If there is no property changed, why would you plan an update?
Related
please can anyone help me understand what a macro is in Laravel Macroable trait, reading this documentation https://laravel.com/api/5.4/Illuminate/Support/Traits/Macroable.html only tells me how to use but why do I use it, what is it meant for.
It is for adding functionality to a class dynamically at run time.
use Illuminate\Support\Collection;
Collection::macro('someMethod', function ($arg1 = 1, $arg2 = 1) {
return $this->count() + $arg1 + $arg2;
});
$coll = new Collection([1, 2, 3]);
echo $coll->someMethod(1, 2);
// 6 = 3 + (1 + 2)
echo $coll->someMethod();
// 5 = 3 + (1 + 1)
We have 'macroed' some functionality to the Collection class under the name someMethod. We can now call this method on the Collection class and use its functionality.
We just added a method to the class that didn't exist before without having to touch any source files.
For more detail of what is going on, please check out my article on Macros in Laravel:
asklagbox - blog - Laravel Macros
It allows you to add new functions. One call to ::macro adds one new function. This can be done on those of the internal framework classes which are Macroable.
This action of adding the function to the class is done at run time. Note there was/is an already existing perfectly good name for this action, which isn't the word "macro", which I'll explain at the end of this post.
Q. Why would you do this?
A. If you find yourself juggling with these internal classes, like
request & response, adding a function to them might make your code more
readable.
But as always there is a complexity cost in any
abstraction, so only do it if you feel pain.
This article contains a list of the classes you can add functions to using the static call "::macro"
Try not to swallow the word macro though, if you read that article - if you're like me it will give you big indigestion.
So, let's now add one extra function to an internal framework class. Here is the example I have just implemented:
RedirectResponse::macro('withoutQuery', function() {
return redirect()->to(explode('?', url()->previous())[0]);
});
This enables me in a controller to do this:
redirect()->back()->withoutQuery();
(You can just do back() but I added redirect() to make it clear).
This example is to redirect back and where the previous route was something like:
http://myapp.com/home?something=something-else
this function removes the part after '?', to redirect to simply:
http://myapp.com/home
I did not have to code it this way. Indeed another other way to achieve this is for me to put the following function in the base class which all controllers inherit from (App\Http\Controllers\Controller).
public function redirectBackWithoutQuery()
{
return redirect()->to(explode('?',url()->previous())[0]);
}
That means I can in any controller do this:
return $this->redirectBackWithoutQuery();
So in this case the "macro" lets you pretend that your new function is part of an internal framework class, in this case the Illuminate/RedirectResponse class.
Personally I like you found it hard to grasp "laravel macros". I thought that because of the name they were something mysterious.
The first point is you may not need them often.
The second point is the choice of the name ::macro to mean "add a function to a class"
What is a real macro?
A true macro is a concept unique to Lisp. A macro is like a function but it builds and returns actual code which is then executed. It is possible to write a function in other languages which returns a string which you then execute as if it was code, and that would be pretty much the same thing. However if you think about it you have all of the syntax to deal with when you do that. Lisp code is actually structured in lists. A comparison might be imagine if javascript was all written as actual json. Then you could write javascript, which was json, which returned json, which the macro would then just execute. But lisp is a lot simpler than json in terms of its syntax so it is a lot easier than what you just imagined. So, a true lisp macro is one of the most beautiful and amazing things you can encounter.
So why are these add-a-function things in laravel called macros?
That's unknown to me I'm afraid, you'd have to ask the author, but I asked myself what they really do and is there already a name for that.
Monkey Patches
TL;DR laravel's ::macro could more accurately be described as monkey patch
So if using laravel ::macro calls, I personally decided to create a MonkeyPatchServiceProvider and put them all there, to reduce unnecessary confusion for myself.
I realise the name might sound a bit derogatory, but that's not intended at all.
It's simply because there's already a name for this, and we have so much terminology to deal with why not use an existing name.
I have a strange problem with \Doctrine\ORM\UnitOfWork::getScheduledEntityDeletions used inside onFlush event
foreach ($unitOfWork->getScheduledEntityDeletions() as $entity) {
if ($entity instanceof PollVote) {
$arr = $entity->getAnswer()->getVotes()->toArray();
dump($arr);
dump($entity);
dump(in_array($entity, $arr, true));
dump(in_array($entity, $arr));
}
}
And here is the result:
So we see that the object is pointing to a different instance than the original, therefore in_array no longer yields expected results when used with stick comparison (AKA ===). Furthermore, the \DateTime object is pointing to a different instance.
The only possible explanation I found is the following (source):
Whenever you fetch an object from the database Doctrine will keep a copy of all the properties and associations inside the UnitOfWork. Because variables in the PHP language are subject to “copy-on-write” the memory usage of a PHP request that only reads objects from the database is the same as if Doctrine did not keep this variable copy. Only if you start changing variables PHP will create new variables internally that consume new memory.
However, I did not change anything (even the created field is kept as it is). The only operations that were preformed on entity are:
\Doctrine\ORM\EntityRepository::findBy (fetching from DB)
\Doctrine\Common\Persistence\ObjectManager::remove (scheduling for removal)
$em->flush(); (triggering synchronization with DB)
Which leads me to think (I might be wrong) that the Doctrine's change tracking method has nothing to do with the issue that I'm experiencing. Which leads me to following questions:
What causes this?
How to reliably check if an entity scheduled for deletion is inside a collection (\Doctrine\Common\Collections\Collection::contains uses in_array with strict comparison) or which items in a collection are scheduled for deletion?
The problem is that when you tell doctrine to remove entity, it is removed from identity map (here):
<?php
public function scheduleForDelete($entity)
{
$oid = spl_object_hash($entity);
// ....
$this->removeFromIdentityMap($entity);
// ...
if ( ! isset($this->entityDeletions[$oid])) {
$this->entityDeletions[$oid] = $entity;
$this->entityStates[$oid] = self::STATE_REMOVED;
}
}
And when you do $entity->getAnswer()->getVotes(), it does the following:
Load all votes from database
For every vote, checks if it is in identity map, use old one
If it is not in identity map, create new object
Try to call $entity->getAnswer()->getVotes() before you delete entity. If the problem disappears, then I am right. Of cause, I would not suggest this hack as a solution, just to make sure we understand what is going on under the hood.
UPD instead of $entity->getAnswer()->getVotes() you should probably do foreach for all votes, because of lazy loading. If you just call $entity->getAnswer()->getVotes(), Doctrine probably wouldn't do anytning, and will load them only when you start to iterate through them.
From the doc:
If you call the EntityManager and ask for an entity with a specific ID twice, it will return the same instance
So calling twice findOneBy(['id' => 12]) should result in two exact same instances.
So it all depends on how both instances are retrieved by Doctrine.
In my opinion, the one you get in $arr is from a One-to-Many association on $votes in the Answer entity, which results in a separate query (maybe a id IN (12)) by the ORM.
Something you could try is to declare this association as EAGER (fetch="EAGER"), it may force the ORM to make a specific query and keep it in cache so that the second time you want to get it, the same instance is returned ?
Could you have a look at the logs and post them here ? It may indicates something interesting or at least relevant to investigate further.
Let's say I've two services:
get('my_first_service')
get('my_second_service')
I want to do something like this:
$this->get('my_first_service')->doSomething($this->get('my_second_service'));
This is some initialization code that must be active everywhere in the project. As far as I see I have a few options (both services originates from the vendor-dir (haven't writtem them myself)):
Add the above code (with some modifications) in app.php and/or app_dev.php
Add the above code to every controller action
Create a service with the services as arguments and add the above code in the constructor
The first option seems a little bit "unusual" as I don't link editing the app.php file for something link this. The second option is too inefficient as I need to do this for dozens of actions. The third action seems better but I still need to call this service from somewhere in order to execute the constructor right?
I have the feeling I'm missing a possibility... there must be a better place for this kind of initialization code right?
If you really want to do execute your code before EVERY action, you can listen for the event kernel.controller.
Check this example in the cookboook.
Your listener is a service so you can inject both of your services if needed.
That looks very similar to setter injection. You could add doSomething as a call, with my_second_service as a parameter. That way when you get the first_service, the method will be called automatically, with the correct parameter passed in, and saves you from an ugly hack.
I assume you are already defining the services in configuration.
Eg in YAML:
services:
my_second_service:
# ...
my_first_service:
class: MyFirstServiceClassName
calls:
- [ doSomething, [ #my_second_service ] ]
If doSomething is returning a different object that you need to use, then it sounds like a factory, which you can also configure. Read through this.
I'm not having any luck using merge(). I'm doing almost exactly what is documented:
/* #var $detachedDocument MyDocumentClass */
$detachedDocument = unserialize($serializedDocument);
$document = $dm->merge($detachedDocument);
$document->setLastUpdated(new \MongoDate());
$dm->persist($document);
but the change never sticks. I have to do this instead:
$dm->createQueryBuilder('MyDocumentClass')
->findAndUpdate()
->field('lastUpdated')->set(new \MongoDate())
->getQuery()
->execute();
merge() seems pretty straightforward, so I'm confused why it doesn't work like I think it should.
In your first code example, merge() followed by persist() is redundant, and you omitted a flush(), which is the only operation that would actually write to the database (unless you execute a query manually, as you did in the second example). If you walk through the code in UnitOfWork::doMerge(), you'll see that it's going to either persist the object (if it has no ID) or fetch the document by its ID. The end result is that merge() returns a managed document. Persist ensures that the document will be managed after it is called (it returns nothing itself). If you poke in UnitOfWork::doPersist(), you'll see that passing a managed object to the method is effectively a NOOP.
Try replacing persist() with flush(). Note that you can flush a single document if necessary, but $dm->flush() processes all managed objects by default.
If that still doesn't help, I'd confirm that the lastUpdated field is properly mapped in ODM. You can inspect the output of $dm->getClassMetadata('MyDocumentClass') to confirm. If it isn't a mapped field, UnitOfWork will detect no changes in the document and there will be nothing to flush.
As an aside: in the second code example, you're executing findAndUpdate() without any search criteria (only the set() is specified). Typically, you'd pair the modification with something like equals() (probably the ID in your case) to ensure that a single document is atomically modified and returned.
I have doctrine's softdelete behavior attached to all of my models. Is there a way I can hard delete a particular record?
In cakephp I remember detaching the behavior... deleting the record and then re attaching the behavior.
Is there something similar in symfony/doctrine ? If so then how do I detach a behavior?
Cheers
umm .. the SoftDelete behavior includes a much nicer way of doing this ... just call
$record->hardDelete();
Think I'd go for Zed's way, but for completeness:
The Event listener method for delete (and select) for the soft delete behaviour contains:
if ( ! $query->contains($field)) {
// do the magic stuff to covert the query to respect softdelete
}
This means that if you explicitly mention the field in the query, it won't apply the transformation to the query.
So, if you do:
$q = Doctrine_Query::create()
->delete('Table t')
->where('t.id = ? AND t.deleted != 2 ', 1);
it won't apply the soft delete stuff and will actually delete the record. Note that you can do anything with t.deleted, I've just done something that will always be true. The alias ('t.') is important too for it to work.
This trick works for selects too, which is where I've normally used it before.
As I say though, I think its nicer to do:
$old_dqlc = Doctrine_Manager::getInstance()->getAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS);
Doctrine_Manager::getInstance()->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, false);
$record->delete();
Doctrine_Manager::getInstance()->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, $old_dqlc);
In particular, you can still use the delete() method rather than having to manually create the query. The one plus for the query method is that if you have other behaviours attached to the record, they will still be respected.
$object->getListener()->setOption('disabled',true);
This will disable all record listeners for this object.
Try calling this, it should disable the behavior handling.
$manager->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, false);
As a dirty way you can generate an SQL query that deletes the entry from the table.
link text i would think that this function and setting the use dql callbacks to false just like on the manager should do the trick :).
Wanted to agree with Joshua Coady that the best way would be to use
$record->hardDelete()
However, I also wanted to add here since it's one of the first results on google for detaching the behavior in doctrine that the easiest way to detach the behavior for "selects" is simply to include "deleted_at" (or whatever you have named your field as in the query. The listener looks to see if it is included and if so does not filter deleted records out.
Doctrine_Core::getTable('Record')->createQuery()->select('id, etc1, etc2')->addSelect('deleted_at')->execute();
will return deleted records.