Добавляем TV тип ввода GalleryItemList для компонента Gallery

Компонент Gallery стал стандартом де-факто для большинства разработчиков сайтов на MODx Revolution в вопросе управления фото галереями. В целом Gallery весьма хорошо справляется с этой задачей, но и его есть куда улучшать.

Сегодня показываем еще одно улучшение, а именно создадим новый тип ввода для TV параметров, позволяющий выбрать отдельное изображение из конкретной галереи.

Картинка для затравки:

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

Основные принципы:

  • Создаем в компоненте Gallery 2 галереи: Ручки, Броненакладки. Заполняем их необходимыми нам изображениями.
  • Создаем 2 TV поля, привязываем их у нужному нам шаблону.
  • Нам необходим новый тип ввода, позволяющий выбрать из конкретной галереи, конкретное изображение. В базе данных мы будем хранить идентификатор этого изображения (id).

Добавляем GalleryItemList TV

Все действия производим в каталоге 

/core/components/gallery/elements/tv/

1. Создаем файл galleryitemlist.input.tpl:

<input id="tv{$tv->id}" type="text" />

{literal}
<script type="text/javascript">
// <![CDATA[
Ext.onReady(function() {
    var galStore{/literal}{$tv->id}{literal} = new Ext.data.ArrayStore({
        fields: ['id','name', 'cover'],
        data : {/literal}{$list}{literal}
    });
    var galTpl{/literal}{$tv->id}{literal} = new Ext.XTemplate(
        '<tpl for="."><div class="search-item" style="padding: 4px">'
        ,'<tpl if="cover"><div style="float: right;"><img src="{cover}" alt="" /></div></tpl>'
        ,'{name}'
		,'<br /><span style="font-size: small; font-style: italic">{description}</span>'
        ,'<div style="clear: both;"></div></div></tpl>'
    );

    var fld = MODx.load({{/literal}
        xtype: 'combo'
        ,store: galStore{$tv->id}
        ,displayField: 'name'
        ,valueField: 'id'
        ,name: 'tv{$tv->id}'
        ,hiddenName: 'tv{$tv->id}'
        ,mode: 'local'
        ,triggerAction: 'all'
        ,applyTo: 'tv{$tv->id}'
        ,value: '{$tv->value}'
        ,tpl: galTpl{$tv->id}
        ,itemSelector: 'div.search-item'
        ,width: {if $params.width}{$params.width}{else}400{/if}
        ,allowBlank: {if $params.allowBlank == 1 || $params.allowBlank == 'true'}true{else}false{/if}
        {if $params.listWidth},listWidth: {$params.listWidth}{/if}
        {if $params.typeAhead}
            ,typeAhead: true
            ,typeAheadDelay: {if $params.typeAheadDelay && $params.typeAheadDelay != ''}{$params.typeAheadDelay}{else}250{/if}
        {else}
            ,editable: false
            ,typeAhead: false
        {/if}
        {if $params.listEmptyText}
            ,listEmptyText: '{$params.listEmptyText}'
        {/if}
        ,forceSelection: {if $params.forceSelection && $params.forceSelection != 'false'}true{else}false{/if}
        ,msgTarget: 'under'
        ,listeners: { 'select': { fn:MODx.fireResourceFormChange, scope:this}}
        
    {literal}});

    var pr = Ext.getCmp('modx-panel-resource');
    if (pr) {
        pr.getForm().add(fld);
    }
    MODx.makeDroppable(fld);
});
// ]]>
</script>
{/literal}

2. Создаем файл galleryitemlist.inputproperties.tpl:

<div id="tv-input-properties-form{$tv}"></div>
{literal}

