один из паттернов ООП – цепочку вызовов. Если вы работали с 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