Здесь показаны различия между двумя версиями данной страницы.
Both sides previous revision Предыдущая версия Следущая версия | Предыдущая версия | ||
eaze:samples:добавление_фотогалереи_в_vt_к_объекту [2011/09/21 09:32] sergeyfast |
eaze:samples:добавление_фотогалереи_в_vt_к_объекту [2011/09/21 11:30] (текущий) sergeyfast |
||
---|---|---|---|
Строка 147: | Строка 147: | ||
* ''<nowiki>{{each(i, photo) photos}}</nowiki>'' - цикл в шаблоне для отображения фотографий. ''photos'' - свойство из элемента массива data. | * ''<nowiki>{{each(i, photo) photos}}</nowiki>'' - цикл в шаблоне для отображения фотографий. ''photos'' - свойство из элемента массива data. | ||
* ''<nowiki>{{tmpl( photo , { counter: $item.photoCounter, albumIndex: $item.counter.index }) "#photoTemplate"}}</nowiki>'' - вызов шаблона photoTemplate, передача дополнительных параметров для рендеринга. | * ''<nowiki>{{tmpl( photo , { counter: $item.photoCounter, albumIndex: $item.counter.index }) "#photoTemplate"}}</nowiki>'' - вызов шаблона photoTemplate, передача дополнительных параметров для рендеринга. | ||
+ | * ''{$prefix}'' - стандартная переменная для data.tmpl.php (префикс объекта). | ||
Шаблон для отображения конкретной фотографии | Шаблон для отображения конкретной фотографии | ||
Строка 257: | Строка 258: | ||
} | } | ||
</code> | </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> | ||
+ | * дополнительно в EntityPhotoFactory мы добавили поиск по entityId. | ||
+ | |||
+ | Далее сделаем так, чтобы фотографии грузились только тогда, когда мы открыли объект для редактирования. | ||
+ | <code php> | ||
+ | /** | ||
+ | * Set Json Albums Data to Template | ||
+ | * @return void | ||
+ | */ | ||
+ | protected function beforeSave() { | ||
+ | if ( $this->action != self::UpdateAction && !empty( $this->currentObject->entityId ) ) { | ||
+ | $this->refillAlbumPhotos( $this->currentObject ); | ||
+ | } | ||
+ | |||
+ | Response::setParameter( 'data', EntityAlbumUtility::PrepareAlbumsData( $this->currentObject ) ); | ||
+ | } | ||
+ | </code> | ||
+ | * EntityAlbumUtility::PrepareAlbumsData() - метод, который подготавливает переменную ''data'' для шаблона photos.tmpl.php. | ||
+ | |||
+ | Теперь, если у объекта есть альбомы и фотографии, то мы уже увидим их в шаблоне :). | ||
+ | |||
+ | Добавим валидацию альбомов и фотографий. | ||
+ | <code php> | ||
+ | protected function validate( $object ) { | ||
+ | $errors = parent::$factory->Validate( $object ); | ||
+ | |||
+ | $albumErrors = EntityAlbumUtility::ValidateAlbums( $object->albums ); | ||
+ | if ( !empty( $albumErrors ) ) { | ||
+ | $errors['fields']['photos']['format'] = 'format'; | ||
+ | Response::setParameter( 'albumErrors', $albumErrors ); | ||
+ | } | ||
+ | |||
+ | return $errors; | ||
+ | } | ||
+ | </code> | ||
+ | * fields => photos поможет нам подсветить таб "Фотографии", если на нем были ошибки (''<div data-row="**photos**"...'' в photos.tmpl.php) и не дать сохранить ошибочный объект в базу. | ||
+ | |||
+ | Теперь можно перейти к сохранению. Для этого сначала нужно заполнить $originalObject фотографиями (в оригинальном объекте они нужны для того, чтобы удалить те фотографии, которые мы удалили с формы). | ||
+ | <code php> | ||
+ | protected function beforeAction() { | ||
+ | if ( !empty( $this->originalObject ) ) { | ||
+ | $this->refillAlbumPhotos( $this->originalObject ); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | add() и update() будут обернуты в транзакцию. | ||
+ | <code php> | ||
+ | /** | ||
+ | * Add Object | ||
+ | * | ||
+ | * @param Entity $object | ||
+ | * @return bool | ||
+ | */ | ||
+ | protected function add( $object ) { | ||
+ | ConnectionFactory::BeginTransaction(); | ||
+ | |||
+ | $result = parent::$factory->Add( $object, array( BaseFactory::WithReturningKeys => true ) ); | ||
+ | $result = $result && EntityAlbumUtility::SaveAlbums( $object, $this->originalObject ); | ||
+ | |||
+ | ConnectionFactory::CommitTransaction( $result ); | ||
+ | |||
+ | return $result; | ||
+ | } | ||
+ | |||
+ | |||
+ | /** | ||
+ | * Update Object | ||
+ | * | ||
+ | * @param Entity $object | ||
+ | * @return bool | ||
+ | */ | ||
+ | protected function update( $object ) { | ||
+ | ConnectionFactory::BeginTransaction(); | ||
+ | |||
+ | $result = parent::$factory->Update( $object ); | ||
+ | $result = $result && EntityAlbumUtility::SaveAlbums( $object, $this->originalObject ); | ||
+ | |||
+ | ConnectionFactory::CommitTransaction( $result ); | ||
+ | | ||
+ | return $result; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Последний штрих. При сохранении фоток мы не получаем их идентификаторы, из-за этого неправильно работает кнопка "Применить" в режиме редактирование. Исправляется это путем переполучения фотографий только при успешном сохранении. | ||
+ | <code php> | ||
+ | protected function afterAction( $success ) { | ||
+ | if ( $this->redirect == 'view' && $success ) { | ||
+ | $this->refillAlbumPhotos( $this->currentObject ); | ||
+ | Response::setParameter( 'data', EntityAlbumUtility::PrepareAlbumsData( $this->currentObject ) ); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Не забудьте посмотреть код [[eaze:samples:добавление_фотогалереи_в_vt_к_объекту_EntityAlbumUtility.php|EntityAlbumUtility.php]]. | ||
+ | |||
+ | |||
+ | ====== Итог ====== | ||
+ | Поставленной цели мы добились. Можно было бы конечно сделать массовый загрузчик на flash и потом и мышкой раскидать фотографии по альбомам, но для этого нужно написать ещё больше javascript'а. | ||
+ | |||
+ | Осталось рассмотреть плюсы и минусы подхода. | ||
+ | |||
+ | ===== Плюсы ===== | ||
+ | * Не нужно дополнительно обрабатывать получение связанных объектов из формы на PHP (albums, photos). | ||
+ | * Не нужно дублировать шаблон отображения album и photo сначала в PHP, потом на JS. Всего используется один шаблон. | ||
+ | * Минимальное количество кода в SaveEntityAction (в основном - только получение листов второго и последующих уровней). | ||
+ | |||
+ | ===== Минусы ===== | ||
+ | * GetFromRequest на втором уровне получает каждый объект через GetById (соответственно сколько файлов - столько запросов при сохранении). //можно исправить, но сложновато// | ||
+ | * Если сохранить страницу при выключенном JS - то все фотографии удалятся (потому что не пришли с формы). //можно исправить через дополнительную переменную, которая выставляется через JS// | ||
+ | * При сохранении страницы для не измененных данных каждый раз выполняется UPDATE. //можно исправить путем добавления проверки на эквивалентность объектов// | ||
+ | |||
+ | //Ещё раз повторюсь, что данный пример не претендует на идеальность и универсальность. Он пытается рассказать, как работать в VT со сложными объектами. Пожелания и дополнения приветствуются. ;)// | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ |