In laravel 5.7 I want to be able to have multiple actions,
for example inserting user in database, sending registration Email,
sending notification, ...
I want to be able to execute these actions both sync and async.
the problem is I don't want to create Job class for every action.
each action is a php callable class.
The thing I don't understand in Laravel Job class is It receives dependencies as handle method arguments and receive It's Input which should process on,in the constructor, I think It's kind of odd.
for example when I want to call send register email action, I want to be able to do sth like :
$registerEmailAction->__invoke($user, true);
second parameter indicates whether to do this action sync or async.
Laravel Job classes are simple objects that only need to implement the ShouldQueue interface and an handle() method. You can dispatch them, or run them immediately explicitly calling the handle method. If you want to take the __invoke route you could so something like this:
class RegisterEmailAction implements ShouldQueue
{
//... more code ...
public function __invoke(User $user, bool $async)
{
$this->setUser($user);
if ($async) {
dispatch($this);
}
else {
$this->handle(); // or dispatch_now($this);
}
}
public function handle()
{
if (!$this->user) {
throw new UserNotFoundException();
}
// ... some other code ...
}
}
Since you don't want to pass the $user as a dependency in the constructor, I would suggest to check for it in the handle method, so that you get an error if some client code tries to call the handle method, without taking the __invoke route. You also may needs to use some traits, like SerializeeModels or Dispatchable (check the docs for more info).
Related
I have a piece of code that takes the user (passed into the job constructor) and notifies the user via a websocket to the job status.
It is effectively one line that needs to be added to the start of the handle method (before the job starts), one to the end of the handle method (after the jobs has completed) and then on in the fail method.
Other than adding this to each job manually, what is the best way to do this? Something like a trait, middleware etc. but I don't think either of these will work.
One way could be extending the job/command class like:
class MyJob extends Job {
public function handle() {
try {
do_stuff_at_start();
$this->process();
} catch (Exception $e) {
do_stuff_when_fails();
}
abstract public function process();
}
and all Your jobs could implement process() method that is responsible for handling logic. Just a loose idea - not sure if it fits Your needs.
I'm struggling with how to do this correctly following best practices. It might be difficult to explain but i'll try my best here.
I have an external API I need to make very many different calls to. So what I did was creating a class in the App folder called Api.php for now. It's using Guzzle for API calls.
In the Controller for the view I create the Api object in the needed functions and call the corresponding function in the API class.
Controller
public function uploadDevice(Request $request)
{
## Validation etc is performed
// Calling the API
$api = new Api();
$api->uploadDevice();
}
Api.php
class Api
{
private $token;
public function __construct(){}
public function checkIfHasToken(){}
public function getTokenFromSession(){}
public function getFreshToken(){}
public function uploadDevice(){}
}
Some questions
The checkIfHasToken() needs to be called before every request. Should it be done in the constructor, first in each function doing API calls or directly from the Controller?
Exceptions : Where should I do the Try/catch etc ? Should it be done in the Api class where it's needed or in the Controller by calling each and every function from the API class and wrapping it in try/catch?
Redirects : I want to redirect back to the Route the request came from with every possible errors or success message included. So if I have a try/catch I want to redirect with the result of the catch included. Where to put this logic? Redirecting from the nested function does not seem to work. So then I'm back to calling each and every function in the Api class from the Controller one by one and handle the exceptions/errors/validations separately in the Controller?
Maybe I'm thinking too much about this or making it more complicated than it needs to be. Not sure anymore.
// Controller
public function __construct(ApiService $apiService)
{
$this->api = $apiService;
}
public function uploadDevice(Request $request)
{
// Ensure that the user has a token in a custom HTTP request or in a middleware somewhere
try {
$this->api->uploadDevice();
}
catch (Exception $exception){
return redirect()->back();
//You can include errors from $exception here.
}
}
// Service
class ApiService
{
public function uploadDevice()
{
return 'I did a thing';
}
}
Explaination
Laravel has many ways to do the same thing, it is all about what you need and how you want your application to scale.
Checking if a token is present or valid should be done in a middleware.
A try catch can be anywhere depending on how much you need to see in the exception, normally just in a controller is ok, but you can
do this in many ways. I personally like to make an event listener
for any http error.
Return redirect back from the controller will be fine to always redirect to the place that invoked the controller
The checkIfHasToken() needs to be called before every request. Should
it be done in the constructor, first in each function doing API calls
or directly from the Controller?
If it needs to be called for every request, I suggest making it middleware as it's made for this purpose.
Exceptions : Where should I do the Try/catch etc ? Should it be done
in the Api class where it's needed or in the Controller by calling
each and every function from the API class and wrapping it in
try/catch?
This depends, if you want to be able to control the output when an exception occurs then you probably want it in your controller. If you can program something to do when the exception occurs (return unsuccessful for instance), do it in a lower level (api).
Redirects : I want to redirect back to the Route the request came from
with every possible errors or success message included. So if I have a
try/catch I want to redirect with the result of the catch included.
Where to put this logic? Redirecting from the nested function does not
seem to work. So then I'm back to calling each and every function in
the Api class from the Controller one by one and handle the
exceptions/errors/validations separately in the Controller?
You can go back by returning redirect()->back() as the response, the best way to show errors would to include them somewhere. I suggest using session()->flash() for this. These calls can be made from the try/catch.
I'm using symfony 4 and want to remove big part from controller's action to another method. But in this part using data from Request object and return Response object.
I have 2 options to move this part:
Move to private method in controller.
Move in service method.
And i have 2 options, what to do with method parameters:
Set Request object as method's argument and get all data from it in method.
Get all data from Request object in controller's action and set it as method's arguments.
Which way is better and why? Maybe there is a better way?
Is it normal practice to return Response object to controller's action from service method?
Code example:
public function index(Request $request): Response
{
if(!$this->hasSuccessAuth($request)) {
return $this->authenticateClient();
}
}
private function hasSuccessAuth(Request $request): bool
{
$passwordCookie = $request->cookies->get('secret', NULL);
if(self::CHECK_AUTH_MODE === $request->query->get('mode') and
$this->authService->isCorrectPasswordCookie($passwordCookie)) {
return true;
}
return false;
}
private function authenticateClient(): Response
{
if($this->authService->isSuccessHttpAuth()) {
$passwordForCookie = $this->authService->getPassword();
return new Response("success\nsecret\n".$passwordForCookie."\n");
}
return new Response('', Response::HTTP_FORBIDDEN);
}
I have 2 options to move this part:
Move to private method in controller.
Move in service method.
You can move into private method in controller or in service method in fact. If you move to private method in controller, my advices is that you should create a wrapper controller class that your controller will extend and you put this private method inside. Obviously this controller will extends the base Symfony Controller if your initial controller already extended it. This can be good if you intend to use hasSuccessAuth and authenticateClient with other controller classes only. Because if you put this logic in a service, other services or command will be able to use it. It is up to you.
If now you want this logic to be accessed everywhere in you application, better create a service. But you should ask yourself, if this new service will have its own data or will benefit from dependency injection. If yes, creating a service is a good idea. If no, that means you will use this logic only to deal with data given as parameters and return a result. In this case you should create a helper class with static methods.
And i have 2 options, what to do with method parameters:
Set Request object as method's argument and get all data from it in method.
Get all data from Request object in controller's action and set it as method's arguments.
It is up to you but if it was my application, I would chosen to give the entire Request object as argument of the method and all data from it inside the method.
And one last thing: your authenticateClient method does not use the Request object.
I'm trying to clean up some of my controllers by moving the functionality from the controller methods to reusable commands that I can just pass on to a command bus. As I've understood, though, a command is not supposed to return data (or did I misunderstand something?). But how else would you suggest for the controller to act on the outcome of the command?
For instance I have a Create endpoint in my ClientController. The controller method dispatches a CreateClient command, and then I'd like to redirect the user to the new Client's edit page. Something like
class ClientController extends Controller
{
public function create($request)
{
try {
$this->dispatch(new CreateClient(/*get something from the request to pass*/));
} catch (\Exception $e) {
return $this->json(['error' => $e->getMessage()]);
}
$client = ...;
$this->redirect('/client/' . $client->id);
}
}
If the creation failed I can throw an exception, which can be handled by the controller. I could of course create a method in my ClientRepository to get the newest created Client, but that seems error prone. But how would you suggest that I got a hold of the newly created client?
Edit:
At the end of https://www.youtube.com/watch?v=fbSYZFZCFS0 they discus this actual problem. Their main suggestions are using UUIDs so the ID can be generated by the controller, or using an asynchronous method to poll for the new item.
They also mention the possibility of using event listeners, but argue that it's an anti-pattern in PHP.
I'm still undecided on how I'll prefer to do it.
In the Yii documentation we can often read "Make sure you call the parent implementation so that the event is raised properly." for beforeSave, beforeValidate, afterSave ...
In the Yii Blog Tutorial I've seen:
protected function beforeSave()
{
if(parent::beforeSave())
{
...
return true;
}
else
return false;
}
What's up with the if-function?
I've also seen just simply:
protected function afterSave()
{
parent::afterSave();
...
}
And:
protected function beforeValidate()
{
...
return parent::beforeValidate();
}
Why do you sometimes wrap the parent::function call in a if-function?
Does it matter if I just call the parent::function(); in the beginning or return it in the end?
It depends on what you would like to do.
You should know that beforeSave and beforeValidate methods can affect the further process of saving/validation by returning true or false, whether afterSave and afterValidate - can't. You should also know that you can have not just one event handler, but any number of them, attached using attachEventHandler method. So, considering this, the place where you call parent function does matter in case of beforeSave and beforeValidate methods when you have multiple event handlers. In other cases it doesn't.
For example, you have beforeSave() handler in your model, and you have also subscribed to this event in another class (it all based on Observer pattern, i suggest you to read about it to understand the events more deeply).
When you're implementing beforeSave() method, you must call parent::beforeSave() to make that other event handler work. And if you've decided that you should not save your model for some reason, you have a choice - whether to run other event handlers or not. So you can return false immediately wihout letting other event handlers being fired (and save some resources may be).
If you consider your own beforeSave() handler less important than other attached handlers, then you should call parent::beforeSave() first, check it's result (like you did in your first example) and execute your code depending on what other event handlers decided to do.
Usually you won't have additional event handlers in your models (if you have, you probably should understand your question yourself), so you can always call parent method in return statement, like in your last examples. And if you want to interrupt the saving or validation, return false instead.