Инструменты пользователя

Инструменты сайта


eaze:samples:добавление_фотогалереи_в_vt_к_объекту

Это старая версия документа.


Добавление фотогалереи в VT у объекта

Задача

В системе существует объект Entity. Необходимо добавить к нему возможность управлять фотографиями, которые группируются в альбомы.

  • У альбома есть название и описание (необязательное поле).
  • У фотографии есть маленькая и большая картинка, из необязательных полей - название.
  • Должна быть возможность сортировки фотографий и альбомов.
  • Количество фотографий у одного объекта будет не больше 30.
  • Поддержка начальной массовой загрузки фотографий.
  • В случае случайного удаления должна быть ручная возможность восстановления данных.

Исходя из этих требований попробуем спроектировать соответствующие таблицы.

База данных

По базе данных все достаточно просто, единственное, что мы добавили - это поле createdAt timestamp default now() на всякий случай.

smallImageId и bigImageId - ссылки на стандартную таблицу vfsFiles.

Т.к. объектов будет не очень много, то все управление поместится в одну дополнительную вкладку.

Интерфейс

Вот что в итоге у нас должно получиться (вид с отображением ошибок).

У альбома есть название и описание (необязательное поле).
У фотографии есть маленькая и большая картинка, из необязательных полей - название.
В случае случайного удаления должна быть ручная возможность восстановления данных.

В базе необходимые поля присутствуют.

Должна быть возможность сортировки фотографий и альбомов.

Порядок элементов меняется путем перетаскивания строчек.

Поддержка начальной массовой загрузки фотографий.

Сначала все файлы загружаем в VFS, а потом выбираем в фотогалерею.

Все первоначальные требования выполнены. Приступим к реализации задачи.

Реализация

Для начала нам нужно создать объекты в MFD, после добавить в data.tmpl.php новый таб «Фотогалерея» и вынести его в другой шаблон photos.tmpl.php. Для реализации пользовательского интерфейса мы будем использовать JQuery Templates, поэтому не забудем добавить его в vt/elements/header.tmpl.php. Далее в photos.tmpl.php создадим шаблоны для jquery templates, весь javascript вынесем в отдельный файл в js://vt/entity-photos.js и подключим его в photos.tmpl.php. В entity-photos.js будет располагаться основной код управления интерфейсом. После перейдем к серверной части и допишем SaveEntityAction.php.

MFD

  1. Добавим таблицы entityPhotos и entityAlbums в MFD с флагом WithoutTemplate (CanPages ставить не нужно).
  2. У объекта Entity пропишем лист albums (EntityAlbum).
  3. У объекте EntityAlbum добавим лист photos (EntityPhoto).
  4. Сохраним объекты.

После того, как мы добавили листы к объектам, у нас будет рекурсивно работать метод IFactory::GetFromRequest(). Это нам пригодится в получении объектов с формы.

data.tmpl.php

В шаблоне data.tmpl.php добавим новый таб «Фотографии».

<ul class="tabs-list">
    <li><a href="#page-0">{lang:vt.common.commonInfo}</a></li>
    <li><a href="#page-1">Фотографии</a></li>
</ul>

И подключим его после <div id="page-0" class="tab-page rows">...</div>

