PHP framework CodeIgniter. Управление внутренней адресацией

Владимир | | CodeIgniter, htaccess, PHP.

CI router
В этой заметке я покажу, как использовать встроенный роутер CodeIgniter'а для решения одной несложной задачи.

Прежде всего, пару слов о том, что это такое и зачем нужно.

Как вы, наверное, знаете CodeIgniter – это php фреймворк, одними из основных достоинств которого являются простота использования и низкая ресурсоемкость. Тем не менее, его возможности практически не уступают более «тяжелым» аналогам.

Одной из таких возможностей и является роутер. Он позволяет с помощью несложных правил настроить внутреннюю адресацию сайта по своему вкусу.

Любой запрос браузера передается роутеру, а уже потом – контроллеру. Таким образом, можно изменить любой из параметров запроса. Например, перенаправить запрос другому контроллеру или добавить/удалить параметры.

Делается это достаточно просто. В файле /application/config/routes.php нужно задать правила в виде:

$route[‘выражение’] = "новый_адрес";

выражение – здесь можно использовать регулярные выражения, которые будут использоваться для проверки того, нужно ли применить данное правило к полученному адресу или нет.

Теперь переходим к нашей задаче.

Допустим, у нас есть сайт (блог) и мы хотим создать для каждой из его записей постоянные ссылки (permalink) (такая функция есть практически во всех движках блогов).

Первоначально наши записи доступны по адресу:
http://mysite/index.php/main/goto/postid

Это стандартная адресация в CodeIgniter. Здесь:
main – названия контроллера;
goto – имя метода контроллера, который будет обрабатывать запрос;
postid – номер записи в базе данных.

Нам нужно, чтобы записи были доступны по адресу вида:
http://mysite/index.php/post_permalink

Сами ссылки создать и хранить не сложно. Для этого достаточно к таблице с записями добавить еще одно поле (permalink) и хранить в нем постоянные ссылки.

Примечание. Все постоянные ссылки должны быть уникальными.

Переходим к роутеру.

Прежде всего, нужно четко понимать как отличить адрес с постоянной ссылкой от обычного. В данном случае отсутствие имени контроллера (main) в начале адреса означает, что запрос содержит постоянную ссылку. Т.е. в этом случае мы должны перенаправить этот запрос методу goto.

Если имя контроллера в адресе найдено, то оставляем все без изменений.

Реализуется вся эта система с помощью двух простых правил:

$route['(^main.*$)'] = "$1";
$route['(^.+$)'] = "main/goto/$1";

Первое правило будет применено только для запросов, которые начинаются со слова main.

Второе правило вставит main/goto/ в начале полученного адреса.

Примечание. Как вы, наверное, догадались, роутер получает только ту часть адреса, которая идет после index.php. И еще одно, порядок следования правил в данном случае важен. Если их поменять местами, то правило $route['(^.+$)'] = "main/goto/$1" будет использовано для всех адресов.

Теперь взгляните на метод goto.

function goto($permalink) {
    $pageData['title'] = "Заголовок страницы";

    //здесь должен быть код получения записи из БД
    //запрос может выглядеть так
    //SELECT * FROM posts WHERE permalink = $permalink
    $this->load->view('header', $pageData);
    $this->load->view('page', $pageData);
    $this->load->view('footer');
}

Как видите, роутер просто меняет адрес и не требует специальной поддержки со стороны контроллера.

Кстати, вы можете передавать любое количество параметров в адресе с постоянной ссылкой. Например:
http://mysite/index.php/post_permalink/par1/par2/...

