Fi1osof 04 мая 2013 3 37
Наверняка много кто использовал на своих сайтах пакет 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-поле. Итак, все по порядку:

1. Создаем TV-шку для превьюхи. С этим все просто и понятно. Единственное — если на сайте будут работать менеджеры, то настройте права доступа на нее, чтобы они даже не видели это поле (это прям в редакторе TV-шки есть вкладка Права доступа).

2. Пишем плагин на сохранение документа. Замысел прост — при сохранении документа, плагин должен проверять значение 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-поле будет заполнено (и мы не будем знать, что для этого ресурса не была создана превьюшка), и значение будет не правильным. Будет потом много гемора.
37 комментариев
den991
den99 12 июля 2013г в 22:52 #
радикально. у меня тут пара сотен страниц с вызовом phpThumbOf завалялась, а на каждой по 16-30 мегабайтных картинок уже есть и переделывать их реально в ломы, проще сервер купить (что и сделано было)=)

воспользуюсь информацией позже, спасибо.
Fi1osof1
Fi1osof 13 июля 2013г в 01:37 #
А на самом деле клева получается. Во-первых, исключается человеческий фактор (не надо учить менеджеров что делать. Один раз прогнал все документы, а потом при любом сохранении документа обновляется превьюха). Во-вторых, идет как чистая статика, сервер вообще не грузится.
den991
den99 13 июля 2013г в 11:53 #
Дай я тебя расцелую за это.
Fi1osof1
Fi1osof 13 июля 2013г в 12:03 #
Не, вот этого точно делать не надо :-))))
bazgyrt1
bazgyrt 01 августа 2013г в 18:26 #
Жесть, слил на 0 настройки, производительность моментально улучшилась.
Правильно ли я понимаю что плагин phpThumbOfCacheManager, который устанавливается вместе с самим компонентом phpThumbOf, очищает полностью папку кэша phpThumbOf? Поэтому его лучше выключать?
Fi1osof1
Fi1osof 01 августа 2013г в 18:36 #
Правильно ли я понимаю что плагин phpThumbOfCacheManager, который устанавливается вместе с самим компонентом phpThumbOf, очищает полностью папку кэша phpThumbOf? Поэтому его лучше выключать?
Да.

Вот еще на этот компонент посмотри: modx.com/extras/package/phpthumbsup
Райн говорит, что он гораздо быстрее работает, чем phpthumbof
den991
den99 03 августа 2013г в 03:32 #
кто уже попробовал phpthumbsup?
а то я довёл уже до максимума (с помощью Философа) скорость работы phpthumbof, так что у меня он как статика уже работает.
кто-то что-то скажет о phpthumbsup?
Fi1osof1
Fi1osof 03 августа 2013г в 09:48 #
Я не пробовал еще. Но вот попробуй если что новую альтернативу от Агеля: github.com/AgelxNash/phpthumbon/releases
den991
den99 03 августа 2013г в 12:31 #
Охренеть, прорыв прям. Тоже еще не пробовал. Надо анонс замутить для поиска, чтобы всосало в поисковики все три альтернативы.
Fi1osof1
Fi1osof 03 августа 2013г в 12:43 #
Все в твоих руках. Протестируй все три решения и напиши обзорный топик.
den991
den99 03 августа 2013г в 17:59 #
Пристал. Я не буду писать обзоры. Я лично могу поделиться, но не более.

Вот например: по первым моим тестам обнаружен недостаток в UP и в ON версиях (по крайней мере на этом этапе). Ни одна из них не может брать картинку с внешнего источника. OF может, то попутно обнаружена задержка в нём (проверка наличия кеша), которая составляет 1.5-3ms на одну картинку. Это тоже нехорошо.

Теперь пример, как это развивалось бы, буть это топик: вылезет какой-то умный троль и скажет, что я криворукий, и методом переделки 20 строк кода в плагине и перелопачивании 20 строк ядра всё будет работать, и что я вообще ничего не увидел, поэтому мой обзор — хламищ, мне двойка и я уволен.

Исключительно и только поэтому я никогда ничего не напишу вообще, даже если все будут аплодировать и в руках у меня будет сокровище.
Fi1osof1
Fi1osof 03 августа 2013г в 18:11 #
Затроллить и коммент могут.
den991
den99 03 августа 2013г в 18:23 #
поэтому я пишу тебе, а не им
den991
den99 03 августа 2013г в 18:22 #
Вообщем, парился я парился и надоело
ON вечно кладёт файл-пустышку в стиле /assets/cache_image/noimage_100x100_31b.jpg, мол
оригинал не найден, хотя оригинал есть и доступен.

UP, напротив, заработал полностью, хотя выхов картинки у него совершенно другой, полностью отличается от OF.
Например так: /cache/w/100/h/100/src/http://modxclub.ru/uploads/images/00/01/66/2013/07/13/avatar_48x48.jpg

Fi1osof1
Fi1osof 03 августа 2013г в 18:25 #
/cache/w/100/h/100/src/http://modxclub.ru/uploads/images/00/01/66/2013/07/13/avatar_48x48.jpg
жесть!)))
den991
den99 03 августа 2013г в 22:08 #
это я вытащил свой аватар с твоего сайта, чтобы для примера не было моего сайта в ссылке. но смысл и расшифровка, думаю, понятны. И гибкость, на мой взгляд, приобретается просто невероятная.
Fi1osof1
Fi1osof 03 августа 2013г в 22:13 #
Ясно.
Fi1osof1
Fi1osof 04 августа 2013г в 00:15 #
Вот обзор различных аналогов: modxclub.ru/blog/173.html
Fi1osof1
Fi1osof 09 августа 2013г в 03:39 #
А вот так я предварительно обновляю все документы, у которых еще нет сгенерированных превьюшек:
<?php

ini_set('max_execution_time', 0);
ignore_user_abort(true);

$modx->switchContext('web');
print '<pre>';

$TV_source = 2;    // Устанавливаете значения своих TV-шек
$TV_target = 17; 

$q = $modx->newQuery('modResource');
$q->innerJoin('modTemplateVarResource', 'tvimage', "tvimage.tmplvarid={$TV_source} and modResource.id = tvimage.contentid and tvimage.value != ''");
$q->leftJoin('modTemplateVarResource', 'tvprev', "tvprev.tmplvarid={$TV_target} and modResource.id = tvprev.contentid");
$q->where(array(
    'tvprev.id' => null,    
));

print "<br />Всего документов без превьюшек: ". $modx->getCount('modResource', $q);

$q->limit(100);

// Создаем самбнейл превьюшки
foreach($modx->getCollection('modResource', $q) as $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);
        // print $thumb;
    }
    // print_r($resource->toArray());
}
M
MisterN 26 августа 2013г в 13:30 #
Скажите пожалуйста, зачем обновлять контекст? И что делать на мультиязычных сайтах?
Fi1osof1
Fi1osof 26 августа 2013г в 13:48 #
Скажите пожалуйста, зачем обновлять контекст?
Если вы про это:
        // На всякий случай меняем контекст, если MODX сам не поменял на контекст документа
        $origin_context = null;
        if($modx->context->key == 'mgr'){
            $origin_context = $modx->context->key;
            $modx->switchContext('web');
        }

то причина следующая: дело в том, что TV-параметры привязываются к источникам файлов (индивидуально для каждого контекста). Но нельзя указать связку TV-поля с конкретным источником файлов в контексте mgr. Если вы не используете отдельного источника файлов. используя по умолчанию filesystem (который от корня сайта работает), то вам бояться нечего. А если у вас в web-контексте другой источник (я часто в основном использую Images (/assets/images/)), то без смены контекста будет использован тот же filesystem и пути побьются, файлы просто не будут найдены.

И что делать на мультиязычных сайтах?
Мультиязычных или мультидоменных? Мультиязычность здесь никакой роли не играет, а если мультидоменный (мультиконтекстный), то используйте во всех контекстах один и тот же источник файлов, что и в web, и никаких проблем не будет.

P.S. на самом деле эта смена контекстов больше имеет значение при выполнении кода в консоли, когда по умолчанию используется текущий контекст mgr, но при редактировании документа через родной редактор страниц, проблем не должно никаких встать.

P.P.S -лучше использовать pThumb вместо phpthumbof, так как первый, в случае если картинка не найдена, использует указанный УРЛ картинки без изменения (то есть просто УРЛ будет на оригинал), в то время как второй или ничего не отдает, или записывается ошибка в TV.
M
MisterN 26 августа 2013г в 14:47 #
Про источники файлов почитаю, спасибо.
Вы может быть не пользуетесь стандартными пакетами, но в наиболее распространённых инструкциях к Babel требуется создание отдельного контекста для языка. По-моему он иначе не работает.
И ещё, я может глупость спрошу, но все-таки: у вас в условии if с бухты-барахты появляется переменная $photo. Я как-то привык сперва объявлять переменную, класть в неё что-то, а потом уже в условии проверять.
ЗЫ вообще я phpThumbOn установил. но что-то он не больно по скорости обгоняет phpThumbof по моим наблюдениям.
Fi1osof1
Fi1osof 26 августа 2013г в 16:14 #
Вы может быть не пользуетесь стандартными пакетами, но в наиболее распространённых инструкциях к Babel требуется создание отдельного контекста для языка.
Я мало использую стандартные пакеты, но во-первых, я знаю хорошо сам MODX (а значит и любой пакет для меня — не проблема), а во-вторых, в свое время не мало пакетов использовал.
Вы в данном случае путаете разные вещи. То, что бабел заставляет делать новые контексты, это еще не говорит, что мультиязычность на что-то влияет. Повторюсь: сама мультиязычность ни на что не влияет. А вот по какой причине созданы контексты — это вообще не важно (бабел вас заставил, или сами захотели), здесь есть факт — другие контексты. И вот про эти контексты и их влияние я написал выше — здесь влияние только связка TV-контекст-Источник файлов. Всего написанного выше достаточно.