{increal:tmpl://vt/entities/photos.tmpl.php}

На этом работа с файлом закончена.

photos.tmpl.php

Данный шаблон будет работать с двумя переменными из Response: $data и $albumErrors.

В $data содержится подготовленный массив с альбомами и фотографиями, который мы будем отдавать в качестве источника данных для JQuery Templates. Можно было бы конечно сделать сразу json_encode( $object→albums ), но так мы получим избыток данных. В подготовленной переменной такого нет. Во время создания шаблона в var data = .. лежал заранее известный массив, который потом стал основой для php-массива (с помощью заранее готового массива можно проще отлаживать шаблон).

В $albumErrors находится массив с ошибками валидации альбомов и фотографий. Его нельзя положить в обычный массив $errors, т.к. у него своя сложная структура.

<? /** @var array $data  */ ?>
<? /** @var array $albumErrors */ ?>
<? JsHelper::PushFile( 'js://vt/entity-photos.js' ); ?>
<div id="page-1" class="tab-page rows albums">
    <div data-row="photos" style="display: none;"></div>
    <div class="row">
        <ul class="actions">
            <li class="edit"><a href="#" id="add-album">Добавить альбом</a></li>
        </ul>
    </div>
</div>
 
<script type="text/javascript">
    var data = <?= ObjectHelper::ToJSON( $data ); ?>;
    <? if ( !empty( $albumErrors ) ) { ?>
    var albumErrors = <?= ObjectHelper::ToJSON( $albumErrors )?>
    <? } ?>
</script>

Создадим шаблон albumTemplate, который будет инициализироваться строчкой (entity-photos.js)

$("#albumTemplate").tmpl(data, { counter: albumCounter, photoCounter: photoCounter } ).appendTo(".albums");
  • data - наш источник данных.
  • albumCounter - объект, с помощью которого мы будем держать текущий индекс альбома (photoCounter - аналогично с фото). Данный объект передается в tmpl() в качестве параметра для рендеринга (доступен в $item в шаблоне).
    /**
     * Counters for album index
     */
    var Counter = function() {
        this.index = -1;
    }
 
    Counter.prototype.nextIndex = function() {
        this.index ++;
        return this.index;
    }
 
    var albumCounter = new Counter();
    var photoCounter = new Counter();

Опишем шаблон для создания строчки с альбомом.

<script id="albumTemplate" type="text/x-jquery-tmpl">
    <div class="row album sort" id="album-${ $item.counter.nextIndex() }">
        <input type="hidden" name="{$prefix}[albums][${ $item.counter.index }][entityAlbumId]" value="${id}" />
        <br />
        <table class="objects" style="width:auto;">
            <tbody>
            <tr>
                <td class="handle">Альбом</td>
                <td class="left"><input type="text" name="{$prefix}[albums][${ $item.counter.index }][title]" value="${title}" /></td>
                <td colspan="2" class="left"><input type="text" name="{$prefix}[albums][${ $item.counter.index }][description]" value="${description}" size="60" style="width: auto;" /></td>
                <td width="10%">
                    <ul class="actions">
                        <li class="edit"><a href="#" class="add-photo" data-album-id="${ $item.counter.index }">Добавить</a></li>
                        <li class="delete"><a href="#" class="delete-album" title="Удалить" data-album-id="${ $item.counter.index }">Удалить</a></li>
                    </ul>
                </td>
            </tr>
            {{each(i, photo) photos}}
                {{tmpl( photo , { counter: $item.photoCounter, albumIndex: $item.counter.index }) "#photoTemplate"}}
            {{/each}}
            </tbody>
        </table>
    </div>
</script><script>''</script>
  • ${ $item.counter.nextIndex() } - пробелы между {} нужны для того, чтобы шаблонизатор в Eaze не подумал, что это переменная {$item}.
  • </script><script>''</script> - workaround для IDE.
  • ${id} - получение свойства id из элемента массива data.
  • {{each(i, photo) photos}} - цикл в шаблоне для отображения фотографий. photos - свойство из элемента массива data.
  • {{tmpl( photo , { counter: $item.photoCounter, albumIndex: $item.counter.index }) "#photoTemplate"}} - вызов шаблона photoTemplate, передача дополнительных параметров для рендеринга.

Шаблон для отображения конкретной фотографии

<script id="photoTemplate" type="text/x-jquery-tmpl">
    <tr class="sort">
        <td><input type="hidden" name="{$prefix}[albums][${ $item.albumIndex }][photos][${ $item.counter.nextIndex() }][entityPhotoId]" value="${id}" /></td>
        <td><input type="text" name="{$prefix}[albums][${ $item.albumIndex }][photos][${ $item.counter.index }][title]" value="${title}" class="title-${ $item.albumIndex }-${num}" size="60" style="width: auto;" /></td>
        <td class="left"><input type="hidden" class="vfsFile" name="{$prefix}[albums][${ $item.albumIndex }][photos][${ $item.counter.index }][smallImageId]" rel="smallImageId-${ $item.albumIndex }-${num}" id="photo-small-${ $item.counter.index }" vfs:previewType="image"  {{if smallImage}}value="${ smallImage.id}" vfs:src="{web:vfs://}${ smallImage.src}" vfs:name="${ smallImage.name}"{{/if}}/></td>
        <td class="left"><input type="hidden" class="vfsFile" name="{$prefix}[albums][${ $item.albumIndex }][photos][${ $item.counter.index }][bigImageId]" rel="bigImageId-${ $item.albumIndex }-${num}" id="photo-big-${ $item.counter.index }" vfs:previewType="image" {{if bigImage}}value="${ bigImage.id}" vfs:src="{web:vfs://}${ bigImage.src}" vfs:name="${ bigImage.name}"{{/if}}/></td>
        <td width="10%">
            <ul class="actions">
                <li class="delete"><a class="delete-photo" href="#" title="Удалить">Удалить</a></li>
            </ul>
        </td>
    </tr>
</script><script>''</script>
  • ${num} - это дополнительное свойство (индекс массива), который используется для поиска элемента при отображении ошибок ($item.counter.nextIndex() имеет сквозную нумерацию, а ${num} начинается с 0 для каждого альбома).
eaze/samples/добавление_фотогалереи_в_vt_к_объекту.1316582490.txt.gz · Последние изменения: 2011/09/21 09:21 — sergeyfast