Магазин на Yii2, часть 28. Админка: выбор родителя и список всех категорий

08.09.2019

Теги: CRUDWeb-разработкаYii2ИерархияИнтернетМагазинКаталогТоваровПанельУправленияПрактикаСписокФреймворк

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

<?php
namespace app\modules\admin\models;

use Yii;
use yii\db\ActiveRecord;

class Category extends ActiveRecord {

    /*...*/

    /**
     * Возвращает массив всех категорий каталога в виде дерева
     */
    public static function getAllCategories($parent = 0, $level = 0, $exclude = 0) {
        $children = self::find()
            ->where(['parent_id' => $parent])
            ->asArray()
            ->all();
        $result = [];
        foreach ($children as $category) {
            // при выборе родителя категории нельзя допустить
            // чтобы она размещалась внутри самой себя
            if ($category['id'] == $exclude) {
                continue;
            }
            if ($level) {
                $category['name'] = str_repeat('— ', $level) . $category['name'];
            }
            $result[] = $category;
            $result = array_merge(
                $result,
                self::getAllCategories($category['id'], $level+1, $exclude)
            );
        }
        return $result;
    }

    /**
     * Возвращает массив всех категорий каталога для возможности
     * выбора родителя при добавлении или редактировании товара
     * или категории
     */
    public static function getTree($exclude = 0, $root = false) {
        $data = self::getAllCategories(0, 0, $exclude);
        $tree = [];
        // при выборе родителя категории можно выбрать значение «Без родителя»,
        // т.е. создать категорию верхнего уровня, у которой не будет родителя
        if ($root) {
            $tree[0] = 'Без родителя';
        }
        foreach ($data as $item) {
            $tree[$item['id']] = $item['name'];
        }
        return $tree;
    }
}

Выбор родителя для категории и товара

Теперь изменим форму добавления или редактирования категории — заменим input на select:

<?php
/*
 * Форма для добавления и редактирования категории, файл modules/admin/views/category/_form.php
 */
use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model app\modules\admin\models\Category */
/* @var $form yii\widgets\ActiveForm */
?>

<?php $form = ActiveForm::begin(); ?>
    <?= $form->field($model, 'name')->textInput(['maxlength' => true]); ?>
    <?php
    // при редактировании существующей категории нельзя допустить, чтобы
    // в качестве родителя была выбрана эта же категория или ее потомок
    $exclude = 0;
    if (!empty($model->id)) {
        $exclude = $model->id;
    }
    $parents = $model::getTree($exclude, true);
    echo $form->field($model, 'parent_id')->dropDownList($parents);
    ?>
    <?= $form->field($model, 'content')->textarea(['rows' => 2, 'maxlength' => true]); ?>
    <?= $form->field($model, 'keywords')->textarea(['rows' => 2, 'maxlength' => true]); ?>
    <?= $form->field($model, 'description')->textarea(['rows' => 2, 'maxlength' => true]); ?>
    <?= $form->field($model, 'image')->textInput(['maxlength' => true]) ?>
    <div class="form-group">
        <?= Html::submitButton('Сохранить', ['class' => 'btn btn-success']) ?>
    </div>
<?php ActiveForm::end(); ?>

И вот что у нас получилось в итоге:

Аналогично, изменим форму для добавления и редактирования товара:

<?php
/*
 * Форма для добавления и редактирования товара, файл modules/admin/views/product/_form.php
 */

use app\modules\admin\models\Brand;
use app\modules\admin\models\Category;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model app\modules\admin\models\Product */
/* @var $form yii\widgets\ActiveForm */
?>

<?php $form = ActiveForm::begin(); ?>
    <?= $form->field($model, 'name')->textInput(['maxlength' => true]); ?>
    <?= $form->field($model, 'category_id')->dropDownList(Category::getTree()); ?>
    <?= $form->field($model, 'brand_id')->dropDownList(ArrayHelper::map(Brand::find()->all(), 'id', 'name')); ?>
    <?= $form->field($model, 'price')->textInput(['maxlength' => true]); ?>
    <?= $form->field($model, 'image')->textInput(['maxlength' => true]); ?>
    <?= $form->field($model, 'content')->textarea(['rows' => 6]); ?>
    <?= $form->field($model, 'keywords')->textarea(['rows' => 2, 'maxlength' => true]); ?>
    <?= $form->field($model, 'description')->textarea(['rows' => 2, 'maxlength' => true]); ?>
    <?= $form->field($model, 'hit')->textInput(); ?>
    <?= $form->field($model, 'new')->textInput(); ?>
    <?= $form->field($model, 'sale')->textInput(); ?>
    <div class="form-group">
        <?= Html::submitButton('Сохранить', ['class' => 'btn btn-success']) ?>
    </div>
<?php ActiveForm::end(); ?>

Список всех категорий

Сейчас список всех категорий показывается с использованием провайдера данных ActiveDataProvider и виджета GridView. Нам это не подходит, потому что категории должны показываться с учетом вложенности. Так что изменим метод контроллера actionIndex() и view-шаблон index.php:

<?php
namespace app\modules\admin\controllers;

use Yii;
use app\modules\admin\models\Category;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

/**
 * Класс OrderController реализует CRUD для категорий
 */
class CategoryController extends AdminController {

    /*...*/

    /**
     * Список всех категорий каталога товаров
     */
    public function actionIndex() {
        return $this->render(
            'index',
            ['categories' => Category::getAllCategories()]
        );
    }

    /*...*/
}
<?php
/*
 * Страница списка всех категорий, файл modules/admin/views/category/index.php
 */
use yii\helpers\Html;

/* @var $this yii\web\View */

$this->title = 'Категории каталога';
?>

<h1><?= Html::encode($this->title); ?></h1>
<p>
    <?= Html::a('Добавить категорию', ['create'], ['class' => 'btn btn-success']); ?>
</p>

<table class="table table-striped table-bordered">
    <thead>
        <tr>
            <th>Наименование</th>
            <th>Мета-тег keywords</th>
            <th>Мета-тег description</th>
            <th><span class="glyphicon glyphicon-eye-open"></span></th>
            <th><span class="glyphicon glyphicon-pencil"></span></th>
            <th><span class="glyphicon glyphicon-trash"></span></th>
        </tr>
    </thead>
    <tbody>
    <?php foreach ($categories as $category): ?>
        <tr>
            <td><?= $category['name']; ?></td>
            <td><?= $category['keywords']; ?></td>
            <td><?= $category['description']; ?></td>
            <td>
                <?=
                Html::a(
                    '<span class="glyphicon glyphicon-eye-open"></span>',
                    ['/admin/category/view', 'id' => $category['id']]
                );
                ?>
            </td>
            <td>
                <?=
                Html::a(
                    '<span class="glyphicon glyphicon-pencil"></span>',
                    ['/admin/category/update', 'id' => $category['id']]
                );
                ?>
            </td>
            <td>
                <?=
                Html::a(
                    '<span class="glyphicon glyphicon-trash"></span>',
                    ['/admin/category/delete', 'id' => $category['id']],
                    [
                        'data-confirm' => 'Вы уверены, что хотите удалить эту категорию?',
                        'data-method' => 'post'
                    ]
                );
                ?>
            </td>
        </tr>
    <?php endforeach; ?>
    </tbody>
</table>

Поиск: Web-разработка • Yii2 • Иерархия • Интернет магазин • Каталог товаров • Практика • Список • Фреймворк • CRUD

Каталог оборудования
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.