Pull to refresh

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

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

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

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

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


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

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

  abstract class ArticleEntryAbstract extends Storable {
	  public function __construct($table, $id = null) {
		  if (!is_null($id)) {
			  $id = array('id' => $id);
		  }
		  parent::__construct($table, '*', $id);
	  }

	  public function purge() {
		  $s = $this->db->prepare('DELETE FROM ' . $this->tableName . ' WHERE pulseEntryId = ?');
		  $s->execute(array($this->data['entryId']));
	  }
  }


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

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


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

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


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

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


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

ArticleEntryAttachment::purge(10);


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

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

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

Оригинал: Late Static Binding: a practical example
Tags:
Hubs:
Total votes 18: ↑15 and ↓3 +12
Views 8.9K
Comments Comments 4