До встречи!

  • Очень полезная заметка, разве что скептически отношусь к регулярным выражениям (ввиду их несомненной тормознутости), особенно там, где можно было бы их заменить обычными строковыми функциями… но тут или юзать более тормозной, но стандартный функционал или дописывать свои механизмы, зато более быстрые… 😎

  • Очень полезная заметка, разве что скептически отношусь к регулярным выражениям (ввиду их несомненной тормознутости), особенно там, где можно было бы их заменить обычными строковыми функциями… но тут или юзать более тормозной, но стандартный функционал или дописывать свои механизмы, зато более быстрые… 😎

  • Sam

    Зря так. Не тормознутые они. Есть такая теорема об экивиалентности регулрного выражения конечному автомату и она доказана. Так что тормознутость только от неумения их писать.

    p.s. советую почитать Фридла.

  • Sam

    Зря так. Не тормознутые они. Есть такая теорема об экивиалентности регулрного выражения конечному автомату и она доказана. Так что тормознутость только от неумения их писать.

    p.s. советую почитать Фридла.

  • Присоединюсь к Sam'у.
    Тормознутость в абсолютном выражении определяется самим регулярным выражением и длинной строки, которая анализируется (это, конечно, упрощение).
    В данном случае длина строки вряд ли превысит несколько сот символов. Поэтому я сильно сомневаюсь, что замена регулярных выражений на более быстрый механизм внесет заметный вклад в скорость создания страницы или нагрузку на сервер.

  • Присоединюсь к Sam'у.
    Тормознутость в абсолютном выражении определяется самим регулярным выражением и длинной строки, которая анализируется (это, конечно, упрощение).
    В данном случае длина строки вряд ли превысит несколько сот символов. Поэтому я сильно сомневаюсь, что замена регулярных выражений на более быстрый механизм внесет заметный вклад в скорость создания страницы или нагрузку на сервер.

  • Алексей

    А как сделать привычные
    и скрипты?
    Можно ли это решить «внутри» кодигнайтера? или только средствами .htaccess?

  • Алексей

    А как сделать привычные
    и скрипты?
    Можно ли это решить «внутри» кодигнайтера? или только средствами .htaccess?

  • Алексей

    Извиняюсь — хотел написать как при работащем кодигнайтере получать с сервера таблицы стилей, скрипты, картинки?

    • Так же как и для обычного сайта. Нужно указать путь к файлам со стилями, скриптами, картинками…

      Например, сайта находится здесь: http://www.mysite.com
      В корне сервера (папка на которую указывает DOCUMENT_ROOT) размещены index.php (обрабатывает все запросы к CodeIgniter) и папка css с файлом styles.css (таблица стилей).
      Чтобы подключить styles.css нужно в заголовке страницы нужно написать:
      <link rel="stylesheet" type="text/css" href="http://www.mysite.com/css/styles.css" />

      Адрес сайта, конечно, лучше не писать, а использовать функцию CI base_url(), которая возвращает адрес прописанный в конфиге (application/config/config.php)
      <link rel="stylesheet" type="text/css" href="".base_url()."css/styles.css" />

      То, что файл, создающий страницу, находится в папке application/views/ не играет никакой роли.

  • Алексей

    Извиняюсь — хотел написать как при работащем кодигнайтере получать с сервера таблицы стилей, скрипты, картинки?

    • Так же как и для обычного сайта. Нужно указать путь к файлам со стилями, скриптами, картинками…

      Например, сайта находится здесь: http://www.mysite.com
      В корне сервера (папка на которую указывает DOCUMENT_ROOT) размещены index.php (обрабатывает все запросы к CodeIgniter) и папка css с файлом styles.css (таблица стилей).
      Чтобы подключить styles.css нужно в заголовке страницы нужно написать:
      <link rel="stylesheet" type="text/css" href="http://www.mysite.com/css/styles.css" />

      Адрес сайта, конечно, лучше не писать, а использовать функцию CI base_url(), которая возвращает адрес прописанный в конфиге (application/config/config.php)
      <link rel=\"stylesheet\" type=\"text/css\" href=\"".base_url()."css/styles.css\" />

      То, что файл, создающий страницу, находится в папке application/views/ не играет никакой роли.

  • Алексей

    ну это и есть решение с помощью .htaccess
    только надо добавить в идущее по-умолчанию правило на передачу запроса index.php исключения для css|scripts

    С этим решением всё более-менее понятно… а если хочу хранить стили, скрипты и файлы для скачивания (назовем так в том числе и картинки) не в корне сервера, а в папке application и не строить регулярных выражений в .htaccess? В принципе вопрос не слишком актуальный — скорее риторический…
    писать контроллер для отдачи таких файлов может и правильно с академической точки зрения, но неправильно с жизненной… интересно как друпал при мультисайтинге отдаёт…

    • Описанное в предыдущем комментарии решение вообще не требует использования .htaccess?

      А вот если вы хотите хранить файлы в папке application, то возможны варианты.
      Если вы используете стандартное размещение:
      DOCUMENT_ROOT/system/application/…
      то нужно просто указать абсолютный путь к файлам:
      http://www.mysite.com/system/application/css/styles.css
      .htaccess здесь не нужен (раз /index.php/ нет в адресе, то и обращения к нему не будет).

      А вот если вы убрали /system/application/ за пределы DOCUMENT_ROOT, то и доступ к файлам не получите (разве что какими-то обходными путями).

      P.S. У меня сложилось впечатление, что у вас в .htaccess есть правило, которое во все адреса вставляет /index.php после имени домена. В этом случае проще всего не применять это правило к папкам /css, /js и т.д.

  • Алексей

    ну это и есть решение с помощью .htaccess
    только надо добавить в идущее по-умолчанию правило на передачу запроса index.php исключения для css|scripts

    С этим решением всё более-менее понятно… а если хочу хранить стили, скрипты и файлы для скачивания (назовем так в том числе и картинки) не в корне сервера, а в папке application и не строить регулярных выражений в .htaccess? В принципе вопрос не слишком актуальный — скорее риторический…
    писать контроллер для отдачи таких файлов может и правильно с академической точки зрения, но неправильно с жизненной… интересно как друпал при мультисайтинге отдаёт…

    • Описанное в предыдущем комментарии решение вообще не требует использования .htaccess?

      А вот если вы хотите хранить файлы в папке application, то возможны варианты.
      Если вы используете стандартное размещение:
      DOCUMENT_ROOT/system/application/…
      то нужно просто указать абсолютный путь к файлам:
      http://www.mysite.com/system/application/css/styles.css
      .htaccess здесь не нужен (раз /index.php/ нет в адресе, то и обращения к нему не будет).

      А вот если вы убрали /system/application/ за пределы DOCUMENT_ROOT, то и доступ к файлам не получите (разве что какими-то обходными путями).

      P.S. У меня сложилось впечатление, что у вас в .htaccess есть правило, которое во все адреса вставляет /index.php после имени домена. В этом случае проще всего не применять это правило к папкам /css, /js и т.д.

  • Алексей

    Точно! Спасибо за наводку — когда начал разбираться с кодигнайтером поставил такое правило в .htaccess
    AddDefaultCharset UTF-8
    RewriteEngine on
    RewriteCond $1 !^(index.php|.*/images|styles|scripts|robots.txt)
    RewriteRule ^(.*)$ /index.php/$1 [L]
    RewriteRule ^(images/)(.*) /system/application/$1$2 [L]

    чтоб убрать index.php из адресации.
    Где-то подсмотрел решение. Да позабыл, что оно не «идет по-умолчанию». Ещё раз спасибо.
    Вот.

  • Алексей

    Точно! Спасибо за наводку — когда начал разбираться с кодигнайтером поставил такое правило в .htaccess
    AddDefaultCharset UTF-8
    RewriteEngine on
    RewriteCond $1 !^(index\.php|.*/images|styles|scripts|robots\.txt)
    RewriteRule ^(.*)$ /index.php/$1 [L]
    RewriteRule ^(images/)(.*) /system/application/$1$2 [L]

    чтоб убрать index.php из адресации.
    Где-то подсмотрел решение. Да позабыл, что оно не «идет по-умолчанию». Ещё раз спасибо.
    Вот.

  • Правильно ли я понимаю:
    Для реализации подобного
    $route[‘(^.+$)’] = «main/goto/$1»;
    необходимо перед этим правилом перечислить правила для всех контроллеров, так?

    Мне кажется более красивым решением будет переписывание роутера так, чтобы любые ненайденные адреса шли не в 404 в определенный контроллер…

    • Не совсем.
      Просто правило
      $route[‘(^.+$)’] = “main/goto/$1?;
      применяется для всех адресов. Т.е. если вы разместите более специфичные правило после этого, то они работать не будут. А вот нужны ли вам эти правила, вопрос другой.

  • Правильно ли я понимаю:
    Для реализации подобного
    $route[‘(^.+$)’] = «main/goto/$1»;
    необходимо перед этим правилом перечислить правила для всех контроллеров, так?

    Мне кажется более красивым решением будет переписывание роутера так, чтобы любые ненайденные адреса шли не в 404 в определенный контроллер…

    • Не совсем.
      Просто правило
      $route[‘(^.+$)’] = “main/goto/$1?;
      применяется для всех адресов. Т.е. если вы разместите более специфичные правило после этого, то они работать не будут. А вот нужны ли вам эти правила, вопрос другой.

  • *шли не в 404, а в определенный контроллер…

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

      По-моему, использовать отдельный контроллер имеет смысл, если в зависимости от url нужно показывать разные страницы с различным описанием ошибок.

  • *шли не в 404, а в определенный контроллер…

    • Это вопрос архитектуры приложения. В CI есть шаблоны для страниц ошибок
      \system\application\errors\
      Поэтому немного переделав этот шаблон вы можете показать посетителю что угодно, например, форму поиска по сайту.

      По-моему, использовать отдельный контроллер имеет смысл, если в зависимости от url нужно показывать разные страницы с различным описанием ошибок.