PHP метод цепочки?

170

Я использую PHP 5, и я слышал о новой функции в объектно-ориентированном подходе, называемой «сцепление методов». Что именно? Как мне это реализовать?

Санджай Хатри
источник
1
Я бы сказал, что большинство, если не все эти вопросы касаются технических аспектов, связанных с цепочкой, а точнее о том, как этого добиться.
Кристоффер Салл-Сторгард
@ Кристоффер ОП мог легко найти, как это достигается из этих вопросов.
Гордон
2
@Kristoffer кроме того, поиск метода цепной PHP на Google дал бы Op в учебник по Salathe в качестве самого первого результата. Я не против ответить на простые вопросы, но некоторые люди просто ленивы.
Гордон
6
Я представляю вашему
вниманию

Ответы:

334

На самом деле это довольно просто, у вас есть ряд методов-мутаторов, которые все возвращают исходные (или другие) объекты, так что вы можете продолжать вызывать методы для возвращаемого объекта.

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

Это выводит "ab"

Попробуйте онлайн!

Кристоффер Салл-Сторгард
источник
11
Это также иногда называют Свободным Интерфейсом
Нитеш Чандра
18
@ Нитеш, это неправильно. Свободные интерфейсы используют Метод Цепочка в качестве основного механизма, но это не то же самое . Цепочка методов просто возвращает хост-объект, а интерфейс Fluent предназначен для создания DSL . Пример: $foo->setBar(1)->setBaz(2)против $table->select()->from('foo')->where('bar = 1')->order('ASC). Последний охватывает несколько объектов.
Гордон
3
public function __toString () {return $ this-> str; } Это не потребует последнего метода "getStr ()", если вы уже повторяете цепочку.
tfont
6
@tfont Правда, но тогда мы представляем магические методы. Одной концепции за раз должно быть достаточно.
Кристоффер Салл-Сторгард
3
Начиная с PHP 5.4 , это возможно даже все в одной строке:$a = (new fakeString())->addA()->addB()->getStr();
Philzen
48

По сути, вы берете объект:

$obj = new ObjectWithChainableMethods();

Вызовите метод, который эффективно делает return $this;в конце:

$obj->doSomething();

Так как он возвращает тот же объект, или, скорее, ссылку на тот же объект, вы можете продолжать вызывать методы того же класса из возвращаемого значения, например так:

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

Это действительно так. Две важные вещи:

  1. Как вы заметили, это только PHP 5. В PHP 4 он не будет работать должным образом, потому что он возвращает объекты по значению, а это означает, что вы вызываете методы для разных копий объекта, что может нарушить ваш код.

  2. Опять же, вам нужно вернуть объект в ваших цепочечных методах:

    public function doSomething() {
        // Do stuff
        return $this;
    }
    
    public function doSomethingElse() {
        // Do more stuff
        return $this;
    }
BoltClock
источник
Не могли бы вы сделать return &$thisв PHP4?
Alex
@alex: У меня нет PHP 4 для тестирования прямо сейчас, но я уверен, что нет.
BoltClock
4
Я тоже так не думаю, но это должно сработать, верно? Возможно, если бы PHP4 не был таким PHP4-ишем.
Alex
Вы можете получить полные простые шаги цепочки методов по адресу techflirt.com/tutorials/oop-in-php/php-method-chaining.html
Кумар Сингх,
28

Попробуйте этот код:

<?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
источник
1
это то, что я называю хорошим объяснением ... методы цепочки всегда дают мне мурашки по коже !!
МИН
Как я идентифицирую (внутри метода) первый и последний элементы (вызовы) в цепочке. Потому что иногда это теперь просто список операций, которые должны быть выполнены по порядку, но что-то, что должно быть сделано после сбора всех элементов. Как и выполнение SQL-запроса здесь - но будьте осторожны, вы можете сделать несколько связанных вызовов для одного объекта! Ферт и последний в каждом.
Андрис
12

Цепочка методов означает, что вы можете связывать вызовы методов:

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

Это означает, что method1 () должен вернуть объект, а method2 () - результат method1 (). Метод 2 () затем передает возвращаемое значение методу 3 ().

Хорошая статья: http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html

AlexN
источник
5
Объяснение немного отклонено. Возвращаемые значения не передаются. Методы просто возвращают хост-объект.
Гордон
@Gordon Ну, хост-объект не возвращается. Любой объект может быть возвращен и прикован цепью.
Алекс
2
Тогда я бы сказал, что это не цепочка методов, как определено Фаулером, например , методы модификатора Make возвращают хост-объект, так что в одном выражении можно вызывать несколько модификаторов. - если вы вернете другие объекты, скорее всего это свободный интерфейс :)
Гордон,
Вау, я понимаю, что комментирую почти 8-летний пост. Но ваша ссылка, которая у вас есть, перенаправляет на какой-то другой сайт. Просто к вашему сведению.
Willbeeler
11

Другой способ для статического метода цепочки:

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();
Рашедул Ислам Сагор
источник
6

Есть 49 строк кода, которые позволяют вам связывать методы между массивами следующим образом:

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

Прочтите эту статью, в которой показано, как объединить все семьдесят функций PHP array_.

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

Лукас Донг
источник
5
Это на самом деле не столько ответ, сколько ссылка на веб-страницу с потенциальным ответом.
faintsignal
-1

Если вы имеете в виду цепочку методов, как в JavaScript (или некоторые люди имеют в виду jQuery), почему бы просто не взять библиотеку, которая приносит этот dev. опыт в PHP? Например, Дополнительно - https://dsheiko.github.io/extras/ Этот тип расширяет типы PHP с помощью методов JavaScript и Underscore и обеспечивает связывание:

Вы можете связать определенный тип:

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

или

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

В качестве альтернативы вы можете стать полиморфным:

<?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"
Дмитрий Шейко
источник
Это на самом деле не отвечает на вопрос («Что такое цепочка методов?»). Кроме того, первоначальному вопросу уже 8 лет, и он уже получил несколько лучших ответов
GordonM
-1

Ниже моя модель, которую можно найти по идентификатору в базе данных. Метод with ($ data) - это мои дополнительные параметры для отношений, поэтому я возвращаю $ this, который является самим объектом. На моем контроллере я могу подключиться.

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
источник
Вы можете объяснить, что это делает?
Ичимару
Любое объяснение, что это делает?
Патрик