Cancel the saving or the update in prePersist - symfony

I would like to cancel the operation if persist after-has test.and i dont know how i tried the redirection goal in vain.
/**
* #param mixed $object
*/
public function prePersist($object)
{
if (is_null($object->getFile())) {
}
}
any help ?

It's probably a bit late for you. Since I was looking for the same thing though, here's my solution.
It seems not to be possible to do any validation in Sonatas prePersist method.
For a simple NotNull validation like you are doing it I'd add a simple validation constraint to the entity or if the field is not mapped to the form element itself.
If you want to do some extra validation during Sonatas save process, override the validate method. This one is called before the prePersis/preUpdate methods and it allows you to add validation messages.
use Sonata\CoreBundle\Validator\ErrorElement;
public function validate(ErrorElement $errorElement, $object)
{
$errorElement->with('file')->addViolation('Hey, this is a validation message');
}

I'm a late too but if as me you're facing the same issue. Here is my work around with Doctrine
use ACME\Exceptions\CustomException;
public function prePersist($object)
{
// Do some stuff with your entity $object
if (//Something) {
$this->getRequest()->getSession()->getFlashBag()->add(
'error',
'Something wrong happens');
throw new CustomException('Something wrong happens');
}
}
/** Overwrite create methode to catch custom error */
public function create($object)
{
try {
$this->em->beginTransaction();
$res = parent::create($object);
$this->em->getConnection()->commit();
return $res;
} catch (CustomException $e) {
$this->em->getConnection()->rollBack();
return null;
}
}
This way you can validate or do anything you want in prePersist (and/or postPersist) and cancel creation throwing an exception.
Unfortunately it still display success flash bag but persist anything in your database.

Related

Doctrine: How to suppress further lifecycle events when updating entities inside entity's listener?

I am working on an app in Symfony 3 using Doctrine.
Whenever I persist or update a specific entity, I need to query for a subset of these entities (same class) and update a property for each.
Currently I have an entity listener class with postPersist() and postUpdate() handlers which call a function performing the aforementioned behaviour.
However, I am finding that when persisting changes for these other entities, it is triggering events leading to additional lifecycle callbacks to postUpdate() on the same listener (as somewhat expected, but undesired).
I wanted to ask if anyone would know of a way to suppress these additional events or possibly have any ideas on alternative solutions to this scenario.
Update: Providing sample code to help illustrate the issue
// Subscribed Entity Listener for Foo
class FooListener
{
...
public function postPersist(Foo $foo, LifecycleEventArgs $event)
{
...
$this->updateFlaggedFoos($event);
}
public function postUpdate(Foo $foo, LifecycleEventArgs $event)
{
...
$this->updateFlaggedFoos($event);
}
private function updateFlaggedFoos(LifecycleEventArgs $event)
{
$user = $this->tokenStorage->getToken()->getUser();
$entityManager = $event->getEntityManager();
$userFoos = $entityManager->getRepository('MyApp:Foo')->getUserFoos($user);
foreach($userFoos as $foo) {
if ($this->determineIfFlagValueNeedsToChange($foo)) {
$foo->setFlagValue( ! $foo->isFlagged());
$entityManager->persist($foo); // Causes postUpdate() to be called again.
}
}
}
...
}

Doctrine Mongodb getOriginalDocumentData on embedded document

In my symfony application i've got my event_subscriber
CoreBundle\EventSubscriber\CloseIssueSubscriber:
tags:
- { name: doctrine_mongodb.odm.event_subscriber, connection: default }
My subscriber simply listen to postPersist and postUpdate events:
public function getSubscribedEvents()
{
return array(
'postPersist',
'postUpdate',
);
}
public function postPersist(LifecycleEventArgs $args)
{
$this->index($args);
}
public function postUpdate(LifecycleEventArgs $args)
{
$this->index($args);
}
In my index function what I need to do is to get if certain field has changed in particular the issue.status field.
public function index(LifecycleEventArgs $args)
{
$document = $args->getEntity();
$originalData = $uow->getOriginalDocumentData($document);
$originalStatus = $originalData && !empty($originalData['issue']) ? $originalData['issue']->getStatus() : null;
var_dump($originalStatus);
var_dump($document->getIssue()->getStatus());die;
}
In my test what I do is change the issue.status field so I expect to receive 2 different values from the var_dump but instead I got the last status from both.
My document is simply something like that:
class Payload
{
/**
* #ODM\Id
*/
private $id;
/**
* #ODM\EmbedOne(targetDocument="CoreBundle\Document\Issue\Issue")
* #Type("CoreBundle\Document\Issue\Issue")
*/
protected $issue;
}
In the embedded issue document status is simply a text field.
I've also try to use the changeset:
$changeset = $uow->getDocumentChangeSet($document);
foreach ($changeset as $fieldName => $change) {
list($old, $new) = $change;
}
var_dump($old->getStatus());
var_dump($new->getStatus());
Also this two var_dumps returns the same status.
By the time of postUpdate changes in the document are already done so originalDocumentData is adjusted and ready for new calculations. Instead you should hook into preUpdate event and use $uow->getDocumentChangeSet($document); there.
I guess that you want to run index once changes have been written to the database, so on preUpdate you can accumulate changes in the listener and additionally hook into postFlush event to re-index documents.
I found the solution to my problem.
What malarzm said in the other answer is correct but not the solution to my problem.
I suppose that I get only one postUpdate/preUpdate postPersist/prePersist just for the Document (Payload) instead I notice that it get called event for the embedded document (don't know why doctrine consider it a persist).
So the main problem is that I'm waiting for a Payload object instead I have to wait for a Issue object.
In other hands I was unable to use the getOriginalDocumentData work right even in the postUpdate and in the preUpdate so I have to use the getDocumentChangeSet().

PHP/Symfony - Parsing object properties from Request

We're building a REST API in Symfony and in many Controllers we're repeating the same code for parsing and settings properties of objects/entities such as this:
$title = $request->request->get('title');
if (isset($title)) {
$titleObj = $solution->getTitle();
$titleObj->setTranslation($language, $title);
$solution->setTitle($titleObj);
}
I'm aware that Symfony forms provide this functionality, however, we've decided in the company that we want to move away from Symfony forms and want to use something simplier and more customisable instead.
Could anybody please provide any ideas or examples of libraries that might achieve property parsing and settings to an object/entity? Thank you!
It seems like a good use case for ParamConverter. Basically it allows you, by using #ParamConverter annotation to convert params which are coming into your controller into anything you want, so you might just create ParamConverter with code which is repeated in many controllers and have it in one place. Then, when using ParamConverter your controller will receive your entity/object as a parameter.
class ExampleParamConverter implements ParamConverterInterface
{
public function apply(Request $request, ParamConverter $configuration)
{
//put any code you want here
$title = $request->request->get('title');
if (isset($title)) {
$titleObj = $solution->getTitle();
$titleObj->setTranslation($language, $title);
$solution->setTitle($titleObj);
}
//now you are setting object which will be injected into controller action
$request->attributes->set($configuration->getName(), $solution);
return true;
}
public function supports(ParamConverter $configuration)
{
return true;
}
}
And in controller:
/**
* #ParamConverter("exampleParamConverter", converter="your_converter")
*/
public function action(Entity $entity)
{
//you have your object available
}

preUpdate() siblings manage into tree: how to break ->persist() recursion?

Let's say I've got an entity like this
class FooEntity
{
$id;
//foreign key with FooEntity itself
$parent_id;
//if no parent level =1, if have a parent without parent itself = 2 and so on...
$level;
//sorting index is relative to level
$sorting_index
}
Now I would like on delete and on edit to change level and sorting_index of this entity.
So I've decided to take advantage of Doctrine2 EntityListeners and I've done something similar to
class FooListener
{
public function preUpdate(Foo $entity, LifecycleEventArgs $args)
{
$em = $args->getEntityManager();
$this->handleEntityOrdering($entity, $em);
}
public function preRemove(Foo $entity, LifecycleEventArgs $args)
{
$level = $entity->getLevel();
$cur_sorting_index = $entity->getSortingIndex();
$em = $args->getEntityManager();
$this->handleSiblingOrdering($level, $cur_sorting_index, $em);
}
private function handleEntityOrdering($entity, $em)
{
error_log('entity to_update_category stop flag: '.$entity->getStopEventPropagationStatus());
error_log('entity splobj: '.spl_object_hash($entity));
//code to calculate new sorting_index and level for this entity (omitted)
$this->handleSiblingOrdering($old_level, $old_sorting_index, $em);
}
}
private function handleSiblingOrdering($level, $cur_sorting_index, $em)
{
$to_update_foos = //retrieve from db all siblings that needs an update
//some code to update sibling ordering (omitted)
foreach ($to_update_foos as $to_update_foo)
{
$em->persist($to_update_foo);
}
$em->flush();
}
}
The problem here is pretty clear: if I persist a Foo entity, preUpdate() (into handleSiblingOrdering function) trigger is raised and this cause an infinite loop.
My first idea was to insert a special variable inside my entity to prevent this loop: when I started a sibling update, that variable is setted and before executing the update code is checked. This works like a charm for preRemove() but not for preUpdate().
If you notice I'm logging spl_obj_hash to understand this behaviour. With a big surprise I can see that obj passed to preUpdate() after a preRemove() is the same (so setting a "status flag" is a fine) but the object passed to preUpdate() after a preUpdate() isn't the same.
So ...
First question
Someone could point me in the right direction to manage this situation?
Second question
Why doctrine needs to generate different objects if two similar events are raised?
I've founded a workaround
Best approach to this problem seem to create a custom EventSubscriber with a custom Event dispatched programmatically into controller update action.
That way I can "break" the loop and having a working code.
Just to make this answer complete I will report some snippet of code just to clarify che concept
Create custom events for your bundle
//src/path/to/your/bundle/YourBundleNameEvents.php
final class YourBundleNameEvents
{
const FOO_EVENT_UPDATE = 'bundle_name.foo.update';
}
this is a special class that will not do anything but provide some custom events for our bundle
Create a custom event for foo update
//src/path/to/your/bundle/Event/FooUpdateEvent
class FooUpdateEvent
{
//this is the class that will be dispatched so add properties useful for your own logic. In my example two properties could be $level and $sorting_index. This values are setted BEFORE dispatch the event
}
Create a custom event subscriber
//src/path/to/your/bundle/EventListener/FooSubscriber
class FooSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(YourBundleNameEvents::FooUpdate => 'handleSiblingsOrdering');
}
public function handleSiblingsOrdering(FooUpdateEvent $event)
{
//I can retrieve there, from $event, all data I setted into event itself. Now I can run all my own logic code to re-order siblings
}
}
Register your Subscriber as a service
//app/config/config.yml
services:
your_bundlename.foo_listener:
class: Your\Bundle\Name\EventListener\FooListener
tags:
- { name: kernel.event_subscriber }
Create and dispatch events into controller
//src/path/to/your/bundle/Controller/FooController
class FooController extends Controller
{
public function updateAction()
{
//some code here
$dispatcher = $this->get('event_dispatcher');
$foo_event = new FooEvent();
$foo_event->setLevel($level); //just an example
$foo_event->setOrderingIndex($ordering_index); //just an examle
$dispatcher->dispatch(YourBundleNameEvents::FooUpdate, $foo_event);
}
}
Alternative solution
Of course above solution is the best one but, if you have a property mapped into db that could be used as a flag, you could access it directly from LifecycleEventArgs of preUpdate() event by calling
$event->getNewValue('flag_name'); //$event is an object of LifecycleEventArgs type
By using that flag we could check for changes and stop the propagation
You are doing wrong approach by calling $em->flush() inside preUpdate, I even can say restricted by Doctrine action: http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#reference-events-implementing-listeners
9.6.6. preUpdate
PreUpdate is the most restrictive to use event, since it is called
right before an update statement is called for an entity inside the
EntityManager#flush() method.
Changes to associations of the updated entity are never allowed in
this event, since Doctrine cannot guarantee to correctly handle
referential integrity at this point of the flush operation.

