The power of PHP and Laravel

The power of PHP and Laravel

Decomposition of a code snippet to showcase the usage of anonymous functions, macros and higher-order functions …

When it comes to the popularity of programming languages I never really understand when people are talking about how certain languages are superior to others.

I always thought that there are problems that we need to solve as programmers and we have tools that are best suited than others for the given job.

One of the languages I’m using for quite a while is PHP. I know, I know … Python, Java, C++ and others are better and more secure, they use strict type control and many other things.

But actually, when you combine PHP 8.x with a powerful framework like Laravel, there are not many tools that are easier to learn and use for web application development.

A couple of months back I started to re-write an application that I created many-many years ago. It was originally written in PHP 5.x with a lot of client-side Javascript and a MySQL backend. A typical set-up one would say.

I knew from the beginning that I will use an MVC framework to reduce development time so I took a look at what was out there.

It turned out that Laravel with PHP 8.x and Laravel Forge worked like a charm for me. I finished the project way quicker than expected and managed to improve and optimize the application in many places.

In this article, I’m not going to show you how I rewrote my application.

I’m only trying to prove why PHP is still one of the most popular programming languages for certain tasks.

The code snippet that I chose for my demonstration is a function that calculates the edges of a graph. To put this into context: edges are network cables connecting nodes, which are actual network devices in a data center.

Now, let's see the code snippet:

public static function populateCablesList(): Builder {

    return count(self::$route)
        ? array_reduce(self::createTuples(),
            fn($q, $tuple) => $q->filterRouteSegment($tuple),
            Cable::query()->with(['cable_type', 'cd_start', 'cd_end', 'owner'])
          )
        : Cable::query()->whereNull('id'); // Return an empty result

}

Let’s go through the above code line-by-line.

As you can see the function is returning a Laravel Eloquent Builder instance that we can use in other parts of our application to return the relevant Cable models. These models then can be used for formatted reports, data tables, data exports etc.

If there is a route between points ‘a’ and ‘b’ then our static property $route contains the vertexes or nodes of that route otherwise $route is an empty array.

Based on whether we have a route in our graph:

  1. we either return a query that will return all Cable models on the given route or

  2. an Eloquent query that will return an empty Laravel collection.

As you can see we are using the higher order PHP function array_reduce to process our $route.

The static function createTuples in our case will generate tuples using the vertexes of our route and pass them over to the array_reduce function.

To give you an example: if $route contains a, b and c vertexes then createTuples will return the following tuples: [a, b] and [b, c].

The anonymous function will take the actual tuple, generate the required SQL query statement and attach it to the query builder instance. This is basically how we are ‘reducing’ the tuples into a Laravel Eloquent query.

To generate the required SQL query statement we are using a Laravel macro called filterRouteSegment.

As you can see it generates an SQL WHERE clause using the actual tuple (our Cable model does not have a direction therefore we need to check it’s connection points at it’s start and end node as well).

Builder::macro('filterRouteSegment',
            fn($tuple) =>
            $this->orWhere(function ($builder) use ($tuple) {
                $builder->where(function ($query) use ($tuple) {
                    $query->where('start', $tuple->start)->where('end', $tuple->end);
                })->orWhere(function ($query2) use ($tuple) {
                    $query2->where('start', $tuple->end)->where('end', $tuple->start);
                });
            })
        );

The last parameter to our higher-order function is it’s initial value.

It is important to note that in our case we are using eager loading the avoid the classical N+1 problem when loading model relationship data in Laravel.

With the above example we have demonstrated the use of higher order functions, macros and anonymous functions in PHP together with Laravel.

With these ‘tools’ we were able to solve a seemingly complex problem with just a few lines of code that is also very easy to read and understand.