Fluent API PHP Class

What is the fluent api? How to apply this design pattern in PHP? How to call a method both statically and not? Let's discover it!

First of all, what is a fluent API?

As wikipedia says:

Fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility.

For example, in Laravel, you can build up an SQL query using the Eloquent DB class (that implements the fluent API):

DB::table('users')->where('age', '>', 18)->limit(100)->orderBy('name')->get();

User::where('age', '>', 18)->limit(100)->orderBy('name')->get();

With the fluent API comes handy calling a method both statically and not. Like this: 

FluentMath::add(10)->subtract(3)->result();

FluentMath::subtract(3)->add(10)->result();

In the example above you can see that the add and the subtract methods have been called both statically and not.

How to call a non-static method with double-colon (::)

You can achieve that using the magic methods __call and __callStatic.

How do the magic methods __call and __callStatic work? 

When you are invoking a static method which does not exist then the call falls back to the __callStatic method. 

class MagicStaticTest
{
    public static function __callStatic($method, $args)
    {
        echo 'The __callStatic has been triggered!<br>';
    }

    public static function definedMethod()
    {
        echo 'The staticMethod has been triggered!<br>';
    }
}

// it outputs: The staticMethod has been triggered!
MagicStaticTest::definedMethod();

// it outputs: The __callStatic has been triggered!
MagicStaticTest::notExistingMethod();

Similarly happen with a not static calls: if you invoke a not static method which does not exist then the call falls back to the magic function __call

class MagicTest
{
    public function __call($method, $args)
    {
        echo 'The call fell back to the method __call<br>';
    }

    public static function definedMethod()
    {
        echo 'The definedMethod has been triggered!<br>';
    }
}

$instance = new MagicTest;

// it outputs: The definedMethod has been triggered!
$instance->definedMethod();

// it outputs: The call fell back to the method __call
$instance->aMethodThatDoesNotExists();

To call a method both statically and not statically you can use a simple trick: you can use the magic methods __call and __callstatic to route the call to the method you want. 

Let’s explain it with an example.

Let’s suppose we want to build a fluent API to add or subtract a quantity from a number. For example what we want to achieve is this:

$res = FluentMath::add(5)
    ->subtract(2)
    ->add(8)
    ->result();

// $res is 5 - 2 + 8 = 11

We can build the FluentMath class as per below: 

class FluentMath
{
    private $result = 0;

    public function __call($method, $args)
    {
        return $this->call($method, $args);
    }

    public static function __callStatic($method, $args)
    {
        return (new static())->call($method, $args);
    }

    private function call($method, $args)
    {
        if (! method_exists($this , '_' . $method)) {
            throw new Exception('Call undefined method ' . $method);
        }

        return $this->{'_' . $method}(...$args);
    }

    private function _add($num)
    {
        $this->result += $num;

        return $this;
    }

    private function _subtract($num)
    {
        $this->result -= $num;

        return $this;
    }

    public function result()
    {
        return $this->result;
    }
}

When you are calling statically FluentMath::add(10) the call falls back to the method __callStatic because the method add does not exist. The method __callStatic checks if the method _add exists (with an underscore _ in the beginning), then it calls the method_add and finally it returns a new class instance.

Instead, when you are calling a method not statically, for example $instance->add(10), then the call falls back to the magic method __call because the method add does not exist. Then the method __call checks if the method _add exists and then it calls the method _add and finally it returns the class instance.

In the FluentMath class all the methods which start with an underscore _ can be called both statically and not. Plus those functions can be chained as well.

Take it further: class for using the fluent API anywhere you want

You can use the class below to extend other classes to build your fluent API class.

On the bottom of this article you can find links to my repo where you can find examples.

class FluentApi
{
    public function __call($method, $args)
    {
        return $this->call($method, $args);
    }

    public static function __callStatic($method, $args)
    {
        return (new static())->call($method, $args);
    }

    private function call($method, $args)
    {
        if (! method_exists($this , '_' . $method)) {
            throw new Exception('Call undefined method ' . $method);
        }

        return $this->{'_' . $method}(...$args);
    }
}

Below you can find an example of usage of the FluentApi class:

class FluentMath extends FluentApi
{
    private $result = 0;

    protected function _add($num)
    {
        $this->result += $num;

        return $this;
    }

    protected function _subtract($num)
    {
        $this->result -= $num;

        return $this;
    }

    public function result()
    {
        return $this->result;
    }
}

When you are using the FluentApi class remember that all the methods that start with an underscore _ (like _add or _subtract in the example above) can be chained and can be called both statically and not. _

Resources

Related Articles

Laravel Metadata

Category: PHP

Laravel Metadata is a lightweight PHP package that helps you to handle extra data on your models without adding any new fields on your database table. GitHub Repository PHP Packagist ...

LAMP Docker Environment

Category: PHP

This Docker LAMP environment is an excellent choice for local development, as it provides the necessary tools and configurations for PHP and frontend development. With the ability to set up multiple ...

WP Social Importer

Category: tools

WP Social Importer is a wordpress plugin that imports news from Facebook, Instagram, Twitter into your website. This plugin transforms automatically the news from your social networks into wordpress ...

Brainy: a simple PHP class for machine learning

Category: resources

Brainy is a PHP class that helps you to create your neural network. I built Brainy just for fun during my artificial intelligence studies. If you are a web developer and you have just started ...

Matrix arithmetic PHP Class

Category: resources

During my studies in AI I wanted to solve the XOR problem; I wanted just to do a quick test so I started writing the code in PHP. Solving that problem I ended up to write a class that handles matrix ...

Coming soon page

Category: resources

If you need a coming soon page, don't waste your time on developing it because you are going to use it for a short period. This coming soon page has a minimal design and it's made with PHP, HTML and ...