Looking for #transaction for controller actions in symfony2

To make sure my actions to proper transaction handling, I find myself repeating this code in my controllers over and over again:
/**
* #Route("/complete", name = "authentication_complete")
*/
public function completeAction(Request $request)
{
$result = null;
try {
$this->getManager()->beginTransaction();
$result = $this->doCompleteAction($request);
$this->getManager()->flush();
$this->getManager()->commit();
} catch (\Exception $e) {
// #codeCoverageIgnoreStart
$this->getManager()->rollback();
throw $e;
// #codeCoverageIgnoreEnd
}
return $result;
}
public function doCompleteAction(Request $request)
{
// do whatever you action is suposed to do
return $response;
}
I'd like to have something like #ManageTransaction. This would go into the comment of the action and saving me a lot of doublicated code. In a perfect world this would also handle controller forwards in a clever way.
If you know Java EE, this would be something like container managed transactions.
Is there a bundle (or an other nice solution) for this?
Since I did not find a solution I decided to create one.
The PluessDoctrineTrxBundle does exactly what I was looking for. Yous add an annotation to your action and all doctrine operations are covered by a transation.
You can use JMSAopBundle to implement a wrapper with AOP in Symfony2. The steps are detailed in this link:
http://jmsyst.com/bundles/JMSAopBundle#transaction-management
In the example, author gets an annotation named #Transactional like nameshake Spring's annotation:
http://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html#transaction-declarative-annotations

Resources