один из паттернов ООП – цепочку вызовов. Если вы работали с PHP фреймворками (Zend, CodeIgniter, CakePHP или другими), то наверняка видели подобный стиль вызова методов классов: [sourcecode language=”php”]$obj->foo()->bar()->anotherMethod();[/sourcecode] Такой стиль называется цепочка вызовов.Рассмотрим использование цепочки вызовов на простом классе: [sourcecode language=”php”]class Person { private $name; private $age; public function setName($Name) { $this->name = $Name; } public function setAge($Age) { $this->age = $Age; } public function findMe() { echo "Меня зовут ".$this->name." и мне ".$this->age." лет."; } }[/sourcecode] Для доступа к классу создадим объект класса Person: [sourcecode language=”php”]$myself = new Person(); $myself->setName(‘Александр’); $myself->setAge(’35’); $myself->findMe(); [/sourcecode] В результате получиться: [sourcecode language=”php”]Меня зовут Алексадр и мне 35 лет.[/sourcecode]

Как сделать цепочку вызовов:

Для того чтобы использовать цепочку вызовов, надо изменить класс, чтобы каждый метод “setXXX” возвращал объект $this. Теперь наш класс выглядит следующим образом: [sourcecode language=”php”] class Person { private $name; private $age; public function setName($Name) { $this->name = $Name; return $this;//возвращаем this, т.е. объект класса Person } public function setAge($Age) { $this->age = $Age; return $this;//опять возвращаем this, т.е. объект класса Person } public function findMe() { echo "Меня зовут ".$this->name." и мне ".$this->age. " лет."; } }[/sourcecode] Теперь можно использовать класс следующим образом: [sourcecode language=”php”]$myself = new Person(); $myself->setName(‘Алексей’)->setAge(’26’)->findMe(); [/sourcecode]

Суть концепции:

Скорее всего вы уже видели, как работает такой код. Но давайте разберем поподробнее. Методы в цепочке вызываются слева направо. $myself = new Person() – создает новый объект класса Person Далее, $myself->setName(‘Алексей’) присваивает значение переменной и возвращает все тот же объект. Т.к. $myself->setName(‘Алексей’) возвращает объект класса Person, то можно использовать методы этого класса. Зададим возраст пользователя $myself->setName(‘Алексей’)->setAge(’26’). Метод setAge(’26’) тоже возвращает объект $this, и можно опять вызывать другой метод этого класса. В конце концов, метод findMe отображает информацию о пользователе: $myself->setName(‘Алексей’)->setAge(’26’)->findMe(); Цепочка вызовов работает.

Цепочки вызовов в нескольких классах:

Если у в проекте несколько классов, то цепочки вызовов могут и тут облегчить вам жизнь. Рассмотрим пример: [sourcecode language=”php”]class ComplexPerson { public function setName($Name) { return new FindNames($Name);//возвращаем объект класса FindAges } public function setAge($Age) { $this->age = $Age; return new FindAges($Age);//возвращаем объект класса FindAges } } class FindNames { private $name; public function __construct($n) { $this->name = $n; } public function printName() { echo "Меня зовут ".$this->n."."; } } class FindAges { private $age; public function __construct($a) { $this->age = $a; } public function printAge() { echo "Мне ".$this->age." лет."; } } [/sourcecode] Теперь мы можем использовать цепочки вызовов в этих классах: [sourcecode language=”php”]$anotherMe = new ComplexPerson(); $anotherMe->setName("Алексадр")->printName(); $anotherMe->setAge("35")->printAge();[/sourcecode]

Цепочки вызовов и магические сеттеры

Как сделать так, чтобы присвоение свойст объекту было красивым и не требовало написания кучи кода? Методы присвоения свойств очень похожи – объединим их, воспользовавшись магическим методом __call. Рассмотрим это на примере: [sourcecode language=”php”] class Email { public $from; public $to = array(); public $subject; public $body; public function send() { // заглушка для отправки почты echo "Отправляем mail"; } public function __call($name, $arguments) { // присвоение переменной значения if (substr($name,0,4) == ‘set_’) { $property = substr($name,4); $this->$property = $arguments[0]; // добавление значения в массив } else if (substr($name,0,4) == ‘add_’) { $property = substr($name,4); array_push($this->$property, $arguments[0]); } // этот метод тоже может быть использован в цепочке вызовов return $this; } } [/sourcecode] Обратите внимание, что методов set_from, set_subject, set_body и add_to нет. Тем не менее Этот код дает возможность использовать класс следующим образом:[sourcecode language=”php”] require_once (‘Email.php’); $my_email = new Email(); $my_email ->set_from(‘test@email.com’) ->add_to(‘programming@gmail.com’) ->add_to(‘smth@gmail.com’) ->set_subject(‘Цепочки ввызовов’) ->set_body(Само сообщение’) ->send(); [/sourcecode]

Цепочки вызовов для валидаторов

Предположим у нас есть такой код для валидации отправляемых данных:[sourcecode language=”php”] $validation = new Validation(); $myvar = $_POST[‘somepostvariable’]; if ($validation->required($myvar) && $validation->isInt($myvar)) { print ‘Все верно’; } else { print ‘Не валидные данные’; }[/sourcecode] Как сделать его более легким для восприятия? Например, таким:[sourcecode language=”php”] $validation = new Validation(); $myvar = $_POST[‘somepostvariable’]; if ($validation->value($myvar)->required()->isInt()->execute()) { print ‘Все верно’; } else { print ‘Не валидные данные’; } [/sourcecode] [sourcecode language=”php”] Class Validation { public $value = ”; public $error_count = 0; public function __construct() { } public function value($value) { $this->value = $value; return $this; } public function required() { if (empty($this->value)) { $this->error_count++; } return $this; } public function isInt() { if (!is_numeric($this->value) || intval($this->value) !=$this->value) { $this->error_count++; } return $this; } public function execute() { if ($this->error_count>0) { $this->error_count = 0; return false; } else { return true; } } }[/sourcecode]

Цепочка вызовов в построении запроса к БД

Другим хорошим примером цепочки вызовов является стандартная реализация паттерна Active Record [sourcecode language=”php”] $db = new Database; $customers = $db->select("name,email") ->from("customers") ->where("name","joe") ->limit(5) ->execute();[/sourcecode] В результате будет сделан запрос: [sourcecode language=”sql”] SELECT name,email FROM customers WHERE name=’joe’ LIMIT 0,5[/sourcecode] Цепочки вызовов в PHP  
Цепочки вызовов в PHP