Магазин на Yii2, часть 3. Виджет для вывода меню каталога

04.05.2019

Теги: Web-разработкаYii2ВиджетИнтернетМагазинКаталогТоваровПлагинПрактикаФреймворк

Теперь нам нужно как-то выводить меню каталога в левой колонке. Поскольку это меню показывается на многих страницах сайта, реализуем его в виде виджета. И тогда сможем вставить меню в любом месте шаблона одной строкой кода. Для создания виджета нам потребуется директория components, внутри нее создаем файл класса TreeWidget.php.

<?php
namespace app\components;

use yii\base\Widget;
use app\models\Category;
use Yii;

/**
 * Виджет для вывода дерева разделов каталога товаров
 */
class TreeWidget extends Widget {

    /**
     * Выборка категорий каталога из базы данных
     */
    protected $data;

    /**
     * Массив категорий каталога в виде дерева
     */
    protected $tree;

    public function run() {
        // пробуем извлечь данные из кеша
        $html = Yii::$app->cache->get('catalog-menu');
        if ($html === false) {
            // данных нет в кеше, получаем их заново
            $this->data = Category::find()->indexBy('id')->asArray()->all();
            $this->makeTree();
            if ( ! empty($this->tree)) {
                $html = $this->render('tree', ['tree' => $this->tree]);
            } else {
                $html = '';
            }
            // сохраняем полученные данные в кеше
            Yii::$app->cache->set('catalog-menu', $html, 60);
        }
        return $html;
    }

    /**
     * Функция принимает на вход линейный массив элеменов, связанных
     * отношениями parent-child, и возвращает массив в виде дерева
     */
    protected function makeTree() {
        if (empty($this->data)) {
            return;
        }
        foreach ($this->data as $id => &$node) {
            if ( ! $node['parent_id']) {
                $this->tree[$id] = &$node;
            } else {
                $this->data[$node['parent_id']]['childs'][$id] = &$node;
            }
        }
    }
}
При вызове метода all() возвращается массив строк, индексированный последовательными целыми числами. Чтобы сделать индекс по указанному столбцу, нужно вызвать метод indexBy() перед вызовом метода all().

Теперь создаем шаблон menu.php для меню каталога в директории components/views:

<?php
/*
 * Файл components/views/menu.php
 */
use yii\helpers\Html;
use yii\helpers\Url;
?>

<ul id="accordion">
<?php foreach ($tree as $item1): ?>
    <li><a href="<?= Url::to(['catalog/category', 'id' => $item1['id']]); ?>">
        <?= Html::encode($item1['name']); ?>
        <?php if (isset($item1['childs'])): ?>
        </a>
        <span class="badge pull-right"><i class="fa fa-plus"></i></span>
            <ul>
            <?php foreach ($item1['childs'] as $item2): ?>
                <li><a href="<?= Url::to(['catalog/category', 'id' => $item2['id']]); ?>">
                    <?= Html::encode($item2['name']); ?>
                    <?php if (isset($item2['childs'])): ?>
                    </a>
                    <span class="badge pull-right"><i class="fa fa-plus"></i></span>
                        <ul>
                        <?php foreach ($item2['childs'] as $item3): ?>
                            <li><a href="<?= Url::to(['catalog/category', 'id' => $item3['id']]); ?>">
                                <?= Html::encode($item3['name']); ?>
                                <?php if (isset($item3['childs'])): ?>
                                    </a>
                                    <span class="badge pull-right"><i class="fa fa-plus"></i></span>
                                    <ul>
                                    <?php foreach ($item3['childs'] as $item4): ?>
                                        <li><a href="<?= Url::to(['catalog/category', 'id' => $item4['id']]); ?>">
                                            <?= Html::encode($item4['name']); ?>
                                            <?php if (isset($item4['childs'])): ?>
                                                </a>
                                                <span class="badge pull-right"><i class="fa fa-plus"></i></span>
                                                <ul>
                                                <?php foreach ($item4['childs'] as $item5): ?>
                                                    <li><a href="<?= Url::to(['catalog/category', 'id' => $item5['id']]); ?>">
                                                        <?= Html::encode($item5['name']); ?></a></li>
                                                <?php endforeach; ?>
                                                </ul>
                                            <?php else: ?>
                                                </a>
                                            <?php endif; ?>
                                        </li>
                                    <?php endforeach; ?>
                                    </ul>
                                <?php else: ?>
                                    </a>
                                <?php endif; ?>
                            </li>
                        <?php endforeach; ?>
                        </ul>
                    <?php else: ?>
                        </a>
                    <?php endif; ?>
                </li>
            <?php endforeach; ?>
            </ul>
        <?php else: ?>
            </a>
        <?php endif; ?>
    </li>
<?php endforeach; ?>
</ul>

Осталось только добавить вызов виджета в view-шаблоне views/site/index.php:

<h2>Каталог</h2>
<?= TreeWidget::widget(); ?>

Для того, чтобы элементы меню можно было свернуть и развернуть, создадим файл accordion.js и сохраним его в директории web/js. Для стилевого оформления меню создадим файл accordion.css и сохраним его в директории web/css.

jQuery(document).ready(function($) {
    $('#accordion ul').hide();
    $('#accordion .badge').on('click', function () {
        var $badge = $(this);
        var closed = $badge.siblings('ul') && !$badge.siblings('ul').is(':visible');

        if (closed) {
            $badge.siblings('ul').slideDown('normal', function () {
                $badge.children('i').removeClass('fa-plus').addClass('fa-minus');
            });
        } else {
            $badge.siblings('ul').slideUp('normal', function () {
                $badge.children('i').removeClass('fa-minus').addClass('fa-plus');
            });
        }
    });
});
#accordion {
    list-style: none;
    padding-left: 0;
}
    #accordion ul {
        list-style: none;
    }
    #accordion .badge {
        cursor: pointer;
    }

И подключим эти два файла подключаем к нашему комплекту ресурсов:

<?php
namespace app\assets;

use yii\web\AssetBundle;
use yii\web\View;

/**
 * Main application asset bundle
 */
class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/font-awesome.min.css',
        'css/accordion.css',
        'css/main.css'
    ];
    public $js = [
        'js/accordion.js',
        'js/main.js'
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapPluginAsset',
    ];

    public function registerAssetFiles($view) {
        /*.....*/
    }
}

Мы использовали метод хелпера Url::to() для создания ссылок на разделы каталога. Сейчас ссылка имеет вид

http://www.server.com/catalog/category?id=123

Давайте изменим ее на

http://www.server.com/catalog/category/123

Для этого изменяем правила роутинга в файле конфигурации config/web.php:

$config = [
    /*...*/
    'components' => [
        /*...*/
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
                'catalog/category/<id:\d+>' => 'catalog/category'
            ],
        ],
        /*...*/
    ],
    /*...*/
];

Поиск: Web-разработка • Yii2 • Виджет • Интернет магазин • Каталог товаров • Плагин • Фреймворк

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.