<script type="text/javascript">
// <![CDATA[
var params = {
{/literal}{foreach from=$params key=k item=v name='p'}
 '{$k}': '{$v|escape:"javascript"}'{if NOT $smarty.foreach.p.last},{/if}
{/foreach}{literal}
};
var oc = {'change':{fn:function(){Ext.getCmp('modx-panel-tv').markDirty();},scope:this}};
MODx.load({
    xtype: 'panel'
    ,layout: 'form'
    ,autoHeight: true
    ,labelWidth: 150
    ,border: false
    ,items: [{
        xtype: 'textfield'
        ,fieldLabel: 'Альбом'
        ,description: '{/literal}{$gl.parent_desc}{literal}'
        ,name: 'inopt_album'
        ,id: 'inopt_album{/literal}{$tv}{literal}'
        ,value: params['album'] || ''
        ,width: 300
        ,listeners: oc
    },{
        xtype: 'combo-boolean'
        ,fieldLabel: _('required')
        ,description: _('required_desc')
        ,name: 'inopt_allowBlank'
        ,hiddenName: 'inopt_allowBlank'
        ,id: 'inopt_allowBlank{/literal}{$tv}{literal}'
        ,value: params['allowBlank'] == 0 || params['allowBlank'] == 'false' ? false : true
        ,width: 300
        ,listeners: oc
    },{
        xtype: 'combo'
        ,store: [['rank','{/literal}{$gl.rank}{literal}'],['name','{/literal}{$gl.name}{literal}']]
        ,fieldLabel: '{/literal}{$gl.sort}{literal}'
        ,description: '{/literal}{$gl.sort_desc}{literal}'
        ,name: 'inopt_sort'
        ,hiddenName: 'inopt_sort'
        ,forceSelection: true
        ,typeAhead: false
        ,editable: false
        ,triggerAction: 'all'
        ,id: 'inopt_sort{/literal}{$tv}{literal}'
        ,value: params['sort'] || 'rank'
        ,width: 300
        ,listeners: oc
    },{
        xtype: 'combo'
        ,store: [['ASC','{/literal}{$gl.ascending}{literal}'],['DESC','{/literal}{$gl.descending}{literal}']]
        ,fieldLabel: '{/literal}{$gl.sortdir}{literal}'
        ,description: '{/literal}{$gl.sortdir_desc}{literal}'
        ,name: 'inopt_dir'
        ,hiddenName: 'inopt_dir'
        ,forceSelection: true
        ,typeAhead: false
        ,editable: false
        ,triggerAction: 'all'
        ,id: 'inopt_dir{/literal}{$tv}{literal}'
        ,value: params['dir'] || 'DESC'
        ,width: 300
        ,listeners: oc
    },{
        xtype: 'textfield'
        ,fieldLabel: '{/literal}{$gl.limit}{literal}'
        ,description: '{/literal}{$gl.limit_desc}{literal}'
        ,name: 'inopt_limit'
        ,hiddenName: 'inopt_limit'
        ,id: 'inopt_limit{/literal}{$tv}{literal}'
        ,value: params['limit'] || 0
        ,width: 300
        ,listeners: oc
    },{
        xtype: 'textfield'
        ,fieldLabel: '{/literal}{$gl.start}{literal}'
        ,description: '{/literal}{$gl.start_desc}{literal}'
        ,name: 'inopt_start'
        ,hiddenName: 'inopt_start'
        ,id: 'inopt_start{/literal}{$tv}{literal}'
        ,value: params['start'] || 0
        ,width: 300
        ,listeners: oc
    }]
    ,renderTo: 'tv-input-properties-form{/literal}{$tv}{literal}'
});
// ]]>
</script>
{/literal}

3. Создаем файл input/galleryitemlist.php:

<?php

$modx->lexicon->load('tv_widget','gallery:default');
$modx->smarty->assign('base_url',$this->xpdo->getOption('base_url'));

$corePath = $modx->getOption('gallery.core_path',null,$modx->getOption('core_path').'components/gallery/');
$modx->addPackage('gallery',$corePath.'model/');

if (empty($params)) $params = array();

/* setup default properties */
$sort = $modx->getOption('sort',$params,'rank');
$dir = $modx->getOption('dir',$params,'ASC');
$limit = intval($modx->getOption('limit',$params,0));
$start = intval($modx->getOption('start',$params,0));
$album = intval($modx->getOption('album',$params,''));

/* get albums */
$c = $modx->newQuery('galAlbumItem');
$c->setClassAlias('AlbumItem');
$c->innerJoin('galItem','Item', '');
$c->select($modx->getSelectColumns('galAlbumItem','AlbumItem', '', array('album', 'rank')));
$c->select($modx->getSelectColumns('galItem','Item', '', array('id','name', 'filename', 'active')));

if ($album != '') {
    $c->where(array(
        'AlbumItem.album' => $album,
    ));
}
$c->where(array(
    'Item.active' => 1,
));
$c->sortby($sort,$dir);
if ($limit > 0) {
    $c->limit($limit,$start);
}

#$c->prepare();
#echo $c->toSql();
#die;
#$items = $modx->getCollection('galAlbumItem',$c);
$items = array();
$list = array();
if ($c->prepare() && $c->stmt->execute()) {
  $items = $c->stmt->fetchAll(PDO::FETCH_ASSOC);
}

$list[] = array('', '(Нет)', '');
for($i = 0; $i < count($items); $i++){
  $row = array();
  $row[] = $items[$i]['id'];
  $row[] = $items[$i]['name'];
  $row[] = '/assets/gallery/.thumb/'.$items[$i]['filename'];
  $list[] = $row;
}


$modx->smarty->assign('list',$modx->toJSON($list));

return $modx->smarty->fetch($corePath.'elements/tv/galleryitemlist.input.tpl');

4. Создаем файл inputoptions/galleryitemlist.php

<?php
$modx->lexicon->load('tv_widget','gallery:tvprops');
$modx->smarty->assign('base_url',$modx->getOption('base_url'));

$corePath = $modx->getOption('gallery.core_path',null,$modx->getOption('core_path').'components/gallery/');
$modx->addPackage('gallery',$corePath.'model/');

/* get TV input properties specific language strings */
$lang = $modx->lexicon->fetch('galtv.',true);
$modx->smarty->assign('gl',$lang);

return $modx->smarty->fetch($corePath.'elements/tv/galleryitemlist.inputproperties.tpl');

Обратите внимание, что 2 последних файла создаются в каталогах.

Внимание! В этой статье используется прием генерации превью изображения при загрузке, описанный в этой статье. Без нее у вас не будут показываться изображения! В этом случае вам надо изменить строку #53 в файле input/galleryitemlist.php, указав другой путь к картинке, так, как это делается в Gallery по умолчанию, через connector.php. Для примера смотрите файл /core/components/gallery/model/gallery/galitem.class.php

Результат

Если вы все сделали правильно, то теперь при создании нового TV поля в админке, в поле Тип ввода (вкладка Параметры ввода) появился новый доступный тип galleryitemlist:

Обратите внимание, на выделенное желтым. Это дополнительные параметры/настройки для нашего TV поля. В нем обязательно необходимо указать Id Альбома в компоненте Gallery, из которого мы будем брать фотографии.

Теперь сохраняем TV, и при редактировании ресурса можно увидеть картину, аналогичную первому изображению в этой статье!

Как вывести изображение из TV на сайте?

Мы используем небольшие сниппеты для этой цели. Полный код я приводить не буду, лишь небольшой, но в тоже время понятный напросок:

  // получаем фото, myTV - значение, хранящееся в TV поле ресурса
  $galItem = $modx->getObject('galItem', $myTV);
  // если такое изображение существует
  if($galItem != null){
    // нам доступны все поля в нем
    $id = $res->get('id');
    $name = $galItem->get('name');
    $image = $galItem->get('absoluteImage');
    // ... остальные поля можно посмотреть в таблице  modx_gallery_items
  }

Если есть вопросы и замечания - оставляйте в комментариях.