вторник, 16 июня 2015 г.

§2.4 Yii2. Меню категорий товаров

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


Поле parent_id ссылается на ту же таблицу категорий, таким образом можно создать любую глубину вложенности категорий.
Я хочу получить отображение структуры категорий товаров. Эту структуру будет отдавать действие контроллера Category.
План действий такой: нужен будет файл отображения, который будет выводить структуру на экран, в виде html текста, например в нем можно использовать виджет Menu::widget. Он подойдет, чтобы отобразить список пунктов меню. В последствии этот файл отображения можно будет рендерить там, где нужно меню категорий. Виджет меню принимает данные в виде массива items пунктов этого меню. Данный массив будет выдавать модель Category. Контроллер все это свяжет вместе. Итак начнем с контроллера:
В контроллере (файл var\www\yii2\basic\controllers\CategoryController.php) создаю действие:

 
public function actionStructure()

    {

        $model = new Category();

        return $this->render('structure', [

            'data' => $model->getStructure() ,

            ]);

    }

Действие Structure рендерит оботражение structure и передает ему данные из метода модели getStructure().
Создаем файл-отображение var\www\yii2\basic\views\category\structure.php
В нем только виджет Menu::widget и div-ы, чтобы меню не расползалось
 
<?php

use yii\bootstrap\Nav;

use yii\bootstrap\NavBar;

use yii\widgets\Menu;

?>

<div class = "col-lg-3">

 <div class="clearfix">

 </div>

 <?php

 NavBar::begin(['brandLabel' => 'Каталог',

   'brandOptions' => [

    'class' => 'visible-xs-block',

   ],

   'options' => [

    'class' => 'navbar',

   ],

  ]);

 echo Menu::widget([

   'items' => $data,

   'submenuTemplate' => "\n<ul class='nav nav-pills nav-stacked my_menu' role='menu'>\n{items}\n</ul>\n",

   //'linkTemplate' => ' < a href = "{url}" class = "href_class" id = "href_id" style = "" > {label}</a > ',

   'itemOptions'=>array('id'   =>'item_id','class'=>'nav nav-pills nav-stacked'),

   'activateParents'=>true,

   'options' => [

    'class' => 'nav nav-pills nav-stacked my_menu',

    'id'=>'navbar-id',

    'style'=>'font-size: 14px;',

    'data-tag'=>'yii2-menu',

   ],

  ]);

 NavBar::end();

 ?>

</div>


Теперь самое сложное - сгенерировать массив items элементов структуры меню. Делается это в моделе. В файле var\www\yii2\basic\models\Category.php создал метод getStructure:


 
     /**

 * Создает массив items для виджетов menu или navbar.

 *

 * @return array например $result =  Array(

 * [0] => Array

 * (

 *        [label] => Одежда

 *        [url] => assortiment/7

 *        [active] =>

 *        [options] => Array

 *            (

 *                [class] => dropdown

 *             )

 *        [items] => Array

 *            (

 *                [0] => Array

 *                    (

 *                        [label] => Носки

 *                        [url] => assortiment/8

 *                        [active] => 1

 *                        [options] => Array

 *                            (

 *                                [class] => dropdown

 *                            )

 *         )

 * [1] => Array

 * (

 *    [label] => Обувь

 *    [url] => assortiment/9

 *    [active] =>

 *     [options] => Array

 *        (

 *            [class] => dropdown

 *        )

 *  )

 * )

 */

 public function getStructure()

 {

  //запрос к базе данных в $result попадают все записи из таблицы в виде массива

  $result = Category::find()->asArray()->all();

  //

  if (!$result) {

   return NULL;

  }

  // $arr_cat будет создаваться массив категорий, где индексы, это parent_id

  $arr_cat = array();



  //В цикле формируем массив

  for ($i = 0; $i < count($result);$i++) {

   $row = $result[$i];

   if ($row['parent_id'] == NULL)

   $row['parent_id'] = 0;

   //Формируем массив, где ключами являются id родительской категории

   if (empty($arr_cat[$row['parent_id']])) {

    $arr_cat[$row['parent_id']] = array();

   }

   $arr_cat[$row['parent_id']][] = $row;

  }



// $view_cat - лямда функция для создания массива категорий, который будет передан в отображение

  $view_cat =

  function ($data, $parent_id = 0) use ( & $view_cat)

  {

   $result = NULL;

   if (empty($data[$parent_id])) {

    return;

   }

   $result = array();



   //перебираем в цикле массив и выводим на экран

   for ($i = 0; $i < count($data[$parent_id]);$i++) {

    $result[] = ['label' => $data[$parent_id][$i]['name_ru'],

     'url' => 'assortiment/'.$data[$parent_id][$i]['id'],

      //можно пометить какой либо пункт как активный     

     'active' => $data[$parent_id][$i]['id'] == 8,

     'options' => ['class' => 'dropdown' ],

     'items' => $view_cat($data,$data[$parent_id][$i]['id'])];

    //рекурсия - проверяем нет ли дочерних категорий

   }

   return $result;

  };

  $result = $view_cat($arr_cat);

  return $result;

 }

Массив $arr_cat выглядит примерно так:

Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [id] => 7
                    [parent_id] => 0
                    [name_ru] => Одежда
                    [name_et] => Riietus
                    [name_en] => Clothing
                    [shown] => yes
                )

            [1] => Array
                (
                    [id] => 9
                    [parent_id] => 0
                    [name_ru] => Обувь
                    [name_et] =>  Kingad
                    [name_en] => Shoes
                    [shown] => yes
                )

            [2] => Array
                (
                    [id] => 10
                    [parent_id] => 0
                    [name_ru] => Аксесуары
                    [name_et] => Lisandid
                    [name_en] => Accessories
                    [shown] => 
                )

            [3] => Array
                (
                    [id] => 12
                    [parent_id] => 0
                    [name_ru] => Головные уборы
                    [name_et] => Peakate
                    [name_en] => Headgear
                    [shown] => yes
                )

        )

    [7] => Array
        (
            [0] => Array
                (
                    [id] => 8
                    [parent_id] => 7
                    [name_ru] => Носки
                    [name_et] => Sokid
                    [name_en] => Socks
                    [shown] => yes
                )

        )

    [8] => Array
        (
            [0] => Array
                (
                    [id] => 11
                    [parent_id] => 8
                    [name_ru] => С начесом
                    [name_et] => Tokerjas
                    [name_en] => Shaggy
                    [shown] => yes
                )

        )

    [9] => Array
        (
            [0] => Array
                (
                    [id] => 13
                    [parent_id] => 9
                    [name_ru] => Шлепанцы
                    [name_et] => Samm-ins
                    [name_en] => Step-ins
                    [shown] => yes
                )

        )

    [12] => Array
        (
            [0] => Array
                (
                    [id] => 14
                    [parent_id] => 12
                    [name_ru] => Головной убор индейского воина
                    [name_et] => Peakate indiaani sõjamees
                    [name_en] => Headdress Indian warrior
                    [shown] => yes
                )

        )

)

его передаем в  $view_cat, которая вызывая саму себя создает массив, который будет передан через контроллер отображению, где создается виджит menu.

В результате меню выглядит так:

Сделать его красивее, выпадающим, или каким ни будь еще, это дело CSS стилей и JS скриптов

В итоге, при обращении на http://yii2.lamp/index.php?r=category%2Fstructure
получаем отображение меню. В будущем я собираюсь передавать в него ID активной категории, чтобы подсвечивать активный пункт, или сделаю ссылки меню в виде controller/action, тогда они должны будут подсвечиваться автоматически.

Комментариев нет:

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