Chaînage de méthodes PHP?

170

J'utilise PHP 5 et j'ai entendu parler d'une nouvelle fonctionnalité de l'approche orientée objet, appelée «chaînage de méthodes». C'est quoi exactement? Comment le mettre en œuvre?

Sanjay Khatri
la source
1
Je dirais que la plupart, sinon toutes, de ces questions portent sur des aspects techniques concernant le chaînage, c'est plus spécifiquement sur la façon d'y parvenir.
Kristoffer Sall-Storgaard
@Kristoffer l'OP aurait pu facilement trouver comment y parvenir à partir de ces questions.
Gordon
2
@Kristoffer en plus, la recherche de méthodes de chaînage php sur Google aurait donné à l'OP un tutoriel de Salathe comme tout premier résultat. Cela ne me dérange pas de répondre à des questions faciles, mais certaines personnes sont trop paresseuses.
Gordon le
6
Je soumets à votre lecture, l'arbre de décision définitif de chaînage de méthode
rdlowrey

Réponses:

334

C'est plutôt simple en fait, vous avez une série de méthodes de mutation qui retournent toutes les objets originaux (ou autres), de cette façon vous pouvez continuer à appeler des méthodes sur l'objet retourné.

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

Cela génère "ab"

Essayez-le en ligne!

Kristoffer Sall-Storgaard
la source
11
Ceci est également parfois appelé Interface Fluent
Nithesh Chandra
18
@Nitesh qui est incorrect. Les interfaces Fluent utilisent le chaînage de méthodes comme mécanisme principal, mais ce n'est pas la même chose . Le chaînage de méthodes renvoie simplement l'objet hôte, tandis qu'une interface Fluent vise à créer un DSL . Ex: $foo->setBar(1)->setBaz(2)vs $table->select()->from('foo')->where('bar = 1')->order('ASC). Ce dernier couvre plusieurs objets.
Gordon
3
public function __toString () {return $ this-> str; } Cela ne nécessitera pas la dernière méthode "getStr ()" si vous faites déjà écho à la chaîne.
tfont
6
@tfont True, mais nous introduisons ensuite des méthodes magiques. Un concept à la fois devrait suffire.
Kristoffer Sall-Storgaard
3
Depuis PHP 5.4, il est même possible de tout en une seule ligne:$a = (new fakeString())->addA()->addB()->getStr();
Philzen
48

En gros, vous prenez un objet:

$obj = new ObjectWithChainableMethods();

Appelez une méthode qui fait effectivement un return $this;à la fin:

$obj->doSomething();

Comme il renvoie le même objet, ou plutôt une référence au même objet, vous pouvez continuer à appeler des méthodes de la même classe à partir de la valeur de retour, comme ceci:

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

C'est ça, vraiment. Deux choses importantes:

  1. Comme vous le notez, il s'agit uniquement de PHP 5. Cela ne fonctionnera pas correctement en PHP 4 car il renvoie les objets par valeur et cela signifie que vous appelez des méthodes sur différentes copies d'un objet, ce qui briserait votre code.

  2. Encore une fois, vous devez renvoyer l'objet dans vos méthodes chaînables:

    public function doSomething() {
        // Do stuff
        return $this;
    }
    
    public function doSomethingElse() {
        // Do more stuff
        return $this;
    }
BoltClock
la source
Pourriez-vous faire return &$thisen PHP4?
alex
@alex: Je n'ai pas PHP 4 à tester pour le moment, mais je suis sûr que non.
BoltClock
4
Je ne le pensais pas non plus, mais cela devrait fonctionner correctement? Peut-être si PHP4 n'était pas aussi PHP4-ish.
alex
Vous pouvez obtenir les étapes simples et complètes du chaînage de méthodes sur techflirt.com/tutorials/oop-in-php/php-method-chaining.html
Ankur Kumar Singh
28

Essayez ce code:

<?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
la source
1
c'est ce que j'appelle une bonne explication ... les méthodes d'enchaînement me donnent toujours la chair de poule !!
MYNE
Comment j'identifie (à l'intérieur de la méthode) les premier et dernier éléments (appels) de la chaîne. Parce que parfois, il ne s'agit plus que d'une liste d'opérations à exécuter dans l'ordre, mais quelque chose qui devrait être fait après avoir collecté tous les éléments. Comme exécuter une requête SQL ici - mais attention, vous pouvez faire plusieurs appels chaînés sur un objet! Premier et dernier dans chacun.
Andris
12

Le chaînage de méthodes signifie que vous pouvez enchaîner les appels de méthode:

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

Cela signifie que method1 () doit renvoyer un objet et method2 () reçoit le résultat de method1 (). Method2 () passe ensuite la valeur de retour à method3 ().

Bon article: http://www.talkphp.com/advanced-php-programming/1163-php5-method-chaining.html

Alexn
la source
5
L'explication est un peu fausse. Les valeurs de retour ne sont pas transmises. Les méthodes renvoient simplement l'objet hôte.
Gordon
@Gordon Eh bien, l'objet hôte n'est pas retourné. Tout objet peut être retourné et enchaîné.
alexn
2
Ensuite, je dirais que ce n'est pas le chaînage de méthodes tel que défini par Fowler, par exemple, les méthodes de modificateur Make retournent l'objet hôte afin que plusieurs modificateurs puissent être invoqués dans une seule expression. - si vous retournez d'autres objets, il s'agit plus probablement d'une interface Fluent :)
Gordon
Wow, je me rends compte que je commente un article de presque 8 ans. Mais votre lien que vous avez là-bas redirige vers un autre site Web. Juste FYI.
willbeeler
11

Une autre façon de chaîner les méthodes statiques:

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

Appel

echo Maker::words(['foo', 'bob', 'bar'])->concate('-')->get();

echo "<br />";

echo Maker::words(['foo', 'bob', 'bar'])->concate('>')->get();
Rashedul Islam Sagor
la source
6

Il y a 49 lignes de code qui vous permettent d'enchaîner des méthodes sur des tableaux comme celui-ci:

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

Voir cet article qui vous montre comment enchaîner les soixante-dix fonctions array_ de PHP.

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

Lukas Dong
la source
5
Ce n'est pas vraiment une réponse, mais plutôt un lien vers une page Web avec une réponse potentielle.
faintsignal
-1

Si vous voulez dire le chaînage de méthodes comme en JavaScript (ou que certaines personnes gardent à l'esprit jQuery), pourquoi ne pas simplement prendre une bibliothèque qui apporte ce développement. expérience en PHP? Par exemple Extras - https://dsheiko.github.io/extras/ Celui-ci étend les types PHP avec les méthodes JavaScript et Underscore et fournit le chaînage:

Vous pouvez chaîner un type particulier:

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

ou

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

Vous pouvez également devenir polymorphe:

<?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"
Dmitry Sheiko
la source
Cela ne répond pas vraiment à la question ("Qu'est-ce que le chaînage de méthodes?"). De plus, la question originale a 8 ans et a déjà obtenu un certain nombre de meilleures réponses
GordonM
-1

Voici mon modèle qui est capable de trouver par ID dans la base de données. La méthode with ($ data) est mes paramètres supplémentaires pour la relation, donc je retourne le $ this qui est l'objet lui-même. Sur mon contrôleur, je peux l'enchaîner.

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
la source
pouvez-vous expliquer ce que cela fait?
ichimaru
une explication ce que cela fait?
Patrick