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

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


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

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Both sides previous revision Предыдущая версия
Следущая версия
Предыдущая версия
Следущая версия Both sides next revision
eaze:samples:добавление_фотогалереи_в_vt_к_объекту [2011/09/20 19:27]
sergeyfast
eaze:samples:добавление_фотогалереи_в_vt_к_объекту [2011/09/21 09:49]
sergeyfast
Строка 61: Строка 61:
 <​code>​ <​code>​
 {increal:​tmpl://​vt/​entities/​photos.tmpl.php} {increal:​tmpl://​vt/​entities/​photos.tmpl.php}
 +</​code>​
 +
 +На этом работа с файлом закончена.
 +
 +===== photos.tmpl.php =====
 +Данный шаблон будет работать с двумя переменными из Response: ''​$data''​ и ''​$albumErrors''​. ​
 +
 +В ''​$data''​ содержится подготовленный массив с альбомами и фотографиями,​ который мы будем отдавать в качестве источника данных для JQuery Templates. Можно было бы конечно сделать сразу ''​json_encode( $object->​albums )'',​ но так мы получим избыток данных. В подготовленной переменной такого нет. Во время создания шаблона в ''​var data = ..''​ лежал заранее известный массив,​ который потом стал основой для php-массива (с помощью заранее готового массива можно проще отлаживать шаблон).
 +
 +В ''​$albumErrors''​ находится массив с ошибками валидации альбомов и фотографий. Его нельзя положить в обычный массив ''​$errors'',​ т.к. у него своя сложная структура.
 +
 +<code php>
 +<? /** @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>​
 +</​code>​
 +
 +Создадим шаблон ''​albumTemplate'',​ который будет инициализироваться строчкой (entity-photos.js)
 +<code javascript>​$("#​albumTemplate"​).tmpl(data,​ { counter: albumCounter,​ photoCounter:​ photoCounter } ).appendTo("​.albums"​);</​code>​
 +  * ''​data''​ - наш источник данных.
 +  * ''​albumCounter''​ - объект,​ с помощью которого мы будем держать текущий индекс альбома (''​photoCounter''​ - аналогично с фото). Данный объект передается в tmpl() в качестве параметра для рендеринга (доступен в $item в шаблоне).
 +<code javascript>​
 +    /**
 +     * 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();
 +</​code>​
 +
 +Опишем шаблон для создания строчки с альбомом.
 +<code html>
 +<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>​
 +</​code>​
 +
 +  * ''​${ $item.counter.nextIndex() }''​ - пробелы между {} нужны для того, чтобы шаблонизатор в Eaze не подумал,​ что это переменная ''​{$item}''​.
 +  * ''</​script><​script><​nowiki>''</​nowiki></​script>''​ - [[http://​youtrack.jetbrains.net/​issue/​IDEA-73686|workaround]] для IDE.
 +  * ''​${id}''​ - получение свойства id из элемента массива data.
 +  * ''<​nowiki>​{{each(i,​ photo) photos}}</​nowiki>''​ - цикл в шаблоне для отображения фотографий. ''​photos''​ - свойство из элемента массива data.
 +  * ''<​nowiki>​{{tmpl( photo , { counter: $item.photoCounter,​ albumIndex: $item.counter.index }) "#​photoTemplate"​}}</​nowiki>''​ - вызов шаблона photoTemplate,​ передача дополнительных параметров для рендеринга.
 +  * ''​{$prefix}''​ - стандартная переменная для data.tmpl.php (префикс объекта).
 +
 +Шаблон для отображения конкретной фотографии
 +<code html>
 +<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>​
 +</​code>​
 +  * ''​${num}''​ - это дополнительное свойство (индекс массива),​ который используется для поиска элемента при отображении ошибок (''​$item.counter.nextIndex()''​ имеет сквозную нумерацию,​ а ''​${num}''​ начинается с 0 для каждого альбома).
 +
 +На этом работа с photos.tmpl.php завершена.
 +
 +===== entity-photos.js =====
 +//​Данный файл не претендует на идеальность :)//
 +
 +Начинается данный файл с описания счетчиков для альбомов и фотографий (см. код выше).
 +
 +После этого опишем поведение кнопок добавления и удаления альбомов и фотографий
 +<code javascript>​
 +    /**
 +     * Album Controls
 +     */
 +    $(function() {
 +        $("#​add-album"​).live('​click',​ function(e) {
 +            $("#​albumTemplate"​).tmpl(null,​ { counter: albumCounter } ).appendTo("​.albums"​);​
 +            rebindAlbumControls();​
 +            e.preventDefault();​
 +        });
 +
 +        $("​.add-photo"​).live('​click',​ function(e) {
 +            var id = $(this).data("​albumId"​);​
 +            $("#​photoTemplate"​).tmpl( null, { counter: photoCounter,​ albumIndex: id } ).appendTo("#​album-"​ + id + " .objects"​);​
 +            vfsSelector.Init();​
 +            e.preventDefault();​
 +        });
 +
 +        $("​.delete-photo"​).live('​click',​ function(e) {
 +            $(this).parent().parent().parent().parent().remove();​
 +            e.preventDefault();​
 +        });
 +
 +        $("​.delete-album"​).live('​click',​ function(e) {
 +            var id = $(this).data("​albumId"​);​
 +            $("#​album-"​ + id).remove();​
 +            e.preventDefault();​
 +        });
 +    });
 +</​code>​
 +
 +''​rebindAlbumControls()''​ необходимо для переинициализации vfs и сортировок
 +<code javascript>​
 +    function rebindAlbumControls() {
 +        vfsSelector.Init();​
 +
 +        $('​.objects'​).sortable({
 +            '​items':​ '​tr.sort'​
 +            , '​forceHelperSize':​ true
 +            , '​forcePlaceholderSize':​ true
 +            , '​handle':​ '​td:​first-child'​
 +        });
 +
 +        $('​.albums'​).sortable({
 +            '​items':​ '​.album'​
 +            , '​forceHelperSize':​ true
 +            , '​forcePlaceholderSize':​ true
 +            , '​handle':​ '​.handle'​
 +        });
 +    }
 +</​code>​
 +
 +При загрузки страницы запустим рендеринг
 +<code javascript>​
 +    $(function() {
 +        $("#​albumTemplate"​).tmpl(data,​ { counter: albumCounter,​ photoCounter:​ photoCounter } ).appendTo("​.albums"​);​
 +        rebindAlbumControls();​
 +        displayAlbumErrors();​
 +    });
 +</​code>​
 +
 +И отобразим ошибки
 +<code javascript>​
 +    function displayAlbumErrors() {
 +        if ( typeof( albumErrors ) == '​undefined'​ ) {
 +            return;
 +        }
 +
 +        $.each( albumErrors,​ function( albumIndex, album ) {
 +            $.each( album, function( albumField, albumData ) {
 +                if ( albumField == '​photos'​ ) {
 +                    $.each( albumData, function(photoIndex,​ photoField ){
 +                        $.each( photoField, function( fieldName, fieldError ) {
 +                            $( '​[rel='​ + fieldName + '​-'​ + albumIndex + '​-'​ + photoIndex + '​]'​ ).parent().append( ​ ' <span style="​color:​red;">​*</​span>'​ );
 +                        });
 +                    });
 +                } else {
 +                    $("​input[name='​entity[albums]["​ + albumIndex + "​]["​+ albumField + "​]'​]"​).parent().append( ' <span style="​color:​red;">​*</​span>'​ );
 +                }
 +            });
 +        });
 +    }
 +</​code>​
 +
 +Со клиентской частью мы закончили.
 +Данного кода достаточно для управления альбомами без серверной части, только они не будут сохранятся и валидироваться,​ но в ''​$object''​ данные попадут,​ т.к. мы используем полные пути к свойствам объекта (например ''​{$prefix}[albums][${ $item.albumIndex }][photos][${ $item.counter.index }][smallImageId]''​ ) - GetFromRequest соберет объект так, как нужно.
 +
 +Попробуем модифицировать SaveEntityAction таким образом,​ чтобы он сохранял альбомы в базу.
 +
 +===== SaveEntityAction.php =====
 +Прежде всего мы должны помнить о том, что albums у объекта Entity - лист. ''​EntityFactory::​GetById( $id, array( BaseFactory::​WithLists => true ) )''​ выберет только альбомы,​ без фотографий. Фотографии будем выбрать вручную.
 +Создадим метод ''​refillAlbumPhotos()'',​ который добавляет к альбомам фотографии
 +<code php>
 +protected function refillAlbumPhotos( $object ) {
 +    $photos = EntityPhotoFactory::​Get( array( '​entityId'​ => $object->​entityId ) );
 +    $photos = ArrayHelper::​Collapse( $photos, '​entityAlbumId'​ );
 +
 +    if ( !empty( $photos ) ){
 +        foreach( $object->​albums as $album ) {
 +            if ( isset( $photos[$album->​entityAlbumId] ) ) {
 +                $album->​photos = $photos[$album->​entityAlbumId];​
 +            }
 +        }
 +    }
 +}
 </​code>​ </​code>​
eaze/samples/добавление_фотогалереи_в_vt_к_объекту.txt · Последние изменения: 2011/09/21 11:30 — sergeyfast