Как стать автором
Обновить

Выбор изображений просто и эффективно

Время на прочтение8 мин
Количество просмотров5.7K
Добрый день. Рассмотрим следующую функциональность приложения: добавление изображения к тексту. Изображения не встраиваются в сам текст (как, например, в википедии), а существуют отдельно. Текст может иметь одно или несколько изображений. Процедуру привязки хотелось сделать максимально дружелюбной по отношению к пользователю.
Выбор пал на представление данных с помощью двух областей: слева находятся все картинки, доступные для выбора, в правой — выбранные. Пользователь может выбирать изображения как с помощью мышки, так и с помощью дополнительных кнопок.
Пример работы плагина


Предпосылки


Далее в статье речь пойдет про фреймворк CakePHP. Я предполагаю, что читатель с ним знаком и такие слова как «контроллер», «поведение», «представление»(view) его не пугают. Знакомство с MeioUpload будет плюсом.

Полный исходный код плагина находится на GitHub'е. Здесь же я расскажу только об основных моментах и использовании.

Почему плагин?


Плагины в CakePHP позволяют легко добавлять функциональность к уже существующему коду. Плагин содержит в своих недрах все необходимое для своей работы: контроллеры, модели данных, правила CSS, файлы с функциями на javascript'e и т.д. Приложение может использовать плагин через наследование. Мой плагин состоит из контроллера ImageSelects, поведения ImageUpload, представления preview и дополнительных файлов CSS, javascript.

Реализацию плагина можно разбить на несколько подзадач:
  • сделать поддержку загружаемых файлов;
  • сделать панели для вывода изображений, обеспечить минимальную функциональность по выбору изображений.

Отдельным пунктом идет использование плагина. Идея в том, что контроллер, связанный с таблицей в базе данных, наследуется от плагина. Затем этот контроллер используется в представлении другого контроллера через AJAX.

Загрузка файлов


Для загрузки файлов я модифицировал поведение MeioUpload. Мое поведение ImageUpload унаследованно от MeioUpload и вносит в него некоторые изменения. MeioUpload хранит все загруженные файлы в одной папке. Я ввел параметр maxFiles, который определяет максимальное кол–во файлов в одной директории. Структура папок проста: <base_dir>/<i>, где base_dir это базовая директория, а i порядковый номер. Нумерация начинается с 1. В каждой папке с номером создается дополнительная папка для хранения миниатюр.

В отличии от MeioUpload, который использует оригинальное название файла, я генерирую уникальное имя файла
$new_filename = md5(uniqid(rand(), true));


Поскольку мое поведение наследуется от MeioUpload, то все настройки, применимые к MeioUpload, можно использовать и с ImageUpload. С помощью них можно задать максимальный размер загружаемого файла, допустимые типы файлов, размеры миниатюр и т.д.

В базу данных записывается информация о файле, а так же относительный путь к файлу, например, «uploads/1». В будущем планирую записывать в базу относительный путь до миниатюры изображения, который может выглядеть так «uploads/1/thumb320».

Выбор изображений


Для выбора изображений используется контроллер ImageSelects. Он состоит из одного метода — preview. В качестве параметров в этот метод можно передать массив идентификаторов выбранных изображений. Это понадобится, когда пользователь захочет изменить уже существующий набор привязанных картинок.

  1. function preview()
  2. {
  3.     $this->model_instance->recursive = 0;
  4.  
  5.     // get the id of selected items:
  6.     if (!empty($this->params['form']['selected']))
  7.     {
  8.         $selected = $this->params['form']['selected'];
  9.         $selected_ids = array('id' => $selected);
  10.         $conditions = array(«NOT» => $selected_ids);
  11.         $this->set('allselected', $this->model_instance->find('all', array('conditions' => $selected_ids)));
  12.     }
  13.     else
  14.     {
  15.         $conditions = array();
  16.         $this->set('allselected', array());
  17.     }
  18.     $data = $this->paginate($conditions);
  19.     $this->set('allphotos', $data);
  20.  
  21.     $this->set('modelClass', $this->modelClass);
  22.  
  23.     // Point that we are using plugin and should use plugin's .ctp for rendering
  24.     $this->plugin = 'image_select';
  25.     $this->render(false, null, '/image_selects/preview');
  26. }
  27.  

Функция возвращает два массива allphotos и allselected, а так же немного подправляет механизм вывода. Отображать страницу нужно через представление плагина.

Функциональность самого выбора реализована с помощью jQuery. Все доступные картинки находятся в контейнере с идентификатором leftimgs. Каждая картинка находится внутри контейнера, у которого есть атрибут selected. Допустимые значение атрибута: строка selected и пустая строка. С помощью jQuery можно легко найти все отмеченные изображения:
$("#leftimgs > div[selected='selected']")


