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

vincent15000's avatar

Services for business logic

Hello,

I have found this webpage.

https://farhan.dev/tutorial/laravel-service-classes-explained/

I never wrote any service.

Is there any difference between services, service containers and service providers ?

I don't have found any documentation about the services on the Laravel website (only service providers and containers).

Thanks for your help.

V

0 likes
17 replies
OussamaMater's avatar

These two videos might help you understand Service classes

In short a Service is a class that performs a certain action and can be used across your code, so for me it's like a refactor, if you find yourself doing an action more than one time, why not extracting it into a Service.

2 likes
vincent15000's avatar

@OussamaMater Thank you for the links, the videos are a good example of why a service, why an action, why a job, ...

1 like
vincent15000's avatar

@JeffreyWay Thank you for the links to these videos. It allows me to compare the three concepts and it's easier to understand for me ;).

vincent15000's avatar

@jeffreyway @oussamamater Perhaps some more help around a concrete example.

I have this code in a controller and I need to retrieve the list of the sessions inside the Inertia::render() method. I already found that the code became heavy, so I decided to put it in another method, the list() method.

Typically could the list() method be in a service ? According to the video, I would say no, because this method doesn't do anything else than retrieving data from a query. But another answer could be yes, because the code in the controller would be cleaner.

Thanks for your help.

class SessionController extends Controller
{

    public function index(Request $request, DateTimeService $datetimeService)
    {
        return Inertia::render('Sessions/Index', [
            'sessions' => fn () => $this->list($request),
            'years' => fn () => $datetimeService->getYears(),
            'students' => fn () => StudentResource::collection(Student::
                withCount('sessions')
                ->with('funding')
                ->with('path')
                ->with('project')
                ->orderBy('firstname')
                ->orderBy('lastname')
                ->where('active', true)
                ->get()
            ),
            'types' => fn () => TypeResource::collection(Type::
                withCount('sessions')
                ->orderBy('name')
                ->get()
            ),
            'levels' => fn () => LevelResource::collection(Level::
                withCount('sessions')
                ->orderBy('name')
                ->get()
            ),
            'states' => fn () => StateResource::collection(State::
                withCount('sessions')
                ->orderBy('name')
                ->get()
            ),
        ]);
    }

    public function list(Request $request)
    {
        $now = Carbon::now();
        $year = isset($request->year) ? $request->year : $now->year;
        $month = isset($request->month) ? $request->month : $now->month;
        $studentId = isset($request->studentId) ? $request->studentId : null;
        $typeId = isset($request->typeId) ? $request->typeId : null;
        $levelId = isset($request->levelId) ? $request->levelId : null;
        $stateId = isset($request->stateId) ? $request->stateId : null;
        $sessions = Session::
            with('student')
            ->with('funding')
            ->with('path')
            ->with('project')
            ->with('level')
            ->with('type')
            ->with('state')
            ->when($studentId, function($query) use ($studentId) {
                $query->where('student_id', $studentId);
            })
            ->when($typeId, function($query) use ($typeId) {
                $query->where('type_id', $typeId);
            })
            ->when($levelId, function($query) use ($levelId) {
                $query->where('level_id', $levelId);
            })
            ->when($stateId, function($query) use ($stateId) {
                $query->where('state_id', $stateId);
            })
            ->when($year > 0, function($query) use ($year) {
                $query->whereRaw('year(date) = ?', [$year]);
            })
            ->when($month > 0, function($query) use ($month) {
                $query->whereRaw('month(date) = ?', [$month]);
            })
            ->orderByDesc('date')
            ->get();
        return $sessions;
    }

...
JeffreyWay's avatar

@vincent15000 Keep it simple, if you can. If that's the only place you'll ever execute that session query, then I think it's fine in your controller. If you want, you could move it into your Session model. Maybe a method name, like Session::fromRequest(). Otherwise, controller is fine. 👍

You could also refactor that list() method a good bit to shorten it. Maybe start by combining all those ->with() calls into a single one, and then pass an array for each relationship that you want to eager load.

1 like
vincent15000's avatar

@JeffreyWay Good idea ... you mean something like this ? I'm not sure to understand for the array for each relationship.

with(
    ['student' => function ($query) use ($studentId) {
        $query->when($studentId, function($query) use ($studentId) {
            $query->where('student_id', $studentId);
        });
    }],
	...

I have tried and it doesn't work, because I need to filter the sessions and not the students, types, levels, ... perhaps I haven't understood what you meant.

MichalOravec's avatar

@vincent15000 That method could look like this:

protected function list(Request $request)
{
    $now = now();

    $year = $request->year ?? $now->year;
    
    $month = $request->month ?? $now->month;

    return Session::with(['student', 'funding', 'path', 'project', 'level', 'type', 'state'])
        ->when($request->studentId, fn ($query, $studentId) => $query->where('student_id', $studentId))
        ->when($request->typeId, fn ($query, $typeId) => $query->where('type_id', $typeId))
        ->when($request->levelId, fn ($query, $levelId) => $query->where('level_id', $levelId))
        ->when($request->stateId, fn ($query, $stateId) => $query->where('state_id', $stateId))
        ->when($year > 0, fn ($query) => $query->whereYear('date', $year))
        ->when($month > 0, fn ($query) => $query->whereMonth('date', $month))
        ->orderByDesc('date')
        ->get();
}

Btw I would use query object instead of protected method in the controller.

1 like
vincent15000's avatar

@MichalOravec Thank you for this suggestion.

This method is now public, so I can use it for a simple ajax request. I use InertiaJS and I sometimes need to use an axios request instead of an InertiaJS request.

OussamaMater's avatar

@vincent15000 From what I understood ( I might be mistaken though), he means something like this

with(['student', 'funding', 'path', 'project', 'level', 'type', 'state'])

And you can make that array dynamic for sure, so you can pass the relationships you want to eager, you don't have to hard code it, I just gave an example.

I am sorry I didn't reply with a refactor when you mentioned me, I mean Jeffrey is here XD he's someone I learn from, so yes XD

1 like
vincent15000's avatar

@OussamaMater He said to pass an array for EACH relationship I need to eager load. It's this point I don't understand.

OussamaMater's avatar

@vincent15000 yes he means like if you want to eager load the 'student' relationship, and let's 'funding' you can make an array like this, that's the EACH he referring to

$relations = ['student', 'funding'];
..->with($relations) // here we passed an array to eager load these two relationships we want, can be more can be less

See @michaloravec refactor above, I guess he did the same thing I said (yet I am not really sure).

1 like
OussamaMater's avatar

@vincent15000 Yes he didn't say it will optimize your code or make less queries, it's just a cleaner way to do things, don't you think a single 'with' is cleaner than duplicates ones?

1 like

Please or to participate in this conversation.