Encadeamento de método PHP?

170

Estou usando o PHP 5 e ouvi falar de um novo recurso da abordagem orientada a objetos, chamado 'encadeamento de métodos'. O que é isso exatamente? Como faço para implementá-lo?

Sanjay Khatri
fonte
1
Eu diria que a maioria, se não todas essas perguntas, são sobre aspectos técnicos do encadeamento, isso é mais especificamente sobre como alcançá-lo.
Kristoffer Sall-Storgaard
@Kristoffer, o OP poderia facilmente descobrir como é alcançado com essas perguntas.
Gordon
2
@ Kristoffer, além disso, procurar o método encadeamento de php no Google daria ao OP um tutorial de Salathe como o primeiro resultado. Não me importo de responder perguntas fáceis, mas algumas pessoas são muito preguiçosas.
Gordon
6
I enviar para sua leitura, a árvore de decisão método de encadeamento definitiva
rdlowrey

Respostas:

333

Na verdade, é bastante simples: você tem uma série de métodos mutadores que retornam os objetos originais (ou outros), para que você possa continuar chamando métodos no objeto retornado.

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

Isso gera "ab"

Experimente online!

Kristoffer Sall-Storgaard
fonte
10
Esta é também por vezes referido como Fluent Interface
Nithesh Chandra
17
@ Nitesh está incorreto. As interfaces fluentes usam o encadeamento de métodos como mecanismo principal, mas não é o mesmo . O encadeamento de métodos simplesmente retorna o objeto host, enquanto uma Interface Fluente visa criar uma DSL . Ex: $foo->setBar(1)->setBaz(2)vs $table->select()->from('foo')->where('bar = 1')->order('ASC). O último abrange vários objetos.
Gordon
3
função pública __toString () {return $ this-> str; } Isso não exigirá o último método "getStr ()" se você já estiver repetindo a cadeia.
tfont
6
@tfont True, mas então estamos introduzindo métodos mágicos. Um conceito de cada vez deve ser suficiente.
Kristoffer Sall-Storgaard
3
Desde o PHP 5.4 é ainda possível tudo em uma linha:$a = (new fakeString())->addA()->addB()->getStr();
Philzen
48

Basicamente, você pega um objeto:

$obj = new ObjectWithChainableMethods();

Chame um método que efetivamente faça um return $this;no final:

$obj->doSomething();

Como ele retorna o mesmo objeto, ou melhor, uma referência ao mesmo objeto, você pode continuar chamando métodos da mesma classe para o valor de retorno, da seguinte maneira:

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

É isso mesmo. Duas coisas importantes:

  1. Como você observa, é apenas o PHP 5. Ele não funcionará corretamente no PHP 4 porque retorna objetos por valor e isso significa que você está chamando métodos em diferentes cópias de um objeto, o que quebraria seu código.

  2. Novamente, você precisa retornar o objeto em seus métodos encadeados:

    public function doSomething() {
        // Do stuff
        return $this;
    }
    
    public function doSomethingElse() {
        // Do more stuff
        return $this;
    }
BoltClock
fonte
Você poderia fazer return &$thisno PHP4?
Alex
@alex: Não tenho o PHP 4 para testar agora, mas tenho certeza que não.
BoltClock
4
Também não achei isso, mas deveria funcionar certo? Talvez se o PHP4 não fosse tão PHP4-ish.
alex
Você pode obter os passos completos do encadeamento método em techflirt.com/tutorials/oop-in-php/php-method-chaining.html
Ankur Kumar Singh
28

Tente 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
fonte
1
isso é o que eu chamo de uma boa explicação ... os métodos de encadeamento sempre me dão arrepios !!
MYNE
Como identifico (dentro do método) o primeiro e o último elemento (chamadas) na cadeia. Porque às vezes isso agora é apenas uma lista de operações a serem executadas em ordem, mas algo que deve ser feito após a coleta de todos os elementos. Como executar uma consulta SQL aqui - mas cuidado, você pode fazer várias chamadas encadeadas em um objeto! Primeiro e último em cada um.
Andris
12

O encadeamento de métodos significa que você pode encadear chamadas de métodos:

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

Isso significa que method1 () precisa retornar um objeto e method2 () recebe o resultado de method1 (). Method2 () passa o valor de retorno para method3 ().

Bom artigo: http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html

alexn
fonte
5
A explicação está um pouco errada. Os valores de retorno não são passados. Os métodos simplesmente retornam o objeto host.
Gordon
@ Gordon Bem, o objeto host não é retornado. Qualquer objeto pode ser retornado e encadeado.
alexn
2
Então eu argumentaria que não é um encadeamento de métodos, conforme definido por Fowler, por exemplo, métodos Make modifier retornam o objeto host para que vários modificadores possam ser chamados em uma única expressão. - se você retornar outros objetos, é mais provável uma interface fluente :)
Gordon
Uau, eu sei que estou comentando em um post de quase 8 anos. Mas o link que você tem lá está redirecionando para outro site. Apenas fyi.
willbeeler
11

Outro caminho para o encadeamento de 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);
    }    
}

Chamando

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

echo "<br />";

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

Existem 49 linhas de código que permitem que você encadeie métodos sobre matrizes 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";
});

Veja este artigo que mostra como encadear todas as setenta funções do array_ do PHP.

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

Lukas Dong
fonte
5
Isso não é realmente uma resposta, mas um link para uma página da Web com uma resposta em potencial.
faintsignal
-1

Se você quer dizer encadeamento de métodos como no JavaScript (ou algumas pessoas lembram do jQuery), por que não usar uma biblioteca que traga esse desenvolvedor. experiência em PHP? Por exemplo Extras - https://dsheiko.github.io/extras/ Este estende os tipos PHP com os métodos JavaScript e Underscore e fornece encadeamento:

Você pode encadear um tipo específico:

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

ou

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

Como alternativa, você pode 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
fonte
Isso realmente não responde à pergunta ("O que é encadeamento de métodos?"). Além disso, a pergunta original tem 8 anos e já tem várias respostas melhores
GordonM:
-1

Abaixo está o meu modelo que é capaz de encontrar por ID no banco de dados. O método with ($ data) são meus parâmetros adicionais para relacionamento, então eu retorno o $ this, que é o próprio objeto. No meu controlador eu sou capaz de encadeá-lo.

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);
    }
}
JuanBruno
fonte
você pode explicar o que isso faz?
Ichimaru
alguma explicação o que isso faz?
Patrick