Позднее статическое связывание в PHP (Часть II: Практика)

phpПервую часть читайте здесь.

Теперь приступим к практике. Наиболее показательным примером использования LSB, по-моему, является случай, когда у вас есть набор классов выполняющих похожие действия. В терминах веб-разработки мы часто встречаемся с такими задачами при обращениях к таблицам базы данных, особенно в ORM системах. Все ваши объекты для работы с таблицами будут похожи по сути, но при этом будут иметь собственный функционал ( и, соответственно, свои подклассы).

Допустим, у нас в системе есть класс Storable, который реализует (как вы догадались) (прим. переводчика: я не догадался :) ) паттерн Storable. Мы определяем классы, которые наследуются от класса Storable и задаем имена таблиц в конструкторах. Вот как это выглядит:

<br />
  class ArticleEntry extends Storable {<br />
	  public function __construct($id = null) {<br />
		  if (!is_null($id)) {<br />
			  $id = array('id' => $id);<br />
		  }<br />
		  parent::__construct('articleEntry', '*', $id);<br />
	  }<br />
  }<br />
   <br />
  // вывод текста вхождения:<br />
  $entry = new ArticleEntry(10); // Выборка записи из таблицы articleEntry для которой id = 10;<br />
  echo $entry->html('articleBody'); // вывод тела загруженного вхождения<br />
   <br />
  // обновляем запись:<br />
  $entry['ts'] = time(); // устанавливаем время в NOW<br />
  $entry->save(); // Обновляем запись<br />


Можете пропустить подробности конструктора, он приведен только для того, чтобы вы представляли как работает класс Storable. Как вы уже успели понять, это сэкономит нам немного времени и позволит не тратить его на такие мелочи как простые запросы SELECT, INSERT и UPDATE.

В нашей системе, помимо основной таблицы Статьи (ArticleEntry), будут использоваться еще несколько таблиц, которые содержат мета-данные (в отношении многие-к-одному), например: теги, вложения. Мне также нужен простой способ удаления данных из под-таблиц, прежде чем обновлять данные в основной таблице (проще удалить мета-данные и создать заново, чем беспокоится о синхронизации данных). Итак, чтобы смоделировать код наиболее приближенный к схеме базы данных, я остановился на такой реализации:

<br />
  abstract class ArticleEntryAbstract extends Storable {<br />
	  public function __construct($table, $id = null) {<br />
		  if (!is_null($id)) {<br />
			  $id = array('id' => $id);<br />
		  }<br />
		  parent::__construct($table, '*', $id);<br />
	  }<br />
<br />
	  public function purge() {<br />
		  $s = $this->db->prepare('DELETE FROM ' . $this->tableName . ' WHERE pulseEntryId = ?');<br />
		  $s->execute(array($this->data['entryId']));<br />
	  }<br />
  }<br />


Весь фокус в методе purge(). Свойство $this->tableName является защищенным свойством, которое мы получили от класса Storable. Вот пример использования:

<br />
  class ArticleEntryAttachment extends ArticleEntryAbstract {<br />
	  public function __construct($id = null) {<br />
		  parent::__construct('articleEntryAttachment', $id);<br />
	  }<br />
  }<br />


У меня есть куча таких мелких классов для работы с таблицами мета-данных. К сожалению, поскольку наша система использует PHP версии 5.2, я не мог использовать функциональность LSB описанную в первой части статьи. И чтобы удалять мета-данные, я должен писать:

<br />
  $attach = new ArticleEntryAttachment(10); // SELECTS from the articleEntryAttachment table WHERE entryId = 10<br />
  $attach->purge();<br />


Если посмотрите выше, как определен метод purge(), то увидите, что tableName он получает из класса Storable, который получает его из конструкторов классов-потомков. Помимо этих тривиальных (но абсолютно необходимых) данных, а также получения объекта базы данных в $this->db, мы не достигли ничего создавая объект класса articleentryattachment. Код был бы намного понятнее и чище ( и конечно эффективнее), если бы можно было бы вызвать метод purge() статически. Рассмотрим такой код:

<br />
  abstract class ArticleEntryAbstract extends Storable {<br />
	  public function __construct($table, $id = null) {<br />
		  if (!is_null($id)) {<br />
			  $id = array('id' => $id);<br />
		  }<br />
		  parent::__construct(static::TABLE_NAME, '*', $id);<br />
	  }<br />
   <br />
	  static function purge($entryId) {<br />
		  $db = Db::get(); // получаем синглетон базы данных<br />
		  $s = $db->prepare('DELETE FROM ' . static::TABLE_NAME . ' WHERE pulseEntryId = ?');<br />
		  $s->execute(array($entryId));<br />
	  }<br />
  }<br />
   <br />
  class ArticleEntryAttachment extends ArticleEntryAbstract {<br />
	  const TABLE_NAME = 'articleAttachment';<br />
  }<br />


Первое, что, я надеюсь, вы заметили, это то что ArticleEntryAttachment стал намного проще. Теперь нет необходимости переопределять конструктор для подклассов, потому, что конструктор родительского класса самодостаточен. И теперь можно использовать метод purge() (используя LSB):

<br />
ArticleEntryAttachment::purge(10);<br />


Поскольку, теперь purge() может получать имя таблицы, которое определяется в момент выполнения, мы можем сделать его статическим. В итоге, код — чище, выполнение — эффективней, поддержка (например, добавление новых под-классов) — пустяковая, потому как избыточность полностью убрана. Спасибо разработчикам PHP за то что это стало возможным!

В мануале также обсуждаются другие способы использования LSB, включая использование константы __CLASS__, так что не забудьте посетить php.net

Via Denis.in.ua: Позднее статическое связывание в PHP (Часть II: Практика)

Оригинал: Late Static Binding: a practical example
  • 0
  • 2 июня 2010, 04:30
  • 0x00

Комментарии (0)

RSS свернуть / развернуть

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.