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

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


    Предпосылки


    Далее в статье речь пойдет про фреймворк 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.

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое