Оказывается, есть на свете люди, которые не могут понять Простую модель. Я как большой поклонник этой модели данных попробую им немного помочь.
Рассмотрим простой пример. Допустим, вам нужно создать сайт, состоящий из двух страниц – главной (кто не в курсе, она имеет адрес / :)) и внутренней по адресу /page, например. В соответствии с Простой моделью создаем таблицу категорий с такими записями:
INSERT INTO `site_categories` (`id`, `name`, `content`, `bits`) VALUES ('', 'Главная страница', '', 0), ('page', 'Внутренняя страница', '', 0);
Кто еще не догадался, как сделать роутинг по этой таблице? Можно брать путь из адреса поступившего запроса и обрезать первый слеш при помощи функции ltrim. Можно обрезать первый и последний слеши пути при помощи функции trim (на случай, если путь будет таким: /page/). Посмотрите, как формируются значения $p и $px в статье про единую точку входа. Однако делать выборку из таблицы категорий по практически полному пути было бы слишком просто даже для простой модели.
В рассматриваемой модели символьный идентификатор категории НЕ должен содержать слеши! По крайней мере тогда, когда роутинг по данной категории должен завершаться успехом. Такой подход имеет определенные преимущества: не нужно выполнять запрос к базе данных по многокомпонентному пути, если ожидается поступление только однокомпонентного пути; не нужно дублировать одинаковый первый компонент при хранении многокомпонентных путей.
Вывод: прежде чем выполнять запрос к таблице категорий, нужно выделить первый компонент пути из адреса поступившего запроса. Посмотрите, как это можно сделать, в той же статье про единую точку входа.
Путь из адреса первоначально делится на две части (первый компонент пути и целиком оставшаяся часть пути), причем эти две части выделяются даже для самых простых путей, например (пустые ячейки таблицы обозначают значения, состоящие из пустых строк):
Путь | $p0 | $p1 |
---|---|---|
/ | ||
/page | page | |
/page/1 | page | 1 |
/page/1/extra | page | 1/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
Надеюсь, прочитав данную статью, вы сможете организовать роутинг с одним запросом к базе данных в полном соответствии с Простой моделью.
Кроме параметров $p0 и $p1, выделяемых из пути, Простая модель позволяет контролировать строку GET-параметров, разрешая или запрещая ее присутствие, причем в отдельности для пустого и непустого объектов каждой категории. Более того, фронт-контроллер может выделять отдельные параметры из строки параметров, например в G-Drive для передачи натурального числа в определенном диапазоне выделяется параметр $pn из GET-параметра p. Если данный GET-параметр отсутствует, параметр $pn будет иметь нулевое значение (при этом строка параметров ?p=0 и т.п. является некорректной):
После выделения параметра $pn запускать контроллер нужно по условию
Оно означает: «GET-параметр p отсутствует ИЛИ установлен соответствующий бит разрешения для запрашиваемого (пустого или непустого) объекта».
Спасибо. После такого объяснения почти все понятно.
Получается, при одном запросе не важно, какой тип категории использовать из 1-3? Можно всегда 1?
Можно всегда 1, если вы не собираетесь использовать другие фронт-контроллеры или админку вроде Gency. В Gency категория типа 1 не может быть использована для доступа к объектам БД. Даже значка коллекции и списка объектов вы не увидите.