Kohana Оцените модуль для Kohana -- Presentation Layer

yanis

Новичок
Мною был разработан модуль для Kohana, реализующий идеи Фаулера про Presentation Model. Суть такова: между моделью, поставляющей данные и шиблоном-отображение, по сути, я вляющимся разметкой, встраивается дополнительная модель, которая преобразует данные в подходящий для представления формат.

Реализация правил преобразования в простом виде похожа на синтаксис ORM::filters():
PHP:
class Presentation_Model_Order extends \Yup\Presentation_Model {
   
    public function rules()
    {
        return [
            'created' => ['Date::formatted_time'],
            'state'  => [['self::values', [
                'new'        => __('New'),
                'processing' => __('In process'),
                'completed'  => __('Done'),
                'cancelled'  => __('Cancelled'),
            ]]],
            'note'    => ['HTML::chars', 'nl2br'],           
        ];
    }
}
и потом в шаблоне можно писать
PHP:
<?= $order->note ?>
что выведет отформатированные через HTML::chars и nl2br данные.

Класс работает как декоратор, предоставляя верстальщику ООП-объект с данными, и не затрагивая никак поведение модели.

Был испробован на реальном проекте, причесан, и выложен на gihub. Буду признателен за обратную связь.

Модуль можно скачать с гитхаба, также имеется небольшая документация, пока на русском:
https://github.com/prasol/kohana-presentation

PS: на forum.kohanaframework.org зарегистрироваться я не смог )
 

Adelf

Administrator
Команда форума
PHP:
['HTML::chars', 'nl2br'],
Увидев это я подумал - неужто eval! :) ан нет. разбирает там по полочкам. старается. Но, имхо, все равно оверхед без особой пользы. Да и методы в строках писать - асстой. люблю автокомплит.
 

AmdY

Пью пиво
Команда форума
Зачем тащить этого уродца в наг прекрасный php? у нас прекрасно всё делается через __isset, __get
 

yanis

Новичок
PHP:
['HTML::chars', 'nl2br'],
Увидев это я подумал - неужто eval! :) ан нет. разбирает там по полочкам. старается. Но, имхо, все равно оверхед без особой пользы. Да и методы в строках писать - асстой. люблю автокомплит.
Польза, в принципе, в том, чтобы писать не

PHP:
<h3>User <?= htmlspecialchars($user->first_name) > <?= htmlspecialchars($user->last_name) ?></h3>
<table>
<tr>
<td>Gender: <?= $gender_captions[$user->gender] ?></td>
<td>Last login: <?= $user->last_login === '0000-00-00 00:00:00' ? 'Never' : Date::formatted_time($user->last_login) ?></td>
</tr>
</table>
А

HTML:
<h3>User <?= $user->full_name ?></h3>
<table>
<tr>
<td>Gender: <?= $user->gender ?></td>
<td>Last login: <?= $user->last_login ?></td>
</tr>
</table>
Методы в строках мне тоже не особо нравятся, но:
1. Это просто
2. Это похоже на уже существующий функционал Kohana

Возможно, придумаю позже, как можно обойтись без строк, чтобы без лишней писанины при этом.
 

yanis

Новичок
Зачем тащить этого уродца в наг прекрасный php? у нас прекрасно всё делается через __isset, __get
Поясните, что вы имеете в виду?
Решение данное не от хорошей жизни придумано, а от того, что всякие форматирования дат, htmlchars и подобные вещи писать просто негде -- модели и так перегружены кодом доступа к данным из БД в перемешку с бизнес-логикой, а при написании всего этого в шаблонах, люди начинают ошибаться, забывать, да и верстка становится сложна для понимания и модификации.

Насчет того, что я порчу "прекрасный php" -- так Kohana первая начала )
 
Последнее редактирование:

AmdY

Пью пиво
Команда форума
PHP:
public function __get($attr) {
    $getter = 'get_' . $attr;
    if (method_exists($this, $getter)) {
        return $this->{$getter}();
    }
    parent::__get($attr);
}
public function get_name() {
    return HTML::chars($this->name);
}


$object->name; // выведет правильный отформатированный вариант.
Так не нужны магические строки и можно написать любую логику, а не ограничиваться кэлбэком.
 

yanis

Новичок
Так не нужны магические строки и можно написать любую логику, а не ограничиваться кэлбэком.
Вы правы насчет того, что так красивее. Но:
1. В большинстве случаев сложная логика не нужна, а нужны именно html::chars или date::format десять раз подряд. В итоге получаем 10 одинаковых методов.
2. Возможность вызвать произвольный метод у меня имеется, написав 'name' => 'this::get_name'. Но опять повторюсь, это нужно в 5-10% случаев.

Хотя, ваш подход мне нравится, буду думать, как использовать и его при желании. Ведь у меня только первая версия модуля )
 

yanis

Новичок
К тому же, формат задания правил здесь дело десятое. Удобство заключается в том, что обертки презентации для моделей наклыдваются при вызове рекурсивно, вы можете написать

PHP:
$this->product = new Presentation_Model_Product(new Model_Product(1));
и в шаблоне вызвав
PHP:
$this->product->category->name
Получить наложение правил и для дочернего объекта category.
 

AmdY

Пью пиво
Команда форума
Так этот метод легко работает и с вашим конфигом, только ищем не метод. а в $this->rules()[$attr];
 

Здыхлик

Kohaner
Команда форума
В конце концов, сделать в модели метод render($field), внутри которого искать $this->rules[$field] - если есть, то применяем. И никаких лишних прослоек
 

yanis

Новичок
В конце концов, сделать в модели метод render($field), внутри которого искать $this->rules[$field] - если есть, то применяем. И никаких лишних прослоек
1. Модель в Kohana и так перегружена, совсем не хочется там html-форматирование писать. Как раз смысл в том, чтобы вынести это оттуда.
2. Мой класс работает не только с моделями, но и данными, для которых ORM-моделей нет (например, списки, сформированные из нескольких выборок)
 

Здыхлик

Kohaner
Команда форума
1. Модель в Kohana и так перегружена, совсем не хочется там html-форматирование писать. Как раз смысл в том, чтобы вынести это оттуда.
2. Мой класс работает не только с моделями, но и данными, для которых ORM-моделей нет (например, списки, сформированные из нескольких выборок)
То есть надо каждую такую выборку заворачивать в презент-модель? Чем это удобнее какого-нить хэлпера, в таком случае?
 

yanis

Новичок
То есть надо каждую такую выборку заворачивать в презент-модель? Чем это удобнее какого-нить хэлпера, в таком случае?
Удобства в том, что модели, выполняющей выборки, не нужно следить за их форматированием, равно, как и шаблону, описывающему разметку. Также возможно для некоторых повторяющихся данных использовать ссылку на объект, не выполняя нужные предобразования N раз для различных строк.
 

yanis

Новичок
Я немного доработал модуль с учетом ваших замечаний, которые показались мне вполне резонными.
Сделал упор на анонимные функции в правилах и возможность задавать виртуальные поля методами field_*. Теперь можно не использовать строковые правила для преобразований, если они не нравятся или неудобны.

PHP:
class Presentation_Model_User extends \Yup\Presentation_Model {

    public function rules()
    {
        return [
            'first_name' => 'HTML::chars',
            'last_name'  => function($value){
                return HTML::chars($value); // то же самое, что в верхнем поле, но с меньшей магией
            },
            'last_login' => function($value){
                return ($value === '0000-00-00 00:00:00') ? 'Never' : Date::formatted_time($value);
            },
            'gender' => $this->replace([
                'm' => __('Male'),
                'f' => __('Female'),
            ]),
        ];
    }

    protected function field_full_name() // создаст виртуальное поле full_name
    {
        return $this->first_name . ' ' . $this->last_name;
    }
}
 
  • Like
Реакции: AmdY

AmdY

Пью пиво
Команда форума
Для таких вещей лучше делать честные декораторы с прокси, а не добавлять энтропию в цепочку наследования.
PHP:
$model = new Model_Foo()l
$formater = new Formater($model);
$formater->title;
Меня смущает подход с extends \Yup\Presentation_Model {
 

yanis

Новичок
Для таких вещей лучше делать честные декораторы с прокси, а не добавлять энтропию в цепочку наследования.
PHP:
$model = new Model_Foo()l
$formater = new Formater($model);
$formater->title;
Меня смущает подход с extends \Yup\Presentation_Model {
Это как раз и есть честный прокси-декоратор, модель лежит отдельно. Просто называется Presentation_имя_модели, для удобства автосоздания. Т.е. можно создавать их как:

PHP:
$model = new Model_Foo();
$formater = Formater::model($model);
// в $formater будет создат класс Presentation_Model_Foo(), если такой существует
// $formater->title выведет $model->title с преобразованиями, если они были наложены в Presentation_Model_Foo
Сама же модель, конечно, не знает ни о каких декораторах.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
я бы просто у ORM перекрыл __get, и добавил метод с описанием форматирования по аналогии с rules() и filters()
 

yanis

Новичок
я бы просто у ORM перекрыл __get, и добавил метод с описанием форматирования по аналогии с rules() и filters()
Изначально так и было, но в крупном проекте модели разрастаются, содержат много data-access логики и часть бизнес-логики, как правило, тоже и всякие HTML-обработки там выглядят совсем неуместно. Кроме того, если вам нужно выводить данные модели либо выборок в других, не HTML-контекстах (XML, JSON), то такое решение становится совсем плохим, а у меня можно поставить декоратор другого типа и вперед.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Так я вообще просто нормальным шаблонизатором пользуюсь, и все :)
 
  • Like
Реакции: AmdY
Сверху