Уже выбранные изображения находятся в контейнере с идентификатором rightimgs. Работать с ними можно аналогично картинкам из контейнера leftimgs.

Код отмечающий изображение по одиночному щелчку мыши:
  1. $(document).ready(function() 
  2. {
  3.     $("#leftimgs > div[selected]").click( function(event) { toggle_img(this); });
  4. }
  5.  
  6. function toggle_img(div)
  7. {
  8.     var isselected = div.getAttribute('selected') == 'selected';
  9.     if (isselected)
  10.     {
  11.         div.style.backgroundColor = '';
  12.         div.childNodes[4].childNodes[0].checked = false;
  13.         div.setAttribute('selected', '');
  14.     }
  15.     else
  16.     {
  17.         div.style.backgroundColor = '#3961af';
  18.         div.childNodes[4].childNodes[0].checked = true;
  19.         div.setAttribute('selected', 'selected');
  20.     }
  21. }
  22.  

Как видно из кода, я устанавливая цвет фона в ручную. Правильным будет изменение CSS класса объекта.

Использование


Для использования всего этого нам нужно создать таблицу в базе данных и все необходимое для неё: контроллер, модель данных и представления. Путь таблица называется photos, соответствующий контроллер — PhotosController, модель — Photo. Нам так же понадобится еще одна таблица и все, что с ней связанно. Пусть ее название будет cities, контроллер CitiesController и т.д. как это принято в CakePHP.

Исходный код помещаем в папку «app/plugins/image_select».

Использование поведения


Поведение используется в модели данных контроллера PhotosController (файл photo.php):
  1. <?php
  2. class Photo extends AppModel {
  3. var $name = 'Photo';
  4.  
  5.     var $actsAs = array(
  6.         'ImageSelect.ImageSelect' => array(
  7.             'filename' => array(
  8.                 'dir' => 'uploads',
  9.                 'create_directory' => true,
  10.                 'generateName' => true,
  11.                 'maxFiles' => 5,
  12.                 'useTable' => true,
  13.                 'thumbsizes' => array(
  14.                     'my320' => array('width' => 150, 'height' => 150),
  15.                 ),
  16.             )
  17.         )
  18.     );
  19. }
  20. ?>
  21.  

На странице добавления фотографии пишем:
<?php 
echo $form->create('Photo', array('type' => 'file'));
...
echo $form->input('Photo.filename', array('type' => 'file'));
?>
 

Вот и все. Загрузка картинок готова!

Использование контроллера


Созданный контроллер PhotosController наследует ImageSelectsController:
App::import('Controller', 'ImageSelect.ImageSelects');
class PhotosController extends ImageSelectsController 
{
}

Таким образом, PhotosController получает доступ к методу preview из ImageSelectsController.

Привязка выбора картинок к другому контроллеру


Вспоминаем, что мы еще создали CitiesControlles. На странице добавления нового города (app/views/cities/add.ctp) мы хотим добавить выбор картинок. В файле add.ctp нужно подключить CSS и Javascript от плагина:
<?php
echo $javascript->link('/image_select/js/image_upload.js', false); // load js in header
echo $html->css('/image_select/css/image_select.css', false);
?>
 

Панели и кнопки будут храниться в контейнере с идентификатором imageList:
<div id=«imageList»></div>

В этот контейнер с помощью AJAX загружаются данные из представление preview контроллера ImageSelectsController. За загрузку отвечает функция loadPiece, которая находится в файле «app/plugins/image_select/vendors/js/image_upload.js». Вот она:
  1. function loadPiece(href, divName, data) 
  2. {     
  3.     $(divName).load(href, data, function()
  4.     { 
  5.         var divPaginationLinks = divName+" #pagination a"; 
  6.         $(divPaginationLinks).click(function() 
  7.         {      
  8.             var thisHref = $(this).attr(«href»); 
  9.             loadPiece(thisHref, divName, data); 
  10.             return false; 
  11.         }); 
  12.     }); 
  13. }


Вернемся к файлу app/views/cities/add.ctp. Нам нужно добавить к нему еще вызов функции loadPiece:
<script type=«text/javascript»>
    var selected = new Array();
    //selected[0] = 2; // possible id for the selected images
    //selected[1] = 38;
    var data = new Object();
    data.selected = selected;
    var reload_url = "<?php echo $html->url(array('controller'=>'photos', 'action'=>'preview'));?>";
        $(document).ready(function() 
            {
                loadPiece(reload_url, "#imageList", data);
            }); 
</script>
 


На этом установка заканчивается. Все готово! На плечах разработчика останется только получить идентификаторы выбранных изображений и сохранить их в базе данных. Получить список выбранных изображений можно с помощью jQuery.
Теги:
Хабы:
Всего голосов 10: ↑4 и ↓6-2
Комментарии0

Публикации

Истории

Ближайшие события