Как сделать превью картинки в YII

В этой статье я опишу как сделать ресайз картинки используя фреймворк YII. Здесь мы сделаем несколько копий картинок разного размера и поместим их в соответствующие папки. Инструкция предлагается для общей задачи создания превью в YII и естественно каждый специалист может подстроить данное решение под свои нужды.
Прошлая инструкция по загрузке файлов на хостинг с помощью YII не описывает той проблемы, когда исходное изображение имеет бОльшее расширение, чем нам необходимо. Там у нас ограничение на вес файла — 5 Mb, но если изображение имеет расширение 2000px x 1200px мы его все равно выводили в браузер шириной 150 px.
Для данной задачи улучшим наш алгоритм загрузки картинок в YII следующими опциями :
- при сохранении изображения, оно дополнительно будет сохраняться в трех различных вариантах — ширина 50px, ширина 200px, и ширина 800px;
- если папка, куда сохраняем файл на сервере отсутствует, то мы её создаем с необходимыми правами;
- перепишем функцию вывода картинки — будет картинка-превью со ссылкой на подробную новость, либо большой картинкой в новости (не ссылка!!)
- в административную панель добавим колонку картинок (маленьких, которые уменьшатся до 50px), в списке, выводимом с помощью CGridView в action admin контроллера Post (Post.admin)
Ресайз изображений будем делать с помощью расширения CImageHandler. Для его установки в системе нужно скачать файл CImageHandler.php в папку /protected/components и в файл конфигураций /protected/config/main.php добавляем следующую строку подключения этого расширения:
1 2 3 4 | // application components 'components'=>array( 'ih'=>array('class'=>'CImageHandler'), 'user'=>array(... |
В контроллере Post изменим методы actionCreate и actionUpdate примерно следующим образом:
actionCreate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public function actionCreate(){ $model=new Post; // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); if(isset($_POST['Post'])){//Если пользователь отослал данные $model->attributes=$_POST['Post']; //Заполнить модель данными присланными пользователем $model->icon=CUploadedFile::getInstance($model,'icon'); //Атрибуту icon присвоить указатель на загружаемый файл if ($model->icon){ $sourcePath = pathinfo($model->icon->getName()); $fileName = substr($model->time_start, 5, 5).'-'.$model->alias.'.'.$sourcePath['extension']; $model->image = $fileName; } if($model->save()){ //Если поле загрузки файла не было пустым, то if ($model->icon){ //сохранить файл на сервере в каталог images/2013 под именем //month-day-alias.jpg $file = $_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/'.$fileName; //Переменной $file присвоить путь, куда сохранится картинка без изменений $model->icon->saveAs($file); //Используем функции расширения CImageHandler; $ih = new CImageHandler(); //Инициализация Yii::app()->ih ->load($file) //Загрузка оригинала картинки ->thumb('200', false) //Создание превьюшки шириной 200px ->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/thumbs/'.$fileName) //Сохранение превьюшки в папку thumbs ->reload() //Снова загрузка оригинала картинки ->thumb('50', '50') //Создание превьюшки размером 50px ->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/thumbs_small/'.$fileName) //Сохранение превьюшки в папку thumbs_small ->reload()//Снова загрузка оригинала картинки ->thumb('800', '800') //Создание превьюшки размером 800px ->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/'.$fileName) //Сохранение большой картинки в папку ; } $this->redirect(array('view','id'=>$model->id)); } } $this->render('create',array( 'model'=>$model, )); } |
actionUpdate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | public function actionUpdate(){ $model=$this->loadModel(); if(isset($_POST['Post'])){ $model->attributes=$_POST['Post']; $model->icon=CUploadedFile::getInstance($model,'icon'); if ($model->icon){ $sourcePath = pathinfo($model->icon->getName()); $fileName = substr($model->time_start, 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->time_start, 0, 4).'/'.$model->image)){ //удаляем картинку и все её thumbs @unlink('./images/'.substr($model->time_start, 0, 4).'/'.$model->image); @unlink('./images/'.substr($model->time_start, 0, 4).'/thumbs/'.$model->image); @unlink('./images/'.substr($model->time_start, 0, 4).'/thumbs_small/'.$model->image); $model->image = ''; } } //Если поле загрузки файла не было пустым, то if ($model->icon){ $file = './images/'.mb_substr($model->time_start, 0, 4, 'UTF-8').'/'.$fileName; $model->icon->saveAs($file); //сохранить файл на сервере под именем 12-03-alias.jpg Если файл с таким именем существует, он будет заменен. //Как в actionCreate - используем функции расширения CImageHandler; $ih = new CImageHandler(); //Инициализация Yii::app()->ih ->load($file) //Загрузка оригинала картинки ->thumb('200', false) //Создание превьюшки шириной 200px ->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/thumbs/'.$fileName) //Сохранение превьюшки в папку thumbs ->reload() //Снова загрузка оригинала картинки ->thumb('50', '50') //Создание превьюшки размером 50px ->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/thumbs_small/'.$fileName) //Сохранение превьюшки в папку thumbs_small ->reload()//Снова загрузка оригинала картинки ->thumb('800', '800') //Создание превьюшки размером 800px ->save($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.substr($model->time_start, 0, 4).'/'.$fileName) //Сохранение большой картинки в папку ; } $this->redirect(array('view','id'=>$model->id)); } } $this->render('update',array( 'model'=>$model, )); } |
Что мы здесь делаем: изображение загружается на сервер, так как мы делали это в прошлый раз. Далее изображение уменьшается до 200px и сохраняется в папке thumbs. Далее оригинал опять уменьшается до 50px и сохраняется в папке thumbs_small. В конце оригинал уменьшается до 800px и сохраняется сам в себя. Чтобы не изобретать велосипед, делаем ресайз картинок с помощью расширения YII: CImageHandler. Скачайте файл CImageHandler.php в папку /protected/components. В файл /protected/config/main.php добавляем параметр «ih»:
1 2 3 4 | // application components 'components'=>array( 'ih'=>array('class'=>'CImageHandler'), 'user'=>array(... |
Логика у нас готова, теперь сделаем отображение для admin и перепишем функцию показа изображения. В файле /protected/views/post/admin.php в CGridView вставляем колонку с картинками (здесь буду выводиться маленькие превью размером 50px).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <?php $this->breadcrumbs=array( 'Manage Posts', ); ?> <h1>Manage Posts</h1> <?php $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'posts-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'columns'=>array( array( 'name'=>'id', 'headerHtmlOptions'=>array('width'=>'50px') ), array( 'name'=>'title', 'type'=>'raw', 'value'=>'CHtml::link(CHtml::encode($data->title), $data->url)' ), array( 'name'=>'image', 'type'=>'image', //если файла картинки нет, // то отображается файл noeventimage.png // Значение value обрабатывается функцией eval() поэтому // одинарные ковычки. 'value'=> 'file_exists($_SERVER["DOCUMENT_ROOT"].Yii::app()->urlManager->baseUrl."/images/".substr($data->time_start, 0, 4)."/thumbs_small/".$data->image) ? Yii::app()->urlManager->baseUrl."/images/".substr($data->time_start, 0, 4)."/thumbs_small/".$data->image : Yii::app()->urlManager->baseUrl."/css/noeventimage.png"', 'filter'=>'', 'headerHtmlOptions'=>array('width'=>'54px'), ), ... array( 'name'=>'status', 'value'=>'Lookup::item("PostStatus",$data->status)', 'filter'=>Lookup::items('PostStatus'), ), array( 'name'=>'create_time', //'type'=>'datetime', 'filter'=>false, ), array( 'class'=>'CButtonColumn', ), ), )); ?> |
Наша новая картинка имеет тип «image» В поле value должен приходить url картинки. При использовании типа image, нет возможности передавать значение alt, width или другие атрибуты тэгу «image». Тем более в админке это и не нужно.
В общедоступной части сайта тег <img> нужен тэг обёрнутый ссылкой на странице списка, и не обёрнутый ссылкой в подробном описании. Изменяем функцию event_image(). Находится она в файле контроллера /protected/controllers/PostController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public function event_image($id, $create_year, $alias, $title, $image, $class='material_img',$link=null,$target='_self'){ if(isset($image) && file_exists($_SERVER['DOCUMENT_ROOT'].Yii::app()->urlManager->baseUrl.'/images/'.$create_year.'/'.$image)){ if ($link) return CHTML::link( CHtml::image(Yii::app()->getBaseUrl(true).'/images/'.$create_year.'/thumbs/'.$image, $title, array( 'class'=>$class, ) ), $link, array('target'=>$target,) ); else return CHtml::image(Yii::app()->getBaseUrl(true).'/images/'.$create_year.'/'.$image, $title, array( 'class'=>$class, )); } else return CHtml::image(Yii::app()->urlManager->baseUrl.'/css/noeventimage.png','Нет картинки', array( 'class'=>$class )); } |
Эта функция будет возвращать полную картинку 800 px, или картинку, обёрнутую ссылкой. Или noeventimage.jpg, если нет изображения. Используется эта функция в отображении /protected/views/post/_view.php (выводит картинку-ссылку)
<div> <p> <?php echo $this->event_image($data->id, substr($data->time_start, 0, 4), $data->alias, $data->title, $data->image, 'small_img left', $data->url); ?> </p> </div> |
И в отображении /protected/views/post/_view_full.php (выводит большую картинку)
<div> <?php echo $this->event_image($data->id, mb_substr($data->time_start, 0, 4, 'UTF-8'), $data->alias, $data->title, $data->image); ?> </div> |
Функционал для YII, реализующий загрузку изображения на сервер и создание из него уменьшенных копий готов.