В качестве примера для загрузки файлов реализуем задачу по добавлению постов на пользовательской стороне сайта. Создадим объект Посты со следующими полями
А также через gii сгенерируем Ygin модуль и Ygin модель, и подключим модуль в protected/config/project.php
return array( 'name'=>'Engine macro', 'language' => 'ru', 'theme' => 'business', // используемые в проекте модули 'modules' => array( 'post', ), ... );
Должна получиться следующая структура каталогов:
Саму загрузку файлов будем делать с помощью классов, которые находятся в ygin/components/fileUpload/
. Загрузка будет происходить ajax'ом с помощью виджета FileUploadWidget
, который основывается на расширении XUpload и jquery-плагине JQuery file upload.
Понадобится три представления:
- form - сама форма загрузки
- upload - файл во время загрузки
- download - файл после загрузки
Можно использовать те, которые лежат в ygin/components/fileUpload/fileUploadWidget/views
, но тогда придется писать много js-кода, для обработки событий. Поэтому можно воспользоваться представлениями из ygin/modules/backend/widgets/upload/listFileUpload/views
и ygin/modules/backend/widgets/upload/singleFileUpload/views
для множественной и одиночной загрузки файлов соответственно.
Скопируем их в protected/modules/post/widgets/upload/list
и protected/modules/post/widgets/upload/single
. Должна получиться следущая структура каталогов:
Уберем из файлов protected/modules/post/widgets/upload/list/form.php
и protected/modules/post/widgets/upload/single/form.php
, код связанный с админкой. А именно $.daSticker({text: msg, type: "error"});
(вместо этого можно написать alert(msg);) и строчки, где встречается BackendUploadedFiles
, $this->owner->form->error($this->mainModel, $this->objectParameter->getFieldName());
Подключаем к модели Post
поведение FileUploadableBehavior
public function behaviors() { return array( 'FileUploadableBehavior' => array( 'class' => 'fileUpload.FileUploadableBehavior', 'resetScope' => true, ), ); }
Подключаем экшен FileUploadAction
к контроллеру DefaultController
модуля Post
public function actions() { return array( //для загрузки одиночного файла 'fileUpload' => array( 'class' => 'fileUpload.FileUploadAction', 'multiple' => false, 'createThumb' => true, 'thumbConfig' => array( 'width' => 70, 'height' => 50, 'crop' => 'top', 'postfix' => '_thumb', ), ), //для загрузки списка файлов 'listFileUpload' => array( 'class' => 'fileUpload.FileUploadAction', 'multiple' => true, 'createThumb' => true, 'rewriteIfFileExist' => false, 'thumbConfig' => array( 'width' => 70, 'height' => 50, 'crop' => 'top', 'postfix' => '_thumb', ), ), ); }
Теперь создадим экшен Defaultcontroller::actionSave
и соответствующее представление save.php
public function actionSave($id = null) { $model = null; if ($id) { $model = $this->loadModelOr404('Post', $id); } else { $model = new Post(); } if ($attributes = (array)HU::post('Post', array())) { $model->attributes = $attributes; if ($model->save()) { $this->redirect(array('save', 'id' => $model->id_post)); } } $this->render('save', array( 'model' => $model, )); }
<?php /** * @var DefaultController $this * @var Post $model * @var CActiveForm $form */ ?> <?php Yii::import('xupload.models.XUploadForm'); $form = $this->beginWidget('CActiveForm', array( 'id' => 'post-form', 'method' => 'post', 'htmlOptions' => array( 'class' => 'form-horizontal', ), )); ?> <div class="control-group"> <?php echo $form->label($model, 'title', array('class' => 'control-label')); ?> <div class="controls"> <?php echo $form->textField($model, 'title', array('class' => 'span5')); ?> <?php echo $form->error($model, 'title'); ?> </div> </div> <div class="control-group"> <?php echo $form->label($model, 'id_image', array('class' => 'control-label')); ?> <div class="controls" id="single-file-upload"> <?php $this->widget('fileUpload.fileUploadWidget.FileUploadWidget', array( 'id' => 'single-file-upload', 'multiple' => false, 'url' => array('post/default/fileUpload'), 'formClass' => 'fileUpload.FileUploadForm', 'mainModel' => $model, 'objectParameter' => $model->getObjectInstance()->getParameterObjectByIdParameter(Post::PARAMETER_IMAGE), 'formView' => 'post.widgets.upload.single.form', 'uploadView' => 'post.widgets.upload.single.upload', 'downloadView' => 'post.widgets.upload.single.download', 'thumbConfig' => array( 'width' => 70, 'height' => 50, 'crop' => 'top', 'postfix' => '_thumb', ), 'options' => array( 'prependFiles' => true, ), )); ?> </div> </div> <div class="control-group"> <?php echo $form->label($model, 'short', array('class' => 'control-label')); ?> <div class="controls"> <?php echo $form->textArea($model, 'short', array('class' => 'span5', 'rows' => 8)); ?> <?php echo $form->error($model, 'short'); ?> </div> </div> <div class="control-group"> <label class ="control-label">Загрузка файлов</label> <div class="controls" id="list-file-upload"> <?php $this->widget('fileUpload.fileUploadWidget.FileUploadWidget', array( 'id' => 'list-file-upload', 'multiple' => true, 'url' => array('post/default/listFileUpload'), 'formClass' => 'fileUpload.FileUploadForm', 'mainModel' => $model, 'objectParameter' => $model->getObjectInstance()->getParameterObjectByIdParameter(Post::PARAMETER_FILE_LIST), 'formView' => 'post.widgets.upload.list.form', 'uploadView' => 'post.widgets.upload.list.upload', 'downloadView' => 'post.widgets.upload.list.download', 'thumbConfig' => array( 'width' => 70, 'height' => 50, 'crop' => 'top', 'postfix' => '_thumb', ), )); ?> </div> </div> <div class="control-group"> <?php echo $form->label($model, 'content', array('class' => 'control-label')); ?> <div class="controls"> <?php echo $form->textArea($model, 'content', array('class' => 'span5', 'rows' => 8)); ?> <?php echo $form->error($model, 'content'); ?> </div> </div> <div class="control-group"> <div class="controls"> <button type="submit" class="btn"><?php echo $model->isNewRecord ? 'Создать' : 'Обновить'; ?></button> </div> </div> <?php $this->endWidget(); ?>
Теперь перейдем по адресу http://ваш_сайт/post/default/save
, должно получиться нечто следующее:
Пост после сохранения
Чтобы получить список загруженных файлов, например для TinyMCE, то можно использовать такой экшен
public function actionGetFiles($id = null, $tmpId = null) { if (empty($id) && empty($tmpId)) { throw new CHttpException(400, 'Неверный запрос.'); } $attributes = array( 'id_parent_file' => null, 'id_object' => Post::ID_OBJECT, 'id_parameter' => Post::PARAMETER_FILE_LIST, ); if (!empty($id)) { $attributes['id_instance'] = $id; } elseif (!empty($tmpId)) { $attributes['id_tmp'] = $tmpId; } $files = File::model()->findAllByAttributes($attributes); //дальше обрабатываем в необходимый формат }
где tmpId - это $model->tmpId (временный идентификаатор, пока модель новая и еще не сохранена в базу, берется из FileUploadableBehavior
)
Ошибка
Сделал все по примеру. Получилась ошибка
Fatal error: Undefined class constant 'PARAMETER_IMAGE'
Ошибка
Но возникли еще трудности.
При аяксе в инспектор браузера возвращается такой ответ:
Необходимо заполнить поле «Object Id». Необходимо заполнить поле «Parameter Id».
Не могу понять где их взять и куда их вставить
Ошибка
Нашел ошибку в настройках виджета загрузки необходимо поменять строку
'objectParameter' => $model->getObjectInstance()->getParameterObjectByIdParameter(Post::PARAMETER_IMAGE)
на строку
'objectParameter' => $model->getObjectInstance()->getParameterObjectByField('image')
так как нам необходимо передать объект с параметрами поля картинки для занесения в таблицу файлов. в примере не раскрыто по какому идентификатору можно получить объект, а с помощью метода getParameterObjectByField объект можно получить по названию поля картинки/файла, в моем случае это image
Несколько записей с загрузкой картинок
Появился вопрос. Вывожу несколько записей из базы в которых есть поля для картинок и у каждой есть кнопка загрузки файла и загрузка перестает работать. У первой записи картинка грузится в temp и отображается превью, но в базу не загружается, а у остальных записей при нажатии на кнопку загрузить вообще ничего не происходит.
Если вывести одну запись, то работает все нормально
Подскажите, как решить проблему