Symfony2 Service Response - php

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

Related

Updating data after backend action TYPO3 11.5

Working on Typo3 11.5.13
I'm trying to update some data on my pages table after a be_user changed something.
I read something about setting hooks for that purpose but I can't seem to find a good explanation as to how hooks actually function within Typo3 and how to configure one, especially for my purpose.
As far as I can see, this problem I have should be quickly solved but the complexity of the typo3 doc is hindering my progress again. Maybe you can explain how I can accomplish my goal.
Simply put: A backend user is supposed to choose a date in a datepicker and some dateinterval in the settings of a page. After saving(Or even after picking both values) I would like to update the "Next time happening" field the user can see but not change to be updated to the given date plus the dateinterval chosen.
If you have some sort of idea please share it with me.
Generally hooks are not that good documented. Modern Events are easier to find and better commented. However, if I get your use case right, using DataHandler Hooks are they way to go. That mean, every place which are using the DataHandler to save data are then covered. The backend form engine are using DataHandler.
Basic information about hooks in the core documentation:
https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Events/Hooks/Index.html
How to identify or find hooks, events, signalslots (depending on TYPO3 version):
https://usetypo3.com/signals-and-hooks-in-typo3.html
https://daniel-siepmann.de/posts/migrated/how-to-find-hooks-in-typo3.html
Introduction or "DataHandler" explained:
https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Typo3CoreEngine/Database/Index.html
Basicly, DataHandler has two main kind of processings:
Data manipulations -> process_datamap()
Actions (move,delete, copy, translate) -> process_cmdmap()
For DataHandler, you register a class only for datamap and/or processmap, not for a concrete hook itself.
// <your-ext>/Classes/Hooks/MyDataHandlerHooks.php
namespace <Vendor>\<YourExt>\Hooks;
class MyDataHandlerHooks {}
// <your-ext>/ext_localconf.php
// -> for cmdmap hooks
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['yourextname']
= \Vendor\YourExt\Hooks\MyDataHandlerHooks::class;
// -> for datamap hooks
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['yourextname']
= \Vendor\YourExt\Hooks\MyDataHandlerHooks::class;
You need to register your class only for these kind of hooks you want to consume. And you do not have to implement all hooks.
Hooks can be looked up in \TYPO3\CMS\Core\DataHandling\DataHandler (as hooks are normally searched.
Next step would be to find the proper hook for your use case, and simply add that hook method to your class. Naming the hooks are not chooseable for DataHandler hooks.
TYPO3 Core tests contains a test fixture class for DataHandler hooks - which is not complete, but contains at least the most common ones (along with the needed method signatures) since 8.x:
https://github.com/TYPO3/typo3/blob/main/typo3/sysext/core/Tests/Functional/DataHandling/DataHandler/Fixtures/HookFixture.php
So you may have to look into the version for your core version to get a feeling how the signature should look for that core version.
Generally I would guess one of these two:
processDatamap_postProcessFieldArray(): Hook with prepared field array, and you can simple add your new stuff to write or update it and it will be saved. Good if you need to change the record directly.
processDatamap_afterDatabaseOperations(): Hook after record has been changed. This is a good startpoint if you need to do other things after saving a record.
Given your usecase, I would tip on the first one, so here a example implementation (in the class and registering as datamap hook as explained above):
// <your-ext>/Classes/Hooks/MyDataHandlerHooks.php
namespace <Vendor>\<YourExt>\Hooks;
class MyDataHandlerHooks {
/**
* #param string|int $id
*/
public function processDatamap_postProcessFieldArray(
string $status, // Status of the current operation, 'new' or 'update'
string $table, // The table currently processing data for
$id, // The record uid currently processing data for,
// [integer] or [string] (like 'NEW...')
array &$fieldArray, // The field array of a record, cleaned to only
// 'to-be-changed' values. Needs to be &$fieldArray to be considered reference.
DataHandler $dataHandler
): void
{
// $fieldArray may be stripped down to only the real fields which
// needs to be updated, mainly for $status === 'update'. So if you
// need to be sure to have correct data you may have to retrieve
// the record to get the current value, if not provided as with new
// value.
if ($table === 'be_users'
&& $status === 'update'
&& array_key_exists('target_field_name', $fieldArray)
) {
$valueToReactTo = $fieldArray['target_field_name'];
if ($valueToReactTo === 'some-check-value') {
// needs not to be there
$fieldArray['update-field'] = 'my-custom-value';
}
}
}
}

Controller check requirements to process action

I'm trying to figure out if there is possibility to check requirements before processing controller action. In Nette there are methods like checkRequirements, onStartup, beforeRender where I can check this.
I have api resource album/{albumId}/song/ and I would like to check if album with given id exists every time any action on my SongController is processed and return 404 status code if not.
So far I have found this article in Symfony documentation where I found there are no methods like preExecute and postExecute. However I guess there is bundle or something like that to add those methods. I think it does not make sense to create new class to use it only in one controller.
Are there any other options to do it?
ParamConverter does that. It looks for a entity using the id supplied from the route and throws an exception, returning a 404 if it doesn't find anything.
// paramConverter requires that you type-hint a class, which is a best practice anyway :)
public function getArtist(\Appbundle\Entity\Song $song)
{
//...
}

How to get data back from a command bus?

I'm fairly new to domain driven design concepts and I've run into a problem with returning proper responses in an API while using a command bus with commands and command handlers for the domain logic.
Let's say we’re building an application with a domain driven design approach. We have a back end and front end portion. The back end has all of our domain logic with an exposed API. The front end uses the API to make requests to the application.
We're building our domain logic with commands and command handlers mapped to a command bus. Under our Domain directory we have a command for creating a post resource called CreatePostCommand. It's mapped to its handler CreatePostCommandHandler via the command bus.
final class CreatePostCommand
{
private $title;
private $content;
public function __construct(string $title, string $content)
{
$this->title = $title;
$this->content= $content;
}
public function getTitle() : string
{
return $this->title;
}
public function getContent() : string
{
return $this->content;
}
}
final class CreatePostCommandHandler
{
private $postRepository;
public function __construct(PostRepository $postRepository)
{
$this->postRepository = $postRepository;
}
public function handle(Command $command)
{
$post = new Post($command->getTitle(), $command->getContent());
$this->postRepository->save($post);
}
}
In our API we have an endpoint for creating a post. This is routed the createPost method in a PostController under our Application directory.
final class PostController
{
private $commandBus;
public function __construct(CommandBus $commandBus)
{
$this->commandBus = $commandBus;
}
public function createPost($req, $resp)
{
$command = new CreatePostCommand($command->getTitle(), $command->getContent());
$this->commandBus->handle($command);
// How do we get the data of our newly created post to the response here?
return $resp;
}
}
Now in our createPost method we want to return the data of our newly created post in our response object so our front end application can know about the newly created resource. This is troublesome since we know that by definition the command bus should not return any data. So now we're stuck in a confusing position where we don't know how to add our new post to the response object.
I'm not sure how to proceed with this problem from here, several questions come to mind:
Is there an elegant way to return the post's data in the response?
Am I incorrectly implementing the Command/CommandHandler/CommandBus pattern?
Is this simply just the wrong use case for the Command/CommandHandler/CommandBus pattern?
First, notice that if we wire the controller directly to the command handler, we face a similar problem:
public function createPost($req, $resp)
{
$command = new CreatePostCommand($command->getTitle(), $command->getContent());
$this->createPostCommandHandler->handle($command);
// How do we get the data of our newly created post to the response here?
return $resp;
}
The bus is introducing a layer of indirection, allowing you to decouple the controller from the event handler, but the problem you are running into is more fundamental.
I'm not sure how to proceed with this problem from here
TL;DR - tell the domain what identifiers to use, rather than asking the domain what identifier was used.
public function createPost($req, $resp)
{
// TADA
$command = new CreatePostCommand($req->getPostId()
, $command->getTitle(), $command->getContent());
$this->createPostCommandHandler->handle($command);
// happy path: redirect the client to the correct url
$this->redirectTo($resp, $postId)
}
In short, the client, rather than the domain model or the persistence layer, owns the responsibility of generating the id of the new entity. The application component can read the identifier in the command itself, and use that to coordinate the next state transition.
The application, in this implementation, is simply translating the message from the DTO representation to the domain representation.
An alternative implementation uses the command identifier, and derives from that command the identities that will be used
$command = new CreatePostCommand(
$this->createPostId($req->getMessageId())
, $command->getTitle(), $command->getContent());
Named UUIDs are a common choice in the latter case; they are deterministic, and have small collision probabilities.
Now, that answer is something of a cheat -- we've really only demonstrated that we don't need a result from the command handler in this case.
In general, we would prefer to have one; Post/Redirect/Get is a good idiom to use for updating the domain model, but when the client gets the resource, we want to make sure they are getting a version that includes the edits they just made.
If your reads and writes are using the same book of record, this isn't a problem -- whatever you read is always the most recent version available.
However, cqrs is a common architectural pattern in domain driven design, in which case the write model (handling the post) will redirect to the read model -- which is usually publishing stale data. So you may want to include a minimum version in the get request, so that the handler knows to refresh its stale cache.
Is there an elegant way to return the post's data in the response?
There's an example in the code sample you provided with your question:
public function createPost($req, $resp)
Think about it: $req is a representation of the http request message, which is roughly analogous to your command, and $resp is essentially a handle to a data structure that you can write your result into.
In other words, pass a callback or a result handle with your command, and let the command handler fill in the details.
Of course, that depends on your bus supporting callbacks; not guaranteed.
Another possibility, which doesn't require changing the signature of your command handler, is to arrange that the controller subscribes to events published by the command handler. You coordinate a correlation id between the command and the event, and use that to pull up the result event that you need.
The specifics don't matter very much -- the event generated when processing the command could be written to a message bus, or copied into a mailbox, or....
I am using this approach and I am returning command results. However, this is a solution which works only if the command handlers are part of the same process. Basically, I'm using a mediator, the controller and the command handler get an instance of it (usually as a constructor dependency).
Pseudo code controller
var cmd= new MyCommand();
var listener=mediator.GetListener(cmd.Id);
bus.Send(cmd);
//wait until we get a result or timeout
var result=listener.Wait();
return result;
Pseudo code command handler function
var result= new CommandResult();
add some data here
mediator.Add(result,cmd.Id);
That's how you get immediate feedback. However, this shouldn't be used to implement a business process.
Btw, this has nothing to do with DDD, it's basically a message driven CQS approach which can be and it is used in a DDD app.

Controller as Service - How to pass and return values in an advanced case?

Using Symfony, I am displaying a table with some entries the user is able to select from. There is a little more complexity as this might include calling some further actions e. g. for filtering the table entries, sorting by different criteria, etc.
I have implemented the whole thing in an own bundle, let's say ChoiceTableBundle (with ChoiceTableController). Now I would like to be able to use this bundle from other bundles, sometimes with some more parametrization.
My desired workflow would then look like this:
User is currently working with Bundle OtherBundle and triggers chooseAction.
chooseAction forwards to ChoiceTableController (resp. its default entry action).
Within ChoiceTableBundle, the user is able to navigate, filter, sort, ... using the actions and routing supplied by this bundle.
When the user has made his choice, he triggers another action (like choiceFinishedAction) and the control flow returns to OtherBundle, handing over the results of the users choice.
Based on these results, OtherBundle can then continue working.
Additionally, OtherOtherBundle (and some more...) should also be able to use this workflow, possibly passing some configuration values to ChoiceTableBundle to make it behave a little different.
I have read about the "Controller as Service" pattern of Symfony 2 and IMHO it's the right approach here (if not, please tell me ;)). So I would make a service out of ChoiceTableController and use it from the other bundles. Anyway, with the workflow above in mind, I don't see a "good" way to achieve this:
How can I pass over configuration parameters to ChoiceTableBundle (resp. ChoiceTableController), if neccessary?
How can ChoiceTableBundle know from where it was called?
How can I return the results to this calling bundle?
Basic approaches could be to store the values in the session or to create an intermediate object being passed. Both do not seem particularly elegant to me. Can you please give me a shove in the right direction? Many thanks in advance!
The main question is if you really need to call your filtering / searching logic as a controller action. Do you really need to make a request?
I would say it could be also doable just by passing all the required data to a service you define.
This service you should create from the guts of your ChoiceTableBundleand let both you ChoiceTableBundle and your OtherBundle to use the extracted service.
service / library way
// register it in your service container
class FilteredDataProvider
{
/**
* #return customObjectInterface or scallar or whatever you like
*/
public function doFiltering($searchString, $order)
{
return $this->filterAndReturnData($searchString, $order)
}
}
...
class OtherBundleController extends Controller {
public function showStuffAction() {
$result = $this->container->get('filter_data_provider')
->doFiltering('text', 'ascending')
}
}
controller way
The whole thing can be accomplished with the same approach as lipp/imagine bundle uses.
Have a controller as service and call/send all the required information to that controller when you need some results, you can also send whole request.
class MyController extends Controller
{
public function indexAction()
{
// RedirectResponse object
$responeFromYourSearchFilterAction = $this->container
->get('my_search_filter_controller')
->filterSearchAction(
$this->request, // http request
'parameter1' // like search string
'parameterX' // like sorting direction
);
// do something with the response
// ..
}
}
A separate service class would be much more flexible. Also if you need other parameters or Request object you can always provide it.
Info how to declare controller as service is here:
http://symfony.com/doc/current/cookbook/controller/service.html
How liip uses it:
https://github.com/liip/LiipImagineBundle#using-the-controller-as-a-service

symfony admin generator form object

Hey guys, I've used the Symfony admin generator for a module.
Everything is working, but when the form for my model is instantiated, I need to pass in my own option.
I could do this myself by overriding the executeNew, executeCreate functions in myModuleActions.class.php (which extends myModuleAutoActions).
But I was hoping for a neater solution?
Perhaps overriding one of the configuration classes is the way to go. I basically need to add the current sf_user object ($this->getUser) as an "sf_user" option for the form, to avoid using sfContext in the myModuleForm.
Any ideas?
Welcome to Stack Overflow, jolly18.
I would just use sfContext. For example, in my app, I have a subform that creates a new Note object and assigns the user to it. In my form's configure() I have:
$new_note->setAuthor(sfContext::getInstance()->getUser()->getUsername());
I see the book calls this "The fastest but ugly way" because it makes "a big coupling between the form and the context, making the testing and reusability more difficult." But in practice... this works well and I can move on.
if module was generated using admin-generator :
in apps/backend/modules/books/actions/actions.class.php
modify: in
executeEdit(){
//leave rest unchanged
$values=array('activity_id'=>$activity_id, 'book_id'=>$book_id, 'todo_id'=>$todo_id, 'user_id'=>$this->getUser()->getGuardUser()->getId());
$this->form = new TabelBooksForm($TabelBooks, $values);
}
modify: in
executeNew(){
//leave rest unchanged
$values=array('activity_id'=>$activity_id, 'book_id'=>$book_id, 'todo_id'=>$todo_id, 'user_id'=>$this->getUser()->getGuardUser()->getId());
$this->form = new TabelBooksForm(array(), $values);
}
in TabelBooksForm.class.php
public function configure()
{
if ($this->isNew()) {
$this->setWidget('book_id', new sfWidgetFormInputHidden());
$this->setDefault('book_id', $this->getOption('book_id'));
$this->setWidget('activity_id', new sfWidgetFormInputHidden());
$this->setDefault('activity_id', $this->getOption('activity_id'));
$this->setWidget('todo_id', new sfWidgetFormInputHidden());
$this->setDefault('todo_id', $this->getOption('todo_id'));
}
}
i've been facing this problem for a while but symfony always surprises me with some neat code that i was not aware of.
I assume you'r using sfPropelPlugin, quite standar, if you checkout the code generated in cache (note: this code will be available once you tried to open the module from the browser, so firts try to look at it so we dont get in trouble :P) you may see something like:
cache/{application_name}(generally frontend or backend)/dev(enviromnemt)/autoModule_name( look here for the module)/:
lib
action
The action folder contains an action.class.php file that defines all actions generated by the generator (executeNew, Edit, Create, Update, etc). If you look a the implementation of executeNew and executeEdit, you can see that they ask a configuration instace the actual form to display, here is an example:
public function executeNew(sfWebRequest $request)
{
$this->form = $this->configuration->getForm();
$this->PaymentOrder = $this->form->getObject();
}
The configuration var containt an instance of a configuration class defined in the lib folder i mentioned earlier. That class tweaks the form to fit the object needs (generally by setting a fresh object instance).
So here comes the magic, the classes you see in your module extend from those in cache, so by pure logic, if you modifi the getForm() method in the main module/lib folder to fit your needs, you wont have to hack forms by getting user valuer where you shouldn't.
Hope this helps!

Categories