Видеогалерея на MIGX. Может кому понадобится (решено)
*Помогите толком разобраться, как редактировать поля title, desc, image, duration, которые находятся внутри json массива у видео в виде отдельных полей, а не кривыми ручками
Видеогалерея на MIGX
Видеогалерея на MIGX
- Устанавливаем VideoGallery
- Создаем tv с именем video тип ввода VideoGallery
- Создаем tv с именем videogallery. Тип ввода migx, конфигурация videogallery, выбираем шаблон для отображения
- Создаем сниппет VideoJsonToPlaceholders
! ВНИМАНИЕ в п.2 удалите пробел между & quot;
<?php // Получаем параметры $json = $modx->getOption('json', $scriptProperties, ''); $prefix = $modx->getOption('prefix', $scriptProperties, 'json.'); // 1. Проверяем наличие данных if (empty($json)) { return 'Ошибка: JSON‑строка пуста'; } // 2. Удаляем все & quot; из строки ! Внимание в следующей строке удалите пробел между & quot; $json = str_replace('& quot;', '', $json); // 3. Исправляем экранированные слеши $json = str_replace('\\/', '/', $json); // 4. Парсим JSON $data = json_decode($json, true); if (json_last_error() !== JSON_ERROR_NONE) { return 'Ошибка JSON: ' . json_last_error_msg() . ' (строка: ' . htmlspecialchars($json) . ')'; } // 5. Проверяем, что результат — массив if (!is_array($data)) { return 'Ошибка: JSON не содержит массив'; } // 6. Функция для извлечения корневого адреса (с защитой от повторного объявления) if (!function_exists('getRootUrl')) { function getRootUrl($url) { if (!filter_var($url, FILTER_VALIDATE_URL)) { return null; } $parsed = parse_url($url); if (!$parsed || !isset($parsed['scheme']) || !isset($parsed['host'])) { return null; } $root = $parsed['scheme'] . '://' . $parsed['host']; if (isset($parsed['port'])) { $isDefaultHttp = ($parsed['scheme'] === 'http' && $parsed['port'] === 80); $isDefaultHttps = ($parsed['scheme'] === 'https' && $parsed['port'] === 443); if (!$isDefaultHttp && !$isDefaultHttps) { $root .= ':' . $parsed['port']; } } return $root; } } // 7. Устанавливаем плейсхолдеры с обработкой foreach ($data as $key => $value) { // Приводим строки к UTF‑8 if (is_string($value)) { $value = mb_convert_encoding($value, 'UTF-8', 'UTF-8'); } // Если это поле video и содержит URL — извлекаем корневой адрес if ($key === 'video' && !empty($value)) { $rootUrl = getRootUrl($value); if ($rootUrl) { $modx->setPlaceholder($prefix . 'video_root', $rootUrl); } // Сохраняем исходный URL $modx->setPlaceholder($prefix . 'video', $value); } // Преобразуем videoDuration из PT... в оптимальный формат if ($key === 'videoDuration') { if (empty($value) || !is_string($value)) { $value = '0 с'; } else { preg_match('/PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/', $value, $matches); $hours = isset($matches[1]) ? (int)$matches[1] : 0; $minutes = isset($matches[2]) ? (int)$matches[2] : 0; $seconds = isset($matches[3]) ? (int)$matches[3] : 0; if ($hours > 0) { $value = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds); } elseif ($minutes > 0) { $value = sprintf('%02d:%02d', $minutes, $seconds); } else { $value = $seconds . ' с'; } } } // Подставляем значения по умолчанию для критических полей switch ($key) { case 'title': if (empty($value)) { $value = 'Без названия'; } break; case 'desc': if (empty($value)) { $value = 'Описание отсутствует'; } break; } // Устанавливаем плейсхолдер (если он ещё не установлен через парсинг) if (!in_array($key, ['video', 'video_root'])) { $modx->setPlaceholder($prefix . $key, $value); } } return ''; - Создаем новое MIGx поле videogallery
- Экспортируем туда конфиг
{ "formtabs": [ { "caption": "Видеогалерея", "print_before_tabs": "0", "fields": [ { "field": "video", "caption": "Видео", "inputTV": "video" } ], "pos": 1 } ], "contextmenus": "", "actionbuttons": "", "columnbuttons": "", "filters": "", "extended": { "actionbuttonsperrow": 4, "gridload_mode": 1, "has_jointable": "yes" }, "permissions": {}, "fieldpermissions": "", "columns": [ { "header": "Рендер", "dataIndex": "render", "width": 1, "sortable": "false", "show_in_grid": 0, "renderer": "this.renderChunk", "renderchunktpl": "[[!VideoJsonToPlaceholders? &json=`[[+video]]` &prefix=`video.`]]" }, { "header": "Название", "dataIndex": "video_title", "sortable": "false", "show_in_grid": 1, "renderer": "this.renderChunk", "renderchunktpl": "[[+video.title]]" }, { "header": "Видео", "dataIndex": "video_video", "sortable": "false", "show_in_grid": 1, "renderer": "this.renderChunk", "renderchunktpl": "<iframe src=\"[[+video.video]]\" width=\"160\" height=\"90\" frameborder=\"0\"></iframe>" }, { "header": "Превью", "dataIndex": "video_image", "sortable": "false", "show_in_grid": 1, "renderer": "this.renderChunk", "renderchunktpl": "<img src=\"[[+video.image]]\" width=\"160\">" }, { "header": "Время", "dataIndex": "video_duration", "width": 100, "sortable": "false", "show_in_grid": 1, "renderer": "this.renderChunk", "renderchunktpl": "[[+video.videoDuration]]" }, { "header": "videoId", "dataIndex": "video_videoId", "sortable": "false", "show_in_grid": "0", "renderer": "this.renderChunk", "renderchunktpl": "[[+video.videoId]]" }, { "header": "Описание", "dataIndex": "video_description", "sortable": "false", "show_in_grid": 1, "renderer": "this.renderChunk", "renderchunktpl": "[[+video.desc]]" } ], "category": "" }
Комментарии: 3
Для вывода данной migx videogallery в виде табов Источник1, Источник2 и тд
Чанк videogalleryVideosTabs.tpl
Чанк videogalleryVideos.tpl
Вызов на странице
Чанк videogalleryVideosTabs.tpl
[[!VideoJsonToPlaceholders? &json=`[[+video]]` &prefix=`video[[+idx]].`]]
<li class="nav-item" role="videocase">
<button class="nav-link [[+idx:isequal=`1`:then=`active`:else=``]]" id="videogal[[+idx]]-tab" data-bs-toggle="tab" data-bs-target="#videogal[[+idx]]" type="button" role="tab" aria-controls="videogal[[+idx]]" aria-selected="true">Источник [[+idx]]</button>
</li>`Чанк videogalleryVideos.tpl
<div class="tab-pane fade [[+idx:isequal=`1`:then=`show active`:else=``]]" id="videogal[[+idx]]" role="tabpanel" aria-labelledby="videogal[[+idx]]-tab">
<div class="pb-3">
<div class="embed-responsive embed-responsive-16by9" style="width: 100%;">
<iframe class="embed-responsive-item"
src="[[+video[[+idx]].video]]" title="[[+video[[+idx]].title]]"
frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true" name="main"
style="width:100%;height:100%;background-image:url([[+video[[+idx]].image]]);background-size: cover; background-repeat: no-repeat;background-position: center center;">
</iframe>
</div>
</div>
<div class="pb-3">видео в источнике <ignore><a href="[[+video[[+idx]].video]]" target="_blank">[[+video[[+idx]].video]]</a></ignore></div>
</div>Вызов на странице
<ul class="nav nav-tabs" id="videoTab" role="tablist">
[[!getImageList?
&tvname=`videogallery`
&tpl=`videogalleryVideosTabs.tpl`
]]
</ul>
<div class="tab-content" id="videoTabContent">
{'!getImageList' | snippet : [
'tvname' => 'videogallery',
'tpl' => 'videogalleryVideos.tpl']}
</div>
А вот все то же самое, только на fenom сразу вызов на странице без дополнительных чанков
{set $rows = $.php.array_reverse(json_decode($_modx->resource.videogallery, true))}
{if $rows && is_array($rows)}
<!-- Табы (заголовки) -->
<ul class="nav nav-tabs" id="videoTab" role="tablist">
{foreach $rows as $idx => $row}
<li class="nav-item" role="presentation">
<button class="nav-link {if $idx == 0}active{/if}" id="videogal-tab-{$idx}" data-bs-toggle="tab" data-bs-target="#videogal{$idx}" type="button" role="tab" aria-controls="videogal{$idx}" aria-selected="{if $idx == 0}true{else}false{/if}">
Источник {$idx + 1}
</button>
</li>
{/foreach}
</ul>
<!-- Контент табов -->
<div class="tab-content" id="videoTabContent">
{foreach $rows as $idx => $row}
{* Декодируем JSON из поля video *}
{set $videoData = json_decode(trim($row.video, '"') | replace:'\"':'"', true)}
{if $videoData}
<div class="tab-pane fade {if $idx == 0}show active{/if}" id="videogal{$idx}" role="tabpanel" aria-labelledby="videogal-tab-{$idx}">
<!-- Видео -->
<div class="pb-3">
<div class="embed-responsive embed-responsive-16by9" style="width: 100%;">
<iframe class="embed-responsive-item" src="{$videoData.video}" title="{$videoData.title | default:'Видео'}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true" name="main" style="width:100%; height:100%; background-image:url({$videoData.image | default:''}); background-size: cover; background-repeat: no-repeat; background-position: center center;"></iframe>
</div>
</div>
<!-- Ссылка на источник -->
<div class="pb-3">
Видео в источнике: <ignore><a href="{$videoData.video}" target="_blank">{$videoData.video}</a></ignore>
</div>
<!-- Описание (если есть) -->
{if isset($videoData.desc) && $videoData.desc}
<div class="pb-3">{$videoData.desc | nl2br}</div>
{/if}
</div>
{else}
<!-- Резервный блок при ошибке декодирования -->
<div class="tab-pane fade {if $idx == 0}show active{/if}" id="videogal{$idx}" role="tabpanel" aria-labelledby="videogal-tab-{$idx}">
<p class="text-danger">Не удалось загрузить видео (ошибка JSON).</p>
</div>
{/if}
{/foreach}
</div>
{else}
<p>Нет данных для отображения.</p>
{/if}
Переносим все видео ранее созданные на сайте с tv-полями video =1 и video2 = 26 в новое tv-поле videogallery = 38
-- Шаг 1: увеличиваем лимит для текущей сессии
SET SESSION group_concat_max_len = 1000000;
-- Шаг 2: выполняем основной запрос
INSERT INTO modx_site_tmplvar_contentvalues (tmplvarid, contentid, value)
SELECT
38 AS tmplvarid,
src.contentid,
CONCAT(
'[',
GROUP_CONCAT(
CONCAT(
'{',
'"MIGX_id":"', src.migx_num, '",',
'"video":"',
REPLACE(
REPLACE(
REPLACE(src.value, '\\', '\\\\'),
'"', '\\"'
),
'\n', '\\n'
),
'"}'
)
ORDER BY src.migx_num
SEPARATOR ','
),
']'
) AS value
FROM (
SELECT t1.contentid, t1.value, 1 AS migx_num
FROM modx_site_tmplvar_contentvalues t1
WHERE tmplvarid = 1
UNION ALL
SELECT t26.contentid, t26.value, 2 AS migx_num
FROM modx_site_tmplvar_contentvalues t26
WHERE tmplvarid = 26
) AS src
GROUP BY src.contentid
ON DUPLICATE KEY UPDATE
value = VALUES(value);
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.