Добрый день. Рассмотрим следующую функциональность приложения: добавление изображения к тексту. Изображения не встраиваются в сам текст (как, например, в википедии), а существуют отдельно. Текст может иметь одно или несколько изображений. Процедуру привязки хотелось сделать максимально дружелюбной по отношению к пользователю.
Выбор пал на представление данных с помощью двух областей: слева находятся все картинки, доступные для выбора, в правой — выбранные. Пользователь может выбирать изображения как с помощью мышки, так и с помощью дополнительных кнопок.
Далее в статье речь пойдет про фреймворк 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, который использует оригинальное название файла, я генерирую уникальное имя файла
Поскольку мое поведение наследуется от MeioUpload, то все настройки, применимые к MeioUpload, можно использовать и с ImageUpload. С помощью них можно задать максимальный размер загружаемого файла, допустимые типы файлов, размеры миниатюр и т.д.
В базу данных записывается информация о файле, а так же относительный путь к файлу, например, «uploads/1». В будущем планирую записывать в базу относительный путь до миниатюры изображения, который может выглядеть так «uploads/1/thumb320».
Для выбора изображений используется контроллер ImageSelects. Он состоит из одного метода — preview. В качестве параметров в этот метод можно передать массив идентификаторов выбранных изображений. Это понадобится, когда пользователь захочет изменить уже существующий набор привязанных картинок.
Функция возвращает два массива allphotos и allselected, а так же немного подправляет механизм вывода. Отображать страницу нужно через представление плагина.
Функциональность самого выбора реализована с помощью jQuery. Все доступные картинки находятся в контейнере с идентификатором leftimgs. Каждая картинка находится внутри контейнера, у которого есть атрибут selected. Допустимые значение атрибута: строка selected и пустая строка. С помощью jQuery можно легко найти все отмеченные изображения:
Уже выбранные изображения находятся в контейнере с идентификатором rightimgs. Работать с ними можно аналогично картинкам из контейнера leftimgs.
Код отмечающий изображение по одиночному щелчку мыши:
Как видно из кода, я устанавливая цвет фона в ручную. Правильным будет изменение CSS класса объекта.
Для использования всего этого нам нужно создать таблицу в базе данных и все необходимое для неё: контроллер, модель данных и представления. Путь таблица называется photos, соответствующий контроллер — PhotosController, модель — Photo. Нам так же понадобится еще одна таблица и все, что с ней связанно. Пусть ее название будет cities, контроллер CitiesController и т.д. как это принято в CakePHP.
Исходный код помещаем в папку «app/plugins/image_select».
Поведение используется в модели данных контроллера PhotosController (файл photo.php):
На странице добавления фотографии пишем:
Вот и все. Загрузка картинок готова!
Созданный контроллер PhotosController наследует ImageSelectsController:
Таким образом, PhotosController получает доступ к методу preview из ImageSelectsController.
Вспоминаем, что мы еще создали CitiesControlles. На странице добавления нового города (app/views/cities/add.ctp) мы хотим добавить выбор картинок. В файле add.ctp нужно подключить CSS и Javascript от плагина:
Панели и кнопки будут храниться в контейнере с идентификатором imageList:
В этот контейнер с помощью AJAX загружаются данные из представление preview контроллера ImageSelectsController. За загрузку отвечает функция loadPiece, которая находится в файле «app/plugins/image_select/vendors/js/image_upload.js». Вот она:
Вернемся к файлу app/views/cities/add.ctp. Нам нужно добавить к нему еще вызов функции loadPiece:
На этом установка заканчивается. Все готово! На плечах разработчика останется только получить идентификаторы выбранных изображений и сохранить их в базе данных. Получить список выбранных изображений можно с помощью jQuery.
Выбор пал на представление данных с помощью двух областей: слева находятся все картинки, доступные для выбора, в правой — выбранные. Пользователь может выбирать изображения как с помощью мышки, так и с помощью дополнительных кнопок.
Предпосылки
Далее в статье речь пойдет про фреймворк 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. В качестве параметров в этот метод можно передать массив идентификаторов выбранных изображений. Это понадобится, когда пользователь захочет изменить уже существующий набор привязанных картинок.
- function preview()
- {
- $this->model_instance->recursive = 0;
- // get the id of selected items:
- if (!empty($this->params['form']['selected']))
- {
- $selected = $this->params['form']['selected'];
- $selected_ids = array('id' => $selected);
- $conditions = array(«NOT» => $selected_ids);
- $this->set('allselected', $this->model_instance->find('all', array('conditions' => $selected_ids)));
- }
- else
- {
- $conditions = array();
- $this->set('allselected', array());
- }
- $data = $this->paginate($conditions);
- $this->set('allphotos', $data);
- $this->set('modelClass', $this->modelClass);
- // Point that we are using plugin and should use plugin's .ctp for rendering
- $this->plugin = 'image_select';
- $this->render(false, null, '/image_selects/preview');
- }
Функция возвращает два массива allphotos и allselected, а так же немного подправляет механизм вывода. Отображать страницу нужно через представление плагина.
Функциональность самого выбора реализована с помощью jQuery. Все доступные картинки находятся в контейнере с идентификатором leftimgs. Каждая картинка находится внутри контейнера, у которого есть атрибут selected. Допустимые значение атрибута: строка selected и пустая строка. С помощью jQuery можно легко найти все отмеченные изображения:
$("#leftimgs > div[selected='selected']")
Уже выбранные изображения находятся в контейнере с идентификатором rightimgs. Работать с ними можно аналогично картинкам из контейнера leftimgs.
Код отмечающий изображение по одиночному щелчку мыши:
- $(document).ready(function()
- {
- $("#leftimgs > div[selected]").click( function(event) { toggle_img(this); });
- }
- function toggle_img(div)
- {
- var isselected = div.getAttribute('selected') == 'selected';
- if (isselected)
- {
- div.style.backgroundColor = '';
- div.childNodes[4].childNodes[0].checked = false;
- div.setAttribute('selected', '');
- }
- else
- {
- div.style.backgroundColor = '#3961af';
- div.childNodes[4].childNodes[0].checked = true;
- div.setAttribute('selected', 'selected');
- }
- }
Как видно из кода, я устанавливая цвет фона в ручную. Правильным будет изменение CSS класса объекта.
Использование
Для использования всего этого нам нужно создать таблицу в базе данных и все необходимое для неё: контроллер, модель данных и представления. Путь таблица называется photos, соответствующий контроллер — PhotosController, модель — Photo. Нам так же понадобится еще одна таблица и все, что с ней связанно. Пусть ее название будет cities, контроллер CitiesController и т.д. как это принято в CakePHP.
Исходный код помещаем в папку «app/plugins/image_select».
Использование поведения
Поведение используется в модели данных контроллера PhotosController (файл photo.php):
- <?php
- class Photo extends AppModel {
- var $name = 'Photo';
- var $actsAs = array(
- 'ImageSelect.ImageSelect' => array(
- 'filename' => array(
- 'dir' => 'uploads',
- 'create_directory' => true,
- 'generateName' => true,
- 'maxFiles' => 5,
- 'useTable' => true,
- 'thumbsizes' => array(
- 'my320' => array('width' => 150, 'height' => 150),
- ),
- )
- )
- );
- }
- ?>
На странице добавления фотографии пишем:
<?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». Вот она:
- function loadPiece(href, divName, data)
- {
- $(divName).load(href, data, function()
- {
- var divPaginationLinks = divName+" #pagination a";
- $(divPaginationLinks).click(function()
- {
- var thisHref = $(this).attr(«href»);
- loadPiece(thisHref, divName, data);
- return false;
- });
- });
- }
Вернемся к файлу 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.