Предлагаю вашему вниманию свое решение по генерации html на PHP. Задача вроде бы тривиальная, но хотелось бы, чтобы это было расширяемо, кратко, но в тоже время с хорошим функционалом. Получилось вроде не плохо.
Сразу скажу( как многие считают в комментариях), что задача ставилось не написать шаблонизатор (которых и так много) и не заменить шаблонизатор JavaScript. Я прекрасно знаю, что true way это разделять html и данные. Но мне понадобилось писать html в классах, для создания компонентов фреймворка, на подобие CGridView в yii, стоит ли в таких местах выносить html в отдельные файлы решать вам.
Основная цель, избавится от html в классах и функциях.
Простой пример, обычная кнопка:
Результат:
Ничего хитрого, можно было бы этим и ограничется, но захотелось циклы:
Тут понадобилось вызвать функцию plainArray() которая превращает массив в виде:
Теги внутри цикла могут содержать функции или строки с eval выражениями, вложенность любая, пример с таблицей:
Незакрытые теги закрываются автоматически.
Класс можно расширить, вплоть до использования в формах. Расширить можно за счет наследования или внедрения зависимости, за то, как будет выводится каждый тег и его атрибуты используется одна функция, поэтому можно легко переопределить это поведение.
Само решение можно посмотреть и попробовать на github.
Спасибо за внимание.
Сразу скажу( как многие считают в комментариях), что задача ставилось не написать шаблонизатор (которых и так много) и не заменить шаблонизатор JavaScript. Я прекрасно знаю, что true way это разделять html и данные. Но мне понадобилось писать html в классах, для создания компонентов фреймворка, на подобие CGridView в yii, стоит ли в таких местах выносить html в отдельные файлы решать вам.
Основная цель, избавится от html в классах и функциях.
Простой пример, обычная кнопка:
CHtml::create()
->p()
->a(array('href' => 'http://habrahabr.ru', 'class' => 'btn'))
->text('Перейти')
->render();
Результат:
<p><a href="http://habrahabr.ru" class="btn">Перейти</a></p>
Ничего хитрого, можно было бы этим и ограничется, но захотелось циклы:
$arr = array('1' => 'Первый', '2' => 'Второй');
CHtml::create()
->select($options)
->each(CHtml::plainArray($arr, 'value', 'text'))
->option('array("value" => $data->value)')
->text('$data->text')
->end()
->endEach()
Тут понадобилось вызвать функцию plainArray() которая превращает массив в виде:
$arr = array(
array('value' => '1', 'text' =>'Первый'),
array('value' => '2', 'text' => 'Второй')
);
Теги внутри цикла могут содержать функции или строки с eval выражениями, вложенность любая, пример с таблицей:
$columns = array(
array('id' => 'NAME', 'label' => 'Имя'),
array('id' => 'AGE', 'label' => 'Возраст')
);
$data = array(
array('NAME' => 'Петр', 'AGE' => 29),
array('NAME' => 'Василий', 'AGE' => 32)
);
CHtml::create()
->table()
->thead()
->tr()
->each($columns)
->th()
->text(function($column){
return $column['label'];
})
->end()
->endEach()
->end()
->end()
->tbody()
->each($data)
->tr()
->each($columns)
->td()
->text(function($row, $column) {
return $row[$column['id']];
})
->end()
->endEach()
->end()
->endEach()
->render();
Незакрытые теги закрываются автоматически.
Класс можно расширить, вплоть до использования в формах. Расширить можно за счет наследования или внедрения зависимости, за то, как будет выводится каждый тег и его атрибуты используется одна функция, поэтому можно легко переопределить это поведение.
class CMyHtml extends CHtml {
public function a($options = array()) {
$default = array(
'href' => 'javascript:void(0)'
);
return parent::a(array_replace($default, $options));
}
}
class CForm {
private $_lastLabel = '';
public function __construct(CModel $model, CHtml $html = null) {
$this->_model = $model;
$this->_html = $html ?: CHtml::create();
}
public function __call($method, $ps) {
$options = $ps ? $ps[0]: array();
if ($method === 'label') {
$this->_lastLabel = isset($options['for']) ? $this->_model->getLabel($options['for']) : '';
}
if ($method === 'text' && $this->_lastLabel) {
$options = $options ?: $this->_lastLabel;
$this->_lastLabel = '';
}
$this->_html->$method($options);
return $this;
}
}
Само решение можно посмотреть и попробовать на github.
Спасибо за внимание.