Laravel Pipelines: Optimizing Complex Web-Development

Laravel Pipelines: Optimizing Complex Web-Development

Laravel Pipelines is a lesser-known but powerful feature of Laravel that is not documented in the official Laravel documentation. However, this tool is widely used within the framework for performing sequential operations. In this article, we will explore how Laravel Pipelines can be beneficial in CRM development and provide examples of its use, adhering to clean architecture principles and Domain-Driven Design (DDD).

Where Laravel Pipelines Can Be Beneficial

Consider a scenario where you have some initial data that needs to undergo a series of sequential actions. For example, adding a new contact to a CRM system.

Example 1: Adding a New Contact

When a user adds a new contact, the data is sent to the controller. The contact goes through several steps before being saved to the database:

  1. Check for the presence of all required fields.
  2. Data validation: Ensure the correctness of the email, phone number, etc.
  3. Remove extra whitespace and format text.
  4. Check for unique contact.
  5. Generate a unique identifier for the contact.
  6. Save the contact to the database.

Example 2: Handling a Support Request

When a user creates a support request, the request data also goes through several steps:

  1. Check for the presence of all required fields.
  2. Data validation: Ensure the correctness of the email, problem description, etc.
  3. Remove extra whitespace and format text.
  4. Generate a unique identifier for the request.
  5. Save the request to the database.
  6. Send a notification to the user.
  7. Assign the request to a support staff member.

Code Evolution

Let's see how the code evolves as new steps are added, adhering to clean architecture principles and DDD.

Initial Stage

Initially, we have a controller and a service that handles the core logic. For example, adding a simple contact:

public function store(Request $request, ContactService $contactService)
{
    $contact = $contactService->create($request->all());
    return response()->json($contact, 201);
}

class ContactService
{
    protected $contactRepository;

    public function __construct(ContactRepository $contactRepository)
    {
        $this->contactRepository = $contactRepository;
    }

    public function create(array $data)
    {
        return $this->contactRepository->create($data);
    }
}

class ContactRepository
{
    public function create(array $data)
    {
        return Contact::create($data);
    }
}

Adding New Steps

As new steps are added, the code becomes more complex. We move the logic to separate classes:

public function store(Request $request, ContactService $contactService)
{
    $contact = $contactService->create($request->all());
    return response()->json($contact, 201);
}

class ContactService
{
    protected $contactRepository;

    public function __construct(ContactRepository $contactRepository)
    {
        $this->contactRepository = $contactRepository;
    }

    public function create(array $data)
    {
        $data = (new TrimWhitespace())->handle($data);
        $data = (new ValidateUniqueEmail())->handle($data);
        $data = (new GenerateUniqueId())->handle($data);

        return $this->contactRepository->create($data);
    }
}

Unifying Interfaces

We can unify the interfaces and use Laravel Pipelines for sequential execution of steps:

public function store(Request $request, ContactService $contactService)
{
    $contact = $contactService->create($request->all());
    return response()->json($contact, 201);
}

class ContactService
{
    protected $contactRepository;

    public function __construct(ContactRepository $contactRepository)
    {
        $this->contactRepository = $contactRepository;
    }

    public function create(array $data)
    {
        $pipes = [
            TrimWhitespace::class,
            ValidateUniqueEmail::class,
            GenerateUniqueId::class,
        ];

        $data = app(Pipeline::class)
            ->send($data)
            ->through($pipes)
            ->then(function ($data) {
                return $this->contactRepository->create($data);
            });

        return $data;
    }
}

Example Usage of Laravel Pipelines

Pipe Interface

Each step (pipe) should implement the Pipe interface:

namespace App\Pipes;

use Closure;

interface Pipe
{
    public function handle($data, Closure $next);
}

Pipe Example

namespace App\Pipes;

use Closure;

class TrimWhitespace implements Pipe
{
    public function handle($data, Closure $next)
    {
        $data['name'] = trim($data['name']);
        return $next($data);
    }
}

Associated Issues

Sometimes, you need to interrupt the execution at a certain step or clean up the database from previously executed steps. Here is an example of how to handle such issues:

class ContactService
{
    protected $contactRepository;

    public function __construct(ContactRepository $contactRepository)
    {
        $this->contactRepository = $contactRepository;
    }

    public function create(array $data)
    {
        $pipes = [
            TrimWhitespace::class,
            ValidateUniqueEmail::class,
            GenerateUniqueId::class,
        ];

        try {
            DB::beginTransaction();
            $data = app(Pipeline::class)
                ->send($data)
                ->through($pipes)
                ->then(function ($data) {
                    return $this->contactRepository->create($data);
                });
            DB::commit();
            return $data;
        } catch (Exception $e) {
            DB::rollback();
            throw $e;
        }
    }
}

Here, a transaction is used to ensure data integrity. In case of an error, the transaction is rolled back (`DB::rollback()`), and an error message is returned.

Conclusion

Laravel Pipelines is a powerful tool for managing sequential operations. It helps avoid spaghetti code and makes the code cleaner and more maintainable. In this article, we explored examples of using Laravel Pipelines for managing contacts and support requests in a CRM system, adhering to clean architecture principles and DDD. We hope this information will be useful for your project development.

Popular Posts

My most popular posts

Maximum productivity on remote job
Business

Maximum productivity on remote job

I started my own business and intentionally did my best to work from anywhere in the world. Sometimes I sit with my office with a large 27-inch monitor in my apartment in Cheboksary. Sometimes I’m in the office or in some cafe in another city.

Hello! I am Sergey Emelyanov and I am hardworker
Business PHP

Hello! I am Sergey Emelyanov and I am hardworker

I am a programmer. I am an entrepreneur in my heart. I started making money from the age of 11, in the harsh 90s, handing over glassware to a local store and exchanging it for sweets. I earned so much that was enough for various snacks.

Hire Professional CRM developer for $25 per hour

I will make time for your project. Knowledge of Vtiger CRM, SuiteCRM, Laravel, Vue.js, Wordpress. I offer cooperation options that will help you take advantage of external experience, optimize costs and reduce risks. Full transparency of all stages of work and accounting for time costs. Pay only development working hours after accepting the task. Accept PayPal and Payoneer payment systems. How to hire professional developer? Just fill in the form

Telegram
@sergeyem
Telephone
+4915211100235
Email