Método PHP encadenamiento?

170

Estoy usando PHP 5 y he oído hablar de una nueva característica del enfoque orientado a objetos, llamada 'encadenamiento de métodos'. ¿Qué es exactamente? ¿Cómo lo implemento?

Sanjay Khatri
fuente
1
Diría que la mayoría de las preguntas, si no todas, son sobre tecnicismos relacionados con el encadenamiento, más específicamente sobre cómo lograrlo.
Kristoffer Sall-Storgaard
@Kristoffer el OP podría haber encontrado fácilmente cómo se logra a partir de estas preguntas.
Gordon
2
@Kristoffer además, la búsqueda de un método para encadenar php en Google le habría dado al OP un tutorial de Salathe como primer resultado. No me importa responder preguntas fáciles, pero algunas personas son demasiado flojas.
Gordon
66
Presento

Respuestas:

334

Es bastante simple en realidad, tiene una serie de métodos mutantes que devuelven todos los objetos originales (u otros), de esa manera puede seguir llamando a métodos en el objeto devuelto.

<?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();

Esto genera "ab"

Pruébalo en línea!

Kristoffer Sall-Storgaard
fuente
11
Esto a veces también se conoce como interfaz fluida
Nithesh Chandra
18
@Nitesh eso es incorrecto. Las interfaces fluidas usan el método de encadenamiento como su mecanismo principal, pero no es lo mismo . El encadenamiento de métodos simplemente devuelve el objeto host, mientras que una interfaz fluida está destinada a crear un DSL . Ej: $foo->setBar(1)->setBaz(2)vs $table->select()->from('foo')->where('bar = 1')->order('ASC). Este último abarca múltiples objetos.
Gordon
3
función pública __toString () {return $ this-> str; } Esto no requerirá el último método "getStr ()" si ya está repitiendo la cadena.
tfont
66
@tfont True, pero luego presentamos métodos mágicos. Un concepto a la vez debería ser suficiente.
Kristoffer Sall-Storgaard
3
Desde PHP 5.4 es incluso posible para todo en una línea:$a = (new fakeString())->addA()->addB()->getStr();
Philzen
48

Básicamente, tomas un objeto:

$obj = new ObjectWithChainableMethods();

Llame a un método que efectivamente haga un return $this;al final:

$obj->doSomething();

Dado que devuelve el mismo objeto, o más bien, una referencia al mismo objeto, puede continuar llamando a los métodos de la misma clase del valor de retorno, así:

$obj->doSomething()->doSomethingElse();

Eso es todo. Dos cosas importantes:

  1. Como notará, es solo PHP 5. No funcionará correctamente en PHP 4 porque devuelve objetos por valor y eso significa que está llamando a métodos en diferentes copias de un objeto, lo que rompería su código.

  2. Nuevamente, debe devolver el objeto en sus métodos encadenables:

    public function doSomething() {
        // Do stuff
        return $this;
    }
    
    public function doSomethingElse() {
        // Do more stuff
        return $this;
    }
BoltClock
fuente
¿Podrías hacer return &$thisen PHP4?
alex
@alex: No tengo PHP 4 para probar en este momento, pero estoy bastante seguro de que no.
BoltClock
44
Yo tampoco lo creía, pero debería funcionar, ¿verdad? Quizás si PHP4 no fuera tan PHP4-ish.
alex
Puede obtener los pasos simples y completos del método de encadenamiento en techflirt.com/tutorials/oop-in-php/php-method-chaining.html
Ankur Kumar Singh
28

Prueba este código:

<?php
class DBManager
{
    private $selectables = array();
    private $table;
    private $whereClause;
    private $limit;

    public function select() {
        $this->selectables = func_get_args();
        return $this;
    }

    public function from($table) {
        $this->table = $table;
        return $this;
    }

    public function where($where) {
        $this->whereClause = $where;
        return $this;
    }

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

    public function result() {
        $query[] = "SELECT";
        // if the selectables array is empty, select all
        if (empty($this->selectables)) {
            $query[] = "*";  
        }
        // else select according to selectables
        else {
            $query[] = join(', ', $this->selectables);
        }

        $query[] = "FROM";
        $query[] = $this->table;

        if (!empty($this->whereClause)) {
            $query[] = "WHERE";
            $query[] = $this->whereClause;
        }

        if (!empty($this->limit)) {
            $query[] = "LIMIT";
            $query[] = $this->limit;
        }

        return join(' ', $query);
    }
}

// Now to use the class and see how METHOD CHAINING works
// let us instantiate the class DBManager
$testOne = new DBManager();
$testOne->select()->from('users');
echo $testOne->result();
// OR
echo $testOne->select()->from('users')->result();
// both displays: 'SELECT * FROM users'

$testTwo = new DBManager();
$testTwo->select()->from('posts')->where('id > 200')->limit(10);
echo $testTwo->result();
// this displays: 'SELECT * FROM posts WHERE id > 200 LIMIT 10'

$testThree = new DBManager();
$testThree->select(
    'firstname',
    'email',
    'country',
    'city'
)->from('users')->where('id = 2399');
echo $testThree->result();
// this will display:
// 'SELECT firstname, email, country, city FROM users WHERE id = 2399'

?>
mwangaben
fuente
1
esto es lo que yo llamo una buena explicación ... ¡encadenar métodos siempre me pone la piel de gallina!
MYNE
Cómo identifico (dentro del método) el primer y último elemento (llamadas) en la cadena. Porque a veces esto es solo una lista de operaciones que se ejecutarán en orden, pero es algo que debe hacerse después de recopilar todos los elementos. Como ejecutar una consulta SQL aquí, pero tenga cuidado, ¡podría hacer múltiples llamadas encadenadas en un objeto! Firt y último en cada uno.
Andris
12

El encadenamiento de métodos significa que puede encadenar llamadas a métodos:

$object->method1()->method2()->method3()

Esto significa que method1 () necesita devolver un objeto, y method2 () recibe el resultado de method1 (). Method2 () luego pasa el valor de retorno a method3 ().

Buen artículo: http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html

alexn
fuente
55
La explicación está un poco fuera de lugar. Los valores de retorno no se pasan. Los métodos simplemente devuelven el objeto host.
Gordon
@ Gordon Bueno, el objeto host no se devuelve. Cualquier objeto puede ser devuelto y encadenado.
alexn
2
Entonces argumentaría que no es el método de encadenamiento como lo define Fowler, por ejemplo, los métodos Make modifier devuelven el objeto host para que se puedan invocar múltiples modificadores en una sola expresión. - si devuelve otros objetos, es más probable que sea una interfaz fluida :)
Gordon
Wow, me doy cuenta de que estoy comentando una publicación de casi 8 años. Pero tu enlace que tienes allí, está redirigiendo a otro sitio web. Solo para tu información.
willbeeler
11

