How to Chain Methods in PHP

PHP method chaining or fluent interface?

It's rather simple, really. You have a series of mutator methods that all return the original (or other) object. That way, you can keep calling methods on the returned object.

<?php
class fakeString
{
private $str;
function __construct()
{
$this->str = "";
}

function addA()
{
$this->str .= "a";
return $this;
}

function addB()
{
$this->str .= "b";
return $this;
}

function getStr()
{
return $this->str;
}
}

$a = new fakeString();

echo $a->addA()->addB()->getStr();

This outputs "ab"

Try it online!

How do I chain methods in PHP?

To answer your cat example, your cat's methods need to return $this, which is the current object instance. Then you can chain your methods:

class cat {
function meow() {
echo "meow!";
return $this;
}

function purr() {
echo "purr!";
return $this;
}
}

Now you can do:

$kitty = new cat;
$kitty->meow()->purr();

For a really helpful article on the topic, see here: http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html

How to chain method on a newly created object?

In PHP 5.4+, the parser's been modified so you can do something like this

(new Foo())->xyz();

Wrap the instantiation in parenthesis, and chain away.

Prior to PHP 5.4, when you're using the

new Classname();

syntax, you can't chain a method call off the instantiation. It's a limitation of PHP 5.3's syntax. Once an object is instantiated, you can chain away.

One method I've seen used to get around this is a static instantiation method of some kind.

class Foo
{
public function xyz()
{
echo "Called","\n";
return $this;
}

static public function instantiate()
{
return new self();
}
}

$a = Foo::instantiate()->xyz();

By wrapping the call to new in a static method, you can instantiate a class with method call, and you're then free to chain off that.

PHP: The name of the previous method in chain

A good and more general way to get a track of the previously called method is to use a private or protected property to keep track of the actual method using the constante __FUNCTION__ or __METHOD__

example:

   class colorchanger
{
protected prevMethod;
public function black()
{
/*
your routine
for the black
method here
*/
$this->prevMethod =__FUNCTION__;
return $this;
}

public function white()
{
/*
your routine
for the white
method here
*/
$this->prevMethod =__FUNCTION__;
return $this;
}

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

}

$c = new ColorChanger();
$c->black->colour();
$c->white()->colour();
$c->black()->white->colourMethod();//will return white instead of black

PHP method chain with loop

I've tried to simplify the code to show just this one aspect of what your after...

class Bar
{
private $list;

public function __construct($a=null) {
$this->list = $a;
}

public function each( callable $fn )
{
foreach ( $this->list as $value ) {
$fn($value);
}
return $this;
}
}

$a=array('bob','andy','sue','rob');
$bar1 = (new Bar($a))->each(function ($value) {
print $value."\n";
});

As you can see, I've created the object with the list you have, and then just called each() with a callable. You can see the function just takes the passed in value and echoes it out.

Then in each() there is a loop across all the items in the list provided in the constructor and calls the closure ($fn($value);) with each value from the list.

The output from this is...

bob
andy
sue
rob

As for the chained calls, the idea is (as you've worked out) is to return an object which will be the start point for the next call. Some use $this (as you do) some systems (like Request commonly does) return a NEW copy of the object passed in. This is commonly linked to the idea of immutable objects. The idea being that you never change the original object, but you create a new object with the changes made in it. Psr7 Http Message, why immutable? gives some more insight into this.

Chain methods ability without force to new keyword using php

Really all you need is for the static firstName methods to create a new instance of the class and return it.

The other setters just need to return $this to provide what's referred to as a fluent interface.

If the only way to create an instance is via the static firstName method, you'll also want to add a private / protected constructor.

For example

class Person
{
private $firstName;
private $lastName;
private $age;
private $father;

private function __construct(string $firstName) {
$this->firstName = $firstName;
}

public static function firstName(string $name) {
return new Person($name);
}

public function lastName(string $lastName) {
$this->lastName = $lastName;
return $this;
}

public function age(int $age) {
$this->age = $age;
return $this;
}

public function setFather(Father $father) {
$this->father = $father;
return $this;
}

public function toArray() {
// just an example
return [
'firstName' => $this->firstName,
'lastName' => $this->lastName,
'age' => $this->age,
'father' => $this->father->toArray(),
];
}
}

I would strongly advise against keeping the $name property as static. You don't want to change one instance's $name and have it change all others. This is why I've changed it to private $firstName in my example above.

Can I somehow chain methods in PHP

What you want is essentially an array reduction:

$resp = array_reduce($methods, function ($o, $p) { return $o->$p; }, $this);

It's unclear whether you want $o->$p (property access) or $o->$p() (method call), but you can figure that out.

Method chaining in php

You should make use of exceptions instead, which would handle errors while the methods themselves would always return the current instance.

This becomes:

<?php
class FormValidationException extends \Exception
{
}

class FormValidator
{
private $value;

public function Value($value): self
{
$this->value = trim($value);
return $this;
}

/**
* @return $this
* @throws FormValidationException
*/
public function Required(): self
{
if (empty($this->value)) {
throw new \FormValidationException('Value is required.');
}
return $this;
}

/**
* @param int $length
* @return $this
* @throws FormValidationException
*/
public function MinLength(int $length): self
{
$len = strlen($this->value);
if ($len < $length) {
throw new \FormValidationException("Value should be at least {$length} characters long.");
}
return $this;
}
}

Usage:

$validator = new FormValidator();
try {
$result = $validator->Value("lodddl")->Required()->MinLength(5);
} catch (\FormValidationException $e) {
echo 'Error: ', $e->getMessage();
}

Demo: https://3v4l.org/OFcPV

Edit: since OP is using PHP 5.2 (sadly), here's a version for it, removing \s before root namespace, return type declarations and argument types.

Demo for PHP 5.2: https://3v4l.org/cagWS

PHP Method Chaining and Strings

$chain->AAA()->BBB() is doing the first two 'AAA' and 'BBB' - obvious.

Then the $chain->AAA() which comes inside wrap($chain->AAA()) does the 3rd 'AAA'.

and last, the wrap method takes all the three and wraps them with () and concatenated to the first 'AAA' and 'BBB' using this line: $this->_str[$part] = "({$str})";
which resolves to: AAA BBB (AAA BBB AAA).

UPDATE:
I believe that what you're trying to do, is to avoid the side-effect of returning this from methods AAA() and BBB() - will be achieved with the following changes:

<?php
class Chain {

protected $_str = '';
protected $_part = 0;

public function __toString() {
return implode(' ', $this->_str);
}

public function AAA () {
$this->_str[$this->_part] = 'AAA';
$this->_part++;
return "AAA";
}

public function BBB () {
$this->_str[$this->_part] = 'BBB';
$this->_part++;
return "BBB";
}

public function wrap ($str) {
$part = $this->_part - 1;
$this->_str[$part] = "({$str})";
return $str;
}

}

$chain = new Chain();
$chain->AAA();
$chain->BBB();
$chain->wrap($chain->AAA());
echo $chain->__toString();

?>


Related Topics



Leave a reply



Submit