PHP метод ланцюга?


170

Я використовую PHP 5, і я чув про новий, представлений в об'єктно-орієнтованому підході, який називається "ланцюжок методів". Що це саме? Як це я реалізувати?


1
Я б сказав, що якщо не всі ці питання стосуються технічних питань, пов'язаних з ланцюжком, то конкретніше, як цього досягти.
Крістофер Салл-Сторгард

@Kristoffer ОП може легко зрозуміти, як це досягти з цих питань.
Гордон

2
@Kristoffer, крім того, пошук методу ланцюжка php в Google дав би ОП підручник від Салате як перший результат. Я не проти відповідати на легкі запитання, але деякі люди просто ліниві.
Гордон

6
Я
подаю

Відповіді:


333

Насправді досить просто, у вас є ряд мутаційних методів, які всі повертають оригінальні (або інші) об'єкти, таким чином ви можете тримати методи виклику на поверненому об'єкті.

<?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"

Спробуйте в Інтернеті!


10
Це також іноді називають
плавним

17
@Nitesh це неправильно. Плавні інтерфейси використовують метод ланцюжка як основний механізм, але це не той самий . Метод ланцюжка просто повертає об'єкт хосту, тоді як Fluent Interface спрямований на створення DSL . Приклад: $foo->setBar(1)->setBaz(2)проти $table->select()->from('foo')->where('bar = 1')->order('ASC). Останній охоплює кілька об'єктів.
Гордон

3
публічна функція __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;
    }
    

Не могли б ви зробити return &$thisв PHP4?
alex

@alex: У мене зараз немає PHP 4 для тестування, але я впевнений, що ні.
BoltClock

4
Я так і не думав, але це має працювати правильно? Можливо, якщо PHP4 не був таким PHP4-ish.
алекс


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'

?>

1
це те, що я називаю гарним поясненням ... прив'язка методів завжди дає мені goosebumbs !!
МІНЕ

Як я визначаю (всередині методу) перший та останній елементи (виклики) ланцюга. Тому що іноді це лише перелік операцій, які потрібно виконати в порядку, а щось, що слід зробити після збору всіх елементів. Як і виконувати тут запит SQL - але будьте обережні, ви можете робити кілька ланцюгових дзвінків на одному об’єкті! Фіртка і остання в кожному.
Андріс

12

Ланцюжок методів означає, що ви можете ланцюжок викликів методів:

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

Це означає, що method1 () потрібно повернути об'єкт, а method2 () отримує результат method1 (). Потім Method2 () передає повернене значення method3 ().

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


5
Пояснення трохи не вдається. Повернені значення не передаються навколо. Методи просто повертають об'єкт хосту.
Гордон

@Gordon Добре, об'єкт хосту не повертається. Будь-який об’єкт можна повернути і прикувати.
alexn

2
Тоді я б стверджував, що це не метод ланцюга, як визначено Фаулером, наприклад, Зробити способи модифікатора повернути об'єкт хоста, щоб можна було викликати кілька модифікаторів в одному виразі. - якщо ви повернете інші об'єкти, швидше за все, це Fluent Interface :)
Gordon

Вау, я розумію, що я коментую майже 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.

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


5
Це насправді не така відповідь, скільки посилання на веб-сторінку з потенційною відповіддю.
фансигнал

-1

Якщо ви маєте на увазі способи ланцюга, як у JavaScript (або деякі люди пам'ятають jQuery), чому б просто не взяти бібліотеку, яка приносить цей розробник. досвід роботи в PHP? Наприклад Додаткові дані - https://dsheiko.github.io/extras/ Цей тип розширює типи PHP методами JavaScript та підкреслення та забезпечує ланцюжок:

Ви можете зв’язати певний тип:

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

чи можете ви пояснити, що це робить?
ічімару

будь-яке пояснення, що це робить?
Патрік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.