tURI helper
Сегодня я хочу продемонстрировать одно свое решение задачи получения GET параметров из строки запроса. И затронуть проблему выбора формата URL.
Почти во всех фреймворках или CMS возможно реализовать следующий формат URL:
http://site.com/index.php/user/search/name/joe/location/UK/gender/male
Все, что идет после index.php
— является параметрами запроса.
Для MVC, обычно, первый параметр — это имя контроллера.
Второй параметр — метод контроллера (action, действие).
Третий и последующие — параметры запроса, к которым можно получить доступ во время выполнения приложения. Как правило, программисты при работе с параметрами запроса реализуют один из двух подходов:
1. Простой метод
Явно указывают какие переменные должны поступить в метод контроллера (для удобства буду пользоваться синтаксисом CodeIgniter):
class Page extends MY_Controller { function __construct() { parent::MY_Controller(); } function retrieve($id = 1) { print_r($id) } } |
Вызвав из адресной строки index.php/page/retrieve/10
, мы получим в переменной $id => 10
. Данный подход удобен при простых действиях с не большим числом параметров. Например, при использовании CRUD, который применяется при построении админки. Все операции сводятся к 4 основным действиям: создание, чтение, обновление, удаление:
index.php/page/create/10
index.php/page/retrieve/10
index.php/page/update/10
index.php/page/delete/10
Иногда для методов контроллера требуется несколько переменных. Если переменных всего 2-3 штуки, то их можно перечислить, вот так:
function retrieve($a = 1, $b = 1, $c = 1) { } |
А если параметров 10? Тогда обычно используют другой подход.
2. Гибкий метод
В методе контроллера не указывают значений по умолчанию. Совсем не указывают. Всё необходимое можно получить с помощью встроенных функций из строки запроса:
class Page extends MY_Controller { function __construct() { parent::MY_Controller(); } function show() { $id = $this->uri->segment(3); print_r($id); } } |
Вызвав из адресной строки index.php/page/show/10
, мы как и в первом случае получим $id => 10
. Если нужно получить не одно значение, а несколько, то помогут функции:
$this->uri->segment_array(n)
— возвращает все параметры в виде массива;
$this->uri->uri_to_assoc(n)
— возвращает все параметры в виде ассоциативного массива, комбинируя пары ключ-значение;
Число n указывает на смещение сегментов. Обычно извлечение начинается с 3 сегмента, так как 1 и 2 сегмент — это контроллер и метод.
Рассмотрим следующий URI:
index.php/user/search/name/joe/location/UK/gender/male
$this->uri->segment_array(3)
выдаст:
[array] ( [1] => user [2] => test [3] => name [4] => joe [5] => location [6] => UK [7] => gender [8] => male ) |
$this->uri->uri_to_assoc(3)
выдаст:
[array] ( 'name' => 'joe' 'location' => 'UK' 'gender' => 'male' ) |
Как мы видим, в CodeIgniter есть удобный класс для работы с URI, который так и называется URI. Более подробно с документацией можно ознакомиться тут: http://www.code-igniter.ru/user_guide/libraries/uri.html. В большинстве фреймворков есть аналоги этих функций. Если же вы пишете свою CMS, то можете воспользоваться простым кодом (пример кода приведен в конце статьи).
3. Изобретаем велосипед
Я не буду сейчас обсуждать плюсы и минусы каждого из двух описанных выше подходов, скажу лишь, что при большом числе передаваемых параметров удобнее использовать второй в связке с функцией uri_to_assoc.
Из недостатков, лично для меня:
— При большом числе параметров можно что-то пропустить в URL и тогда мы не получим ожидаемый результат.
— Невозможно передать одному параметру несколько значений.
Я попробовал устранить оба недостатка в своем классе, который можно использовать в любом проекте:
Перед вами простой класс с двумя статическими методами:
turi::uri_to_assoc()
— преобразует строку запроса в ассоциативный массив
turi::assoc_to_uri()
— преобразует ассоциативный массив в строку запроса
Покажу работу класса на примерах.
Строка запроса:
http://localhost/controller/method/name:joe/location:UK
Результат:
Array ( [name] => joe [location] => UK ) |
http://localhost/controller/method/name:joe/location:UK/wrong
Содержимое будет идентично первому примеру. Последний параметр содержит ключ без значения, поэтому он проигнорирован.
http://localhost/controller/method/name:joe/location:UK:RU:UA/age:18:25/gender:male
Array ( [name] => joe [location] => Array ( [1] => UK [2] => RU [3] => UA ) [age] => Array ( [1] => 18 [2] => 25 ) [gender] => male ) |
В этом примере раскрывается вся суть моего «велосипеда». Мы передаем сразу несколько значений для параметров location и age, которые сохраняются в виде массива.
А теперь проверим работу обратного метода. Возьмем массив и попробуем преобразовать его в URL:
$data = array( 'name' => 'joe', 'location' => array (1 => 'UK', 2 => 'RU', 3 => 'UA' ), 'age' => array (18,25), 'gender' => 'male' ); echo hwm::assoc_to_uri($data); // name:joe/location:UK:RU:UA/age:18:25/gender:male/type:asd |
Работает великолепно.
И напоследок покажу, где у меня применялось данное решение. Имеется форма для поиска с большим числом параметров. Можно заполнить форму и получить искомый результат через POST запрос, а можно набрать в адресной строке URL и получить тот же самый результат. Вот пример такого URL:
http://lgnd.ru/gt/rating/lvl/17/r/3/er/1/b/1/eb/3/um/11/eum/11/filter/1/dim/1/art/1
Если мы воспользуемся новым решением, то URL может принять вид:
http://lgnd.ru/gt/rating/lvl:17/r:3/er:1/b:1/eb:3/um:11/eum:11/filter:1/dim:1/art:1
Непосвященному уже проще разобраться в параметрах и меньше шанс допустить ошибку при ручной правке.
Конечно, URL не всегда такие длинные, обычно используется всего 3-4 параметра, вот так:
http://lgnd.ru/gt/rating/lvl/17/r/3/b/1/um/11/eum/11
Часть пользователей легко умеет их читать и изменять как надо, не прибегая к самой форме. К тому же, для этой формы есть пара скрытых фишек, доступ к которым можно получить только напрямую через URL.
PS. Очень часто роутинг перекладывают на различные классы типа «route», в которых любят прибегать к вот таким ЧПУ:
http://site.com/news/article15
или http://site.com/news/article15.html
Я против применения подобной техники. Чтобы получить из такой строки полный набор параметров, нужно прибегать к регулярным выражениям, что не совсем оптимально (с точки зрения нагрузки на сервер).
Так же я против применения голого ЧПУ в URL без указания id записи.
Например, адрес этой страницы: http://tarlyun.com/php/turi-helper
. Для того, что бы WordPress нашел искомую запись среди всех статей он должен выполнить полностекстовый поиск по строковому полю. При большом числе записей в таблице это наихудшее решение (с точки зрения производительности). Хорошим решением будет указание id записи перед названием. Например вот так: http://tarlyun.com/php/167-turi-helper
или вот так: http://tarlyun.com/php/167/turi-helper
.
приложение №1
Листинг класса turi (pastebin.com)
приложение №2
Пример простого решения для реализации функции uri->uri_to_assoc()
из CodeIgniter:
// В массиве $_SERVER находится информация о сервере и среде исполнения. // Нас интерсует элемент PATH_INFO, в котором содержатся параметры запроса. $path = ''; if (isset($_SERVER['PATH_INFO'])) { $path = $_SERVER['PATH_INFO']; } else { // пробуем получить путь из других источников, например, из обычных GET параметров. // все зависит от вашей реализации. } $path = trim($path, '/'); $path_array = explode('/', $path); var_dump($path_array); // контроллер будет под индексом [0], метод [1], остальные элементы массива $path_array - переданные значения. |