PHP method chaining - Fluent interface

By steinmb, 31 July, 2018

If you have used ever used Drupal or any other frameworks like Symfony, Laravel and so on, have you probably come across code that look something like:

$foo->setName('My name')
    ->setPublished(1)
    ->setTitle('This is my title');

A alternative way to write it the code above in a single line.

$foo->setName('My name')->setPublished(1)->setTitle('This is my title'); 

If you have a lot of setter methods is this a rather quick way of populating objects. All this do is calling two setter methods from the class foo was made from. setName(), setTitle() and setPublished(). We called this a Fluent interface. You chain your method calls. The regular way is to write your code like this:

$foo->setName('My name')
$foo->setPublished(1);
$foo->->setTitle('This is my title');

In this article are we going to have a look on how you can make your own class that do this in PHP. Though this is for PHP do many programming languages share look more and less like this. Let us make a simple Class that have three private variables that can only be set with its setter method.

Class User
{
    private $userName;
    private $workTitle;
    private $status;

    public function setName(string $userName): void
    {
        $this->userName = $userName;
    }

    public function setWorkTitle(string $workTitle): void
    {
        $this->workTitle = $workTitle;
    }

    public function setStatus(bool $status): void
    {
        $this->status = $status;
    }

    public function __toString()
    {
        return "User name: {$this->userName}, Work title: {$this->workTitle}";
    }
}

A small Class that store: *user name*, *work title* *employee status*. Lets us create a object from this Class and make sure it works before we continue.

$user = new User();
$user->setName('Modern PHP');
$user->setWorkTitle('Thinker');
$user->setStatus(true);
print $user;

User name: Modern PHP, Work title: Thinker Great. The Class worked, and we verified that the variables got set. Let us try populating this Class by chaining these setter methods.


$user = new User();
$user->setName('Modern PHP')->setWorkTitle('Thinker')->setStatus(true);
print $user;

PHP Fatal error: Uncaught Error: Call to a member function setWorkTitle() on null. So that obvious did not work. We are missing something. The hint is in the error message. *setName* seemed to work but the following method *setWorkTitle* did not. To make method chaining work. Each method needs to return a reference to the object. See [self/this/me](https://en.wikipedia.org/wiki/This_(computer_programming)). Let us try that and update our Class code.


Class User
{
    private $userName;
    private $workTitle;
    private $status;

    public function setName(string $userName): self
    {
        $this->userName = $userName;

        return $this;
    }

    public function setWorkTitle(string $workTitle): self
    {
        $this->workTitle = $workTitle;

        return $this;
    }

    public function setStatus(bool $status): self
    {
        $this->status = $status;

        return $this;
    }

    public function __toString()
    {
        return "User name: {$this->userName}, Work title: {$this->workTitle}";
    }
}

Let us re-test the Class.


$user = new User();
$user->setName('Modern PHP')->setWorkTitle('Thinker')->setStatus(true);
print $user;

User name: Modern PHP, Work title: Thinker Great! It worked. That is all to it but are you new to object oriented programming could this easily confuse you. You and also write the code like this, skipping the separate line when creating the object.


$user = (new User())
  ->setName('Modern PHP')
  ->setWorkTitle('Thinker')
  ->setStatus(true);
print $user;

Before I save and publish. Is the any drawback to this? There is always are some. Nothing comes for free. For me it is debugging. It could make debugging errors a little harder but writing Classes like this where with type hinting all over the place could make debugging easier. Edit: Barney Laurance made a comment about other drawbacks to fluent interfaces, and links to a article written by Marco Pivetta (Ocramius). 

References

  1. [Fluent interface](https://en.wikipedia.org/wiki/Fluent_interface)
  2. [This](https://en.wikipedia.org/wiki/This_(computer_programming))
  3. [Setter method](https://en.wikipedia.org/wiki/Mutator_method#PHP_example)