Yii и ЧПУ для каталога

Дано: сайт с каталогом продукции.
Необходимо: реализовать url вида http://site.ru/marka/model, а иначе говоря /Производитель/Модель.
Решение:
Для реализации ЧПУ подобного вида необходимо внести изменения в параметр конфига urlManager и написать свой класс обработки URL, унаследовав его от CBaseUrlRule.
Правило для URL вида /Производитель/Модель добавляем в параметр urlManager (приоритет, т.е. в начало или конец массива, определите сами).
    array(
        'class' => 'application.components.CatalogRule',
        'connectionID' => 'db',
    ),
 
    // стандартные правила для обработки 'post/update' и др.
    '<controller: w="">/<action: w="">' => '<controller>/<action>',
    // ...
),
Класс CatalogUrl
class  CatalogRule extends CBaseUrlRule
{
    public $connectionID = 'db';

    public function createUrl($manager,$route,$params,$ampersand) {
        if ($route==='catalog/index') {
            if (isset($params['mark'], $params['model']))
                return $params['mark'] . '/' . $params['model'];
            else if (isset($params['mark']))
                return $params['mark'];
        }
        return false;  // не применяем данное правило
    }

    public function parseUrl($manager,$request,$pathInfo,$rawPathInfo) {
        if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches)) {
            // Проверяем $matches[1] и $matches[3] на предмет
            // соответствия производителю и модели в БД.
            $mark = CatalogMark::model()->find('alias=:alias', array(':alias' => $matches[1]));
            if ($mark !== null) {
                $_GET['mark'] = $mark->id;
                $model = CatalogModel::model()->getByAlias($matches[3], $mark->id);
                if($model !== null) {
                    $_GET['model'] = $model->id;
                }
                // Если соответствуют, выставляем $_GET и возвращаем строку с маршрутом 'catalog/index'.
                return 'catalog/index';
            }
        }
        return false;  // не применяем данное правило
    }
}
Свой класс Url обязательно должен реализовать методы createUrl и parseUrl. Приведенный выше пример используется на одном из реализованных мной проектов. Хотя сейчас он претерпел некоторые изменения и оброс дополнительными условиями :)
Для общего развития - очень полезная статья в мануале, откуда я все и почерпнула.

UPDATED
Данный класс размещаем в папке components. И настраиваем конфиг. Так выглядит конфиг у меня:
'urlManager' => array(
   'urlSuffix' => '/',
   'showScriptName' => false,
   'urlFormat' => 'path',
   'rules' => array(
      'http://<domain: w+>.site.ru/' => 'site/index',
      array(
         'class' => 'application.components.CatalogRule',
         'connectionID' => 'db',
      ),
      '<controller: w+>/<id: d+>'=>'<controller>/view',
      '<controller: w+>/<action: w+>/<id: d+>'=>'<controller>/',
      '<controller: w+>/<action: w+>'=>'<controller>/<action>',
      '<module: w+>/<controller: w+>/<action: w+>/<id: d+>' => '<module>/<controller>/<action>',
      '<module: w+>/<controller: w+>/<action: w+>' => '<module>/<controller>/<action>',
      '<module: w+>/<controller: w+>' => '<module>/<controller>/index',
   ),
),


Комментарии

  1. Доброго дня.
    А как вы используете далее созданный класс для создания и разбора url? У меня получилось через добавления кода в файле CWebApplication.php в фу-цию processRequest

    $cat_rule = new CatalogRule();
    $route = $cat_rule->parseUrl('', '', $route, '');

    Насколько это правильно есть другие способы?

    ОтветитьУдалить
    Ответы
    1. это неправильно править файлы фреймворка.
      Наш класс - это компонент. Помещаете его в папку components. И в конфигах указываете, когда следует его применять в секции urlManager. Дополнила пост примером своего конфига.

      Удалить
  2. В конфиге "urlManager" объявление класса в разделе "rules" было после стандартных правил поставил перед ними и заработало.

    ОтветитьУдалить
    Ответы
    1. Все верно. Ведь как только находится совпадение с правилом, остальные правила не отрабатывают. Поэтому наше правило стоит раньше в списке

      Удалить
  3. Спасибо! Теперь полная картина :)

    ОтветитьУдалить
  4. Здравствуйте!
    Не могу разобрать такое url /investment-and-business/e-commerce/kredit-v-sisteme-yandeks-dengi, если Вам не сложно подскажите пожалуйста!

    ОтветитьУдалить
    Ответы
    1. я не особо сильна в регулярных выражениях. Погуглите регулярку для вашего урл, думаю, в сети уже есть масса примеров

      Удалить
  5. Начал изучение yii2. Возникла подобная задача. С пониманием у меня туго. Перерыл массу всего, но этот пример оказался для меня наиболее удачным! =)
    Лучей добра и счастья автору! <3

    ОтветитьУдалить
  6. Этот комментарий был удален автором.

    ОтветитьУдалить
  7. а как быть с пагинацией? у меня валятся все правила

    ОтветитьУдалить
    Ответы
    1. также нужно переписать правила и пагинации, и сортировки.
      Как пример, для вывода новостей у меня был использован CListView. Для него задаем параметр
      'pager'=>array('class'=>'Pager', 'pageVar' => 'News_page'),
      И класс Pager здесь - это наследник класса CLinkPager с переписанным методом createPageUrl

      Удалить
    2. круто! спасибо! остается еще один вопрос. есть форма поиска(вернее фильтрации) ну вроде выбрать марку, потом модель. как переопределить createUrl для формы? форма сделана через CActiveForm, соответственно данные приходят в виде ?modelName model=xxx&.... ну вы понимаете о чем я. сейчас у меня один вариант - сделать экшен, который будет принимать дагнную форму и редиректить с правильным create url, но похоже на костыл какой-то.

      Удалить
    3. http://www.yiiframework.com/doc/api/1.1/CActiveForm#action-detail
      очевидно, вы про это

      Удалить
  8. Вопрос не совсем по этой теме, но мне некуда больше пойти. Я нигде не нашел выхода. Спасите. У меня есть ссылка domen.ru/index.php/site/cars/1
    createurl работает и не ставит index.php но если ручками набрать ссылка прекрасно работает. но мне нужен редирект с нее на domen.ru/cars/1

    в htaccess
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . index.php

    в сonfige
    'urlManager'=>array(
    'urlFormat' => 'path',
    'showScriptName'=>false,
    'rules'=>array(
    '' => 'site/index',
    '' => 'site/',
    array(
    'class' => 'application.components.CarsRule',
    'connectionID' => 'db',
    ),
    ),
    ),

    Что не так?? подскажите, буду должен..

    ОтветитьУдалить
    Ответы
    1. Если необходим именно редирект с подменой адреса в строке браузера, то только htaccess по правилу
      Вопрос лишь в том, точно ли нужен редирект, возможно, нужно сделать так, чтобы ссылки формировались правильные и необходимость редиректа отпадет. Кстати, очень странный формат ссылки domen.ru/index.php/site/cars/1

      Удалить

Отправить комментарий

Популярные сообщения