Оптимизация phpThumbOf
Наверняка много кто использовал на своих сайтах пакет phpThumbOf — отличная штука, чтобы сделать превьюшки. Простой пример в использовании:
[[phpthumbof?input=[[+source]]&options=w=350&h=350&zc=1]]
На лету создаст превьюшку 350 на 350.
Но с этим пакетом не все так просто.
Проблема первая. Чтение кеша
Важно понимать, что это не самостоятельный пакет. Он использует имеющийся в MODX класс phpthumb. А тот в свою очередь уже был ранее замечен, как источник проблем с производительностью. Дело в том, что phpthumb перед созданием кеша файла, подсчитывает общий объем кеша, и это процедура совсем не слабая, если файлов довольно много. Чтобы этого избежать, идем в настройки MODX-а и устанавливаем значение 0 для следующих настроек: phpthumb_cache_maxage, phpthumb_cache_maxfiles и phpthumb_cache_maxsize. Тогда phpthumb не будет подсчитывать объем использованного кеша.
Проблема вторая. Низкая производительность. Сейчас делаю каталог недвижимости, и там на одну страницу выводится 30 карточек. Так вот, на генерацию 30-ти превьюшек уходит 3-4 секунды. Конечно в дальнейшем эти превьюшки уже будут, и повторной генерации не будет, но от этого все равно не легче, так как на сайте более 4000 активных объектов, и будет очень много тормознутых страниц. Да и сниппету каждый раз проверять наличие файла превьюшки — тоже лишняя работа. В общем, эту проблему я решил исправить радикально — просто заранее сгенерировать превьюшки и сохранить их для документа в отдельное TV-поле. Итак, все по порядку:
- Создаем TV-шку для превьюхи. С этим все просто и понятно. Единственное — если на сайте будут работать менеджеры, то настройте права доступа на нее, чтобы они даже не видели это поле (это прям в редакторе TV-шки есть вкладка Права доступа).
- Пишем плагин на сохранение документа. Замысел прост — при сохранении документа, плагин должен проверять значение TV-шки с необрезанной фоткой, и если получает, то выполняет сниппет phpThumbOf и записывает полученный путь до превьюшки в эту специальную TV-шку. Код плагина:
<?php
switch ($modx->event->name) {
case 'OnDocFormSave':
// На всякий случай меняем контекст, если MODX сам не поменял на контекст документа
$origin_context = null;
if ($modx->context->key == 'mgr') {
$origin_context = $modx->context->key;
$modx->switchContext('web');
}
// Создаем самбнейл превьюшки
$TV_source = 2; // Устанавливаете значения своих TV-шек
$TV_target = 17;
$resource = &$scriptProperties['resource'];
if ($photo = $resource->getTVValue($TV_source) and $thumb = $modx->runSnippet('phpthumbof', array(
'input' => $photo,
'options' => 'w=113&h=113&q=100&zc=1',
))
and $photo != $thumb
) {
$resource->setTVValue($TV_target, $thumb);
}
// Если контекст меняли, то меняем его на изначальный
if ($origin_context) {
$modx->switchContext($origin_context);
}
break;
default:;
}
Все. Теперь при сохранении документа сразу будет генериться и сохраняться превьюшка. Но что делать, если уже документы есть, и их много? Ведь надо каждый документ пересохранить… Но с этим тоже все довольно просто — не обязательно каждый документ открывать, можно документ пересохранить программно, через API. Лучше всего это делать через Console. Пишем и выполняем вот такой код:
<?php
ini_set('max_execution_time', 0);
ignore_user_abort(1);
$TV_source = 1; // Устанавливаете значения своих TV-шек
$TV_target = 2;
$q = $modx->newQuery('modResource');
$q->leftJoin('modTemplateVarResource', 'tv', "tv.tmplvarid={$TV_source} AND tv.contentid=modResource.id");
$q->leftJoin('modTemplateVarResource', 'tv2', "tv2.tmplvarid={$TV_target} AND tv2.contentid=modResource.id");
$q->where(array(
'tv.id:!=' => null,
'tv.value:!=' => '',
'tv2.id' => null,
'published' => 1,
));
print "Total: " . $modx->getCount('modResource', $q); // Общее число оставшихся документов
// return;
$q->limit(10);
if ($docs = $modx->getCollection('modResource', $q)) {
foreach ($docs as $doc) {
$modx->error->reset();
$modx->runProcessor('resource/update', $doc->toArray());
}
}
Здесь мы получаем документы с картинками, у которых есть картинки, но нет еще превьюшек, и обновляем их через процессор. А в процессоре уже вызывается событие OnDocFormSave и само собой и наш процессор, срабатывающий на это событие. Вот теперь, после того, как у нас уже есть все превьюшки в специальном TV, нам не надо уже в коде страницы вызывать сниппет phpThumbOf, мы просто вставляем значение этой TV-шки. Нагрузка падает, мы радуемся :-)
UPD: Чуть-чуть дописал плагин. Добавил смену контекста, если текущий контекст — mgr. Дело в том, что для разных контекстов могут быть указаны различные источники файлов, а для mgr этот параметр в TV почему-то не задается, и если источник файлов TV-параметра не базовый (корневой), а контекст mgr, то TV-шка просто вернет неверный результат.
UPD: Добавил еще проверку AND $photo != $thumb. Причина — pThumb, если не смог получить картинку или еще что (ошибка в общем) возвращает оригинальное изображение. Если мы запишем оригинальный путь, то у нас и TV-поле будет заполнено (и мы не будем знать, что для этого ресурса не была создана превьюшка), и значение будет не правильным. Будет потом много гемора.