Магазин на Yii2, часть 31. Админка: загрузка изображений для категорий и брендов
14.09.2019
Теги: CRUD • Web-разработка • Yii2 • Изображение • ИнтернетМагазин • КаталогТоваров • ПанельУправления • Практика • Фреймворк
Добавим загрузку и ресайз изображений для категорий и брендов. Здесь все по аналогии с загрузкой и ресайзом изображений для товаров. Посколько мы это уже делали, нет смысла все подробно описывать. Так что только исходные коды контроллеров, моделей и view-шаблонов с формой.
Загрузка изображения для категории
<?php namespace app\modules\admin\controllers; use Yii; use app\modules\admin\models\Category; use app\modules\admin\models\Product; use yii\data\ActiveDataProvider; use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; use yii\web\UploadedFile; /** * Класс CategoryController реализует CRUD для категорий */ class CategoryController extends AdminController { public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::class, 'actions' => [ 'delete' => ['POST'], ], ], ]; } /** * Список всех категорий каталога товаров */ public function actionIndex() { return $this->render( 'index', ['categories' => Category::getAllCategories()] ); } /** * Просмотр данных существующей категории */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Список всех товаров категории */ public function actionProducts($id) { // получаем массив идентификаторов всех потомков категории, // чтобы запросом выбрать товары и в дочерних категориях $ids = Category::getAllChildIds($id); $ids[] = $id; $products = new ActiveDataProvider([ 'query' => Product::find()->where(['in', 'category_id', $ids]) ]); return $this->render( 'products', [ 'category' => $this->findModel($id), 'products' => $products, ] ); } /** * Создание новой категории */ public function actionCreate() { $model = new Category(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { // загружаем изображение и выполняем resize исходного изображения $model->upload = UploadedFile::getInstance($model, 'image'); if ($name = $model->uploadImage()) { // если изображение было загружено // сохраняем в БД имя файла изображения $model->image = $name; } $model->save(); return $this->redirect(['view', 'id' => $model->id]); } return $this->render('create', [ 'model' => $model, ]); } /** * Обновление существующей категории */ public function actionUpdate($id) { $model = $this->findModel($id); // старое изображение, которое надо удалить, если загружено новое $old = $model->image; if ($model->load(Yii::$app->request->post()) && $model->validate()) { // загружаем изображение и выполняем resize исходного изображения $model->upload = UploadedFile::getInstance($model, 'image'); if ($new = $model->uploadImage()) { // если изображение было загружено // удаляем старое изображение if (!empty($old)) { $model::removeImage($old); } // сохраняем в БД новое имя $model->image = $new; } else { // оставляем старое изображение $model->image = $old; } $model->save(); return $this->redirect(['view', 'id' => $model->id]); } return $this->render('update', [ 'model' => $model, ]); } /** * Удаление существующей категории */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Поиск категории по идентификатору */ protected function findModel($id) { if (($model = Category::findOne($id)) !== null) { return $model; } throw new NotFoundHttpException('The requested page does not exist.'); } }
<?php namespace app\modules\admin\models; use Yii; use yii\db\ActiveRecord; use yii\imagine\Image; /** * Это модель для таблицы БД `category` * * @property int $id Уникальный идентификатор * @property int $parent_id Родительская категория * @property string $name Наименование категории * @property string $content Описание категории * @property string $keywords Мета-тег keywords * @property string $description Мета-тег description * @property string $image Имя файла изображения */ class Category extends ActiveRecord { /** * Вспомогательный атрибут для загрузки изображения */ public $upload; /** * Возвращает имя таблицы базы данных */ public static function tableName() { return 'category'; } /** * Возвращает данные о родительской категории */ public function getParent() { return $this->hasOne(Category::class, ['id' => 'parent_id']); } /** * Возвращает наименование родительской категории */ public function getParentName() { $parent = $this->parent; return $parent ? $parent->name : ''; } /** * Правила валидации полей формы при создании и редактировании категории */ public function rules() { return [ [['parent_id'], 'integer'], [['name'], 'required'], [['name', 'content', 'keywords', 'description'], 'string', 'max' => 255], // атрибут image проверяем с помощью валидатора image ['image', 'image', 'extensions' => 'png, jpg, gif'], ]; } /** * Возвращает имена полей формы для создания и редактирования категории */ public function attributeLabels() { return [ 'id' => 'ID', 'parent_id' => 'Родитель', 'name' => 'Наименование', 'content' => 'Описание', 'keywords' => 'Мета-тег keywords', 'description' => 'Мета-тег description', 'image' => 'Изображение' ]; } /** * Возвращает массив всех категорий каталога в виде дерева */ 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; } /** * Возвращает массив идентификаторов всех потомков категории $id, * т.е. дочерние, дочерние дочерних и так далее */ public static function getAllChildIds($id) { $children = []; $ids = self::getChildIds($id); foreach ($ids as $item) { $children[] = $item; $c = self::getAllChildIds($item); foreach ($c as $v) { $children[] = $v; } } return $children; } /** * Возвращает массив идентификаторов дочерних категорий (прямых * потомков) категории с уникальным идентификатором $id */ protected static function getChildIds($id) { $children = self::find()->where(['parent_id' => $id])->asArray()->all(); $ids = []; foreach ($children as $child) { $ids[] = $child['id']; } return $ids; } /** * Загружает файл изображения категории */ public function uploadImage() { if ($this->upload) { // только если был выбран файл для загрузки $name = md5(uniqid(rand(), true)) . '.' . $this->upload->extension; // сохраняем исходное изображение в директории source $source = Yii::getAlias('@webroot/images/categories/source/' . $name); if ($this->upload->saveAs($source)) { // выполняем resize, чтобы получить маленькое изображение $thumb = Yii::getAlias('@webroot/images/categories/thumb/' . $name); Image::thumbnail($source, 250, 250)->save($thumb, ['quality' => 90]); return $name; } } return false; } /** * Удаляет старое изображение при загрузке нового */ public static function removeImage($name) { if (!empty($name)) { $source = Yii::getAlias('@webroot/images/categories/source/' . $name); if (is_file($source)) { unlink($source); } $thumb = Yii::getAlias('@webroot/images/categories/thumb/' . $name); if (is_file($thumb)) { unlink($thumb); } } } /** * Удаляет изображение при удалении категории */ public function afterDelete() { parent::afterDelete(); self::removeImage($this->image); } }
<?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); ?> <fieldset> <legend>Загрузить изображение</legend> <?= $form->field($model, 'image')->fileInput(); ?> <?php if (!empty($model->image)) { $img = Yii::getAlias('@webroot') . '/images/categories/source/' . $model->image; if (is_file($img)) { $url = Yii::getAlias('@web') . '/images/categories/source/' . $model->image; echo 'Уже загружено ', Html::a('изображение', $url, ['target' => '_blank']); } } ?> </fieldset> <?= $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]); ?> <div class="form-group"> <?= Html::submitButton('Сохранить', ['class' => 'btn btn-success']) ?> </div> <?php ActiveForm::end(); ?>
Загрузка изображения для бренда
<?php namespace app\modules\admin\controllers; use Yii; use app\modules\admin\models\Brand; use yii\data\ActiveDataProvider; use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; use yii\web\UploadedFile; /** * Класс OrderController реализует CRUD для брендов */ class BrandController extends AdminController { public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::class, 'actions' => [ 'delete' => ['POST'], ], ], ]; } /** * Список всех брендов с постраничной навигацией */ public function actionIndex() { $dataProvider = new ActiveDataProvider([ 'query' => Brand::find(), ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } /** * Просмотр данных существующего бренда */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Создание нового бренда */ public function actionCreate() { $model = new Brand(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { // загружаем изображение и выполняем resize исходного изображения $model->upload = UploadedFile::getInstance($model, 'image'); if ($name = $model->uploadImage()) { // если изображение было загружено // сохраняем в БД имя файла изображения $model->image = $name; } $model->save(); return $this->redirect(['view', 'id' => $model->id]); } return $this->render('create', [ 'model' => $model, ]); } /** * Обновление существующего бренда */ public function actionUpdate($id) { $model = $this->findModel($id); // старое изображение, которое надо удалить, если загружено новое $old = $model->image; if ($model->load(Yii::$app->request->post()) && $model->validate()) { // загружаем изображение и выполняем resize исходного изображения $model->upload = UploadedFile::getInstance($model, 'image'); if ($new = $model->uploadImage()) { // если изображение было загружено // удаляем старое изображение if (!empty($old)) { $model::removeImage($old); } // сохраняем в БД новое имя $model->image = $new; } else { // оставляем старое изображение $model->image = $old; } $model->save(); return $this->redirect(['view', 'id' => $model->id]); } return $this->render('update', [ 'model' => $model, ]); } /** * Удаление существующего бренда */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Поиск бренда по идентификатору */ protected function findModel($id) { if (($model = Brand::findOne($id)) !== null) { return $model; } throw new NotFoundHttpException('The requested page does not exist.'); } }
<?php namespace app\modules\admin\models; use Yii; use yii\db\ActiveRecord; use yii\imagine\Image; /** * Это модель для таблицы БД `brand` * * @property int $id Уникальный идентификатор * @property string $name Наименование бренда * @property string $content Описание бренда * @property string $keywords Мета-тег keywords * @property string $description Мета-тег description * @property string $image Имя файла изображения */ class Brand extends ActiveRecord { /** * Вспомогательный атрибут для загрузки изображения */ public $upload; /** * Возвращает имя таблицы базы данных */ public static function tableName() { return 'brand'; } /** * Правила валидации полей формы при создании и редактировании бренда */ public function rules() { return [ [['name'], 'required'], [['name', 'content', 'keywords', 'description'], 'string', 'max' => 255], // атрибут image проверяем с помощью валидатора image ['image', 'image', 'extensions' => 'png, jpg, gif'], ]; } /** * Возвращает имена полей формы для создания и редактирования бренда */ public function attributeLabels() { return [ 'id' => 'ID', 'name' => 'Наименование', 'content' => 'Описание', 'keywords' => 'Мета-тег keywords', 'description' => 'Мета-тег description', 'image' => 'Изображение', ]; } /** * Загружает файл изображения бренда */ public function uploadImage() { if ($this->upload) { // только если был выбран файл для загрузки $name = md5(uniqid(rand(), true)) . '.' . $this->upload->extension; // сохраняем исходное изображение в директории source $source = Yii::getAlias('@webroot/images/brands/source/' . $name); if ($this->upload->saveAs($source)) { // выполняем resize, чтобы получить маленькое изображение $thumb = Yii::getAlias('@webroot/images/brands/thumb/' . $name); Image::thumbnail($source, 250, 250)->save($thumb, ['quality' => 90]); return $name; } } return false; } /** * Удаляет старое изображение при загрузке нового */ public static function removeImage($name) { if (!empty($name)) { $source = Yii::getAlias('@webroot/images/brands/source/' . $name); if (is_file($source)) { unlink($source); } $thumb = Yii::getAlias('@webroot/images/brands/thumb/' . $name); if (is_file($thumb)) { unlink($thumb); } } } /** * Удаляет изображение при удалении бренда */ public function afterDelete() { parent::afterDelete(); self::removeImage($this->image); } }
<?php /* * Форма для добавления и редактирования бренда, файл modules/admin/views/brand/_form.php */ use yii\helpers\Html; use yii\widgets\ActiveForm; /* @var $this yii\web\View */ /* @var $model app\modules\admin\models\Brand */ /* @var $form yii\widgets\ActiveForm */ ?> <?php $form = ActiveForm::begin(); ?> <?= $form->field($model, 'name')->textInput(['maxlength' => true]); ?> <fieldset> <legend>Загрузить изображение</legend> <?= $form->field($model, 'image')->fileInput(); ?> <?php if (!empty($model->image)) { $img = Yii::getAlias('@webroot') . '/images/brands/source/' . $model->image; if (is_file($img)) { $url = Yii::getAlias('@web') . '/images/brands/source/' . $model->image; echo 'Уже загружено ', Html::a('изображение', $url, ['target' => '_blank']); } } ?> </fieldset> <?= $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]); ?> <div class="form-group"> <?= Html::submitButton('Сохранить', ['class' => 'btn btn-success']) ?> </div> <?php ActiveForm::end(); ?>
- Магазин на Yii2, часть 30. Админка: WYSIWYG-редактор и изображение для товара
- Магазин на Yii2, часть 35. Админка: загрузка картинок для страниц и страница 404
- Магазин на Yii2, часть 33. Админка: приводим в порядок CRUD-код для страниц
- Магазин на Yii2, часть 32. Админка: удаление категорий и CRUD для страниц
- Магазин на Yii2, часть 29. Админка: добавляем список товаров категории
- Магазин на Yii2, часть 28. Админка: выбор родителя и список всех категорий
- Магазин на Yii2, часть 27. Админка: приводим в порядок сгенерированный код
Поиск: Web-разработка • Yii2 • Изображение • Интернет магазин • Каталог товаров • Панель управления • Фреймворк • UploadedFile • Практика • CRUD