Otra forma de encadenar métodos estáticos:

class Maker 
{
    private static $result      = null;
    private static $delimiter   = '.';
    private static $data        = [];

    public static function words($words)
    {
        if( !empty($words) && count($words) )
        {
            foreach ($words as $w)
            {
                self::$data[] = $w;
            }
        }        
        return new static;
    }

    public static function concate($delimiter)
    {
        self::$delimiter = $delimiter;
        foreach (self::$data as $d)
        {
            self::$result .= $d.$delimiter;
        }
        return new static;
    }

    public static function get()
    {
        return rtrim(self::$result, self::$delimiter);
    }    
}

Vocación

echo Maker::words(['foo', 'bob', 'bar'])->concate('-')->get();

echo "<br />";

echo Maker::words(['foo', 'bob', 'bar'])->concate('>')->get();
Rashedul Islam Sagor
fuente
6

Hay 49 líneas de código que le permiten encadenar métodos sobre matrices como esta:

$fruits = new Arr(array("lemon", "orange", "banana", "apple"));
$fruits->change_key_case(CASE_UPPER)->filter()->walk(function($value,$key) {
     echo $key.': '.$value."\r\n";
});

Vea este artículo que le muestra cómo encadenar todas las setenta funciones array_ de PHP.

http://domexception.blogspot.fi/2013/08/php-magic-methods-and-arrayobject.html

Lukas Dong
fuente
55
Esto no es realmente una respuesta, sino un enlace a una página web con una respuesta potencial.
faintsignal
-1

Si te refieres al método de encadenamiento como en JavaScript (o algunas personas tienen en cuenta jQuery), ¿por qué no simplemente tomar una biblioteca que trae ese desarrollador? experiencia en PHP? Por ejemplo, Extras: https://dsheiko.github.io/extras/ Este amplía los tipos de PHP con los métodos JavaScript y subrayado y proporciona el encadenamiento:

Puede encadenar un tipo particular:

<?php
use \Dsheiko\Extras\Arrays;
// Chain of calls
$res = Arrays::chain([1, 2, 3])
    ->map(function($num){ return $num + 1; })
    ->filter(function($num){ return $num > 1; })
    ->reduce(function($carry, $num){ return $carry + $num; }, 0)
    ->value();

o

<?php
use \Dsheiko\Extras\Strings;
$res = Strings::from( " 12345 " )
            ->replace("/1/", "5")
            ->replace("/2/", "5")
            ->trim()
            ->substr(1, 3)
            ->get();
echo $res; // "534"

Alternativamente, puede ir polimórfico:

<?php
use \Dsheiko\Extras\Any;

$res = Any::chain(new \ArrayObject([1,2,3]))
    ->toArray() // value is [1,2,3]
    ->map(function($num){ return [ "num" => $num ]; })
    // value is [[ "num" => 1, ..]]
    ->reduce(function($carry, $arr){
        $carry .= $arr["num"];
        return $carry;

    }, "") // value is "123"
    ->replace("/2/", "") // value is "13"
    ->then(function($value){
      if (empty($value)) {
        throw new \Exception("Empty value");
      }
      return $value;
    })
    ->value();
echo $res; // "13"
Dmitry Sheiko
fuente
Esto realmente no responde la pregunta ("¿Qué es el encadenamiento de métodos?"). Además, la pregunta original tiene 8 años y ya ha recibido una serie de mejores respuestas
GordonM
-1

A continuación se muestra mi modelo que puede encontrar por ID en la base de datos. El método with ($ data) son mis parámetros adicionales para la relación, por lo que devuelvo $ this, que es el objeto en sí. En mi controlador puedo encadenarlo.

class JobModel implements JobInterface{

        protected $job;

        public function __construct(Model $job){
            $this->job = $job;
        }

        public function find($id){
            return $this->job->find($id);
        }

        public function with($data=[]){
            $this->job = $this->job->with($params);
            return $this;
        }
}

class JobController{
    protected $job;

    public function __construct(JobModel $job){
        $this->job = $job;
    }

    public function index(){
        // chaining must be in order
        $this->job->with(['data'])->find(1);
    }
}
Juan bruno
fuente
¿Puedes explicar qué hace esto?
ichimaru
alguna explicación de lo que esto hace?
Patrick