问题
I'm creating a custom PUT
operation.
Not using the built-in operations because the affected property is not included in the default normalization for this entity, and also because this operation has a few side-effects that go beyond this entity/model.
Additionally, the update operation should only succeed if the current state of the entity on the DB matches certain expectations.
E.g. for the entity
class Lead {
/**
* @ORM\Column(type="integer", nullable=true)
* @Groups({"lead", "leadReject" })
**/
private $rejectionReason
public function isRejected() {
return $this->rejectionReason !== null;
}
I'm creating the custom operation PUT /lead/{id}/reject
.
class LeadReject {
public function __invoke( Lead $data ): Lead {
// if lead is rejected, we need not only to update its status
// but create an entry in a LeadLog table, and update the state
// for the Lead owner
}
}
Problem is, by the time we get to __invoke()
the $data
I get has already merged the input from the user with the values from the database. Thus, any call to $data->isRejected()
returns true, even if it's still null
on the database.
And before I persist the object and I perform all the other operations, I need to check that Lead
has not been rejected previously.
How can I do this? Can it be done at the operation controller level? I guess I could inject the EntityManager and retrieve the object again, but seems wasteful considering at this point the object has already been retrieved.
回答1:
You can add a Doctrine Listener:
<?php
namespace App\Listener;
use App\Entity\MyEntity;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
class EntityPreUpdateListener
{
/**
* @param MyEntity $entity
* @param LifecycleEventArgs $args
*/
public function preUpdate($entity, LifecycleEventArgs $args)
{
$entityManager = $args->getObjectManager();
// do whatever you need
// you can persist your entity to the database if you need
$entityManager->flush();
// detach your entity, so it won't be updated
$entityManager->detach($entity);
}
}
And just add this line in your entity Doc Block
@ORM\EntityListeners({"App\Listener\EntityPreUpdateListener"})
You can see more information about the doctrine events here:
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/events.html
回答2:
Switch off automatic loading of the object so that retrieving the object from the Controller is not using any extra resources:
/*
* @ApiResource(
* itemOperations={
* "put"={
* "read"=false
* }
* )
*/
class Lead {
回答3:
Let the Entitiy remember the change transiently:
class Lead {
/**
* @ORM\Column(type="integer", nullable=true)
* @Groups({"lead", "leadReject" })
**/
private $rejectionReason
/** @var int|null Not mapped to the db */
private $previousRejectionReason;
public function setRejectionReason($value)
{
$this->previousRejectionReason = $this->rejectionReason;
$this->rejectionReason = $value;
}
public function isRejected() {
return $this->rejectionReason !== null;
}
public function wasAlreadyRejected() {
return $this->previousRejectionReason !== null;
}
}
(Similar result may be obtainted by checking if the property has changed in Doctrines $entityManager::getUnitOfWork->getEntityChangeSet($lead), but i guess that's not what you're looking for...)
来源:https://stackoverflow.com/questions/56733961/how-to-create-custom-update-operation-that-can-be-rejected-depending-on-the-enti