【问题标题】:PHP method chaining or fluent interface?PHP 方法链还是流利的接口?
【发布时间】:2011-04-13 01:16:19
【问题描述】:

我正在使用 PHP 5,并且听说了面向对象方法中的一个新特性,称为“方法链”。究竟是什么?如何实现?

【问题讨论】:

  • 我想说,如果不是所有这些问题,大部分都是关于链接的技术问题,更具体地说,是关于如何实现它。
  • @Kristoffer OP 可以很容易地从这些问题中找到它是如何实现的。
  • @Kristoffer 此外,搜索method chaining php on Google 会得到Salathetutorial 作为第一个结果。我不介意回答简单的问题,但有些人太懒了。

标签: php oop method-chaining fluent-interface


【解决方案1】:

其实很简单。您有一系列mutator methods,它们都返回原始(或其他)对象。这样,您可以继续在返回的对象上调用方法。

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

Try it online!

【讨论】:

  • 这有时也被称为 Fluent Interface
  • @Nitesh 不正确。 Fluent Interfaces 使用 Method Chaining 作为他们的主要机制,但 it's not the same。方法链仅返回宿主对象,而 Fluent 接口旨在创建 DSL。例如:$foo-&gt;setBar(1)-&gt;setBaz(2)$table-&gt;select()-&gt;from('foo')-&gt;where('bar = 1')-&gt;order('ASC)。后者跨越多个对象。
  • 公共函数 __toString() { return $this->str;如果您已经在回显链,则不需要最后一个方法“getStr()”。
  • @tfont 是的,但随后我们将引入魔术方法。一次一个概念就足够了。
  • 从 PHP 5.4 开始,甚至可以在一行中一切$a = (new fakeString())-&gt;addA()-&gt;addB()-&gt;getStr();
【解决方案2】:

方法链接意味着您可以链接方法调用:

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

这意味着method1()需要返回一个对象,method2()得到method1()的结果。 Method2() 然后将返回值传递给 method3()。

好文章:http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html

【讨论】:

  • 解释有点不对劲。返回值不会被传递。这些方法只是返回宿主对象。
  • @Gordon 好吧,没有返回主机对象。任何对象都可以返回和链接。
  • 那么我认为这不是 Fowler 定义的方法链,例如Make modifier methods return the host object so that multiple modifiers can be invoked in a single expression. - 如果你返回其他对象,它更可能是一个 Fluent 接口 :)
  • 哇,我意识到我正在评论一篇已有近 8 年历史的帖子。但您在那里的链接正在重定向到其他网站。仅供参考。
【解决方案3】:

基本上,你拿一个对象:

$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;
    }
    

【讨论】:

  • 你能用 PHP4 做return &amp;$this 吗?
  • @alex: 我现在没有 PHP 4 可以测试,但我很确定没有。
  • 我也不这么认为,但它应该工作对吗?也许如果 PHP4 不是那么 PHP4-ish。
【解决方案4】:

有 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 的所有 70 个 array_ 函数的文章。

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

【讨论】:

  • 这并不是一个真正的答案,而是一个指向具有潜在答案的网页的链接。
【解决方案5】:

试试这个代码:

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

?>

【讨论】:

  • 这就是我所说的很好的解释...链接方法总是让我起鸡皮疙瘩!!
  • 我如何识别(在方法内部)链中的第一个和最后一个元素(调用)。因为有时这只是一个按顺序执行的操作列表,而是在收集所有元素后应该做的事情。就像在这里执行 SQL 查询一样——但要注意,您可以对一个对象进行多次链式调用!每个中的第一个和最后一个。
【解决方案6】:

下面是我的模型,它可以在数据库中通过 ID 找到。 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);
    }
}

【讨论】:

  • 你能解释一下这是做什么的吗?
  • 任何解释这是做什么的?
【解决方案7】:

静态方法链接的另一种方式:

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

【讨论】:

    【解决方案8】:

    如果你指的是 JavaScript 中的方法链(或者有些人记得 jQuery),为什么不直接使用一个库来提供该开发。有PHP经验吗?例如 Extras - https://dsheiko.github.io/extras/ 这是一个使用 JavaScript 和 Underscore 方法扩展 PHP 类型并提供链接:

    您可以链接特定类型:

    <?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 年的历史,并且已经得到了许多更好的答案
    【解决方案9】:

    我认为这是最相关的答案。

    <?php
    
    class Calculator
    {
      protected $result = 0;
    
      public function sum($num)
      {
        $this->result += $num;
        return $this;
      }
    
      public function sub($num)
      {
        $this->result -= $num;
        return $this;
      }
    
      public function result()
      {
        return $this->result;
      }
    }
    
    $calculator = new Calculator;
    echo $calculator->sum(10)->sub(5)->sum(3)->result(); // 8
    

    【讨论】:

    • 这个答案缺少教育解释。
    【解决方案10】:

    流畅的接口允许您链接方法调用,从而在对同一对象应用多个操作时减少键入的字符。

    class Bill { 
    
        public $dinner    = 20;
    
        public $desserts  = 5;
    
        public $bill;
    
        public function dinner( $person ) {
            $this->bill += $this->dinner * $person;
            return $this;
        }
        public function dessert( $person ) {
            $this->bill += $this->desserts * $person;
            return $this;
        }
    }
    
    $bill = new Bill();
    
    echo $bill->dinner( 2 )->dessert( 3 )->bill;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-17
      • 1970-01-01
      • 1970-01-01
      • 2010-10-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多