tURI helper

4th Март 2012 | Категории: PHP | Метки: , ,

Сегодня я хочу продемонстрировать одно свое решение задачи получения 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 - переданные значения.

Subscribe without commenting


Пока комментариев нет.