Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vincent15000's avatar

What is the difference between Actions and Services ?

Hello,

I have just read this about actions.

https://freek.dev/1371-refactoring-to-actions

A new way to manage the business logic to leave the controller to only CRUD methods.

When I compare actions and services, it seems working in a similar way.

I would almost create an action as good as a service : both are reusable, injectable, ...

What's really the difference between actions and services ?

Other question : Fortify uses actions, but the Fortify actions classes doesn't look like the one presented in the above post from free.dev. So are Fortify actions really actions ?

Thanks to help me better understand ;).

Vincent

0 likes
15 replies
martinbean's avatar

@vincent15000 They’re both just classes that hold business logic.

Action classes will usually be concerned with doing one particular action (hence the name), whereas a service class may have multiple methods to perform multiple actions around a particular entity.

15 likes
Snapey's avatar

The distinction to me is that actions are self contained. They might use eloquent or other framework features but they are mostly single purpose.

A service class I associate with interacting with other services or packages. A service class might also have more than one method so a service class that wraps a third party API might have one method for fetching data and another for setting data. A service class might also have an interface so that you can swap implementation A for implementation B and they would both present themselves the same to the rest of the app.

I recommend this talk from Luke Downing https://youtu.be/0Rq-yHAwYjQ?t=1678

6 likes
vincent15000's avatar

@Snapey @martinbean Thank you both ;) ... well ... that's clear for me. So I wonder why Fortify (Fortify has several methods about login, logout, register, ...) doesn't have written its methods in a Service. I also notice that the Fortify actions don't have any execute() method, why ?

Here is an example : I have a common CRUD controller GroupController and I need to add 2 more methods : addUserToGroup and removeUserFromGroup. Some people advise to not add these methods to the GroupController because this controller is primarily for simple CRUD methods. So my idea is, to better organize my code, to write these two methods in another place : Actions or Services.

First question : which one is better between Actions (one add action and one remove action) or Services (one service with an add and a remove method) in my example ?

Then to add or remove a user (by clicking on a button), I need a route and a controller.

Second question : do I have to write another controller (for example SubscriptionsController) which will have more business logic and will for example have an add and a remove method and from these methods I call the actions or the services ? Or is it better to add these methods directly in the GroupController and call the actions / services from there ?

Sinnbeck's avatar

@vincent15000 All of them only have 1 method I believe? Taylor just decided that instead of ->execute() he wasted a word that better described the action. Probably just a personal preference, and maybe easier for new people.

Personally I prefer actions, but others will say services are the best. Its a bit like asking which kind of cola is the best.

Regarding controllers I personally prefer to use crud terms for them all. So fully resource controllers https://www.youtube.com/watch?v=MF0jFKvS4SI

3 likes
vincent15000's avatar

@Sinnbeck Not sure to understand all your answer ... your opinion is that there is no better solution : I can write the additional methods either in the CRUD controller, or in another controller, or in actions or in services ?

Sinnbeck's avatar

@vincent15000 Not quite :)

Pick either actions or services. One or the other.

And regarding controllers. Keep them light. And you asked if you should add add and a remove. And my answer was to check out that video. In that case they would be store and delete. But yes use another controller

2 likes
martinbean's avatar
Level 80

So I wonder why Fortify (Fortify has several methods about login, logout, register, ...) doesn't have written its methods in a Service. I also notice that the Fortify actions don't have any execute() method, why ?

@vincent15000 You‘re asking a question that is basically about Taylor’s personal preference. I don’t know. Neither will any one else.

My best guess is Taylor created individual action classes so Fortify was easier to extend by end developers. If all the methods were in some random “auth service” class, then that would mean developers would need to extend that one class just for the sake of overriding one method.

Here is an example : I have a common CRUD controller GroupController and I need to add 2 more methods : addUserToGroup and removeUserFromGroup. Some people advise to not add these methods to the GroupController because this controller is primarily for simple CRUD methods. So my idea is, to better organize my code, to write these two methods in another place : Actions or Services.

This won’t solve anything. You’d still need a controller to actually invoke these methods. You’re probably being advised not to put them in your existing GroupController because those actions aren’t manipulating a group resource, but actually a relation (users in groups). Therefore, a nested resource controller would be more appropriate that has its own resourceful methods:

  • GroupUserController::store (adds a user to a group)
  • GroupUserController::destroy (remove a user from a group)

do I have to write another controller (for example SubscriptionsController) which will have more business logic and will for example have an add and a remove method and from these methods I call the actions or the services ? Or is it better to add these methods directly in the GroupController and call the actions / services from there ?

What has subscriptions got to do with users and groups…?

All actions and services are, are classes and methods to store business logic. They‘re just places you would lift business logic out of a controller, and in to make reusable. All it is, is instead of this:

public function someControllerAction()
{
    // Some horribly long business logic
    // That spans many, many lines of code
    // And makes your controller actions look ugly
    // And the code unusable in other contexts
    // Such as Artisan commands or queued jobs

    // Return response
}

Is this:

class SomeActionClass
{
    public function execute()
    {
        // The horribly long business logic from the controller
        // That can be executed without any knowledge of request or response logic
    }
}

Or this:

class SomeServiceClass
{
    public function someServiceMethod()
    {
        // The horribly long business logic from the controller
    }

    public function someOtherServiceMethod()
    {
        // Another method containing business logic around a particular entity
    }

    public function yetAnotherServiceMethod()
    {
        // More business logic around a particular entity
    }
}
6 likes
vincent15000's avatar

@martinbean Ok thank you very much ... just to answer your question about subscription, I thought about this word to define the fact to be added to a group, but the word is probably not the best.

vincent15000's avatar

@Sinnbeck The video is interesting, it's a good idea to be able to think CRUD for every method. What I probably need for my own case is to create another controller to manage both actions add to group and remove from group.

More I learn more I realize that beyond the best pratices, a lot of things are only choices to do, that there isn't a real best way to do.

martinbean's avatar

@vincent15000 Like I mentioned, adding a user and removing a user from a group are resourceful operations, they’re just operating on child resources and not the parent “group” resource.

You’re not adding a group, therefore it wouldn’t belong in a GroupController. But you’re adding a user to a group. So this a pivot model/resource you’re interacting with here, hence the suggestion of a GroupUserController with its own store and destroy actions for adding and removing a user from a group respectively.

// Add a user to the group identified by the IDs in the URI
Route::post('groups/{group}/users/{user}', [GroupUserController::class, 'store']);

// Remove a user from the group identified by the IDs in the URI
Route::delete('groups/{group}/users/{user}', [GroupUserController::class, 'destroy']);
class GroupUserController extends Controller
{
    public function store(Group $group, User $user)
    {
        $group->users()->attach($user);
    }

    public function destroy(Group $group, User $user)
    {
        $group->users()->detach($user);
    }
}
1 like
vincent15000's avatar

@martinbean Ok that's all right for me. Thank you very much.

I understand that it's not really necessary for me to create an action or a service to manage these actions (add / remove). A simple controller is sufficient. Unless I need to reuse the code at a different place.

The video shared by @sinnbeck is very interesting and shows how it is possible to consider all actions as simple CRUD. And it's exactly what you explain to me ;).

vincent15000's avatar

@martinbean I can even simplify because the group will always be the authenticated user group.

Route::post('groups/users/{user}', [GroupUserController::class, 'store']);
...
public function store(User $user)
{
    $auth()->user()->group->users()->attach($user);
}

Please or to participate in this conversation.