sidorenkoda
@sidorenkoda
Программист, верстальщик, руководитель проектов

Как в Yii2 загрузка изображений и последующее хранение их имен в БД?

Здравствуйте!
Есть атрибут в базе данных - image1
Я хочу в него класть название изображения, а само изображение хранить в отдельной папке на сервере.
Делаю это так: $model->setAttribute('image1', 'Название файла с расширением');
Проблема в том, что из-за того, что в модели я указываю правило (rules), что тип данных - image, то он не дает мне сохранить именное мной значения этого атрибута в базе.
Так как же все-таки правильно хранить изображения на файловой системе и ссылку на них в БД? :)

Шаблон
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?>
<?= $form->field($model, 'image1')->fileInput([/*'multiple' => true, */'accept' => 'image/*']) ?>

Модель
public function rules() {
	return [
		[['image1'], 'image', 'extensions' => 'png, jpg, jpeg, gif', 'maxFiles' => 4, 'skipOnEmpty' => false]
	];
	...
}
public function upload($dir, $imageObj) {
	$name = $imageObj->baseName;
	$ext = $imageObj->extension;
	$imageObj->saveAs($dir . $name . '.' . $ext);
	return true;
}

Контроллер
public function actionUpdate($id) {
	$model = $this->findModel($id);
	$model->image1 = UploadedFile::getInstance($model, 'image1');
	if (Yii::$app->request->post()) {
		if ($model->image1) {
			$dir = $_SERVER['DOCUMENT_ROOT'] . '/upload/manufacturers/';
			// Меняем содержимое аттрибута, записываем туда название файла, чтобы это лежало в БД
			$model->setAttribute('image1', 'myFileName.' . $model->image1->getExtension());
		}
	}
	if ($model->load(Yii::$app->request->post()) && $model->save()) {
		return $this->redirect(['view', 'id' => $model->id_manufacturer]);
	}
}


P.S. изображение в нужной директории появляется

а вот если перед выводом шаблона после попытки поменять значение атрибута распечатать модель с помощью функции print_r(), то можно заметить вот такую ошибку:
if ($model->load(Yii::$app->request->post()) && $model->save()) {
    print_r($model);die;
    return $this->redirect(['view', 'id' => $model->id_manufacturer]);
}
[_validators:yii\base\Model:private] => ArrayObject Object
    (
        [storage:ArrayObject:private] => Array
            (
                [0] => yii\validators\ImageValidator Object
                    (
                        [notImage] => The file "{file}" is not an image.
  • Вопрос задан
  • 13515 просмотров
Решения вопроса 2
Мой вам совет. Создайте дополнительную модель для формы. В ней производите валидацию непосредственно данных введенных пользователем, в том числе и самого файла.
А после загрузки файла и валидации всей формы - создавайте объект вашей модели, записывайте туда имя файла и сохраняйте ее.
Это нормальная практика иметь одну модель для связи с базой, и другую для валидации формы.

В данном примере модель формы имеет метод SaveModel. Который сохраняет модельку сущности из базы. В моем случае не нужно редактировать файл изображения (Attachment), поэтому я просто создаю новый, а старый удаляю. Имхо не самый лучше код, но какой сумел быстро найти.

<?php
/**
 * Created by PhpStorm.
 * User: Jakeroid
 * Date: 17-Jul-15
 * Time: 13:28
 */

namespace app\modules\cp\models;

use app\models\Attachment;
use Yii;
use yii\base\Model;
use app\models\Team;

class TeamForm extends Model
{
    public $name;
    public $logo;

    /**
     * @var Team
     */
    private $dbModel;

    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            [['name'], 'required'],
            [['name'], 'string', 'max' => 255],
            [['logo'], 'file', 'extensions' => 'gif, jpg, jpeg, png', 'skipOnEmpty' => false],
        ];
    }

    /**
     * @return array customized attribute labels
     */
    public function attributeLabels()
    {
        return [
            'name' => Yii::t('app', 'Team name'),
            'logo' => Yii::t('app', 'Team logo (flag)'),
        ];
    }

    /**
     * @param $is_update boolean
     * @return boolean from save method of dbModel
     */
    public function saveModel($is_update = false)
    {
        $attachment = new Attachment();
        $attachment->type = 'team_logo';
        $attachment->attachments_group_id = 0;
        $attachment->saveWithFile($this->logo);
        if ($is_update) {
            $this->dbModel->logo->delete();
        }
        $this->dbModel->logo_id = $attachment->id;
        $this->dbModel->name = $this->name;
        return $this->dbModel->save();
    }

    public function loadModel($model)
    {
        $this->dbModel = $model;
        $this->name = $model->name;
    }

    /**
     * @return Team from current dbModel
     */
    public function getModel()
    {
        return $this->dbModel;
    }
}


Контроллер такой:
public function actionUpdate($id)
    {
        $model = new TeamForm();
        $model->loadModel($this->findModel($id));

        if ($model->load(Yii::$app->request->post())) {
            $model->logo = UploadedFile::getInstance($model, 'logo');
            if ($model->validate() && $model->logo) {
                $model->saveModel(true);
                return $this->redirect(['view', 'id' => $model->getModel()->id]);
            }
        }

        return $this->render('update', [
            'model' => $model,
        ]);
    }
Ответ написан
sidorenkoda
@sidorenkoda Автор вопроса
Программист, верстальщик, руководитель проектов
Ivan Karabadzhak А я вот взял и извратил вашу концепцию
Я создал вторую модель, которую наследовал от первой и в ней переопределил rules();

И получилось вот так:
public function actionUpdate($id) {
		$model = new Manufacturer();
		$model = $model->findOne($id);
		if (Yii::$app->request->post()) {
			$model->load(Yii::$app->request->post());
			$model->image1 = UploadedFile::getInstance($model, 'image1');
			if ( $model->image1 ) {
				$dir = $_SERVER['DOCUMENT_ROOT'] . '/upload/manufacturers/';
				if ( $model->upload($dir, $model->image1, $id) ) { /* set flash - file update is good */ }
				// Наследуем модель с "мягкими" rules
				$model = new ManufacturerDB();
				$model = $model->findOne($id);
				$model->load(Yii::$app->request->post());
				$model->setAttribute('image1', $id . '.jpg');
			} else {
				unset($model->image1);
			}
			$model->save();
			return $this->redirect(['view', 'id' => $id]);
		}
		return $this->render('update', [
			'model' => $model,
		]);
	}


Соответствует это вашей концепции?
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы