Введение
На определенной стадии изучения серверного программирования мне захотелось написать свой простенький фреймворк. Я рассчитывал, что это поможет более глубоко понять идеологию MVC и Zend Framework в частности. Когда дело дошло до части представления и генерации html я вспомнил о паттерне Компоновщик (Composite pattern). Возможно я несколько исказил его применение, но мысль пошла оттуда.
К сути
Любая страница состоит из объектов, которые так-же могут состоять из объектов. Этакая вложенность наталкивает на мысль рекурсивной генерации html-кода. Так же хочется иметь возможность дополнять содержимое любой части странице в любой момент исполнения логики приложения и генерировать весь html-код разом.
К делу
Любой объект может содержать другой объект и т.д. Каждый объект обладает методом draw(), который отображает html-код своего объекта и вызывает метод draw() для всех дочерних объектов. Объекты могут быть разных типов (например: статья, список, пост, фотоальбом, шапка в конце концов). Конечно реализация метода draw() для каждого типа объектов будет своя. Хотелось бы выразить выше написанное кодом.
abstract class AbstractView {
public $fillings;
abstract public function draw();
protected function insert($filling_name){
if(isset($this->fillings[$filling_name])){
$this->fillings[$filling_name]->draw();
}
}
}
Метод insert() — метод, с помощью которого в методе draw() будут вызываться методы draw() дочерних объектов. Все дочерние объекты содержатся в ассоциативном массиве $fillings. Часто необходимо не генерировать html-код, а просто написать текст или число. Можно конечно Для таких целей наследовать объект View, метод draw() которого будет просто выводить необходимое значение, указанное, например, в конструкторе или дополнительном свойстве. Но это крайне не удобно и будет усложнять процесс формирования страницы. Поэтому создадим второй ассоциативный массив для хранения таких значений и метод для их вывода.
abstract class AbstractView {
public $fillings;
public $values;
abstract public function draw();
protected function insert($filling_name){
if(isset($this->fillings[$filling_name])){
$this->fillings[$filling_name]->draw();
}
}
protected function write($value_name){
if(isset($this->values[$value_name])){
echo $this->values[$value_name];
}
}
}
Теперь напишем два простейших наследника для демонстрации. Один будет представлять шапку, а второй приветствие.
class LayoutView extends AbstractView {
public function draw(){
include '/layout.phtml';
}
}
class IndexView extends AbstractView{
public function draw(){
include '/index.phtml';
}
}
Что же такое файлы layout.phtml и index.phtml?
layout.phtml:
<!DOCTYPE html>
<html>
<head>
<title><?php $this->write('title')?></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<?php $this->insert('content'); ?>
</body>
</html>
index.phtml:
<div>Hello, Habrahabr!</div>
Соединим все и с генерируем страницу:
$page = new LayoutView();
$page->values['title'] = "greating";
$page->fillings['content'] = new IndexView();
$page->draw();
Результат:
<!DOCTYPE html>
<html>
<head>
<title>greating</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div>Hello, Habrahabr!</div>
</body>
</html>
На данный момент такая реализация кажется мне удобной, но не исключено, что в скором времени я изменю свое мнение.
Как сказал Вольтер:
Я могу быть не согласным с Вашим мнением, но я готов отдать жизнь за Ваше право высказывать его.
Автор: tegoo