mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-20 23:16:52 +01:00
297 lines
26 KiB
Plaintext
297 lines
26 KiB
Plaintext
Управління URL
|
||
==============
|
||
|
||
Управління URL-адресами у веб-додатках включає у себе два аспекти:
|
||
|
||
1. Додатку необхідно розібрати запит користувача, що надходить у вигляді URL, на окремі параметри.
|
||
2. Додаток повинен надавати спосіб формування адрес URL, з якими воно зможе коректно працювати.
|
||
|
||
У додатках на Yii ці завдання вирішуються з використанням класу [CUrlManager].
|
||
|
||
> Note|Примітка: Ви можете не користуватися Yii для генерації URL, однак, так робити не рекомендується, тому що ви не зможете легко поміняти URL додатка через конфігурацію без зміни коду.
|
||
|
||
Створення адрес URL
|
||
-------------------
|
||
|
||
В принципі, адреси URL можна задати прямо в коді представлень контролера, проте куди зручніше створювати їх динамічно:
|
||
|
||
~~~
|
||
[php]
|
||
$url=$this->createUrl($route,$params);
|
||
~~~
|
||
|
||
де `$this` відноситься до екземпляра контролера; `$route` відповідає [маршруту](/doc/guide/basics.controller#route) запиту, а `$params` є списком параметрів `GET` для додавання до URL.
|
||
|
||
За замовчуванням, адреси створюються за допомогою [createUrl|CController::createUrl] в `get`-форматі. Наприклад, при значеннях параметрів `$route='post/read'` і `$params=array('id'=>100)`, отримаємо такий URL:
|
||
|
||
~~~
|
||
/index.php?r=post/read&id=100
|
||
~~~
|
||
|
||
де параметри зазначені у вигляді набору пар `імʼя=значення`, зʼєднаних знаком `&`, а параметр `r` вказує на [маршрут](/doc/guide/basics.controller#route). Однак, цей формат не дуже дружелюбний по відношенню до користувача.
|
||
|
||
> Tip|Підказка: Для того, щоб згенерувати URL з хештегом, наприклад, `/index.php?r=post/read&id=100#title`,
|
||
необхідно передати параметр `#` наступним чином: `$this->createUrl('post/read',array('id'=>100,'#'=>'title'))`.
|
||
|
||
Ми можемо зробити так, щоб адреса, наведена у якості приклада вище, виглядала більш акуратно і зрозуміло за рахунок використання формату `path`, який виключає використання рядка запиту і включає всі GET-параметри в інформаційну частину адреси URL:
|
||
|
||
~~~
|
||
/index.php/post/read/id/100
|
||
~~~
|
||
|
||
Для зміни формату представлення адреси URL, потрібно налаштувати компонент додатка [urlManager|CWebApplication::urlManager] таким чином, щоб метод [createUrl|CController::createUrl] міг автоматично переключитися на використання нового формату, а додаток міг коректно сприймати новий формат адрес URL:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
…
|
||
'components'=>array(
|
||
…
|
||
'urlManager'=>array(
|
||
'urlFormat'=>'path',
|
||
),
|
||
),
|
||
);
|
||
~~~
|
||
|
||
Зверніть увагу, що вказувати клас компонента [urlManager|CWebApplication::urlManager] не потрібно, тому що він вже оголошений як [CUrlManager] в [CWebApplication].
|
||
|
||
> Tip|Підказка: Адреса URL, генерована методом [createUrl|CController::createUrl] є відносним. Для того, щоб отримати абсолютну адресу, потрібно додати префікс, використовуючи `Yii::app()->request->hostInfo`, або викликати метод [createAbsoluteUrl|CController::createAbsoluteUrl].
|
||
|
||
Людинозрозумілі URL
|
||
-------------------
|
||
|
||
Якщо у якості формата адреси URL використовується `path`, то ми можемо визначити правила формування URL, щоб зробити адреси більш привабливими і зрозумілими з точки зору користувача. Наприклад, ми можемо використовувати коротку адресу `/post/100` замість довгого варіанту `/index.php/post/read/id/100`. [CUrlManager] використовує правила формування URL як для створення, так і для обробки адрес.
|
||
|
||
Правила формування URL задаються шляхом конфігурації властивості [rules|CUrlManager::rules] компонента додатку [urlManager|CWebApplication::urlManager]:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
…
|
||
'components'=>array(
|
||
…
|
||
'urlManager'=>array(
|
||
'urlFormat'=>'path',
|
||
'rules'=>array(
|
||
'pattern1'=>'route1',
|
||
'pattern2'=>'route2',
|
||
'pattern3'=>'route3',
|
||
),
|
||
),
|
||
),
|
||
);
|
||
~~~
|
||
|
||
Правила задаються у вигляді масиву пар шаблон-шлях, де кожна пара відповідає одному правилу. Шаблон правила - рядок, який повинен збігатися із шляхом в URL. Шлях правила повинен вказувати на існуючий [шлях контролера](/doc/guide/basics.controller#route).
|
||
|
||
Крім показаного вище способу завдання правил, можна описати правило із зазначенням додаткових параметрів:
|
||
|
||
~~~
|
||
[php]
|
||
'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
|
||
~~~
|
||
|
||
Починаючи з версії 1.1.7, можна використовувати показаний нижче формат. Тобто патерн вказується як елемент масиву, що дозволяє вказати кілька правил одного патерну:
|
||
|
||
~~~
|
||
[php]
|
||
array('route1', 'pattern'=>'pattern1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
|
||
~~~
|
||
|
||
Тут масив містить список додаткових параметрів для правила. Можливо вказати наступні параметри:
|
||
|
||
- [pattern|CUrlRule::pattern]: патерн, який буде використаний при зіставленні і створенні URL. Дана можливість доступна з версії 1.1.7.
|
||
|
||
- [urlSuffix|CUrlRule::urlSuffix]: суфікс URL, який використовується виключно для даного правила. За замовчуванням дорівнює null, що означає використання значення [CUrlManager::urlSuffix].
|
||
|
||
- [caseSensitive|CUrlRule::caseSensitive]: чи враховує правило регістр. За замовчуванням дорівнює null, що означає використання значення [CUrlManager::caseSensitive].
|
||
|
||
- [defaultParams|CUrlRule::defaultParams]: GET-параметри за замовчуванням (`імʼя=>значення`) для даного правила. При спрацьовуванні правила параметри будуть додані в `$_GET`.
|
||
|
||
- [matchValue|CUrlRule::matchValue]: чи повинні значення GET-параметрів при створенні URL збігатися з відповідними підвиразами в основному правилі. За замовчуванням дорівнює null, що означає використання значення [CUrlManager::matchValue]. При значенні параметра `false` правило буде використано для створення URL тільки якщо імена параметрів збігаються з іменами в правилі. При значенні `true` значення параметрів додатково повинні збігатися з підвиразами у правилі. Варто зазначити, що установка значення в `true` знижує продуктивність.
|
||
|
||
- [verb|CUrlRule::verb]: тип HTTP запиту (наприклад, `GET`, `POST`, `DELETE`), для якого працює дане правило. За замовчуванням дорівнює null, що означає роботу правила з будь-якими HTTP запитами. Якщо необхідно вказати кілька типів запитів, їх треба розділити комами. У тому випадку, коли правило не збігається з поточним типом запиту, воно пропускається на етапі розбору запиту. Дана опція використовується тільки для розбору запиту і введена для підтримки URL у стилі REST. Дана можливість доступна з версії 1.1.7.
|
||
|
||
- [parsingOnly|CUrlRule::parsingOnly]: чи використовувати правило тільки на етапі розбору запиту. За замовчуванням дорівнює `false`, що означає, що правило використовується як для розбору запиту, так і для побудови URL. Дана можливість доступна з версії 1.1.7.
|
||
|
||
Використання іменованих параметрів
|
||
----------------------------------
|
||
|
||
Правило може бути асоційоване з кількома GET-параметрами. Ці параметри вказуються у шаблоні правила у вигляді маркерів наступним чином:
|
||
|
||
~~~
|
||
<ParamName:ParamPattern>
|
||
~~~
|
||
|
||
де `ParamName` відповідає імені GET-параметра, а необовʼязковий `ParamPattern` - регулярному виразу, який використовується для перевірки відповідності значенню GET-параметра. Якщо `ParamPattern` не зазначений, то параметр повинен відповідати будь-яким символам, крім слеша `/`. У момент створення URL маркери будуть замінені на відповідні значення параметрів, а в момент обробки URL, відповідним GET-параметрами будуть присвоєні результати обробки.
|
||
|
||
Для наочності наведемо кілька прикладів. Припустимо, що наш набір правил складається з трьох правил:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
'posts'=>'post/list',
|
||
'post/<id:\d+>'=>'post/read',
|
||
'post/<year:\d{4}>/<title>'=>'post/read',
|
||
)
|
||
~~~
|
||
|
||
- Виклик `$this->createUrl('post/list')` згенерує `/index.php/posts`. Тут було застосовано перше правило.
|
||
|
||
- Виклик `$this->createUrl('post/read',array('id'=>100))` згенерує `/index.php/post/100`. Застосовано друге правило.
|
||
|
||
- Виклик `$this->createUrl('post/read',array('year'=>2008,'title'=>'a sample post'))` згенерує `/index.php/post/2008/a%20sample%20post`. Використано третє правило.
|
||
|
||
- Виклик `$this->createUrl('post/read')` згенерує `/index.php/post/read`. Жодне з правил не було застосовано.
|
||
|
||
При використанні [createUrl|CController::createUrl] для генерації адреси URL, маршрут і GET-параметри, передані методу, використовуються для визначення правила, яке потрібно застосувати. Правило застосовується у тому випадку, коли всі параметри, асоційовані з правилом, присутні серед GET-параметрів, а маршрут відповідає параметру маршруту.
|
||
|
||
Якщо ж кількість GET-параметрів більше, ніж вимагає правило, то зайві параметри будуть включені до рядка запиту. Наприклад, якщо викликати `$this->createUrl('post/read',array('id'=>100,'year'=>2008))`, ми отримаємо `/index.php/post/100?year=2008`. Для того, щоб зайві параметри були відображені в інформаційній частині шляху, необхідно додати до правила `/*`. Таким чином, використовуючи правило `post/<id:\d+>/*` отримаємо URL вигляду `/index.php/post/100/year/2008`.
|
||
|
||
Як вже говорилося, друге завдання правил URL - розбирати URL-запити. Цей процес зворотний процесу створення URL. Наприклад, коли користувач запитує `/index.php/post/100`, застосовується друге правило з прикладу вище і запит перетворюється в маршрут `post/read` і GET-параметр `array('id'=>100)` (доступний через `$_GET`).
|
||
|
||
> Note|Примітка: Використання правил URL знижує продуктивність додатку. Це відбувається з тієї причини, що у процесі парсинга запитаного URL [CUrlManager] намагається знайти відповідність кожного правила до тих пір, поки яке-небудь з правил не буде застосовано. Чим більше правил, тим більше втрати продуктивності. Тому у разі високонавантажених додатків використання правил URL варто мінімізувати.
|
||
|
||
Параметризація маршрутів
|
||
------------------------
|
||
|
||
Ми можемо використовувати іменовані параметри у маршруті правила. Таке правило може бути застосоване до декількох маршрутах, що збігаються з правилом. Це може допомогти зменшити число правил і, таким чином, підвищити продуктивність додатку.
|
||
|
||
Для того, щоб показати параметризацію маршрутів, використовуємо наступний набір правил:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
'<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>' => '<_c>/<_a>',
|
||
'<_c:(post|comment)>/<id:\d+>' => '<_c>/read',
|
||
'<_c:(post|comment)>s' => '<_c>/list',
|
||
)
|
||
~~~
|
||
|
||
Ми використовували два іменованих параметри в маршруті правил: `_c` і `_a`. Перший відповідає назві контролера і може дорівнювати `post` або `comment`, другий - назві action-а і може приймати значення `create`, `update` або `delete`. Ви можете називати параметри по-іншому, якщо їх імена не конфліктують з GET-параметрами, які можуть використовуватися в URL.
|
||
|
||
При використанні правил, наведених вище, URL `/index.php/post/123/create` буде оброблено як маршрут `post/create` з GET-параметром `id=123`. По маршруту `comment/list` з GET-параметром `page=2`, ми можемо створити URL `/index.php/comments?page=2`.
|
||
|
||
Параметризація імен хостів
|
||
--------------------------
|
||
|
||
Також можливо використовувати імена хостів в правилах для розбору і створення URL. Можна виділяти частину імені хоста в GET-параметр. Наприклад, URL `http://admin.example.com/en/profile` може бути розібраний в GET-параметри `user=admin` і `lang=en`. З іншого боку, правила з іменами хостів можуть також використовуватися для створення URL адрес.
|
||
|
||
Щоб використовувати параметризрвані імена хостів, увімкніть імʼя хоста у правила URL:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile',
|
||
)
|
||
~~~
|
||
|
||
Приклад вище говорить, що перший сегмент імені хоста повинен стати параметром `user`, а перший сегмент шляху — параметром `lang`. Правило відповідає маршруту `user/profile`.
|
||
|
||
Памʼятайте, що [CUrlManager::showScriptName] не працює при створенні URL адреси з використанням правил з параметризованим імʼям хоста.
|
||
|
||
Варто відзначити, що правило з параметризованим імʼям хоста не повинно містити піддиректорій у тому випадку, якщо додаток знаходиться у піддиректорії кореня веб-сервера. Приміром, якщо додаток розташовується за адресою `http://www.example.com/sandbox/blog`, ми повинні використовувати точно таке ж правило URL, як описано вище. Без піддиректорії: `sandbox/blog`.
|
||
|
||
Приховуємо `index.php`
|
||
----------------------
|
||
|
||
З метою зробити адресу URL ще більш привабливою можна сховати імʼя вхідного скрипта `index.php`. Для цього необхідно налаштувати веб-сервер і компонент додатку [urlManager|CWebApplication::urlManager].
|
||
|
||
Спочатку зконфігуруємо веб-сервер таким чином, щоб адреса URL без вказівки імені вхідного скрипта як і раніше передавався на обробку вхідного скрипта. Для сервера [Apache HTTP server](http://httpd.apache.org/) це досягається шляхом включення механізму перетворення URL і завданням кількох правил. Для цього необхідно створити файл `/wwwroot/blog/.htaccess`, що містить правила, наведені нижче. Ті ж правила можуть бути розміщені у файлі конфігурації Apache в секції `Directory` для `/wwwroot/blog`.
|
||
|
||
~~~
|
||
RewriteEngine on
|
||
|
||
# if a directory or a file exists, use it directly
|
||
RewriteCond %{REQUEST_FILENAME} !-f
|
||
RewriteCond %{REQUEST_FILENAME} !-d
|
||
|
||
# otherwise forward it to index.php
|
||
RewriteRule . index.php
|
||
~~~
|
||
|
||
Далі потрібно встановити властивість [showScriptName|CUrlManager::showScriptName] компонента
|
||
[urlManager|CWebApplication::urlManager] рівним `false`.
|
||
|
||
Тепер, викликавши `$this->createUrl('post/read',array('id'=>100))`, ми отримаємо URL `/post/100`. Що важливо, ця адреса URL буде коректно розпізнана нашим веб-додатком.
|
||
|
||
Підміна закінчення у адресі URL
|
||
-------------------------------
|
||
|
||
На додаток до всього перерахованого вище, ми можемо додати до наших адрес URL закінчення. Наприклад, ми можемо отримати `/post/100.html` замість `/post/100`, представивши користувачеві начебто статичну сторінку. Для цього потрібно просто налаштувати компонент [urlManager|CWebApplication::urlManager] шляхом встановлення властивості [urlSuffix|CUrlManager::urlSuffix] будь-якого бажаного закінчення.
|
||
|
||
Використання свого класу правила URL
|
||
------------------------------------
|
||
|
||
> Note|Примітка: дана можливість доступна з версії 1.1.8.
|
||
|
||
За замовчуванням кожне правило URL для [CUrlManager] представлено обʼєктом класу [CUrlRule]. Цей обʼєкт розбирає запити і створює URL по заданому правилу. Незважаючи на те, що [CUrlRule] досить гнучкий і підходить для роботи із більшістю форматів URL, іноді потрібні які-небудь особливі можливості.
|
||
|
||
Наприклад, для сайту з продажу автомобілів може знадобитися підтримувати URL виду `/Виробник/Модель`, де і `Виробник` і `Модель` повинні відповідати даним з певної таблиці бази даних. У цьому випадку клас [CUrlRule] не підійде так як він, в основному, працює із статично описаними регулярними виразами, а не з базою даних.
|
||
|
||
У даному випадку можна реалізувати новий клас правила URL, успадкувавши [CBaseUrlRule], і використовувати його в одному або декількох правилах. Для наведеного вище прикладу з продажу автомобілів підійдуть такі правила URL:
|
||
|
||
~~~
|
||
[php]
|
||
array(
|
||
// стандартне правило для обробки '/' як 'site/index'
|
||
'' => 'site/index',
|
||
|
||
// стандартне правило для обробки '/login' як 'site/login' і т.д.
|
||
'<action:(login|logout|about)>' => 'site/<action>',
|
||
|
||
// власне правило для URL вигляду '/Виробник/Модель'
|
||
array(
|
||
'class' => 'application.components.CarUrlRule',
|
||
'connectionID' => 'db',
|
||
),
|
||
|
||
// стандартне правило для обробки 'post/update' та ін.
|
||
'<controller:\w+>/<action:\w+>' => '<controller>/<action>',
|
||
),
|
||
~~~
|
||
|
||
Выше мы использовали свой класс правила URL `CarUrlRule` для обработки URL
|
||
вида `/Производитель/Модель`. Данный класс может быть реализован следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
class CarUrlRule extends CBaseUrlRule
|
||
{
|
||
public $connectionID = 'db';
|
||
|
||
public function createUrl($manager,$route,$params,$ampersand)
|
||
{
|
||
if ($route==='car/index')
|
||
{
|
||
if (isset($params['manufacturer'], $params['model']))
|
||
return $params['manufacturer'] . '/' . $params['model'];
|
||
else if (isset($params['manufacturer']))
|
||
return $params['manufacturer'];
|
||
}
|
||
return false; // не застосовуємо дане правило
|
||
}
|
||
|
||
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
|
||
{
|
||
if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))
|
||
{
|
||
// Перевіряємо $matches[1] і $matches[3] на предмет
|
||
// відповідності виробнику і моделі у БД.
|
||
// Якщо відповідають, виставляємо $_GET['manufacturer'] та/або $_GET['model']
|
||
// і повертаємо рядок із маршрутом 'car/index'.
|
||
}
|
||
return false; // не застосовуємо дане правило
|
||
}
|
||
}
|
||
~~~
|
||
|
||
Свій клас правила URL повинен реалізувати два абстрактних методи, оголошених у [CBaseUrlRule]:
|
||
|
||
* [createUrl()|CBaseUrlRule::createUrl()]
|
||
* [parseUrl()|CBaseUrlRule::parseUrl()]
|
||
|
||
Крім показаного вище типового використання, свій клас URL може стати у нагоді і в інших ситуаціях. Наприклад, можна реалізувати клас правила, який буде записувати у журнал адреси URL, що розбираються і створюються. Це може стати у нагоді на етапі розробки. Також можна реалізувати клас, який показує особливу сторінку помилки 404 у тому випадку, коли всі інші правила не спрацювали для адреси URL, що розбирається. Варто зазначити, що в цьому випадку правило з цим спеціальним класом повинно вказуватися останнім у списку.
|
||
|
||
<div class="revision">$Id: topics.url.txt 3591 2012-02-17 21:44:32Z qiang.xue@gmail.com $</div> |