Простая модель для «чайников»

Оказывается, есть на свете люди, которые не могут понять Простую модель. Я как большой поклонник этой модели данных попробую им немного помочь.

Рассмотрим простой пример. Допустим, вам нужно создать сайт, состоящий из двух страниц – главной (кто не в курсе, она имеет адрес / :)) и внутренней по адресу /page, например. В соответствии с Простой моделью создаем таблицу категорий с такими записями:

INSERT INTO `site_categories` (`id`, `name`, `content`, `bits`) VALUES
('', 'Главная страница', '', 0),
('page', 'Внутренняя страница', '', 0);

Кто еще не догадался, как сделать роутинг по этой таблице? Можно брать путь из адреса поступившего запроса и обрезать первый слеш при помощи функции ltrim. Можно обрезать первый и последний слеши пути при помощи функции trim (на случай, если путь будет таким: /page/). Посмотрите, как формируются значения $p и $px в статье про единую точку входа. Однако делать выборку из таблицы категорий по практически полному пути было бы слишком просто даже для простой модели.

В рассматриваемой модели символьный идентификатор категории НЕ должен содержать слеши! По крайней мере тогда, когда роутинг по данной категории должен завершаться успехом. Такой подход имеет определенные преимущества: не нужно выполнять запрос к базе данных по многокомпонентному пути, если ожидается поступление только однокомпонентного пути; не нужно дублировать одинаковый первый компонент при хранении многокомпонентных путей.

Вывод: прежде чем выполнять запрос к таблице категорий, нужно выделить первый компонент пути из адреса поступившего запроса. Посмотрите, как это можно сделать, в той же статье про единую точку входа.

Путь из адреса первоначально делится на две части (первый компонент пути и целиком оставшаяся часть пути), причем эти две части выделяются даже для самых простых путей, например (пустые ячейки таблицы обозначают значения, состоящие из пустых строк):

Путь$p0$p1
/
/pagepage
/page/1page1
/page/1/extrapage1/extra

Комбинация, когда $p0='', а $p1='непустая строка', обычно невозможна, т.к. пути с множественными (т.е. несколькими подряд идущими) слешами преобразуются в «нормализованный» вид, например путь //page преобразуется просто в /page (только за счет (l)trim можно избавиться от множественных первых слешей, что позволяет избежать появления показанной комбинации).

После выделения двух частей пути в параметры $p0 и $p1 выполняется запрос к таблице категорий по значению первого из них, т.е. происходит попытка найти категорию с id='$p0'. В общем-то на этом можно было остановиться и запускать контроллер с именем из поля module* найденной записи, но фронт-контроллер обычно не ограничивается этим и, если это позволяет тип найденной категории, выполняет запрос к соответствующей таблице объектов по значению параметра $p1, т.е. происходит попытка найти объект с id='$p1'.

* Когда поле module отсутствует или пустое, имя контроллера определяется по значению поля id найденной категории. При этом категория с id='' не должна никого смущать. За счет добавления окончания 'Controller' или расширения имени файла может формироваться вполне осязаемое имя контроллера или файла контроллера.

Получается, что значение $p0 является селектором категории, а значение $p1 – селектором объекта. Когда $p0='', можно говорить о наличии пустой категории, а когда $p0='непустая строка' – о наличии непустой категории. Аналогично когда $p1='', можно говорить о наличии пустого объекта, а когда $p1='непустая строка' – о наличии непустого объекта.

Запрос к таблице объектов выполняется НЕ всегда даже при наличии непустого объекта. И наоборот может быть выполнен даже при наличии пустого объекта. Точное условие выполнения второго запроса показано в этом комментарии. А для «чайников» я составил такую таблицу:

Тип категорииВид объектаВторой запрос
1 (012)любойНЕ выполняется
2 (102)пустойНЕ выполняется
2 (102)непустойвыполняется
3 (112)любойвыполняется

Для категории типа 0 (002) использовать непустые объекты запрещено, поэтому я не включил этот тип в таблицу. Но все-таки скажу, что фронт-контроллер не будет выполнять запрос к таблице объектов (для поиска пустого объекта). И он должен гарантировать, что объект всегда пуст, поэтому в контроллере можно не обращать внимания на значение параметра $p1.

Даже если вы создаете простой фронт-контроллер, который выполняет только один запрос, нужно запускать контроллер по условию «тип категории больше 0 ИЛИ объект пустой». Если более формально:

$mode > 0 || strlen($p1) == 0

Если в $mode могут храниться значения, отличные от 0, 1, 2 и 3, то вместо $mode нужно написать ($mode & 3). Сравнение > 0 можно опустить, при этом и скобки вокруг $mode & 3 можно не использовать:

$mode & 3 || strlen($p1) == 0

Надеюсь, прочитав данную статью, вы сможете организовать роутинг с одним запросом к базе данных в полном соответствии с Простой моделью.

Комментарии: 3

  1. Михаил

    Кроме параметров $p0 и $p1, выделяемых из пути, Простая модель позволяет контролировать строку GET-параметров, разрешая или запрещая ее присутствие, причем в отдельности для пустого и непустого объектов каждой категории. Более того, фронт-контроллер может выделять отдельные параметры из строки параметров, например в G-Drive для передачи натурального числа в определенном диапазоне выделяется параметр $pn из GET-параметра p. Если данный GET-параметр отсутствует, параметр $pn будет иметь нулевое значение (при этом строка параметров ?p=0 и т.п. является некорректной):

    $pn = isset($_GET['p']) ? intval($_GET['p']) : 0;
    

    После выделения параметра $pn запускать контроллер нужно по условию

    $pn == 0 || $mode & (strlen($p1) > 0) * 4 + 4
    

    Оно означает: «GET-параметр p отсутствует ИЛИ установлен соответствующий бит разрешения для запрашиваемого (пустого или непустого) объекта».

  2. Serdar

    Спасибо. После такого объяснения почти все понятно.

    Получается, при одном запросе не важно, какой тип категории использовать из 1-3? Можно всегда 1?

  3. Михаил

    Можно всегда 1, если вы не собираетесь использовать другие фронт-контроллеры или админку вроде Gency. В Gency категория типа 1 не может быть использована для доступа к объектам БД. Даже значка коллекции и списка объектов вы не увидите.

Отправить комментарий

Ваш адрес E-mail не будет опубликован.