Описание механизма View

SKY / WINGS / FIRST /
VIEW1
Для фронтальной части приложений, при формировании ответа сервера, всегда используется функция view(), как минимум один раз (top-view). В ней всегда могут использоваться шаблоны парсера Jet, а также подготовленные переменные:

001
002
003
004
005
006
007
<?php
 
$view->body # имя шаблона Body
$view->_v # массив переменных представлений, используемых в шаблонах Body
View::$layout # имя шаблона Layout
View::$_y # массив переменных представлений, используемых в Layout
$sky->style

View::$layout - содержит имя layout. По умолчанию, перед запуском action (метода) контроллера, для top-view, для не ajax запросов, устанавливается `desktop` или `mobile`. Для ajax, по умолчанию - пустая строка. Пример шаблона layout: `view/y_desktop.php`. Префикс `y_` указывает на то что это файл layout. Если View::$layout - пустая строка, значит layout макет не используется. Если и $view->body и View::$layout - пустая строка, ответом сервера есть $view->stdout (перехватывается вывод echo.. в контроллерах). В этом случае, Jet и шаблоны не используются вообще.

Макеты layout обычно содержат одну директиву Jet @body, или @inc(*) или @require(*), в это место вставится результат рендеринга body-части.

В переменную `$view->body`, по умолчанию, для top-view, всегда устанавливается значение первого ключа массива $_GET (или строка 'main', для главных страниц). В контроллерах это значение может корректироваться. Если в контроллере сделано `echo ..`, не стоит заботиться о том, чтобы подкорректировать значение $view->body, пустая строка присвоится автоматически. В конечном итоге, в функцию view() может передаваться:

1) пустая строка - это значит, что шаблон body не используется и необходимый вывод данных уже сделан в контроллере, остальные переменные и layout при этом могут использоваться;
2) не пустая строка - используется шаблон из файла, например: 'news' соответствует файлу `view/_news.php`;
3) строка с точкой, например 'news.edit' соответствует части файла `view/_news.php` с маркером `edit`;

Подробно о маркерах можно прочесть в узле JET.

Приложения редко используют множественные стили, поэтому $sky->style часто не используется и по умолчанию содержит пустую строку. Для приложений со стилями $sky->style содержит имя стиля, а шаблоны Jet помещаются в папку `view/имя стиля`, файлы в папке pub, также: `pub/имя стиля`, например view/summer/_main.php или pub/summer/front.css. Выбранные пользователями стили можно помещать в $user->v_style или $user->u_style. После идентификации, присваивать стиль: $sky->style = $user->v_style;

Вспомогательные view, метки и hardcache

Вызовы функции view() могут быть вложенными, т.е. в шаблоне можно использовать директиву Jet @view(..). Вместо нее, в шаблон вставится результат рендеринга. Также view() можно вызвать до заполнения переменных $view->_v и View::$_y и использовать ее рендеринг в подготовке top-view или другого промежуточного view.

Запуск top-view происходит автоматически, при этом функция view() вызывается без параметров. Вспомогательные view вызывают функцию с параметрами:

mixed view([$action_name = false, [$return_string = false, [$parameter = null]]])

Первый параметр - прототип имени action контроллера, второй - вернуть ли рендеринг в строке или выдать в stdout и третий - параметр передаваемый в action. Если $action_name не имеет префикса, автоматически добавится `x_`. Метод контроллера будет искаться вначале в "своем" контроллере, и если не найдет, то в общем `common_c`. Пример главного контроллера:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
<?php
 
class c_main extends Controller
{
    function empty_a() {
        echo cache('main', function() { # на самом деле, в данном случае, можно написать только лишь:
            return view('main'true);  # echo cache('main', 'view');
        });
    }
 
    function x_main() {
        $row $this->t_article->get_article('main'3);
        $this->_v = [
            'e_packs' => $this->m_gate->listing(0false),
            'e_blogs' => $this->t_blog->listing(0true),
            'content' => label($row->content),
        ];
    }
# ...
}

