Laravel Controller Code: Move to Model, Service, Action or Job?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello guys it's a pretty well known fact in laravel or the rule of thumb is that controllers should not have a lot of logic in them this is clear what is unclear and what is really often asked on social media and on forums everywhere where to put that logic to from controller to where and i want to give you an example with discussing the logic where can we put the code from the controller and this video will not be about rules where you should do that it will be my own opinion which you can challenge in the comments and of course the actual solution will also depend on your project on your team on what features are planned with that particular function in the future and a lot of other factors so in this video i will show you how to put this code from controller to model or to service class or to action class or to a job and this idea came from a tweet that i've retweeted from architect a good tip about laravel to move the logic from the controller to its own model class model method so instead of creating the invoice in the controller you create a method create invoice in the order model and then just call that from the controller so this is one way of doing that i kind of challenged this with my retweet that i would probably create my own service class and would not put that in the model why watch this video and i will explain my opinion again it's not a sacred opinion you can challenge that but let's look at the actual code i have tried to recreate the example from the original tweet but basically what is happening here is in the routes api you have a call to create the invoice from an order order is with route model binding so we can use that as eloquent object and we check if the invoice exists we throw an error in this case i just return the validation error from the api instead of throwing any exception but we will get to the exceptions in a minute and then we create the invoice in the transaction so we create the invoice which has one relationship which is in order model has one here and then we also assign the status which is push status which is a model function i don't know from the original tweet what was inside of that push status but i assume that in addition to the update there's something else happening otherwise it won't be just one method with one line probably and then we return the invoice number which is automatically generated in the invoice model with boot model this is one way of doing that max plus one and to launch that and to test that i have created a feature test invoices test so it won't be like a postman call or something it's just automate a test to create a user an order and then try to launch that api and check if the result is as expected and also another test if i call that api twice then the second time should throw a validation and now if we launch that php artisan test both tests are green and it's all working but the code is in the controller and this is a simplified scenario imagine if that controller contains much more logic so some more checks for example file upload some validation additionally sending emails sending notifications a lot of that stuff so it totally should not be in the controller the next question where and the first method it comes from the original tweet is to move that to the model of order so instead of that store method i've commented out we have this order model object create invoice and that create invoice looks like this it returns the full invoice and it basically is doing the same thing checking if the invoice exists but in this case it throws an exception which may or may not be caught in the controller so you shouldn't return anything from the model or from whatever internal class like service or action or anything you should throw an exception which should be caught in the controller in most cases people use exceptions like that but if the exception doesn't happen again transaction create the invoice push status but here we have this push status not order push status and then we return the full invoice and we don't even need the variable for that and now our goal is completed controller is really short well we have additional try catch but still if that hypothetically contained like 20 or 30 or 40 lines of code instead it's one line so it's much shorter and much more readable and it's also like separation of concerns so order create its own invoice now why personally i prefer service class instead of model so let's take a look at that model and in this example it's really simple and short model it has only one relationship fillables has factory by default but if we're talking about bigger applications if we put all potential logic methods into model it potentially can become really long class there is also even a term called god class which means a class responsible for basically everything so if you have more methods around order like send order validate order assign driver whatever may happen with the order or is contents if under that logic we put everything from controllers into order model itself it may become really big which is not that bad in itself it's a personal decision again it's a personal preference in my philosophy in my mind i consider model as kind of a settings class on top of eloquent so it should contain everything related to fields database tables relationships some attributes with accessors and mutators so it's kind of like config class for the model the actions around that model i consider it personally something different and even the word model if you compare that to the word service for example model if you close your eyes and try to imagine what is a model it is something static it's like a statue it's a model of something static thing and if you want to perform action some action on that model related to that model you probably should have some other entity some other class and this is where we get to other options where we can extract that code not to the model but for example to a service or action or a job and as i said that word service kind of means action serve something provide a service not sure if i'm not getting too deep into meanings of words and semantics but this is how i feel so example number three is what if we create a service class that performs that action so it's not that different in the controller from the model it's just the service class it's not the model so in the model then we can remove those methods push status and create invoice or comment them out and then in the service we are doing the same thing create invoice but then we need to pass the order as a parameter which order and that actually may be an argument toward using model instead of service because model works with specific object of that class the exact order for the service service is for any orders so you need to pass the parameter which order it is and then everything else is identical we also get the push status method into the service as well and we also need to pass order to that and the general logic from the controller is then identical we call the service method we catch the exception and return the invoice number so the difference between model and service both work it's your personal preference as i explained earlier in my personal opinion model is config class for eloquent service is for actions and by the way what i didn't mention is that service is auto resolved in the laravel controller so if you just type hint order service class that variable is automatically available for you inside of the controller so you don't need to create new service or something all laravel controllers should auto resolve the class if it's passed with type hint now let's get back to action and speaking of actions what if we try to use action class action classes became quite popular in laravel in recent years it's basically if you want to perform one single action like create invoice you create just separate class for that and you call that class main method whether it's execute run or handle or something like that action like a service it's not laravel class it's any php class so there's no artisan command of make action or make service you just create a class in app actions folder you name space it correctly similar to services and this is how it looks so create invoice is a class that is also auto resolved in the controller method so create invoice if we open that it's almost the same as the service it's just the method is execute the order is the parameter and it's almost identical we just return the push status method to the order model because we need that it technically can be a private method in the action class but somehow i felt that push status and order should be back in the model and then again in the controller it's not very different from what you saw with services or even with the model you call one method you catch the exception if it happens and that's it now why service and why actions service class to me is a class with a set of actions related to one model so again repeating if we have order publish order dispatch order assign drive or order something that should be all in the service if you have that list of actions it should be probably a service class but if you have just one action like create invoice in this case and you're not planning more actions well maybe two actions but they are kind of separate rare one-time thing kind of like a job but we will get to jobs in a minute as well so if there's not too many actions around that model actions may be another solution but again i'm repeating myself constantly it's your personal preference how you prefer to structure those classes and in here some of you may ask what about repository pattern repository class they were really popular since laravel 4 back in the days when almost all the examples of the first larval versions contained repositories but then at some point the whole community kind of shifted away from the repositories and you can find quite a lot of reasons why i have a few videos around it and i will link them in the description below so in most cases in 99 of the cases you should not use repository pattern in laravel unless you really know what you're doing and you're absolutely sure that repository pattern is the best so the pattern itself is great but in laravel eloquent itself is kind of a repository so yeah watch more about repositories in the videos below and now let's move to the last example of where you could put that method of creating the invoice it could be just a job so you can for example instead of try catching you just dispatch the job to create the invoice pass the order and kind of forget about it it's pretty similar to action but the main difference is that job may be queueable you may launch the job without the queues but technically speaking the most practical usage of the jobs is the queue and then you don't expect any result from that job it's kind of like a separate process in the background almost so for this specific scenario job isn't a fit because it doesn't return anything so you don't have the result and you cannot return anything to the end user in this case but the job is also possible if you have other cases for a job that you just launched and you don't care about the result unless the failure happens then and then you catch those errors or exceptions in the queue later but generally you pass the order to the constructor and then familiar code with just this order inside of the job so yeah getting back to the original tweet and my retweet i kind of want to explain my logic of what can you do and the choice is yours it's probably number one debatable topic around laravel where to put those methods and properties and everything so i guess let's start the discussion again so in the comments share your opinion what do you disagree with what would you do differently in my example maybe you have other ideas share in the comments i also have my philosophy explained in my own course called how to structure laravel project it's one of 25 courses i have on my teachable account and it's kind of pretty old it was at laravel 5.7 back in 2019 and i will reshoot that as laravel 9 comes out at the end of the january so this is on my list to reshoot but a lot of those ideas philosophically are still very relevant so you can take that course now it's actually cheaper than other courses or you can subscribe to all my courses with yearly membership for 99 per year plus taxes if that applies to you in 2022 i'm planning on course per month as it became a tradition in 2021 so get all of that by subscribing to yearly membership by doing that you're supporting this youtube channel so i can push the free videos to you and see you guys in those other videos
Info
Channel: Laravel Daily
Views: 104,409
Rating: undefined out of 5
Keywords:
Id: SoCuPpCFj7Y
Channel Id: undefined
Length: 12min 50sec (770 seconds)
Published: Fri Jan 14 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.