Yii: Добавляем поле загрузки картинки (изображения) к статье, переименовываем и сохраняем её в папку на сервере и в БД
Этот же способ, но для Yii 2 смотрите - http://yiico.ru/blog/493-zagruzka-izobrazhenii-i-failov-v-yii2-ih-sohranenie-v-papku-na-servere-i-v-bd
До недавнего времени у нас было поле input для пути к файлу изображения. При добавлении новой статьи приходилось закачивать картинку по ftp и вручную прописывать в это поле путь к ней. Надоело, теперь мы изменили поле с типа input на тип file. Картинка загружается с компьютера, переименовывается и сохраняется в папку согласно дате (если материал добавлен в 2011 году, то изображение сохраняется в папку 2011, в 2012 - в папку 2012; а в начало имени файла прописывается месяц и день добавления статьи).
К каждой статье можно загрузить одно изображение.
Мы также посчитали правильным хранить имя изображения в базе данных, для чего добавили в таблицу со статьями поле image. Теперь всё удобно работает.
Расскажем как мы это реализовавыли.
В модель статей добавляем 2 новых атрибута в самом верху:
class Material extends CActiveRecord { public $icon; // атрибут для хранения загружаемой картинки статьи public $del_img; // атрибут для удаления уже загруженной картинки
Переменная $icon для хранения загружаемой картинки и $del_img для того, чтобы пользователь мог удалить уже загруженную картинку.
Эти атрибуты надо описать так же, как и атрибуты, значения которых хранятся в базе данных: определить правила валидации rules() и названия полей в понятном для человека виде attributeLabels()
В том же файле модели статей дополняем:
public function rules() { // NOTE: you should only define rules for those attributes that // will receive user inputs. return array( array('title, alias, anons, description, keywords, content, status', 'required'), array('del_img', 'boolean'), array('status', 'in', 'range'=>array(1,2,3)), array('title', 'length', 'max'=>128), array('source, image', 'length', 'max'=>255), array('icon', 'file', 'types'=>'jpg, gif, png', 'maxSize'=>1024 * 1024 * 5, // 5 MB 'allowEmpty'=>'true', 'tooLarge'=>'Файл весит больше 5 MB. Пожалуйста, загрузите файл меньшего размера.', ), array('id, title, status, source', 'safe', 'on'=>'search'), ); }
И названия атрибутов:
public function attributeLabels() { return array( 'id' => 'Id', 'title' => 'Заголовок', 'alias' => 'Псевдоним', 'anons' => 'Анонс', 'content' => 'Статья полностью', 'status' => 'Статус', 'create_time' => 'Create Time', 'update_time' => 'Update Time', 'source' => 'Источник (URL без http://)', 'author_id' => 'Автор', 'icon' => 'Картинка к статье', 'del_img'=>'Удалить картинку?', 'count_views' => 'Количество просмотров', ); }
Эти новые атрибуты отличает то, что их значения нигде не хранятся (нужны только чтобы загрузить картинку на сервер или удалить уже загруженную).
Если для какой-то статьи картинка не загружена, нужно будет вывести пустую картинку («Нет изображения»).
Для этого в контроллере protected/controllers/MaterialController.php создаю действие material_image()
public function material_image($id, $create_year, $alias, $title, $image, $width='150', $class='material_img') { if(isset($image) && file_exists($_SERVER['DOCUMENT_ROOT']. Yii::app()->urlManager->baseUrl. '/images/'.$create_year.'/'.$image)) return CHtml::image(Yii::app()->getBaseUrl(true).'/images/'.$create_year.'/'.$image, $title, array( 'width'=>$width, 'class'=>$class, )); else return CHtml::image(Yii::app()->getBaseUrl(true).'/images/pics/noimage.gif','Нет картинки', array( 'width'=>$width, 'class'=>$class )); }
Обратите внимание на Yii::app()->getBaseUrl(true) - это делает url абсолютным. Результатом этой функция будет HTML тег image с нужной картинкой, или с картинкой noimage.gif.
Теперь в форме /protected/views/material/_form.php добавляем вывод картинки к статье, чекбокс на удаление этой картинки и поле выбора новой картинки:
<div class="row pull-1 span-3 box"> <?php echo $form->labelEx($model,'icon'); ?> <?php // Вывод уже загруженной картинки или изображения No_photo echo $this->material_image($model->id, substr($model->create_time, 0, 4), $model->alias, $model->title, $model->image, '150','small_img left');?> <br clear="all"> <?php //Если картинка для данного товара загружена, предложить её удалить, отметив чекбокс if(isset($model->image) && file_exists($_SERVER['DOCUMENT_ROOT']. Yii::app()->urlManager->baseUrl. '/images/'.substr($model->create_time, 0, 4).'/'.$model->image)) { echo $form->checkBox($model,'del_img',array('class'=>'span-1')); echo $form->labelEx($model,'del_img',array('class'=>'span-2')); } ?> <br /> <?php //Поле загрузки файла echo CHtml::activeFileField($model, 'icon'); ?> </div>
Не забудьте дописать, что форма теперь стала типа multipart:
<?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'file-form', 'enableAjaxValidation'=>false, 'htmlOptions'=>array('enctype'=>'multipart/form-data'), )); ?>
Обратим внимание: на $form->checkBox($model,'del_img' ); и CHtml::activeFileField($model, 'icon'). Там имена новых атрибутов модели, которые не хранятся в базе данных.
Теперь, чтобы обработать данные формы вернемся в контроллер /protected/controllers/MaterialController.php Изменим методы «создать» и «обновить»:
public function actionCreate() { $model=new Material; // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); if(isset($_POST['Material'])){ $model->attributes=$_POST['Material']; //Полю icon присвоить значения поля формы icon $model->icon=CUploadedFile::getInstance($model,'icon'); if ($model->icon){ $sourcePath = pathinfo($model->icon->getName()); $fileName = date('m-d').'-'.$model->alias.'.'.$sourcePath['extension']; $model->image = $fileName; } if($model->save()){ //Если поле загрузки файла не было пустым, то if ($model->icon){ //сохранить файл на сервере в каталог images/2011 под именем //month-day-alias.jpg $file = $_SERVER['DOCUMENT_ROOT']. Yii::app()->urlManager->baseUrl. '/images/'.date('Y').'/'.$fileName; $model->icon->saveAs($file); } $this->redirect(array('view','id'=>$model->id)); } } $this->render('create',array( 'model'=>$model, 'sectionnames'=>$sectionnames )); }
Выделенная строка $model->image = $fileName обязательно вызывается до сохранения формы, иначе в базу данных не запишется значение в поле image.
Аналогично в действии обновить:
public function actionUpdate() { $model=$this->loadModel(); if(isset($_POST['Material'])) { $model->attributes=$_POST['Material']; $model->icon=CUploadedFile::getInstance($model,'icon'); if ($model->icon){ $sourcePath = pathinfo($model->icon->getName()); $fileName = substr($model->create_time, 5, 5).'-'.$model->alias.'.'.$sourcePath['extension']; $model->image = $fileName; } if($model->save()){ //Если отмечен чекбокс «удалить файл» if($model->del_img) { if(file_exists($_SERVER['DOCUMENT_ROOT']. Yii::app()->urlManager->baseUrl. '/images/'.substr($model->create_time, 0, 4).'/'.$model->image)) { //удаляем файл unlink('./images/'.substr($model->create_time, 0, 4).'/'.$model->image); $model->image = ''; } } //Если поле загрузки файла не было пустым, то if ($model->icon){ $file = './images/'.substr($model->create_time, 0, 4).'/'.$fileName; //сохранить файл на сервере под именем //month-day-alias.jpg Если файл с таким именем существует, он будет заменен. $model->icon->saveAs($file); } $this->redirect(array('view','id'=>$model->id)); } } $this->render('update',array( 'model'=>$model, 'sectionnames'=>$sectionnames )); }
Теперь добавляем вывод картинки в статью в файлах /protected/views/material/view.php и _view.php:
<?php echo $this->material_image($data->id, substr($data->create_time, 0, 4), $data->alias, $data->title, $data->image, '150','small_img left');?>
С помощью метода material_image() выводим картинки.
Стили: стили css в файле /css/main.css
Добавляю:
.material_img { margin: 10px 10px 10px 0; float:left }
Чтобы картинку текст обтекал справа, и
div.view { padding: 10px; margin: 10px 0; border: 1px solid #C9E0ED; min-height: 250px; height: auto !important; height: 250px; }
Чтобы не расползался список статей, каждая статья имеет ячейку не менее 250 пикселей высотой.
В следующей статье научимся делать ресайз картинки (сделаем несколько картинок разного размера для одной исходной и сохраним в разные папки).
Edit 12.05.2012: Несколько иначе загрузку картинок показал Юрий Беляков - http://belyakov.su/content/yii-magazin-3_1-dopilivaem-book (там используется проверка is_object, код покороче).
almix
Разработчик Loco, автор статей по веб-разработке на Yii, CodeIgniter, MODx и прочих инструментах. Создатель Team Sense.
Вы можете почитать все статьи от almix'а.
- 14 Разработка приложения на Yii. Урок 14: Выводим список категорий как новый виджет на сайте (29.04.2015)
- 13 Разработка приложения на Yii. Урок 13: Внедряем категории для статей. (29.04.2015)
- 12 Разработка приложения на Yii. Урок 12: Профилирование приложения, включаем кеширование. (20.01.2015)
- 11 Yiico. Видеокурс по разработке сайта на Yii. Урок 11: Отладка приложения, включаем журналирование. (20.01.2015)
- 10 Yiico. Видеокурс по разработке сайта на Yii. Урок 10: Если ваше приложение находится не в корневой папке, а во вложенной. (19.10.2014)
- 9 Разработка сайта на Yii с нуля. Урок 9. Выборка статей определённого автора. (13.08.2014)
- 8 Разработка сайта на Yii с нуля. Урок 8. Вызов в моделях функции, общей для них. Как избегать дублирования кода? (25.07.2014)
- 7 Разработка сайта на Yii с нуля. Урок 7. Изменение количества выводимых записей на странице в CGridView. Включаем сессии Yii. (04.09.2013)
- 6 Разработка сайта на Yii с нуля. Урок 6. Автоматич. отправка оповещений об одобренных комментариях на email автора комментария (11.08.2013)
- 5 Разработка сайта на Yii с нуля. Урок 5. Переименовываем blog в yiico. Изменяем "Home" в breadcrumbs. Включаем Gzip-сжатие. (09.08.2013)
- 4 Курс по Yii с нуля. Урок 4. Дорабатываем простую работу с пользователями: хранение пароля при редактировании пользователя. (13.04.2013)
- 3 Курс по Yii с нуля. Урок 3. Создаём новых пользователей. Организуем простую систему авторизации. Аутентификация, пароли, соли. (20.07.2014)
- 2 Yii + Git (github) на Mac. (24.03.2013)
- 2 Курс по Yii с нуля. Урок 2. Переносим и настраиваем Yii и проект нашего сайта на рабочем сервере. Избавляемся от index.php в url (09.07.2014)
- 1 Курс по Yii с нуля. Урок 1. Устанавливаем Yii на локальном компьютере. Заводим проект будущего сайта. (10.03.2013)
- 0 Composer – пакетный менеджер PHP. Что и как? (22.07.2014)
- 0 MySQL: проверить содержится ли значение в поле столбца (в столбце хранится строка значений через запятую) (22.05.2014)
- 0 Yii: Статичные страницы (создание, редактирование, удаление) (28.04.2014)
- 0 Yii: Расширение ECKEditor = Связка ckeditor + kcfinder (визуальный редактор с бесплатным файловым менеджером) (28.04.2014)
- 0 Yii: Bootstrap tabs, делаем активной вкладку на которую выполняется переход по ссылке (20.03.2014)
- 0 Yii: Доступ к атрибуту модели из файла шаблона (Вызов в шаблоне какого-либо атрибута модели). (17.02.2014)
- 0 Yii: Множественный автокомплит с помощью CJuiAutoComplete (автокомплит нескольких значений в одно поле) (07.02.2014)
- 0 Yii: Фотогалерея через поведение (доработка расширения imagesgallerymanager) (08.03.2016)
- 0 Yii: Установка и настройка Yii-app заготовки приложения от Crisu83 (09.03.2014)
- 0 Twitter Bootstrap Carousel Crossfade (09.09.2013)
12 комментариев
Шерлок счастливчик, или несчастный человек?
и вот...
public function data_img($id, $width='100',
$class='data_img')
{
Не подскажите почему может выполнятся только условие else выводит картинку нет фото, и никак не высвечивает id_data.jpg
if(file_exists($_SERVER['DOCUMENT_ROOT'].
Yii::app()->urlManager->baseUrl.
'/data_img/'.$id.'_data.jpg'))
return CHtml::image( '/data_img/'.$id.'_data.jpg',
array(
'width'=>$width,
'class'=>$class,
));
else
return CHtml::image('/data_img/no_photo.gif','No photo',
array(
'width'=>$width,
'class'=>$class
));
}
У меня в процессе использования тоже было так, добавил проверку на установку поля image:
if(isset($model->image) && file_exists($_SERVER['DOCUMENT_ROOT'].....
Алексей, вы описали интересную проблему!
Она обсуждалась на форуме Yiiframework - http://www.yiiframework.com/forum/index.php/topic/33889-file-upload-field-becomes-empty/page__view__findpost__p__163258 (ответ Maurizio Domba прольёт свет на проблему. Возможны 2 распространённых решения: