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?
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?
Respostas:
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"
$foo->setBar(1)->setBaz(2)
vs $table->select()->from('foo')->where('bar = 1')->order('ASC)
. O último abrange vários objetos.
$a = (new fakeString())->addA()->addB()->getStr();
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:
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.
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;
}
return &$this
no PHP4?
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'
?>
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
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);
}
}
echo Maker::words(['foo', 'bob', 'bar'])->concate('-')->get();
echo "<br />";
echo Maker::words(['foo', 'bob', 'bar'])->concate('>')->get();
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
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"
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);
}
}