Генерация URL для вложенных категорий в Yii

Стандартный класс CUrlManager в Yii (да и в других фреймворках) позволяет собирать URL динамически на основе правил маршрутизации. В интернет-магазинах и блогах часто используются многоуровневые категории. Попробуем использовать их в Yii.
Правила маршрутизации для категорий
Предположим, что в нашем магазине (блоге, портфолио или в любом другом разделе сайта) нужно обеспечить работу, например, с такими адресами:
http://site.com/shop/computers - категория "Компьютеры" http://site.com/shop/computers/23 - товар с ID = 23
Для разбора и создания таких адресов нам достаточно добавить в конфигурацию urlManager такие правила:
'shop/<action:cart|order>'=>'shop/<action>', 'shop/<category:[\w_-]+>/<id:[\d]+>'=>'shop/show', 'shop/<category:[\w_-]+>'=>'shop/category', 'shop'=>'shop/index',
В первой строке мы предусмотрительно описали действия контроллера, которые не нужно путать с категориями (чтобы по адресу shop/cart мы попадали в корзину, а не в товары категории cart). От этого можно избавиться также предварив адрес категории ключевым словом category. Тогда наши адреса вместо shop/... примут вид shop/category/...:
'shop/category/<category:[\w_\/-]+>/<id:[\d]+>'=>'shop/show', 'shop/category/<category:[\w_\/-]+>'=>'shop/category', 'shop'=>'shop/index',
В любом случае, эти правила позволят работать с нашим контроллером:
class ShopController extends Controller { public function actionIndex() { // Вывод списка всех товаров } public function actionCategory($category) { // Вывод списка товаров категории } public function actionShow($category, $id) { // Отображение страницы товара } }
Но что делать, если у нас должна быть поддержка вложенных категорий? Разберём такой пример:
http://site.com/shop/computers/printers/laser - вложенная категория "Лазерные принтеры" http://site.com/shop/computers/printers/laser/37 - товар с ID = 37
Предыдущие правила для такого случая не подойдут. Категория должна передаваться в контроллер целиком (в виде computers/printers/laser), то есть именованный параметр <category> должен включать в себя и обратный слэш «/». Добавим этот символ в наши шаблоны:
'shop/<action:cart|order>'=>'shop/<action>', 'shop/<category:[\w_\/-]+>/<id:[\d]+>'=>'shop/show', 'shop/<category:[\w_\/-]+>'=>'shop/category', 'shop'=>'shop/index',
В крайнем случае можно разрешить все символы, используя точку:
'shop/<action:cart|order>'=>'shop/<action>', 'shop/<category:.+>/<id:[\d]+>'=>'shop/show', 'shop/<category:.+>'=>'shop/category', 'shop'=>'shop/index',
Заметьте, что жадный шаблон <category:[\w_\/-]+> «проглотит» весь адрес до конца строки, поэтому дополнительные параметры должны либо располагаться в начале адреса в виде
shop/тип/...категория...'shop/<type:\w+>/<category:[\w_\/-]+>/<id:[\d]+>'=>'shop/show',либо должны быть предварены любым каким-нибудь легко распознаваемым префиксом вроде
shop/...категория.../type/...илиshop/...категория.../type_..., соответственно правила будут такими'shop/category/<category:[\w_\/-]+>/type/<type:\w+>'=>'shop/category',
Теперь при переходе по адресу http://site.com/shop/computers/printers/laser мы будем попадать на действие ShopController::actionCategory, параметр $category которого будет содержать путь computers/printers/laser. Остаётся лишь найти нужную модель категории по этому пути и вывести список товаров:
class ShopController extends Controller { const PRODUCTS_PER_PAGE = 20; public function actionCategory($category) { // Ищем категорию по переданному пути $category = ShopCategory::model()->findByPath($category); if ($category === null) throw new CHttpException(404, 'Not found'); $criteria = new CDbCriteria(); $criteria->addInCondition('t.category_id', array_merge(array($category->id), $category->getChildsArray())); $dataProvider = new CActiveDataProvider(ShopProduct::model()->cache(3600), array( 'criteria'=>$criteria, 'pagination'=> array( 'pageSize'=>self::PRODUCTS_PER_PAGE, 'pageVar'=>'page', ) )); $this->render('category', array( 'dataProvider'=>$dataProvider, 'category'=>$category, )); } public function actionShow($category, $id) { $model = $this->loadModel($id) $this->render('show', array('model'=>$model)); } }
Здесь мы воспользовались методом findByPath и getChildsArray модели ShopCategory. Условие
$criteria->addInCondition('t.category_id', array_merge(array($category->id), $category->getChildsArray()));
позволяет выбрать товары из текущей категории и всех её дочерних.
Эти методы можно создать в модели самому, а можно подключить для этих целей поведение DCategoryTreeBehavior.
Созданние URL для вложенных категорий
Воспользуемся стандартным методом CUrlManager::createUrl или CUrlManager::createAbsoluteUrl для сборки адреса для категории computers:
echo $this->createAbsoluteUrl('shop/index'); echo $this->createAbsoluteUrl('shop/category', array('category'=>'computers')); echo $this->createAbsoluteUrl('shop/show', array('category'=>'computers', 'id'=>12));
Мы получим адреса в полном соответствии с заданными нами маршрутами:
http://site.com/shop http://site.com/shop/computers http://site.com/shop/computers/12
Теперь попробуем сделать это же, но с вложенными категориями
echo $this->createAbsoluteUrl('shop/category', array('category'=>'computers/printers/laser')); echo $this->createAbsoluteUrl('shop/show', array('category'=>'computers/printers/laser', 'id'=>12));
Мы получим не совсем то, что хотели:
http://site.com/shop/computers%2Fprinters%2Flaser http://site.com/shop/computers%2Fprinters%2Flaser/12
Это происходит из-за того, что все параметры метод CUrlRule::createUrl кодирует функцией urlencode. Соответственно, в нашей категории перекодируются и все слэши.
Есть несколько способов исправить это неудобство:
- Создать класс
ShopUrlRuleи использовать его вместо маршрутов; - Переопределить
CUrlManagerи убрать из его методаcreateUrlэкранирование слэшей; - Не использовать
createUrlдля создания адресов, а конкатенировать их вручную.
Первый способ требует некоторых усилий, но он не универсальный, так как нужно будет создавать клоны этого класса для каждого раздела сайта. Третий способ не подойдёт, так как createUrl требуется для генерации ссылок на страницы виджетом CListPager.
Рассмотрим второй способ. Создадим класс-наследник UrlManager, который будет заменять код «%2F» обратно на слэш:
class UrlManager extends CUrlManager { public function createUrl($route, $params=array(), $ampersand='&') { return $this->fixPathSlashes(parent::createUrl($route, $params, $ampersand)); } protected function fixPathSlashes($url) { return preg_replace('|\%2F|i', '/', $url); } }
Это весь код. Нужно указать наш класс UrlManager в параметре class конфигурации компонента Yii::app()->urlManager:
return array( 'components'=>array( 'urlManager'=>array( 'class'=>'UrlManager', 'urlFormat'=>'path', 'showScriptName'=>false, 'rules'=>array( 'shop/<category:[\w_\/-]+>/<id:[\d]+>'=>'shop/show', 'shop/<category:[\w_\/-]+>'=>'shop/category', 'shop'=>'shop/index', // ... ), ), ), );
Теперь подобные адреса у нас будут строиться правильно на всём сайте.
Упрощение создания адресов
Как известно, в представлениях мы можем использовать $this->createUrl, а в виджетах и моделях Yii::app()->createUrl или Yii::app()->controller->createUrl для создания адресов ссылок.
Если у нас есть модель
class ShopProduct extends CActiveRecord { public function relations() { return array( 'category' => array(self::BELONGS_TO, 'ShopCategory', 'category_id'), ); } }
и если у категории есть метод getPath(), который генерирует полную строку вида parent/parent/category, то в представлении мы можем «легко» генерировать адреса ссылок:
<h2><a href="<?php echo $this->createUrl('shop/show', array('category'=>$product->category->getPath(), 'id'=>$product->id)); ?>"><?php echo CHtml::encode($product->title); </h2> <p>Категория: <a href=" echo $this->createUrl('shop/category', array('category'=>$product->category->getPath())); "> echo CHtml::encode($product->category->title); </h2>
Действительно «легко»? Чтобы не запоминать каждый раз маршруты и не путаться в них проще добавить геттер getUrl в наши модели. Для работы ShopCategory::getPath возьмём то же поведение DCategoryTreeBehavior:
class ShopCategory extends CActiveRecord { public function behaviors() { return array( 'CategoryBehavior'=>array( 'class'=>'DCategoryTreeBehavior', 'titleAttribute'=>'title', 'aliasAttribute'=>'alias', 'parentAttribute'=>'parent_id', 'parentRelation'=>'parent', 'requestPathAttribute'=>'category', 'defaultCriteria'=>array( 'order'=>'t.sort ASC, t.title ASC' ), ), ); } private $_url; public function getUrl() { if ($this->_url === null) $this->_url = Yii::app()->createUrl('shop/category', array('category'=>$this->cache(3600)->getPath())); return $this->_url; } }
class ShopProduct extends CActiveRecord { public function relations() { return array( 'category' => array(self::BELONGS_TO, 'ShopCategory', 'category_id'), ); } private $_url; public function getUrl() { if ($this->_url === null) $this->_url = Yii::app()->createUrl('shop/show', array('category'=>$this->category->cache(3600)->getPath(), 'id'=>$this->id)); return $this->_url; } }
Теперь можно использовать метод $model->getUrl() или эквивалентное свойство $model->url. Код представления предельно упрощается:
<h2><a href="<?php echo $product->url; ?>"><?php echo CHtml::encode($product->title); </h2> <p>Категория: <a href=" echo $product->category->url; "> echo CHtml::encode($product->category->title); </h2>
Это теперь можно использовать для защиты от дублирования адресов товаров:
class ShopController extends Controller { public function actionShow($category, $id) { $model = $this->loadModel($id) if (Yii::app()->request->getUrl() != $model->url) $this->redirect($model->url, true, 301); $this->render('show', array('model'=>$model)); } }
Теперь если будет неправильно указана категория
http://site.com/shop/computers/printers/laser/37 http://site.com/shop/computers/laser/37 http://site.com/shop/anypath/37
то произойдёт перенаправление на правильный URL.
После этого можно спокойно переносить товар между категориями и не беспокоиться о редиректах.
АлександрА как дела с пагинацией и сортировками?
Указывать их через "?"
Дмитрий ЕлисеевЕсли важны ЧПУ, то можно, например, указать все сочетания в маршруте:
lareinЕсть несколько способов исправить это неудобство ...
есть 4ый способ, у подключив к CUrlManager поведение (behaviors) в конфиге, добавив например метод createPathUrl()
но вызывать придется так: Yii::app()->urlManager->createPathUrl();
Дмитрий ЕлисеевДа, это будет работать, но только при генерации ссылок вручную. Метод createUrl используем не только мы, но и сам Yii. Если мы оставим оригинальный createUrl, то CLinkPager и CSorter (используемые в ClistView и CGridView) будут по-прежнему вызывать его для генерации сортировки и паджинации, и всё сломается.
lareinне подумал. хотя в своем проекте использую вариант изложенный в статье.
ГенадийСпасибо за статью, очень помогло. Можно вас попросить, не могли бы написать про УРЛ на поддоменах, к примеру пользователей вынести на поддомены, или разделы, или города?
Дмитрий ЕлисеевСпасибо. На похожий вопрос я отвечал уже на форуме здесь. Если не сможете разобраться, то могу подробнее с примерами кода изложить.
СтасОтличная статья, равно как и все. Радует что yii описывайте именно Вы. Лаконично, полно, вообщем не просто "лишь бы написать".
Спасибо.
Дмитрий ЕлисеевСпасибо за отзыв.
ЕвгенийСделал все как описано, но если категория родительская, то она даже не попадает в метод actionCategory и показывается Error 404, думаю это из-за urlManager, но там все как описано у вас, включая переопределенный класс UrlManager
Дмитрий ЕлисеевА какие маршруты для них написаны? Используются ли дефисы или подчёркивания?
ЕвгенийВсе как у вас, только вместо ShopController у меня CategoryController.
'rules' => array( 'category/<category:[\w_\/-]+>/<id:[\d]+>' => 'site/view', 'category/<category:[\w_\/-]+>' => 'category/category', 'category' => 'category/index', ),если ссылка /science то получаю 404.
если /science/cosmos то все нормально. где-то на маршрутах вызов спотыкается.
Дмитрий ЕлисеевВроде второе правило должно срабатывать на адреса
ЕвгенийА как вы думаете в чем может быть ошибка?
Дмитрий ЕлисеевДаже не знаю. Попробуйте переставлять местами эти и другие правила, разрбраться, какой именно контроллер генерирует ошибку 404.
ЕвгенийА насколько важна в методе findByPath($path) строчка
$this->parentAttribute . '=0'
Потому что без неё родительские категории работают. В базе данных у меня parent_id определенно как NULL а не 0. Как лучше решить эту проблему?
Дмитрий ЕлисеевЧтобы при переходе по адресу /shoes он по слову shoes не нашёл случайно какую-нибудь одноимённую вложенную вроде /man/winter/shoes.
Исправил в поведении этот фрагмент на
Теперь должно работать.
ЕвгенийСпасибо. Теперь работает
standaloneА как быть с nestedset категориями?
Дмитрий ЕлисеевТакже организовать в моделях рекурсивные меоды getPath(), findByPath() и подобные.
lareinесть у меня маленькое поведение реализующее findByPath(), getPath() и некоторые другие методы, которое подключается одновременно с NestedSetBehavior. закинул сюда http://codepad.org/XBhyQfxb
lareinз.ы. некоторые моменты не доработаны для универсальности, но всегда же можно допилить)
Евгений – icemen.ruАбалдеть. Все так классно придумано и устроено, что смог все у себя сделать используя ваши поведения, но со своим контентом и другими адресами, но все почему-то) заработало. Теперь надо разобраться где я так все удачно угадал).
Вы убили сразу два зайца, даже три. И так все коротко и ясно. Теперь мы знаем что такое поведения, как сделать вложенные категории и работать с ЧПУ. Вы гений!
Pavlov AlexeyПодскажите пожалуйста, а можно как нибудь избавиться от названия контроллера? Что бы ур выглядел вот так: http://site.com/computers/23
Спасибо
Дмитрий Елисеев
РоманПодскажите, пожалуйста, почему не отрабатывает правило:
если только явно не прописать правило:
где admin_news:
Хотя бы подскажите направление, куда копать.
Дмитрий ЕлисеевСначала определитесь, где admin/news или admin_news у Вас. Что справа, а что слева. Посчитайте число слешэй в правилах.
РоманУ меня модуль - admin, контроллер - news и действие - create. Не отрабатывает стандартное правило, в чем может быть проблема, не пойму..
Дмитрий ЕлисеевНе срабатывает по адресу /admin/news/create?
РоманДа
РоманНе поможете?
Дмитрий ЕлисеевНапишите, какие вообще есть правила до стондартных и какая ошибка отображается.
Роман'urlManager' => array( 'urlFormat' => 'path', 'showScriptName' => false, 'rules' => array( // Front '/' => 'front/index', // Admin //'admin' => 'admin/index', 'admin/user' => 'admin_user/index', 'admin/config' => 'admin_config/index', //'admin/news' => 'admin_news/index', 'admin/news/admin' => 'admin_news/admin', 'admin/news/view' => 'admin_news/view', 'admin/news/create' => 'admin_news/create', 'admin/news/update' => 'admin_news/update', 'admin/news/delete' => 'admin_news/delete', '<controller:\w+>/<id:\d+>' => '<controller>/view', '<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>', '<controller:\w+>/<action:\w+>' => '<controller>/<action>', '<module:\w+>/<controller:\w+>/<id:\d+>' => '<module>/<controller>/view', '<module:\w+>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<module>/<controller>/<action>', '<module:\w+>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>', ), ),Ошибка по url: /admin/news/create - "Системе не удалось найти запрашиваемое действие "news"."
Романесли закомментировать
то ошибка, не срабатывает правило последнее правило (module/controller/action)
Дмитрий Елисеев'rules' => array( '/' => 'front/index', '<module:\w+>/<controller:\w+>/<id:\d+>' => '<module>/<controller>/view', '<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', '<module:\w+>' => '<module>/default/index', ),
РоманТак не работает, в этом весь и вопрос..
Дмитрий ЕлисеевЕсли у Вас только один модуль admin, то сделайте так:
'rules' => array( '/' => 'front/default/index', '<module:admin>/<controller:\w+>/<id:\d+>' => '<module>/<controller>/view', '<module:admin>/<controller:\w+>/<action:\w+>/<id:\d+>' => '<module>/<controller>/<action>', '<module:admin>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>', '<module:admin>/<controller:\w+>' => '<module>/<controller>/index', '<module:admin>' => '<module>/default/index', '<controller:\w+>/<id:\d+>' => '<controller>/view', '<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>', '<controller:\w+>/<action:\w+>' => '<controller>/<action>', ),
Романfront - у меня тоже отдельным модулем (2 модуля)
Дмитрий ЕлисеевТогда для чего нужны правила для простых контроллеров? Вот эти:
РоманЕсли удалить все правила кроме
выдает ошибку: Невозможно обработать запрос /admin/news/create
Евгений$criteria->addInCondition('t.category_id', array($category->id) + $category->getChildsArray());
У меня + затер первый массив с category->id осталось только то что из getChildsArray()!!! пришлось воспользоваться array_merge()
Дмитрий ЕлисеевСпасибо! Исправил.
Denis AnonimousЗдравствуйте.
Буду очень благодарен если объясните такой момент. Никак не получается разобраться.
Желаемая структура url:
Такое вообще возможно?
Какой тогда должен быть контроллер с какими экшенами и какие правила писать в urlManager?
Спасибо
Denis AnonimousВопрос решил. Сделал так.
Получил свой желаемый вид
ДмитрийСтатья хорошая, но мне кажется что тут есть недоработка и давольно таки серьезная. Допустим есть у нас 3 категории вложенные, в поле бд в последней будет url "category1/category2/category3", и тут мы решаем что нужно переименовать "category2" на "test" ее alias автоматически тоже меняется и теперь нам нужно найти ее дочерние категории и все дочерние дочерних и заменить у них url - в донной ситуации мы должны получить "category1/test/category3". А у вас это совсем не упоменается. Как быть в этой ситуации?
Дмитрий ЕлисеевУ меня нет поля url в БД.
ДмитрийА где вы берете "alias" для каждой из категорий?
Дмитрий ЕлисеевПоле alias как раз есть.
ДмитрийТак в "alias" хранится толь псевдоним. А откуда вы берете данные для генерации url - например второго уровня и выше? Или вы используете "Nested Sets" для этого?
Дмитрий ЕлисеевПосмотрите на методы getUrl() в статье.
ДмитрийНасколько я понимаю, в методе "ShopProduct->getUrl()" вы обращаетесь к методу "ShopCategory->getPath()" который в свою очередь находится в поведении "DCategoryTreeBehavior", а вот в поведении уже вы в цикле получаете родителей и добавляете их в массив, который в свою очередь преобразовываете в строку - URI.
Правильно я вас понимаю?
Если правильно - тогда вопрос а "Nested Sets" или элементарная генерация URI в момент добавления записи и хранения ее в базе, не эффективнее ли будет?
Дмитрий ЕлисеевNested Sets эффективнее в любом случае.
alex – zawebis.comспасибо. а для yii2 можно пример добавить?
alex – zawebis.comхотя как вариант просто переписать под себя уже готовый виджет Menu и все.
а вот как правильно составить правила для многоуровнего меню?
АлексейДелаю категории в action index
public function actionIndex($id = false) { if ($id){ $query = Articles::find()->where(['status' => articles::ARTICLE_STATUS_ACTIVE, 'category_id'=>$id]); } else { $query = Articles::find()->where(['status' => articles::ARTICLE_STATUS_ACTIVE]); } $dataProvider = new ActiveDataProvider([ 'query' => $query, 'sort'=> ['defaultOrder' => ['created_at'=>SORT_DESC]] ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); }подскажите, пожалуйста, как мне в меню указать ссылку на категорию? ($id категории куда дописать?)
Дмитрий Елисеев
ВадимУ меня есть пара вопросов.
в конфигурации приложения:
'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'user'], ], ]везде написано вставьте, а куда конкретно не могу нигде нарыть.
1 какой файл является конфигом приложения? config/web.php
2 это сюда или еще куда?
$config = [ 'id' => 'basic', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'components' => [ 'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'statistics'], ], ], 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 'cookieValidationKey' => 'фыва', ], ...
NikolayДмитрий, а как реализовать такие же красивые ссылки для фильтров(типа: цена, цвет, размер,высота) на странице категории, если все параметры динамически создаются в админке?
В конечном результате хочеться увидить что-то типа
Где computers/laptop - категория и подкатегория
Apple - бренд
Spacegray - цвет
2015 - год выпуска
Дмитрий ЕлисеевЧерез своё правило UrlRule, которое будет в нужной последовательности склеивать и парсить.
СергейДмитрий, вопрос немного в оффтоп. Не подскажете как реализовать вывод подкатегорий и категорий в Breadcrumbs (категории/подкатегории хранятся в одной таблице) на странице записи в Yii 2?
Дмитрий Елисеев$category = $model->category; do { array_unshift($this->params['breadcrumbs'], [ 'label' => $category->title, 'url' => ['category', 'id' => $category->id], ]); } while ($category = $category->parent); $this->params['breadcrumbs'][] = $model->title;
СергейОгромное спасибо, Дмитрий!
Oleg UaСпасибо за помощь :)
СергейДмитрий, не подскажите пожалуйста как реализовать генерацию url через отдельный класс с помощью интерфейса UrlRuleInterface когда записи хранятся внутри вложенной категории(третий уровень вложенности) в Yii2? То есть через методы parseRequest($manager, $request) и createUrl($manager, $route, $params).
В обычном правиле rules это выглядит так:
И еще ошибка при таком правиле, что не выводятся картинки с библиотеки yii2-images, хотя путь в атрибуте src у них прописывается. Может регулярные выражения неверно прописаны? Если убрать одну вложенность, то картинки нормально отображаются.
Буду очень признателен за помощь.
Дмитрий ЕлисеевРазбиваете путь на части:
$chunks = explode('/', $request->pathInfo);и последовательно ищете по этим частям.
Григорий СтепенкоДмитрий, спасибо за отличную статью. Использовал описанный в статье метод вместе с DAO и наткнулся на препятствие при решении вопроса фильтрации товаров в рубриках и дочерних рубриках по свойствам товаров. Для хранения свойств товаров использую две таблицы в одной названия свойства, во второй значения свойств, привязка к товарам и привязка к названию свойства. Не испытываю затруднений в формировании DAO запросов, а вот при передаче значений в действие контроллера возникли затруднения.
Подскажите, каким образом можно фильтровать товары по значениям свойств товаров внутри рубрик при такой организации работы UrlManager? Похожий вопрос задавал Nikolay 26.02.2016 но у меня немного другая ситуация. И если можно раскройте немного ответ который был дан Николаю.
Дмитрий ЕлисеевОтвет Николаю - сделать класс, который будет парсить адрес и компоновать его обратно.
боби фиоА как исключить экшн из правила, чтобы получилось что-то типа
чтобы в урл не дублировалась страница по / и 'site/index
.htaccess не в счет
ПетрЗдравствуйте, Дмитрий. Подскажите, пожалуйста, как сделать ЧПУ для фильтров на сайте. Неужели нужно все варианты в rules перебрать? И что делать, если не все параметры обязательны? Например, допустим есть четыре фильтра : сортировать по языку (lang), по минимальной сумме (min), по максимальной сумме (max) и по определенной валюте (valuta).
Дмитрий ЕлисеевВсё нестандартное можно сделать через класс-правило.
ПетрБлагодарю за помощь и быстрый ответ )))) попробуем разобраться
Андрей – nova.dsk-stolica.ruДобрый день.
Использую Yii2 - хочу сделать SEO ссылки вида:
мой-сайт/название-категории/название-продукта.html
мой-сайт/nike/nike-air-90.html
Мои UrlManager правила:
'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ [ 'pattern'=>'<url:.+>', 'route' => 'product/view', 'suffix' => '.html', ], [ 'pattern'=>'<url_category:.+>', 'route' => 'category/view', 'suffix' => '', ], 'search' => 'category/search', ], ],Мой контроллер КАТЕГОРИЙ:
class CategoryController extends AppController{ public function actionView($url_category){ $category = Category::find()->where(['url_category' => $url_category])->one(); $id = $category->id; //id $url_category = $category->url_category; //url if(empty($category)) throw new \yii\web\HttpException(404, 'Такой категории нет.'); $query = Product::find()->where(['category_id' => $id]); $pages = new Pagination(['totalCount' => $query->count(), 'pageSize' => 500, 'forcePageParam' => false, 'pageSizeParam' => false]); $products = $query->offset($pages->offset)->limit($pages->limit)->all(); //Формируем Title страницы $this->setMeta($category->name, $category->keywords, $category->description); return $this->render('view', compact('products', 'pages', 'category'), ['compcatname' => $compcatname]); }}Мой контроллер ТОВАРА:
class ProductController extends AppController{ public function actionView($url_category, $url){ $product = Product::find()->where(['url' => $url])->one(); $id = $product->id; //id $url = $product->url; //url //Вывод ошибок 404 if(empty($product)) throw new \yii\web\HttpException(404, 'Такого товара в каталоге не существует.'); $hits = Product::find()->where(['hit' => '1'])->limit(12)->all(); $this->setMeta($product->name, $product->keywords, $product->description); return $this->render('view', compact('product', 'hits', 'productname')); }1) Вопрос
Категории у меня работают правильно - то есть открываются по ссылкам вида:
мой-сайт/название-категории
мой-сайт/nike
А вот товары не получается никак реализовать, чтобы сначала шла КАТЕГОРИЯ/ТОВАР вида:
мой-сайт/название-категории/название-продукта.html
мой-сайт/nike/nike-air-90.html
2) Вопрос
Как мне правильно формировать ссылки во view?
Сейчас у меня ссылки вида:
Ссылка на категорию:
<?= Url::to(['/nike'])?> А хочется получать ссылку автоматом.
Ссылка на товар:
мой-сайт/nike-air-90.html
А хочется получать ссылку на товар вместе с категорией вида:
мой-сайт/nike/nike-air-90.html
Андрей – nova.dsk-stolica.ruРазобрался сам вот решение :) Но есть 1 косяк. сначала покажу код:
[ 'pattern'=>'<url_category:[a-z0-9-_]+>/<url:[a-z0-9-_]+>', 'route' => 'product/view', 'suffix' => '.html', ], [ 'pattern'=>'<url_category:[a-z0-9-_]+>', 'route' => 'category/view', 'suffix' => '', ],Теперь ссылки приняли настоящий SEO + ЧПУ вид:
мой-сайт/nike
мой-сайт/nike/nike-air-90.html
А теперь проблема:
Когда я захожу на товар по такой ссылке:
вид такой ссылки получается: http://мой-сайт/nike/nike-air-90.html
Но, если убрать в ссылке Категории любую букву или ее изменить например так:
мой-сайт/ne/nike-air-90.html или мой-сайт/nеkkie-re-ewe/nike-air-90.html
а таких категорий в Базе Данных нет!
То почему то ссылка все равно отрабатывает и показывается! Как я понимаю Категория не проверяется из БД - что очень плохо. Как в этом случае быть подскажите пожалуйста. Ведь если неправильно написать ссылку то должен срабатывать ошибка 404 код в контроллере Категория:
if(empty($category)) throw new \yii\web\HttpException(404, 'Такой категории нет.');а проверка не отрабатывает - почему?
Дмитрий ЕлисеевТакие нестандартные вещи решаются созданием своих классов-правил.
ПавелКак можно в yii2 исправить проблему с кодированием слеша '/' в %2F при добавлении LinkPager::widget на вложенную страницу каталога.
Интернет магазин имеет вложенные категории /catalog/muzhskoe в которой много товаров. нужна пагинация. в итоге при формировании url виджетом пагинатора получается catalog%2Fmuzhskoe ?
Дмитрий ЕлисеевМожно, напирмер, через свой класс-правило.
ПавелСпасибо, разрешил данным способом
АлексейЗдравствуйте!
При отправке формы у меня создается примерно такой урл:
tagkls.ru/color?form[greenRound]=22&form[greenSquare]=12
могли бы подсказать как изменить имена get-параметров и убрать название формы
(например - form[greenRound] - переименовать в -- greenR), чтобы получилось вот так:
tagkls.ru/color?greenR=22&greenS=12
Спасибо.
Дмитрий ЕлисеевВ Yii2 в модели формы переопределите метод formName:
public function formName() { return ''; }
АлексейОказывается всё так просто.
Большое спасибо!
ДмитрийЗдравствуйте, как сделать так, чтобы тире между двумя параметрами не учитывалось?
Есть правило вида:
После его разбора если города идут через тире или пробел, то ничего не работает.
Если добавить в правило - ( [\w\- ] ) - то города могут некорректно читаться, учитывается следующее тире после параметра.
Ust-kaminsk-ufa - получится fromcity = Ust-kaminsk-ufa, toCity = ''
Дмитрий ЕлисеевСоздайте класс-правило и парсите в нём.
АртурПодскажите, пожалуйста...
А как получить товары категории с учетом вложенных...
Например по адресу site.com/shop/computers/printers/laser
нужно получить товары, у которых категория = "laser"...
А вот на странице site.com/shop/computers
нужно получить товары из категорий: "computers", "printers", "lasers" и т.д., то есть из текущей и всего дерева вглубь...
Дмитрий ЕлисеевДостать все id вложенных категорий и искать товары по всем ['category_id' => $ids].