Сен 01 2008
Фильтрация списка по категориям в CakePHP
Допустим, есть у нас таблица категорий «categories», построенная по самому простому принципу — id, name, parent_id. И есть список товаров, привязанных к этим категориям по полю «category_id». Список довольно большой — свыше двух тысяч наименований. Стандартным шеллом CakePHP можно подготовить функции в контроллере и вид (view) для постраничного просмотра списка товаров с помощью PaginatorHelper. Но проматывать больше сотни страниц для того чтобы найти нужный товар, крайне непродуктивно. Соответственно, надо сделать фильтрацию по категории товаров.
Подразумевается, что связь hasMany между категориями и товарами уже настроена. Обязательно также нужна и обратная ей — belongsTo.
Создаем в виде форму для отображения списка категорий. С использованием AdminRoutes это будет файл views/products/admin_index.ctp.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <div class="actions"> <?php //Устанавливаем дополнительный параметр PaginatorHelper'а, //чтобы при переходе между страницами не терялась сортировка по категориям $paginator->options(array('url' => $current_category)); //Создаем форму echo $form->create('Product', array('action'=>'/index/')); //Поясняющее сообщение и заодно ссылка, отменяющая фильтрацию echo 'Показать ', $html->link('Все категории', '/admin/products/index/'), ' или выбрать из списка: '; echo '<div align="left">'; //Здесь появится выпадающий список echo $form->input('cat_id', array('label'=>'','options'=>$categories)); echo $form->end('Показать'); echo '</div>'; ?> </div> |
Форма представляет собой простой выпадающий список со списком категорий. Первым пунктом будут идти «Все категории», а в остальных пунктах для отображения уровня вложенности категории будем использовать символ подчеркивания. Для правильного выбора всех категорий с учетом уровня вложенности нам понадобится дополнить модель категорий Category парой простых функций. Выборка производится рекурсивно, я намеренно не стал делать никаких оптимизаций для большей наглядности.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function get_rec_categories($parent_id = 0, $level = 1){ $ret_category = array(); $categories = $this->findall('parent_id = '.$parent_id, array('id', 'name', 'parent_id')); foreach ($categories as $category) { $ret_category[] = array('id' => $category['Category']['id'], 'name' => $category['Category']['name'], 'level' => $level-1); $ret_category= $this->_ac($ret_category, $this->get_rec_categories($category['Category']['id'], $level+1)); } return $ret_category; } function _ac($a1 = array(), $a2 = array()){ foreach ($a2 as $a) { $a1[] = $a; } return $a1; } |
Вызов функции get_rec_categories() возвращает массив вида:
1 2 3 4 5 6 7 8 9 10 | Array ( [1] => Array ( [id] => 1 [name] => Some Category Name [level] => 1 ) ... ) |
Осталось вызвать эту функцию в контроллере, обработать ее результаты, передать в вид и если категория выбрана — отфильтровать товары. Дописываем все это в функцию admin_index() контроллера products_controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | function admin_index($category_id=null) { //Получаем все категории в порядке вложенности и с отметкой о уровне вложения из модели $categories = $this->Product->Category->get_rec_categories(); //Первым пунктом в списке будет идти «Все категории» $categories_prepared = array(0=>'Все категории'); //Добавляем перед названием количество подчеркиваний, соответствующее уровню вложения foreach ($categories as $category) { $categories_prepared[$category['id']]=str_repeat('_', $category['level']).$category['name']; } //Передаем список в вид $this->set('categories', $categories_prepared); //Получаем из формы номер выбранной категории if ($this->data['Product']['category_id'] <> null) { $category_id = intval($this->data['Product']['category_id']); } //Полученный из формы или из запроса номер категории надо обязательно привести к числовому типу, //чтобы злоумышленник не мог подсунуть вместо номера категории sql-код $category_id = intval($category_id); //Если номер категории не ноль, то добавляем его к условиям поиска токара if ($category_id <> 0) { $conditions = array('category_id'=>$category_id); } else { $conditions = null; } //Передаем в вид номер текущей категории $this->set('current_category',$category_id); //А это чтобы в списке был выбран соответствующий пункт $this->data['Product']['category_id'] = $category_id; //Настраиваем параметры выбора списка товаров. Нам нужны только товары, по 30 штук на странице $this->Product->recursive = 0; $this->paginate['limit'] = 30; //Передаем список товаров в вид $this->set('products', $this->paginate(null, $conditions)); } |
Сентябрь 8th, 2008 at 23:23
[...] может быть обозначена префиксами, как в примере в моем предыдущем посте на эту [...]