Магазин на Yii2, часть 7. Показываем список товаров категории

08.05.2019

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

Итак, мы вывели на главной странице список лидеров продаж. Теперь покажем список товаров выбранной категории. Нам нужно добавить в модель два метода: один — для получения информации о категории и второй — для получения списка товаров. Или, сделать еще проще — добавить только первый метод, а потом использовать ленивую загрузку, чтобы получить в view-шаблоне массив товаров категории.

Но, обо всем по порядку. Мы добавляем метод, позволяющий получить данные о категории. И у нас описана связь таблицы category с таблицей product.

class Category extends ActiveRecord {
    /**
     * Метод описывает связь таблицы БД `category` с таблицей `product`
     */
    public function getProducts() {
        return $this->hasMany(Product::class, ['category_id' => 'id']);
    }
    /*
     * Метод возвращает информацию о категории с идентификатором $id
     */
    public function getCategory($id) {
        return self::findOne($id);
    }
}

В контроллере нам достаточно получить только данные о категории и передать их в view-шаблон:

class CatalogController extends AppController {
    public function actionCategory($id) {
        $id = (int)$id;
        $category = (new Category())->getCategory($id);
        return $this->render('category', compact('category'));
    }
}

А данные о товарах категории можно получить прямо в шаблоне views/catalog/category.php:

// данные о товарах категории с использованием ленивой загрузки
$products = $category->products;
if (!empty($products)) {
    echo '<ul>';
    foreach ($products as $product) {
        echo '<li>', $product->name, '</li>';
    }
    echo '</ul>';
} else {
    echo '<p>Нет товаров в этой категории</p>';
}

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

<?php
namespace app\models;

use yii\db\ActiveRecord;

class Category extends ActiveRecord {

    /**
     * Метод возвращает имя таблицы БД
     */
    public static function tableName() {
        return 'category';
    }

    /**
     * Метод описывает связь таблицы БД `category` с таблицей `product`
     */
    public function getProducts() {
        return $this->hasMany(Product::class, ['category_id' => 'id']);
    }

    /**
     * Метод описывает связь таблицы БД `category` с таблицей `category`
     */
    public function getParent() {
        return $this->hasOne(self::class, ['id' => 'parent_id']);
    }

    /**
     * Метод описывает связь таблицы БД `category` с таблицей `category`
     */
    public function getChildren() {
        return $this->hasMany(self::class, ['parent_id' => 'id']);
    }

    /**
     * Возвращает информацию о категории с иденификатором $id
     */
    public function getCategory($id) {
        return self::findOne($id);
    }

    /**
     * Возвращает массив товаров в категории с идентификатором $id и во
     * всех ее потомках, т.е. в дочерних, дочерних-дочерних и так далее
     */
    public function getCategoryProducts($id) {
        // получаем массив идентификаторов всех потомков категории
        $ids = $this->getAllChildIds($id);
        $ids[] = $id;
        return Product::find()->where(['in', 'category_id', $ids])->all();
    }

    /**
     * Возвращает массив идентификаторов всех потомков категории $id,
     * т.е. дочерние, дочерние дочерних и так далее
     */
    protected function getAllChildIds($id) {
        $children = [];
        $ids = $this->getChildIds($id);
        foreach ($ids as $item) {
            $children[] = $item;
            $c = $this->getAllChildIds($item);
            foreach ($c as $v) {
                $children[] = $v;
            }
        }
        return $children;
    }

    /**
     * Возвращает массив идентификаторов дочерних категорий (прямых
     * потомков) категории с уникальным идентификатором $id
     */
    protected function getChildIds($id) {
        $children = self::find()->where(['parent_id' => $id])->asArray()->all();
        $ids = [];
        foreach ($children as $child) {
            $ids[] = $child['id'];
        }
        return $ids;
    }
}

А контроллер у нас будет таким:

<?php
namespace app\controllers;

use app\models\Category;
use app\models\Product;
use Yii;

class CatalogController extends AppController {

   /* ... */

    /*
     * Категория каталога товаров
     */
    public function actionCategory($id) {
        $id = (int)$id;
        $temp = new Category();
        // товары категории
        $products = $temp->getCategoryProducts($id);
        // данные о категории
        $category = $temp->getCategory($id);
        // устанавливаем мета-теги для страницы
        $this->setMetaTags(
            $category->name . ' | ' . Yii::$app->params['shopName'],
            $category->keywords,
            $category->description
        );
        return $this->render('category', compact('category', 'products'));
    }
}

Файл view-шаблона views/catalog/category.php. В нем ничего нового — используем два виджета (меню каталога и популярные бренды) и цикл для показа товаров категории:

<?php
/*
 * Страница раздела каталога, файл views/catalog/category.php
 */

use app\components\TreeWidget;
use app\components\BrandsWidget;
use yii\helpers\Html;
use yii\helpers\Url;

$this->title = $category['name'];
?>

<section>
    <div class="container">
        <div class="row">
            <div class="col-sm-3">
                <div class="left-sidebar">
                    <h2>Каталог</h2>
                    <div class="category-products">
                        <?= TreeWidget::widget(); ?>
                    </div>

                    <h2>Бренды</h2>
                    <div class="brand-products">
                        <?= BrandsWidget::widget(); ?>
                    </div>
                </div>
            </div>

            <div class="col-sm-9">
                <?php if (!empty($products)): ?>
                    <h2><?= Html::encode($category['name']); ?></h2>
                    <div class="row">
                        <?php foreach ($products as $product): ?>
                            <div class="col-sm-4">
                                <div class="product-wrapper text-center">
                                    <?=
                                    Html::img(
                                        '@web/images/products/medium/'.$product['image'],
                                        ['alt' => $product['name'], 'class' => 'img-responsive']
                                    );
                                    ?>
                                    <h2><?= $product['price']; ?> руб.</h2>
                                    <p>
                                        <a href="<?= Url::to(['catalog/product', 'id' => $product['id']]); ?>">
                                            <?= Html::encode($product['name']); ?>
                                        </a>
                                    </p>
                                    <a href="#" class="btn btn-warning">
                                        <i class="fa fa-shopping-cart"></i>
                                        Добавить в корзину
                                    </a>
                                    <?php
                                    if ($product['new']) { // новинка?
                                        echo Html::tag(
                                            'span',
                                            'Новинка',
                                            ['class' => 'new']
                                        );
                                    }
                                    if ($product['hit']) { // лидер продаж?
                                        echo Html::tag(
                                            'span',
                                            'Лидер продаж',
                                            ['class' => 'hit']
                                        );
                                    }
                                    if ($product['sale']) { // распродажа?
                                        echo Html::tag(
                                            'span',
                                            'Распродажа',
                                            ['class' => 'sale']
                                        );
                                    }
                                    ?>
                                </div>
                            </div>
                        <?php endforeach; ?>
                    </div>
                <?php else: ?>
                    <p>Нет товаров в этой категории.</p>
                <?php endif; ?>
            </div>
        </div>
    </div>
</section>

Поиск: 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.