Method Chains PHP Oop

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!

PHP OOP: Method Chaining

You cannot use Method Chaining with static methods because you cannot return a class level scope (return self won't do). Change your methods to regular methods and return $this in each method you want to allow chaining from.

Notice that you should not use T_PAAMAYIM_NEKUDOTAYIM to access instance methods as it will raise an E_STRICT Notice. Use T_OBJECT_OPERATOR for calling instance methods.

Also see:

  • Chaining Static Methods in PHP?

PHP: Chaining Method Calls

It's called method chaining. Have a look at this question on SO.

Here's a way you can do what you're trying to achieve:

class File 
{
protected $_fileName;

public function selectFile($filename)
{
$this->_fileName = $filename;
return $this;
}

public function getData()
{
return file_get_contents($this->_fileName);
}
}

$file = new File();
echo $file->selectFile('hello.txt')->getData();

Notice we return $this in the selectFile, this enables us to chain another method onto it.

PHP Last Object of Method Chaining

You could keep a list of things that needs to be initialised and whether they
have been so in this instance or not. Then check the list each time you use
one of the initialisation methods. Something like:

class O {
private $init = array
( 'red' => false
, 'green' => false
, 'blue' => false
);

private function isInit() {
$fin = true;
foreach($this->init as $in) {
$fin = $fin && $in;
}
return $fin;
}

public function green($n) {
$this->init['green'] = true;
if($this->isInit()) {
$this->insert();
}
}

public function red($n) {
$this->init['red'] = true;
if($this->isInit()) {
$this->insert();
}
}

public function blue($n) {
$this->init['blue'] = true;
if($this->isInit()) {
$this->insert();
}
}

private function insert() {
echo "whee\n";
}
}

But personally I think this would be more hassle then it's worth. Better imo
to expose your insert method and let the user of you code tell when the
initialisation is finished. So something that should be used like:

$o->red(1)->green(2)->blue(0)->insert();

-update-

If it's the case that it's impossible to predict what functions need to be called
you really do need to be explicit about it. I can't see a way around that. The reason
is that php really can't tell the difference between

$o1 = new A();
$o2 = $o1->stuff();

and

$o2 = (new A())->stuff();

In a language that allows overloading = I guess it would be possible but really
really confusing and generally not a good idea.

It is possible to move the explicit part so that it's not at the end of the call
chain, but I'm not sure if that would make you happier? It would also go against
your desire to not use another instance. It could look something like this:

class O {
public function __construct(InitO $ini) {
// Do stuff
echo "Whee\n";
}
}

class InitO {
public function red($n) {
return $this;
}
public function green($n) {
return $this;
}
public function blue($n) {
return $this;
}
}

$o = new O((new InitO())->red(10)->red(9)->green(7));

You can of course use just one instance by using some other way of wrapping
but the only ways I can think of right now would look a lot uglier.

How to return function values when using method chaining in PHP?

If you want to chain methods, you need to return the current instance from any method which has another call chained on after it. However, it's not necessary for the last call in the chain to do so. In this case that means you're free to return whatever you like from test2()

Just bear in mind, if you return something different from test2() you'll never be able to chain anything onto it in future. For example, $data = Test::test(...)->test2(...)->test1(...); wouldn't work.

Protip: it's worth documenting your code with some comments explaining which ones are and aren't chainable so you don't forget in future.

PHP: Method Chain with new self()?

If I'm not misunderstanding what you're trying to do, if you don't make the second method static, you can return an object and it'll be passed in as $this in the chained call;

class SomeClass {

public $id = 0;

public static function one($id)
{
$obj = new self(); // Create and return new object
$obj->id = $id;
return $obj;
}

public function two()
{
$this->id = 3; // The new object is $this here
return $this;
}
}

$obj = SomeClass::one(5)->two();

When chaining methods in PHP, Is it possible to determine which method was called first?

I think it is not necessary to check sequence of called methods. You can create one finally method (for example execute() ), and in it check all attributes you needed for query, if any attribute is missing (for example select) you can set default value for it: if available(for example 'select' => '*').

But in any case if you want to check is some method called before concrete method, you can set private attribute to keep methods requirements. For example:

private $_methodsRequirements = array(
'from' => array('select'),
'set' => array('update', 'where'),
// and all requiriments for each method
)
private $_calledMethods = array();

and create additional method for checking methods "callability":

private function checkCallability($method) {
$requiredMethods = $this->_methodsRequirements[$method];
foreach($requiredMethods as $m) {
if(!in_array($m, $this->_calledMethods)) {
throw new Exception('You must call method "'.$m.'" before calling method "'.$method.'"!');
}
}
return true;

}

and on begin of each method you must call it:

public function select() {
$this->checkCallability(__METHOD__);
// your query generation
array_push($this->_calledMethods, __METHOD__);
}


Related Topics



Leave a reply



Submit