JMSSerializerBundle - continue deserialization in the custom handler - php

I have a custom handler that performs deserialization of my object:
public function deserialize( JsonDeserializationVisitor $visitor,
$data,
array $type,
DeserializationContext $context)
I don't want to take all deserialization job on my own, I only want to do some of that. For example, I have an Album (id, name, description, photos) and I want to deserialize "description" by myself, and left all other job to the bundle.
if (!empty($data['id']))
$album = $albumManager->createWithId($data['id']);
else
$album = $albumManager->create();
$album->setDescription($albumDescriptionParser->parse($data['description']));
// and now I want to delegate other deserialization job to JMSSerializer
// ....
return $album;
I know this is done using context, visitor and navigator, but I cant figure how

Okay, I found the way to implement that. It seems it cannot be easy handled in the deserialization handler.
Instead, I used my custom ObjectConstructorInterface interface implementation and serializer.post_deserialize event.
Creation process can be handled in the custom ObjectConstructorInterface implementation and all "extra settings" can be set after deserialization actually done - when serializer.post_deserialize event occur.

Related

How to get the created object in controller when implementing CQRS

I am evaluating the CQRS pattern and wonder what would be the best way to obtain an Entity created by a command in the same action so I can render it in the view.
The two options I can think of are.
1) Create an id in the controller and send it with the command to fetch the entity by finding it by id.
2) Create an instance of the entity and send it with the command so I have a reference to it after it's populated
Example code
public function createEntityAction(array $data) {
$eventDispatcher = $this->get('event_dispatcher');
$eventDispatcher->dispatch(
CreateEntityHandler::name, // Handler
new Entity($data) // Command
);
// Placeholder //
$entity = get-the-created-entity
// //
return $this->view($entity, Response::HTTP_OK);
}
Second option is not really an option. "Entity creation", which is in fact is a business operation, is a command handling.
Generally speaking, the one who sends a command, whose handler creates an entity, should send the entity id with it. In what way the identity is generated is just an implementation concern.
Usually, command handlers either do what they suppose to do and return nothing (or ACK) or throw (or NAK).

Add data return in all actions with #Template annotation

I need to return default data in all actions in my Symfony project.
For example search form in side bar, viewers counter etc...
So i need to return some default data in all actions
return array(
'form' => $form->createView(),
'short_search' => $shortSearch->createView(),
);
I found Add data to return of all actions in a Symfony controller solution, but it fails when I'm using #Template annotation.
Of course i can call render function from twig, but it seems like it's not fast and good idea.
What component I should override in this case???
The Controllers section of the Symfony Best Practices document advises against using the #Template() annotation, so the easy fix to your problem would be to simply not use #Template().
The reason overriding the base Controller's render method doesn't work is because you're not actually calling it, and neither is the framework. Instead, the SensioFrameworkExtraBundle #Template annotation works by installing an event listener for KernelEvents::VIEW (kernel.view) and (after having used a different event to guess the template name, if necessary), directly uses the templating service to render the response.
In the generic case, what you can do instead is install an event listener on kernel.view with a higher priority, and using the GetResponseForControllerResultEvent event provided to add in your parameters. This event listener might look something like
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$parameters = $event->getControllerResult();
//modify parameters
$event->setControllerResult($parameters);
}
with whatever services necessary to get the additional parameters passed in via dependency injection. You may also want to look at the implementation for #Template's TemplateListener for reference.
The Symfony Cookbook has more information on how to set up event listeners.
In your specific case, you're probably going to be generating your $form and $shortSearch entirely from within that event handler, so at the least, your event handler is going to need at least the form service injected.
In my opinion, this is all largely more trouble than it's worth, and it would be better to just remove the #Template annotation instead. (As a bonus, you'll get a minor performance boost, especially if you disable the annotations entirely, because you won't have the overhead of calling those event listeners on every request.)

Symfony2 Service Response

i was trying to setup a general service which handles common function i use very often everywhere in my project. For example if a user wants to purchase something for virtual currency there would be a function which checks, if the user has enough virtual currency in his account.
If the user doesnt have enough virtual currency I want this function to make a JSOn Response, but of cource, only controllers are allowed to response. But this means i have to check in every action I use this function, whether the purchase is valid or not.
Here is the function call in my Controller:
$purchaes= $this->get('global_functions')->payVirtualCurrency($user_id, $currency_amount);
if($change instanceof JsonResponse){
return $change;
}
And the function:
public function payVirtualCurrency($user_id, $currency_amount){
$user = $this->dm->getRepository('LoginBundle:User')->findOneById($user_id);
if($user->getVirtualCurrency() < $currency_amount){
return new JsonResponse(array('error' => $this->trans->trans('Insufficient amount of virtual Currency')));
}
return true;
}
Is there a better way to do this? I really want to avoid doing the same thing in the controller over and over again.
Thanks in advance!
Two options come to my mind, both are quite elegant solutions but both require little work:
1. Create custom exception listener
Create custom exception, let's call it InsufficientMoneyException. Then, your sevice can be as it is, but instead of returning response it throws your custom exception (in case user does not have enough money). Then, you create custom exception listener which listenes to InsufficientMoneyException custom exception and returns your desired JsonResponse.
2. Create custom annotation
You can create custom annotation and flag a controller action with this annotation. It would look something like this
/**
* #MinimumMoneyRequired("50")
*/
public function buyAction()
{
(...)
}
This option is really nice and decoupled but it require quite a lot of configuration. This is nice blog post with detailed description how to create custom annotations

Yii: Multiple events best practices

Events in Yii looks great, but several questions still wakes me at night:
If I raise an event and create several PHP event handler classes in chain, can I pass different data between them (like return value)?
Is the event designed for this goal? As far as I see, the event seems to be one-direction way of notification and passing data back is not a common practice, is that correct?
Lets say:
I have 3 handlers : Handler1, Handler2, Handler3 executed in this order. Each Handler concatenates some string data.
Can I pass the concatenated sting between handlers and are the handlers assumed to do this?
In a event chain, is throwing an exception in an event handler a good practice?
You're correct that the event system was primarily designed (or at least: documented) as a read-only notification system. However, it is possible to do what you want by creating your own subclassed Event that defines a public property for the data you want to pass around.
For example, start with a custom event class:
class MyEvent extends \yii\base\Event
{
public $data;
}
Trigger this event:
$event = new MyEvent([
'data' => 'hello world'
]);
$this->trigger('myEvent', $event);
echo "After passing through the entire event chain, data is now: " . $event->data;
And add behaviors (or handlers) that listen to it:
public function onMyEvent($event)
{
$event->data .= ', goodbye world';
}
If all went well, this should end up echo'ing hello world, goodbye world

Return data back to dispatcher from event observer in Magento

I have an extension for product registration that dispatches an event after the registration is saved. Another extension uses that event to generate a coupon for a virtual product if it is related to the registered product.
I need to get back data on the generated coupon to send to the user in an email along with the details of their product registration.
Is there a way to return data from the observer back to where the event is dispatched?
There is a trick available in Magento for your purpose. Since you can pass event data to the observers, like product or category model, it also possible to create a container from which you can get this data.
For instance such actions can be performed in dispatcher:
$couponContainer = new Varien_Object();
Mage::dispatchEvent('event_name', array('coupon_container' => $couponContainer));
if ($couponContainer->getCode()) {
// If some data was set by observer...
}
And an observer method can look like the following:
public function observerName(Varien_Event_Observer $observer)
{
$couponContainer = $observer->getEvent()->getCouponContainer();
$couponContainer->setCode('some_coupon_code');
}
Enjoy and have fun!
No, there's nothing built in to the system for doing this. The Magento convention is to create a stdClass or Varien_Object transport object.
Take a look at the block event code
#File: app/code/core/Mage/Core/Block/Abstract.php
...
if (self::$_transportObject === null)
{
self::$_transportObject = new Varien_Object;
}
self::$_transportObject->setHtml($html);
Mage::dispatchEvent('core_block_abstract_to_html_after',
array('block' => $this, 'transport' => self::$_transportObject));
$html = self::$_transportObject->getHtml();
...
Since self::$_transportObject is an object, and PHP objects behave in a reference like manner, any changes made to the transport object in an observer will be maintained. So, in the above example, if an observer developer said
$html = $observer->getTransport()-setHtml('<p>New Block HTML');
Back up in the system block code self::$_transportObject would contain the new HTML. Keep in mind that multiple observers will have a chance to change this value, and the order observers fire in Magento will be different for each configured system.
A second approach you could take is to use Magento's registry pattern. Register a variable before the dispatchEvent

Categories