у вас в условии if с бухты-барахты появляется переменная $photo. Я как-то привык сперва объявлять переменную
У меня написано:
if($photo = $resource->getTVValue($TV_source)

В данном случае не только происходит присвоение, но и ленивое объявление (сам php ее автоматом заводит). Ни логической, ни технической ошибки здесь никакой нет. А ваше желание объявлять переменные в шапке — это ваше право. Я тоже бывает переменные объявляю, но только тогда, когда они в коде расположены далеко друг от друга. Здесь переменная используется в двух строчках кода, близко расположенных друг к другу. Какой смысл их где-то объявлять?

ЗЫ вообще я phpThumbOn установил. но что-то он не больно по скорости обгоняет phpThumbof по моим наблюдениям.
Я не говорил ставить phpThumbOn. Я говорил про pThumb.
А
Андрей Балкин 12 мая 2015г в 12:33 #
Обнаружил такую штуку, что на выходе от сниппета phpthumbof, выходят изображения немного бледные нежели оригинал, в чем может быть проблема? Помогите очень серьезный баг на мог взгляд. Причем если даже размер ставить в настройках один в один с оригиналом, все равно обрабатывает и делает изображение превьюшки более бледной по цвету. Кто сталкивался?
proxyfabio1
proxyfabio 12 мая 2015г в 12:34 #
`&q=100`?
Fi1osof1
Fi1osof 14 мая 2015г в 07:06 #
Сталкивались. q=100 решало (100% качество задает). Больше подсказать нечего.
А
Андрей Балкин 15 мая 2015г в 17:01 #
Николай, да не решает в том то и дело ) Сам был очень удивлен
А
Андрей Балкин 12 мая 2015г в 12:36 #
Сергей нет, ставил и с &q=100 и без не помогает
proxyfabio1
proxyfabio 12 мая 2015г в 12:41 #
Тогда смотри применяемые фильтры. Наверняка какой-то из них рубит качество.
А
Андрей Балкин 12 мая 2015г в 12:42 #
да нет фильтров то то и оно
А
Андрей Балкин 12 мая 2015г в 12:53 #
{block name=params}
    {$options = "&q=100&h=360"}
{/block}
{$image = $object.image|default:$object.imageDefault}
{$src = $modx->runSnippet('phpthumbof', [
    "input" => $image,
    "options"   => $options
])}
M
MisterN 15 мая 2015г в 17:18 #
На другом хостинге пробовали?
А
Андрей Балкин 15 мая 2015г в 17:20 #
А хостинг тут причем?
M
MisterN 15 мая 2015г в 17:30 #
Ну, как бы phpthumb работает через модули php. А вдруг чего не установлено или функции отключены?
А
Андрей Балкин 15 мая 2015г в 17:38 #
как вариант конечно можно попробовать раз уж других нет )
proxies1
proxies 11 июля 2015г в 02:29 #
Автору респект!!! В голову не приходило что так можно сделать, нарадоваться не могу.

Но вот не люблю создавать лишний раз тв, пришла идея записывать превью в неиспользуемое поле, например introtext или link_attributes. Сниппету каталога не придется обращаться к тв за картинкой, тем самым еще больше ускорим загрузку.
В php я не силен, сделал как смог, поправьте говнокод, буду очень признателен)

        $TV_source = 3;
        $target = 'introtext';
        $resource = & $scriptProperties['resource'];
        $photo = $resource->getTVValue($TV_source); 
        
        if($photo){
            $thumb = $modx->runSnippet('phpthumbon', array(
                'input'     => $photo,
                'options'   => 'w=113&h=115&q=100&zc=1',
            ));
            $resource->set($target, $thumb);
            $resource->save();
        }
Fi1osof1
Fi1osof 11 июля 2015г в 12:17 #
Пожалуйста.

Но вот использовать зарезервированные поля под хранение данных, не для которых они были созданы — это не правильно. Хотите в ресурс сохранять, изучайте эту статью: habrahabr.ru/post/253737/
Авторизуйтесь или зарегистрируйтесь (можно через соцсети ), чтобы оставлять комментарии.