Магазин на Yii2, часть 13. Страница товара и хлебные крошки

12.06.2019

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

Следующий этап — создаем модель, контроллер и представление для показа страницы товара. Для снижения нагрузки полученные от модели данные кешируем в контроллере. Заодно добавим хлебные крошки для страницы товара и для раздела каталога. Для этого создадим виджет ChainWidget и будем вызывать его в view-шаблонах товара и категории.

Контроллер, модель и представление для товара

<?php
namespace app\controllers;

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

class CatalogController extends AppController {

    /*...*/

    /**
     * Страница товара с идентификатором $id
     */
    public function actionProduct($id) {
        $id = (int)$id;
        // пробуем извлечь данные из кеша
        $data = Yii::$app->cache->get('product-'.$id);
        if ($data === false) {
            // данных нет в кеше, получаем их заново
            $product = (new Product())->getProduct($id);
            $brand = (new Brand())->getBrand($product['brand_id']);
            $data = [$product, $brand];
            // сохраняем полученные данные в кеше
            Yii::$app->cache->set('product-'.$id, $data);
        }
        list($product, $brand) = $data;
        // устанавливаем мета-теги
        $this->setMetaTags(
            $product['name'] . ' | ' . Yii::$app->params['shopName'],
            $product['keywords'],
            $product['description']
        );
        // получаем товары, похожие на текущий
        $similar = Yii::$app->cache->get('similar-'.$product['id']);
        if ($similar === false) {
            // товары из той же категории того же бренда
            $similar = Product::find()
                ->where([
                    'category_id' => $product['category_id'],
                    'brand_id' => $product['brand_id']
                ])
                ->andWhere(['NOT IN', 'id', $product['id']])
                ->limit(3)
                ->asArray()
                ->all();
            Yii::$app->cache->set('similar-'.$product['id'], $similar);
        }
        return $this->render(
            'product',
            compact('product', 'brand', 'similar')
        );
    }
}
<?php
namespace app\models;

use yii\db\ActiveRecord;

class Product extends ActiveRecord {

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

    /**
     * Возвращает родительскую категорию
     */
    public function getCategory() {
        // связь таблицы БД `product` с таблицей `category`
        return $this->hasOne(Category::class, ['id' => 'category_id']);
    }

    /**
     * Возвращает информацию о товаре с иденификатором $id
     */
    public function getProduct($id) {
        return self::find()->where(['id' => $id])->asArray()->one();
    }

}
<?php
/*
 * Страница товара, файл views/catalog/product.php
 */

use app\components\TreeWidget;
use app\components\BrandsWidget;
use app\components\ChainWidget;
use yii\helpers\Url;
use yii\helpers\Html;
?>

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

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

            <div class="col-sm-9">
                <h1><?= Html::encode($product['name']); ?></h1>
                <div class="row">
                    <div class="col-sm-5">
                        <div class="product-image">
                            <?=
                            Html::img(
                                '@web/images/products/large/'.$product['image'],
                                ['alt' => $product['name'], 'class' => 'img-responsive']
                            );
                            ?>
                        </div>
                    </div>
                    <div class="col-sm-7">
                        <div class="product-info">
                            <p class="product-price">
                                Цена: <span><?= $product['price']; ?></span> руб.
                            </p>
                            <form>
                                <label>Количество</label>
                                <input name="count" type="text" value="1" />
                                <button type="button" class="btn btn-warning">
                                    <i class="fa fa-shopping-cart"></i>
                                    Добавить в корзину
                                </button>
                            </form>
                            <p>Артикул: 1234567</p>
                            <p>Наличие: На складе</p>
                            <p>
                                Бренд:
                                <a href="<?= Url::to(['catalog/brand', 'id' => $brand['id']]); ?>">
                                    <?= Html::encode($brand['name']); ?>
                                </a>
                            </p>

                        </div>
                    </div>
                </div>
                <div class="product-descr">
                    <?= $product['content']; ?>
                </div>
                <?php if (!empty($similar)): /* похожие товары */ ?>
                    <h2>Похожие товары</h2>
                    <div class="row">
                        <?php foreach ($similar as $item): ?>
                            <div class="col-sm-4">
                                <div class="product-wrapper text-center">
                                    <?=
                                    Html::img(
                                        '@web/images/products/medium/'.$item['image'],
                                        ['alt' => $item['name'], 'class' => 'img-responsive']
                                    );
                                    ?>
                                    <h2><?= $item['price']; ?> руб.</h2>
                                    <p>
                                        <a href="<?= Url::to(['catalog/product', 'id' => $item['id']]); ?>">
                                            <?= Html::encode($item['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 endif; ?>
            </div>
        </div>
    </div>
</section>

Хлебные крошки

Создаем файл components/ChainWidget.php:

<?php
namespace app\components;

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

/**
 * Виджет для вывода цепочки навигации (хлебные крошки)
 */
class ChainWidget extends Widget {

    /**
     * Идентификатор текущей категории
     */
    public $itemCurrent;

    /**
     * Показывать текущую категорию?
     */
    public $showCurrent = true;

    public function run() {
        if (empty($this->itemCurrent)) {
            return '';
        }
        // пробуем извлечь данные из кеша
        $show = $this->showCurrent ? 'true' : 'false';
        $key = 'widget-chain-'.$this->itemCurrent.'-show-'.$show;
        $html = Yii::$app->cache->get($key);
        if ($html === false) {
            // данных нет в кеше, получаем их заново
            $chain = (new Category())->getParents($this->itemCurrent);
            if (!$this->showCurrent) {
                array_pop($chain);
            }
            $html = $this->render('chain', ['chain' => $chain]);
            // сохраняем полученные данные в кеше
            Yii::$app->cache->set($key, $html);
        }
        return $html;
    }

}

И создаем файл components/views/chain.php

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

if (empty($chain)) {
    return;
}
?>

<ol class="breadcrumb">
<?php foreach ($chain as $item): ?>
    <li>
        <a href="<?= Url::to(['catalog/category', 'id' => $item->id]); ?>">
            <?= Html::encode($item->name); ?>
        </a>
    </li>
<?php endforeach; ?>
</ol>

Вызов виджета для страницы категории каталога:

<?= ChainWidget::widget(['itemCurrent' => $category['id'], 'showCurrent' => false]); ?>

Вызов виджета для страницы товара каталога:

<?= ChainWidget::widget(['itemCurrent' => $product['category_id']]); ?>

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