Currently, whenever a user opens a URL '/users/new'
, I use the following route that leads to the controller called 'UsersController'
:
Route::post('/users/new', [AppHttpControllersApiUserController::class, 'newUser']);
Inside the Controller, the 'newUser'
method is responsible for sending the request data (name_user
, email_user
, password_user
) to a Provider called UserProvider
:
public function newUser(Request $request){
$allRequests = $request->all();//get all POST fields
$resultForm = UserProvider::validateFORM($allRequests);//Validade all POST fields
if($resultForm == null){//If 'null', the validation process is OK
UserProvider::addNewUser($allRequests);//Send POST to be insert in database
return json_encode(array('success' => true));//Return Success
}
return json_encode($resultForm);//Return the message of validation process
}
In UserProvider
is the place where all the data processing logic takes place, from queries to the Models, communication with external APIs, validations and so on.
Regarding this, I would like to know if this is the best way to do this, that is, to put all the processing logic inside a Provider.
If not, what would be the best location?
2
Answers
In my opinion, There is no best way that can be implemented in all projects. On the other hand, It is better to separate the layers of your application because of testing, maintenance, extending, … etc. For example, in your method:
1- All validations can be placed in a FormRequest,
So you can validate the incoming data freely and set proper messages or even change some data before entering it into the controller.
2- Controllers should manage to provide the intended response, not do it themselves. They call other classes to fulfill this purpose. There are two common ways to implement the logic of the endpoint:
3- The logic is written in the action class or A method of service class (Depending on your choice). It can be done here if you need to write queries or call external Apis. (My personal preference is to do it in separate classes)
4- Finally, returning the response by ApiResource, you can decorate the data for or use the view method.
In the end, you can write tests for each layer easily. In addition, each class are reusable;
My last recommendation is to use Data Transfer Objects Laravel-data for sending data through the layers instead of using Request object.
So, let me try to help you.
Let’s first "beautify" the code, not only formatting (that is very oppinionated, so you can ignore that part) but also some lines of code (they could be replaced), I will change lines to what the ideal code would be for Laravel:
So:
newUser
tostore
, because you should only have CRUD actions:index
,show
,create
,store
,edit
,update
,delete
,forceDelete
(if you have soft deletion). Read more about controllers and this in the corresponding section.Request
with a Form RequestStoreRequest
, you could also call itNewUserRequest
, but I personally prefer to haveAppHttpRequestsUserStoreRequest
than havingAppHttpRequestsNewUserRequest
.Accept: application/json
), so you should not need to bother about it.success => true
, I would avoid that as it is meaningless because you are already returning200
HTTP code, that means it worked, you could even return201
->->json(['success' => true], 201);
that meansResource Created
in API terminology).return response()->emptyResponse();
and that should return nothing but200
. I cannot remember if it isemptyResponse
oremptyContent
or something like that.Continuing, you shared this route:
Ideally it should be like this:
Because
Route::post
is already going to meanSTORE
(create), if you wanted to update data it should beRoute::put
. Then no need to specify/new
, as, again, havingRoute::post
already means new resource stored. Read the first link I shared, you have a list ofRoute::xxxx
and URLs, how Laravel interprets them when usingRoute::resource
orRoute::apiResource
.Lastly, if you would like to have
User::add
or anything similar that takes care of storing a newUser
or resource, I would try to approach the Domain Driven Design. I do not know much about it, but what I ended up doing every times it creating aDomain
folder insideapp
folder, and then you can separate by concerns, for example:This last part is very oppinionated and depends on you and your team, there i no "best way" but the way that works for you and your team and the system you are working with. See this approach as a tool, there is no best tool for EVERYTHING, but just tools for every job, choose the best one that fits your workflow.
One more thing, do not mix
Providers
andServices
,Providers
are exclusively for Laravel ->ServiceProvider
, Laravel uses that to set binds, abstracts, and way more things (more info here) to aid you in the usage of the Framework,Services
should only be read as classes that communicate your app with an external system, it could be a local system on the same machine (but not on the Laravel app code) or it could be external, it could be an API REST, SOAP, GraphQL, etc. Do not useServices
as a "domain" logic class, it is going just to be a naming mess and confuse everyone, that is why I useAppDomain
.Of course there is more to know but you will have to look for that. I can share this similar approach that was shared in a Laracon (do watch them in youtube):
And, of course, read the documentation, fully read it and see what the framework has: https://laravel.com/docs/10.x/