Back to Blog 7 min read

service classes in laravel | Usable or Not?

A few days back I was working on a task that was written by some other developer. So, what he did there, just to show himself cool he wrote the crud...

Gurpreet Kait

Gurpreet Kait

Author

A few days back I was working on a task that was written by some other developer. So, what he did there, just to show himself cool he wrote the crud in the service class. And this post is not about blaming him for doing things in that way. I thought that deep dive a bit into this topic and think about what exactly the requirement should be when we are thinking about using a service class in Laravel.

What Is A Service Class In Laravel

In simple words, we write a service class in the services directory and it works the same as any PHP class, it's not anything special. It basically contains classes that we use for your main business logic. A service class is a plain PHP class that encapsulates a specific business logic or functionality of the application. It can be used to perform tasks like sending an email, updating a database record, or performing a complex calculation. Service classes are typically registered with the Laravel service container and can be injected into other parts of the application as needed.

I hope you understood that Laravel follows MVC architecture, so, in that case, Controller handles the request and does the job of retrieving data from the model. But again in this case, if you have something bigger say some extra lines of business logic. Then Services help to maintain code in a better way.

Benefits Of Using Service Class

For example, If we are going to build a new house. In that, we need a lot of things like wood, bricks, cement, and other things. All parts of the house depend on each other. In the code, we do the same thing all pieces of code may depend on each other or may not. But if some code depends on some specific functionality(a piece of code) then the service class makes it easier to handle different parts of the same module in a single place. If we do the same thing in the controller then it may not be that much easy to maintain. In service we keep all things separate which makes it readable and testable.

Testability

Why testable? Because let's say we have a module to integrate woo-commerce into your Laravel application. If you can imagine it's not just simple crud it has lot of things like api's, jobs and commands if you use. So, these things we cannot do in a single controller. Because we want to keep our code clean and readable most probably when you are working in a team. It's important to keep things easy and readable. In Testing we can test a small piece of code easily.

Let's say we are coding a part of code to get orders from woo-commerce and in that case what happens is you can write your main business logic in service class and controller to play with request and other things but not with logic. In that case if you can imagine everything will be on place. If you are working with some huge code base then you can look up to Repositery Pattern and Modules Approach in Laravel.

Reusablity

I think that's why we choose any framework or mostly OOP's concept to make code reusable. I can show you an example i think, let me show you the example which can explain this in better way.

I went to open-source and found a project called "pterodactyl/panel" to manage game servers. In this I found a service class file that explain this heading in a better way.

panel/app/Services/Activity/ActivityLogBatchService.php

<?php

namespace Pterodactyl\Services\Activity;

use Ramsey\Uuid\Uuid;

class ActivityLogBatchService
{
    protected int $transaction = 0;
    protected ?string $uuid = null;

    /**
     * Returns the UUID of the batch, or null if there is not a batch currently
     * being executed.
     */
    public function uuid(): ?string
    {
        return $this->uuid;
    }

    /**
     * Starts a new batch transaction. If there is already a transaction present
     * this will be nested.
     */
    public function start(): void
    {
        if ($this->transaction === 0) {
            $this->uuid = Uuid::uuid4()->toString();
        }

        ++$this->transaction;
    }

    /**
     * Ends a batch transaction, if this is the last transaction in the stack
     * the UUID will be cleared out.
     */
    public function end(): void
    {
        $this->transaction = max(0, $this->transaction - 1);

        if ($this->transaction === 0) {
            $this->uuid = null;
        }
    }

    /**
     * Executes the logic provided within the callback in the scope of an activity
     * log batch transaction.
     */
    public function transaction(\Closure $callback): mixed
    {
        $this->start();
        $result = $callback($this->uuid());
        $this->end();

        return $result;
    }
}

In the above code you can see how developers have wrote the code. This is perfect example which I'm trying to explain that we can have small piece of code that we can use multiple times in the same class. And if needed then can use in some other class as well. In thsi uuid() we can in some other methods if needed. This is reusablity that I was trying to explain. I hope you understood and one more thing is I may be wrong while talking about these things I'll suggest check once on Github so that you can understand better.

Scalability

Most important thing that we are going to discuss is scalability. Well, Scalability which is very important. If you are starting an application today it may not be same after a year or two. Because example; if we are creating a Saas application then we need scalable code and for that it is important to keep things simple and extendable. Again in service it's possible to write a scalable code. Because without diving into the controllers or model we can move the code from service it's like without effecting the another part of code. It works dynamically and smoothly. Let's see the example how a piece of code will be scalable;

As I'm giving example of woo-commerce so let's consider we need to create a module to handle payments for orders. In that case we will do the code using service class. So, let's create service class for processing payments and then will see what does mean by scalability in service class.

Service Class: PaymentService

namespace App\Services;

use App\Models\Order;
use App\Models\Payment;
use Stripe\Charge;

class PaymentService
{
    public function process(Order $order, $amount)
    {
        // Use the Stripe API to process the payment
        $charge = Charge::create([
            'amount' => $amount * 100,
            'currency' => 'usd',
            'source' => $order->payment_token,
            'description' => "Payment for order #{$order->id}",
        ]);

        // Create a new payment record in the database
        $payment = new Payment([
            'order_id' => $order->id,
            'amount' => $amount,
            'status' => 'success',
            'transaction_id' => $charge->id,
        ]);
        $payment->save();

        // Update the order status to "completed"
        $order->status = 'completed';
        $order->save();
    }
}

In this example, the PaymentService class handles the payment processing logic using the Stripe API. It also creates a new payment record in the database and updates the order status to "completed" once the payment has been processed.

By encapsulating the payment processing logic in a separate class, we can make the code more modular and easier to test. We can also reuse the PaymentService class in other parts of the application where payment processing is needed, such as in a cron job that processes payments in the background.

Overall, using a service class like PaymentService can make our code more scalable and maintainable over time as our application grows in complexity.

Conclusion

So, basically all over the conclusion is to explain how we should use a serviceClass because I saw that an open-source project was using service class for there simple crud operations not that much complex things were there. So, if you are playing with some complex business logics it's important to make code reusable and scalable that we can do by using Services. And not just limited to this pattern for more large projects you can look at Moduler Approach and Repositery pattern.

Request : This is humble request to you guys if you know something better which I missed out above please write down in the comment below. So, that this article/blog can become more useful for others devs as well. Thanks.

Benifits Of Using Migrations in Laravel