PHP 方法链接还是流畅的接口?

我正在使用 PHP5,我听说过面向对象方法中的一个新特性,称为“方法链接”。到底是什么?我如何实现它?

99964 次浏览

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

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

上网试试!

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

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

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

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

基本上,你拿一个物体:

$obj = new ObjectWithChainableMethods();

调用一个在最后有效执行 return $this;的方法:

$obj->doSomething();

因为它返回相同的对象,或者更确切地说,返回相同对象的 参考文献,所以您可以继续从返回值调用相同类的方法,如下所示:

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

就是这样,真的,两件重要的事:

  1. 正如您注意到的,它只是 PHP5。它在 PHP4中无法正常工作,因为它通过值返回对象,这意味着您正在调用对象的不同副本上的方法,这将破坏您的代码。

  2. 同样,您需要在可链接方法中返回对象:

    public function doSomething() {
    // Do stuff
    return $this;
    }
    
    
    public function doSomethingElse() {
    // Do more stuff
    return $this;
    }
    

有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 _ function。

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

试试这个代码:

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


?>

下面是我的模型,它能够在数据库中通过 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);
}
}

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

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

如果你指的是像 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"

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

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

连贯接口允许您链接方法调用,这将导致在同一对象上应用多个操作时产生较少的类型字符。

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;