В примере выше, при открытии главной страницы сайта, автоматически запускается метод c_main::empty_a(), после его отработки, автоматически запускается top-view, формируется ответ сервера. Так как в контроллере есть `echo`, значение выставленное по умолчанию 'main' в $view->body автоматически заменится на пустую строку и в view/y_desktop.php для секции body будет использовано <?php echo $view->stdout ?>. Так как View::$layout не пустая строка, в методе common_c::tail_a() заполнится массив View::$_y для layout. Функция `cache` проверяет наличие и TTL файла `var/cache/ru_main.html`, если ОК, то считывает файл и возвращает его содержимое, при этом view('main', true) не вызывается вообще. Иначе вызывает view, который, в свою очередь вызывает c_main::x_main(). Для этого метода автоматически установлено View::$layout - пустая строка и шаблон $view->body равен 'main', т.е. будет использован файл `view/_main.php`. В методе проходят вызовы к моделям, заполняется массив ->_v для шаблона. Функция label() заменяет метки в строке, считанной из БД, на рендеринг вызовов view(), чтобы обеспечить сложный вывод для которого требуется PHP.

Переменные для шаблонов


Переменные для master-layout обычно заполняются в common_c::tail_a() в массив View::$_y. Общий контроллер вызывается автоматически для всех страниц, подробные детали смотрите в узле MVC. В массиве ключами являются прототипы имен переменных в шаблонах. Значениями этих переменных, обычно являются значения, что присваиваются элементам массива View::$_y. Если ключ не имеет префикса, автоматически добавляется `y_`. В шаблонах переменные с префиксом $y_ это основные переменные для layout. Но в массиве View::$_y можно явно указывать ключи и с другими префиксами:
1) d_ - позволяет получить в шаблоне переменную без префикса. Этим нужно пользоваться в крайнем случае;
2) v_ - определяет или переопределяет переменную для `body` шаблона. Это редко может понадобиться, так как для `body` есть свой массив $view->_v;
3) h_ - переменные прошедшие через функцию htmlentities(). Тоже может редко понадобиться так как проще делать вывод в шаблоне через оператор Jet {{ $var }} который также применяет htmlentities(). Однако используя этот префикс, можно автоматически заескейпить рекурсивно массив строк.
4) k_ - корректирование или определение переменных массива $sky->vars. В этот массив собираются автоматически вычисленные ядром переменные, который передается во все шаблоны и даже в файлы админ. раздела. Например $k_tkd содержит массив из 3 элементов: title, keywords, description, значения которым присваиваются в админ. разделе, в конфигурации. Если вы забудете определить ключевые слова для конкретной страницы, то используются ключевые слова по умолчанию из переменной $k_tkd. Или же $k_type всегда содержит эвристически вычисленный тип CRUD операции. $k_type может быть автоматически предустановлена в 'list', 'edit', 'new' или 'show'.
5) e_ - ключам с этим префиксом, необходимо присваивать массив, но в шаблонах массив преобразуются в объект типа stdObject. Это позволяет группировать переменные в один объект, но и также используется для итераторов Jet.

Для массива $view->_v, который передает переменные в шаблон @body все работает аналогичным образом, но только переменным без префикса, автоматически присваивается префикс `v_`.

Метод общего контроллера common_c::head_a() вызывается раньше главного метода центрального контроллера, поэтому можно определить часть переменных массива View::$_y в common_c::head_a(), потом в центральном контроллере подкорректировать значение переменной для layout и в common_c::tail_a() до-собирать переменные для layout. В общем такая система сбора переменных имеет довольно большие, удобные возможности.

Подробно об итераторах Jet


Присвоенный, в контроллере массив переменных, элементу массива ->_v с ключем, который имеет префикс `e_`, в шаблоне преобразуется в stdObject. У переменных с префиксом $e_ есть три специальных свойства объекта stdObject, с их помощью организуются отложенные вызовы для организации листингов, это 'query', 'row_c' и 'after_c'. Смотрите пример, код в контроллере:

001
002
003
004
005
006
007
008
009
010
011
012
013
<?php
 
class c_posts extends Controller
{
    function empty_a() { #list
        if ($_POST$this->a_delete();
        $this->_v = [
            'h1' => 'Листинг постов',
            'e_posts' => $this->t_posts->listing(),
        ];
    }
# ... другие методы контроллера
}

Код в модели:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
<?php
 
class t_posts extends Model_t
{
    static $rubrics = [
        'Строительство',
        'Сад и огород',
        # ...
    ];
 
    function listing() {
        return [
            'query' => sql(SQL::PARSE'select * from $_ order by id'),
            'btn' => false,
            'row_c' => function ($row) {
                global $user;
                if ($user->root || $row->user_id == $user->id)
                    $row->checkbox sprintf(TPL_CHECKBOX$row->id'');
                $row->rubric self::$rubrics($row->rubric_id);
            },
            'after_c' => function ($row) {
            // вызывается после окончания отработки текущего итератора. Это может быть полезно,
            // когда за текущим следует еще один итератор и необходимо установить начальные значения
            }
        ];
    }
# ... другие методы модели
}

Код в шаблоне:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
<?php #файл view/_posts.php ?>
 
<h1>{{ $v_h1 }}</h1>
 
<form method="post">
@for ($e_posts : $item)
    #{{1 + $a}}. {{ $item->rubric }} / {{ $item->text }}
    @if (isset($item->checkbox))
        {! $item->checkbox !}
        @php $e_posts->btn = true ~php
    ~if
    <br>
    @empty
        Нет постов
    @else
        Total items: {{ $a }} ~{- Это напечатается только если посты были -}
~for
 
@if ($e_posts->btn)
    <input type="submit" value="delete checked">
~if
</form>
 

Чтобы использовать итераторы Jet, в модели, практически всегда, нужно делать так: из функции листинга возвратить массив, в котором ключу 'query' должен быть присвоен ресурс (объект) запроса, а ключу 'row_c' Closure или callback функция, которая корректирует считанные ряды из БД. В контроллере возвращенный из модели массив присвоить ключу с префиксом e_, как в примере, тогда в шаблоне, можно использовать итераторы Jet. По сути, специальный синтаксис циклов @for ($e_posts), Jet превращает в код:

001
002
003
004
005
006
<?php
 
$a 0; while ($row SQL::row($e_posts$a)):
# если не указать `: $item` в цикле @for, подставится переменная по умолчанию $row
 
?>

Теперь посмотрите определение SQL::row(..)

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
<?php ... class SQL
    static function row($e, &$i) {
        if (!isset($e->query))
            $e->query = -1;
        if (!$e->query || -!= self::$max_i && $i self::$max_i)
            return false;
        if (is_int($e->query)) {
            self::$max_i $e->query;
            if (!is_callable($e->row_c))
                throw new Error('$e->row_c is not callable >>' gettype($e->row_c) . '<<');
            $e->query call_user_func_array($e->row_c, [(object)['_loop' => $i++]]);
            return self::row($e$i);
        }
        $is_ary is_array($e->query);
 
        if ($row $is_ary ? (object)$e->query mysqli_fetch_object($e->query)) {
            $row->_loop $i;
            $return is_callable($e->row_c) ? call_user_func_array($e->row_c, [&$row]) : false;
            if ($is_ary)
                $e->query $return;
            elseif ('continue' === $return)
                return self::row($e$i);
        } else {
            mysqli_free_result($e->query);
            $e->query false;
        }
        if (!$e->query && is_callable($e->after_c))
            call_user_func($e->after_c);
            
        return $row;
    }
    ...
 

Итераторы Jet могут также, формировать значения динамически, без обращения к БД. Для этого, параметр 'query' не используем, а в 'row_c' просто возвращаем массив значений каждой итерации. Для прекращения итераций, необходимо вернуть false. На этапе написания кода, вы можете указать: 'query' => 100,, т.е. установить значение не ресурс запроса к БД, а значение integer. Это ограничит максимальное значение циклов итераций. Так вы гарантированно не подвесите компьютер на бесконечную итерацию. Когда код будет отлажен, 'query' => 100 можно будет удалить.

опубликовано ENERGY - 18 Mar 2016 13:36 GMT
последнее редактирование - 25 Mar 2019 05:52 GMT
 +  0  -  комментировать