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?
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?
Respuestas:
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"
$foo->setBar(1)->setBaz(2)
vs $table->select()->from('foo')->where('bar = 1')->order('ASC)
. Este último abarca múltiples objetos.
$a = (new fakeString())->addA()->addB()->getStr();
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:
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.
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;
}
return &$this
en PHP4?
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'
?>
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
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();
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
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"
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);
}
}