Загружаем сниппеты и чанки с диска
Кроме нескольких осведомленных человек, есть люди, которые подозревают, что в MODX все элементы хранятся в БД. У сообщества есть твердое мнение, что в данном решении больше минусов, чем плюсов — и время затрачивается на запросы к базе, и разрабатывать не очень удобно (хоть и появились недавно статические элементы), но главное — это невозможность нормального деплоя и поддержки версионности. Мне эта мысль тоже не давала покоя. Поэтому я вчера вечерком решил поэкспериментировать с любимым pdoTools. Дописал в парсер несколько строчек и добавил в pdotools.class.php функцию runSnippet.
Теперь парсер умеет загружать чанки и сниппеты с диска. Для этих элементов я ввел специальный токен — знак подчеркивания (все остальные уже заняты). И если парсер встречает такие тэги
А как он понимает, откуда грузить? По-умолчанию, он ищет их в /core/elements/snippets и /core/elements/chunks соответственно для сниппетов и чанков. Эти пути можно переопределить в системных настройках — snippet_files_path и chunks_files_path.
Понятно, что хранить все чанки и сниппеты в одной папке не удобно. Поэтому в имени чанка или сниппета можно указывать папки через точку, как мы это делаем при загрузке классов. Т.е. при обработке такого тэга
Из вышеописанного видно правило — у сниппетов расширение должны быть «php», а у чанков — «tpl».
ОТЛИЧИЯ РАБОТЫ СО СНИППЕТАМИ
Тут нужны пояснения. Правила вызова этих чанков и сниппетов в ресурсах не изменились. Вызываем как обычно с любыми фильтрами и тэгами. Но немного изменилась логика работы со сниппетами.
Во-первых, изменился порядок определения переменных из $scriptProperties. Раньше параметры, указанные при вызове сниппета, переопределяли значения по умолчанию. Теперь значений по умолчанию нет. Их нужно самим определять в сниппете. А параметры вызова передаются в сниппет. Это важно учитывать. Поясню. При вызове сниппета
Чтобы этого избежать, в сниппете нужно проверять их наличие
И в третьих, в данном случае нужно переделывать вызовы сниппетов с набором параметров. Так как не имеет смысла лезть за ними в базу коли мы уж совсем отказываемся от такого принципа. Т.е. их также нужно определять в самом сниппете. Такой код работать не будет
ФУНКЦИЯ pdoTools::runSnippet()
Некоторые знают, что в pdotools есть функция getChunk(). Если вызвать ее так
Я для комплекта добавил функцию runSnippet('Имя сниппета', параметры, путь), который делает тоже самое, т.е. грузит сниппет с диска. Вызывается он так
Таким образом, мы получаем вполне пригодный механизм для улучшения поддержки версионности и деплоя, а также более удобную разработку. По выигрышу по времени не готов дать точную выкладку. Полноценный тест не проводил. Но думаю, что выигрыш должен быть.
В общем жду экспертную оценку моего творчества. Какие подводные камни могут быть? Нужно ли включать такой функционал в pdoTools? Это вопрос, конечно, к Василию.
П.С. Также не проверял работу с феномом. Еще с ним не разобрался. Жду курса Василия, чтобы ускорить процесс ознакомления. По идее все должно работать.
ИТОГ
Данное решение признано не перспективным.
Теперь парсер умеет загружать чанки и сниппеты с диска. Для этих элементов я ввел специальный токен — знак подчеркивания (все остальные уже заняты). И если парсер встречает такие тэги
[[!_snippet]]
// или
[[!$_chunk]]он не лезет в базу, а грузит их с диска. А как он понимает, откуда грузить? По-умолчанию, он ищет их в /core/elements/snippets и /core/elements/chunks соответственно для сниппетов и чанков. Эти пути можно переопределить в системных настройках — snippet_files_path и chunks_files_path.
Понятно, что хранить все чанки и сниппеты в одной папке не удобно. Поэтому в имени чанка или сниппета можно указывать папки через точку, как мы это делаем при загрузке классов. Т.е. при обработке такого тэга
[[!_category.mysnippet]] парсер будет искать сниппет с именем mysnippet.php в папке /core/elements/snippets/category/, а при обработке тэга [[!$_category.mychunk]] парсер будет инклюдить чанк /core/elements/chunks/category/mychunk.tpl. Из вышеописанного видно правило — у сниппетов расширение должны быть «php», а у чанков — «tpl».
ОТЛИЧИЯ РАБОТЫ СО СНИППЕТАМИ
Тут нужны пояснения. Правила вызова этих чанков и сниппетов в ресурсах не изменились. Вызываем как обычно с любыми фильтрами и тэгами. Но немного изменилась логика работы со сниппетами.
Во-первых, изменился порядок определения переменных из $scriptProperties. Раньше параметры, указанные при вызове сниппета, переопределяли значения по умолчанию. Теперь значений по умолчанию нет. Их нужно самим определять в сниппете. А параметры вызова передаются в сниппет. Это важно учитывать. Поясню. При вызове сниппета
[[!_snippet? ¶m1=`1` ¶m2=`2`]]внутри сниппета уже будут определены $scriptProperties['param1'] и $scriptProperties['param2'], а также $param1 и $param2. Это нужно учесть, чтобы случайно не затереть их.Чтобы этого избежать, в сниппете нужно проверять их наличие
$scriptProperties['prop'] = isset($scriptProperties['prop']) ? $scriptProperties['prop'] : 'Новое значение';
// или так
$scriptProperties['prop'] = isset($prop) ? $prop : 'Новое значение';
// Или так
/* параметры по умолчанию */
$properties['prop1'] = 'Новое значение 1';
$properties['prop2'] = 'Новое значение 2';
/* Сливаем параметры */
$scriptProperties = array_merge($properties,$scriptProperties);Во-вторых, возвращать значение из сниппета нужно не через return, а как обычно через print или echo. Мне кажется это привычнее для php разработчиков.И в третьих, в данном случае нужно переделывать вызовы сниппетов с набором параметров. Так как не имеет смысла лезть за ними в базу коли мы уж совсем отказываемся от такого принципа. Т.е. их также нужно определять в самом сниппете. Такой код работать не будет
[[!_mysnippet@propertiesSet]]ФУНКЦИЯ pdoTools::runSnippet()
Некоторые знают, что в pdotools есть функция getChunk(). Если вызвать ее так
$pdoTools->getChunk('@FILE /chunks/mychunk.tpl');то чанк загружается с диска. Я для комплекта добавил функцию runSnippet('Имя сниппета', параметры, путь), который делает тоже самое, т.е. грузит сниппет с диска. Вызывается он так
$path = $modx->getOption('snippet_files_path',null,$modx->getOption('core_path').'elements/snippets/');
$res = $pdoFetch->runSnippet('@FILE mysnippet.php',array('prop1'=>1),$path);
// Можно и без последнего параметра. По-умолчанию, $path = '/core/elements/snippets/'.
// А в имени сниппета можно указать подпапку
$res = $pdoFetch->runSnippet('@FILE category/mysnippet.php',array('prop1'=>1));Если не указан @FILE, то вызовется обычный сниппет MODX.Таким образом, мы получаем вполне пригодный механизм для улучшения поддержки версионности и деплоя, а также более удобную разработку. По выигрышу по времени не готов дать точную выкладку. Полноценный тест не проводил. Но думаю, что выигрыш должен быть.
В общем жду экспертную оценку моего творчества. Какие подводные камни могут быть? Нужно ли включать такой функционал в pdoTools? Это вопрос, конечно, к Василию.
П.С. Также не проверял работу с феномом. Еще с ним не разобрался. Жду курса Василия, чтобы ускорить процесс ознакомления. По идее все должно работать.
ИТОГ
Данное решение признано не перспективным.
Комментарии: 32
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
По поводу print vs return в корне не согласен.
А вообще плюсанул за труд.
В MODX перед вызовом сниппета зачитываются его параметры по умолчанию, к ним добавляются параметры вызова и все это передается в сам сниппет. А в данном случае, если сниппета в базе нет, то нет и параметров по умолчанию (хранящихся в поле properties таблицы modx_site_snippets). В данном случае их негде взять. Поэтому, их нужно определять самостоятельно. Но я не вижу в этом проблемы. Многие, в том числе и я, в своих сниппетах проверяют эти параметры. Вот пример. При таком подходе вообще переделывать ничего не нужно.
Тоже и по поводу return. Для php программиста привычнее и логичнее print. А для MODX-программиста — return. На самом деле ничего сложного поменять логику нет. Пара строчек кода. Вопрос именно в подходе — делать как в обычном php или как в MODX.
Мнения разные важны. В данном случае не соглашусь. Это совсем не костыли. Тут работа именно с чистым php по его же стандартам. Т.е. человеку, который знает php и решил разрабатывать на MODX не нужно знать тонкости и условности этого движка. Как привык так и пиши. Очень часто именно это является камнем в огород со стороны пользователей других движков.
Fi1osofСергей, вы здесь в корне не правы, сорри. Поясню. Просто проследите ход выполнения метода MODx::runSnippet(). Самое важное: $output= $snippet->process($params); Уточняю: в данном случае выполняется присвоение. Вывод print/echo просто так присвоить нельзя. Для этого в modScript (расширяемый классом modSnippet) используется костыль ob_start()/ob_get_contents()/ob_end_clean(), и используется он как раз потому что многие именно MODX-разработчики вместо return пишут print/echo в своих сниппетах. Таким образом им просто облегчили жизнь. Но все-таки правильно именно возврат значения делать через return;
Еще аргумент: есть негласное правило у программистов: любая функция должна выполнять return, даже если она ничего не возвращает. Это как минимум для читабельности кода. Так вот, сейчас в modScript выполняется include кеш-файла сниппета, а вот еще каких-то пару лет назад код сниппета преобразовывался в функции и сниппет вызывался именно как функция. А еще раз повторюсь: функции должны выполнять возврат, а не принт.
Я не собираюсь отстаивать именно print. Тут как раз я хочу получить экспертное мнение как лучше.
Это как раз и не самое важное. Присвоение идет внутри process(). Отличие лишь в том, что для return надо прописывать а для print нет Просто мне казалось, что для стороннего php программиста сниппет будет выглядеть привычнее с print. Он может написать любой сниппет для MODX менеджера, который сможет его подключить. Я так думаю. Я могу и ошибаться, ибо не профи в этом.
Fi1osofА то, как у вас сейчас сделано, просто не позволит мне выполнить что-то типа такого:
Мне в таком случае опять-таки придется использовать костыль с обфлэшем.
Ничто вам не мешает писать print include $this->_scriptFilename;
Куча принтов в сниппетах — это всегда было плохо. Пусть мне кучу минусов к коменту напихают несогласные. Сниппет — это логика. Она не должна ничего принтить. Принт — это вопрос шаблонизации, а это уже дело шаблонов и чанков. Но чанки опять-таки должны вызываться кодом-обработчиком.
Я не собираюсь упорствовать. Должен быть, пусть будет. Тем более, что это все равно оказалось никому не нужно :)
Fi1osofВо-вторых, они (MODX-разработчики) потому и пишут, что привыкли на php так делать. А тут надо знать про return, именно знать, ибо об это нигде не написано.
Fi1osofРетурн так ретурн. В принципе, какая разница. Завтра забудем. :)
Fi1osofНа мой взгляд вряд ли сниппеты станут работать быстрее, ибо они и сейчас все кэшируются в /core/cache/includes/elements/, а потом подключаются как файлы.
Я считаю, что нет, не нужно.
Со времен Evolution в любом сниппете можно сделать
и работать с файлами. Есть статические элементы, есть Gitify, есть установочные пакеты (лично я переношу изменения через них).
Короче, без серьёзного профита в удобстве или скорости это просто любопытный эксперимент, не более.
Статические элементы, как мы знаем от Евгения Борисова, на продакшене нужно отключать.
Gitify для меня как высшая математика. Для простых пользователей очень сложно. Установочные пакеты — мейби.
Мне кажется так удобнее и практичнее для разработки. Раз, создал файл. Два, в ресурсе его просто вызываешь как обычно. Все как и было, только файл не в базе, а на диске. Отредактировал, нажал F5 и все сразу обновилось. Используя статические элементы, например, нужно сначала кэш почистить.
По скорости, допустим, сниппеты кэшируются, а чанки нет. За ними нужно лезть в базу.
Согласен, но у меня нет больших и сложных компонентов. А мне кажется, на маленьких данных преимуществ не будет видно. Но попробую ради интереса.
П.С. Даже просто ради эксперимента, мне понравилось.
А какой профит будет от вызова сниппетов? Их что, 50 штук на странице вызывается, чтобы только от способа вызова оно тормозило? Сомневаюсь. Евгений свои исследования давно делал, с тех пор много версий MODX сменилось — нужно перепроверять.
И если очень хочется сниппеты из файлов, в любом месте, смотри как это просто:
Сниппет loadSnippet, который грузит любой код из файлов. Кажется, я это даже где-то уже видел на продакшене.
В общем, еще раз, нужны тесты производительности.
Прикольно. Хорошее решение.
Если нужно без pdoTools, то просто еще один сниппет loadChunk:
Пока я вижу только усложнение работы без явной выгоды. Указывать пути, отказываться от наборов параметеров, echo вместо return — зачем это всё?
Проблему деплоя это не решает, потому что вызовы сниппетов и чанков кто-то еще должен прописать в шаблонах или ресурсах — а их как делать статическими? Всё равно нужен какой-то скрипт распаковки, или установочный пакет.
А если у нас должен быть установочный пакет, то он и так всё может собрать — и сниппеты и чанки, и наборы параметров, и шаблоны, и ресурсы. Собственно, Theme.Bootstrap является простейшим примером такой установки.
Лично я так же пакетами разворачиваю изменения на modstore.pro и на modhost.pro — проблем пока не было.
Согласен, с шаблонами и плагинами тоже надо что-то придумывать. Особенно, с плагинами. Разработчики, как правило, в ресурсы не лезут.
Никакого усложнения нет. На самом деле и наборы можно прикрутить. Вместо echo сделать return дело 2 секунд (я считал print лучше, меня переубедили). А уж про пути, конечно, прикольно. Т.е. вот это просто
А у меня путь указать, который кстати, можно и не указывать, это невероятно сложно. Улыбнуло.
Про пакеты я писал, что это тоже решение. Просто кто как привык и какие задачи нужно решить.
pdoTools позволяет загрузить чанки из файлов. Просто можно было бы сделать и загрузку сниппетов из файлов. В продолжении логики и развития функционала pdoTools.
Я это сделал не для поспорить. Мне было интересно поковыряться, экспертное мнение я услышал. Значит отложил в сторону. Пойду лучше с детьми погуляю, раз время свободное выдалось. :)
П.С. А за дефолтными параметрами сниппета MODX все равно в базу лезет. :)
Но почему-то не сделано. Почему, интересно? Наверное потому, что я по-прежнему не вижу никакой выгоды это делать.
Если только «чтобы было».
Вот шаблон страницы. Чанки простые, без фильтров и премудростей.
Вызов в различных комбинациях. 5 раз для каждого метода. Показаны средние значения.
Видно, что запросов в базу стало меньше. Но выигрыш небольшой. По крайней мере, на небольших сайтах. При кэшировании разницы вообще нет. Может быть на больших сайтах с вызовом некэшируемых элементов эта разница будет заметнее. Но мне проверить негде.
Вывод. На небольших сайтах результаты в районе погрешности. Преимуществ нет.
Вот такое получилось исследование.
П.С. Оставлю себе для разработки, чтобы не морочится со статическими элементами — каждому нужно прописывать пути в отличие от данного приема. Для простоты поменял логику парсера. Теперь он проверяет, есть ли такой чанк на диске, если есть — грузит, нет — берет из базы. Без всяких подчеркиваний.