Хватить это терпеть! Или зачем столько чанков в FormIt!?
Привет сообществу!
Небольшая хитрость которая поможет новичкам избавиться от множества чанков/файлов писем с сайта и реализовать их в одном чанке/файле. Кроме этого пару слов о том, что происходит с MODX в конце заметки.
Предисловие:
Пару месяцев назад в чате был небольшой джентльменский спор между Павлом Зарубином и Евгением на тему «Так ли хорош FormIt?». В этом споре, я разместился где-то посередине так как я вижу и плюсы и минусы данного компонента. Эта заметка об одном из минусов, ну и конечно же решение.
Итак, проблема
А если быть точнее, столько же, сколько и различных форм на сайте. Когда ко мне попадают чужие проекты, я вижу там 100500 чанков которые отличаются только тем, что у них разное количество полей или просто разные поля.
Они выглядят примерно так:
Хоть я и в бэкенде недавно, но меня это удручает, поскольку сегодня существует понятие «Наследование» и например в miniShop2 те же самые письма реализованы прекрасно с помощью fenom.
Предлагаю самое простое решение, воспользоваться хуком и немного схитрить используя свойство fieldNames сниппета FormIt который служит тому чтобы при записи данных форм в БД с помощью родного хука FormItSaveForm переименовывать названия полей, ведь по умолчанию будут использованы ключи глобального массива с формой. Синтаксис такой: Название поля==Новое название и это всё через запятую. Название поля — это атрибут name у полей (Вдруг кто не знал).
«Что этот сниппет себе позволяет?!» или что сниппет/хук делает объясню вкратце:
Забирает значение свойства fieldNames и указанных данных пользователя заполнившего форму и с помощьюмагии методов создает новое поле fields, т.е. в чанке письма будет доступен плейсхолдер
[[+fields]] в котором будет JSON массив с названием и значением от пользователя.
Теперь в вызове сниппета FormIt указывайте в свойство fieldNames список нужных полей и пользуйтесь одним чанком.
Недавно Иван Бочкарёв писал о том, что происходит с MODX на Github и мне также хочется призвать разработчиков к активности. Я считаю, что MODX переживает не лучшие времена и понятно, что его развитие в основном зависит от более опытных разработчиков и от MODX LLC, но мы ведь тоже можем хоть немного повлиять на развитие нашей платформы. Я также в свою очередь делаю вклад насколько мне позволяют знания, опыт, время и возможности. Вступайте в группу MODX Contributors
Вам спасибо за внимание, а мне потому что…
☕ Угостить чашкой кофе
UPD
Обновлен сниппет для значений radio
UPD: 14.11.2019
Обновлен сниппет/хук, добавлен trim
Небольшая хитрость которая поможет новичкам избавиться от множества чанков/файлов писем с сайта и реализовать их в одном чанке/файле. Кроме этого пару слов о том, что происходит с MODX в конце заметки.
Предисловие:
Пару месяцев назад в чате был небольшой джентльменский спор между Павлом Зарубином и Евгением на тему «Так ли хорош FormIt?». В этом споре, я разместился где-то посередине так как я вижу и плюсы и минусы данного компонента. Эта заметка об одном из минусов, ну и конечно же решение.
Много чанков
А если быть точнее, столько же, сколько и различных форм на сайте. Когда ко мне попадают чужие проекты, я вижу там 100500 чанков которые отличаются только тем, что у них разное количество полей или просто разные поля.
Они выглядят примерно так:
...
<li>Имя: [[+name]]</li>
<li>Телефон: [[+phone]]</li>
...
...
<li>Имя: [[+name]]</li>
<li>Email: [[+email]]</li>
...
...
<li>Имя: [[+name]]</li>
<li>Email: [[+email]]</li>
<li>Тема обращения: [[+subject]]</li>
<li>Сообщение: [[+message]]</li>
...
и так далее.Хоть я и в бэкенде недавно, но меня это удручает, поскольку сегодня существует понятие «Наследование» и например в miniShop2 те же самые письма реализованы прекрасно с помощью fenom.
Решение
Предлагаю самое простое решение, воспользоваться хуком и немного схитрить используя свойство fieldNames сниппета FormIt который служит тому чтобы при записи данных форм в БД с помощью родного хука FormItSaveForm переименовывать названия полей, ведь по умолчанию будут использованы ключи глобального массива с формой. Синтаксис такой: Название поля==Новое название и это всё через запятую. Название поля — это атрибут name у полей (Вдруг кто не знал).
1. Вызов FormIt
// Синтаксис MODX
[[FormIt?
...
&hooks=`fields,email` // сниппет fields мы с вами создадим в следующем шаге
&fieldNames=`name==Имя,phone==Контактный телефон`
&emailTpl=`email.tpl`
]]
// Синтаксис fenom
{'FormIt' | snippet : [
...
'hooks' => 'fields,email', // сниппет fields мы с вами создадим в следующем шаге
'fieldNames' => 'name==Имя,phone==Контактный телефон',
'emailTpl' => '@FILE chunks/email/email.tpl'
]}
2. Создание сниппета/хука
Создадим сниппет и укажем fields в качестве названия с таким кодом:<?php
$fields = explode(',', $hook->formit->config['fieldNames']);
foreach ($fields as $key => $field) {
$f = explode('==', trim($field));
$value = $hook->getValue($f[0]);
if (is_array($value)) {
$value = implode(', ', $value);
}
$result[] = array(
'name' => $f[1],
'value' => $value,
);
}
$hook->setValue('fields', $modx->toJSON($result));
return true;
«Что этот сниппет себе позволяет?!» или что сниппет/хук делает объясню вкратце:
Забирает значение свойства fieldNames и указанных данных пользователя заполнившего форму и с помощью
[[+fields]] в котором будет JSON массив с названием и значением от пользователя.
3. Чанк/файл email.tpl
Я лично из лагеря fenom, но стараюсь и ради тех кто всё еще пользуется родным синтаксисомЕсли вы не пользуйтесь fenom, то весьма кстати вам пригодиться сниппет getImageList который идёт вместе с MIGX.
Кстати, у меня есть пара заметок о MIGX где я подробно рассказывал о нём. Вот первая заметка, а вот вторая, почитайте, уверен, будет интересно, если еще не читали.
// Синтаксис MODX
<h3>Обратная связь:</h3>
[[getImageList?
&value=`[[+fields]]`
&tpl=`@CODE:<li>[[+name]]: [[+value]]</li>`
]]
// Синтаксис fenom
<h3>Обратная связь:</h3>
{foreach $fields | fromJSON as $field}
<li>{$field['name']}: {$field['value']}</li>
{/foreach}
Итог
Теперь в вызове сниппета FormIt указывайте в свойство fieldNames список нужных полей и пользуйтесь одним чанком.
__________________________________
Пару слов о самом MODX
К данной заметке это не относится, но хочется выразится о MODX и о MODX3Недавно Иван Бочкарёв писал о том, что происходит с MODX на Github и мне также хочется призвать разработчиков к активности. Я считаю, что MODX переживает не лучшие времена и понятно, что его развитие в основном зависит от более опытных разработчиков и от MODX LLC, но мы ведь тоже можем хоть немного повлиять на развитие нашей платформы. Я также в свою очередь делаю вклад насколько мне позволяют знания, опыт, время и возможности. Вступайте в группу MODX Contributors
Вам спасибо за внимание, а мне потому что…
☕ Угостить чашкой кофе
UPD
Обновлен сниппет для значений radio
UPD: 14.11.2019
Обновлен сниппет/хук, добавлен trim
Комментарии: 21
Хорошее решение, а вот ссылки на телеграмм уже почти год не работают. Не устаю повторять, что для телеги конверсия ссылок в формате: @groupname гораздо выше
Справедливое замечание
Я поменял тип группы: t.me/modx_contributors
Я поменял тип группы: t.me/modx_contributors
Хорошее решениеСпасибо
а вот ссылки на телеграмм уже почти год не работают. Не устаю повторять, что для телеги конверсия ссылок в формате: @groupname гораздо вышеПоменял в заметке
не знал, что формит умеет в 'emailTpl' => '@FILE chunks/email/email.tpl'
или я где-то пропустил подключение pdoTools?
я использовую formit через ajaxform, и у меня не получалось в чанках письма юзать fenom — только родной синтаксис и тот без модификаторов. то есть если значение [[+phone]] пустое, то в письме так и будет — "[[+phone]]"
или я где-то пропустил подключение pdoTools?
я использовую formit через ajaxform, и у меня не получалось в чанках письма юзать fenom — только родной синтаксис и тот без модификаторов. то есть если значение [[+phone]] пустое, то в письме так и будет — "[[+phone]]"
- Added default file-based chunks as objects
- Added support for pdoTools getChunk method
не знал, что формит умеет в 'emailTpl' => '@FILE chunks/email/email.tpl'В версии 3.0.0 появилась:
или я где-то пропустил подключение pdoTools?
да, надо читать чейндж-логи))
Вот такой вариант, работает
<table>
[[+name:notempty=`<tr>
<td style="padding:2px 7px 2px 0px"><b>Имя:</b></td>
<td>[[+name]]</td>
</tr>`]]
[[+phone:notempty=`<tr>
<td style="padding:2px 7px 2px 0px"><b>Телефон:</b></td>
<td>[[+phone]]</td>
</tr>`]]
[[+email:notempty=`<tr>
<td style="padding:2px 7px 2px 0px"><b>Почта:</b></td>
<td>[[+email]]</td>
</tr>`]]
[[+link:notempty=`<tr>
<td style="padding:2px 7px 2px 0px"><b>Ссылка:</b></td>
<td>[[+link]]</td>
</tr>`]]
[[+mess:notempty=`<tr>
<td style="padding:2px 7px 2px 0px"><b>Сообщение:</b></td>
<td>[[+mess]]</td>
</tr>`]]
<tr>
<td style="padding:2px 7px 2px 0px"><b>Страница:</b></td>
<td><a href="[[++site_url]][[+uri]]">[[+page]]</a></td>
</tr>
</table>
Добавляю все поля которые мне нужны из разных форм. Клиенту приходят только те поля, что заполнены.
Выложу свой способ управления множеством форм для сниппета ajaxForm, может пригодится.
Создаём чанк ajaxForms:
Это единый чанк, в котором вызывается сниппет ajaxForm с необходимыми параметрами.
В моём случае каждая отдельная форма — это отдельный чанк. Если эти чанки мало чем различаются, то можно использовать extends из fenom, переписывая лишь изменяющиеся значения, а остальное не трогать.
И далее в шаблоне страницы вызов нужной формы:
В нём мы подменили значения, передающиеся в чанк ajaxForm.
Пример чанка callback с формой:
Если нужно вызвать похожую форму но с другим заголовком и другими параметрами, то создадим чанк question, который наследует чанк callback:
И вызов этой формы в шаблоне страницы:
Таким образом, для всех форм мы можем задать единые параметры вызова сниппета ajaxForm, и при необходимости перезаписать их. А также под каждую форму либо создать отдельные чанки, либо унаследовать от одного чанка кучу других с индивидуальными параметрами.
Дополнительно я скрываю уведомления jgrowl js-скриптом:
Все уведомления об ошибках показываются под полями форм, в коде span c классом error. А если форма успешно отправлена, то с помощью скрипта выше мы скрываем popup-форму, если она была открыта, и показываем другое popup-окно:
Оно же всплывает и при отправке обычных не popup-форм. Да, насчёт popup-окон, актуально при использовании flexbox3.
Создаём чанк ajaxForms:
{if !$form} {set $form = ''}{/if}
{if !$hooks} {set $hooks = 'email,FormItSaveForm'}{/if}
{if !$emailSubject} {set $emailSubject = 'Тема письма'}{/if}
{if !$emailTo} {set $emailTo = $_modx->config['callback_email']}{/if}
{if !$validationErrorMessage} {set $validationErrorMessage = 'В форме содержатся ошибки!'}{/if}
{if !$successMessage} {set $successMessage = '<div class="name">Спасибо</div><p>Ваше сообщение успешно отправлено</p>'}{/if}
{if !$vTextMaxLength} {set $vTextMaxLength = '<div>Проверьте правильность заполнения</div>'}{/if}
{if !$vTextMinLength} {set $vTextMinLength = '<div>Проверьте правильность заполнения</div>'}{/if}
{if !$vTextRequired} {set $vTextRequired = 'Это поле обязательно для заполнения'}{/if}
{if !$validate} {set $validate = 'name:required'}{/if}
{if !$formFields} {set $formFields = 'name,pageId,pagetitle,email,message,file'}{/if}
{if !$formName} {set $formName = 'Имя формы'}{/if}
{if !$validationErrorMessage} {set $validationErrorMessage = 'В форме содержатся ошибки!'}{/if}
[[!ajaxForm?
&form=`{$form}`
&hooks=`{$hooks}`
&emailSubject=`{$emailSubject}`
&emailTo=`{$emailTo}`
&validationErrorMessage=`{$validationErrorMessage}`
&successMessage=`{$successMessage}`
&vTextMaxLength=`{$vTextMaxLength}`
&vTextMinLength=`{$vTextMinLength}`
&vTextRequired=`{$vTextRequired}`
&validate=`{$validate}`
&validationErrorMessage=`{$validationErrorMessage}`
&formName=`{$formName}`
&formFields=`{$formFields}`
&fieldNames=`{$fieldNames}
`
]]
Это единый чанк, в котором вызывается сниппет ajaxForm с необходимыми параметрами.
В моём случае каждая отдельная форма — это отдельный чанк. Если эти чанки мало чем различаются, то можно использовать extends из fenom, переписывая лишь изменяющиеся значения, а остальное не трогать.
И далее в шаблоне страницы вызов нужной формы:
{include 'ajaxForms'
form='callback'
emailSubject='Обратный звонок'
validate='name:required,phone:required'
formFields='name,phone,pagetitle'
formName='Обратный звонок'
}
В нём мы подменили значения, передающиеся в чанк ajaxForm.
Пример чанка callback с формой:
<div style="display: none;" id="{block 'id'}callback{/block}" class="popup">
<div class="title">{block 'title'}Заказать обратный звонок{/block}</div>
<div class="desc">{block 'desc'}Оставьте заявку, и мы свяжемся с вами в самое ближайшее время{/block}</div>
<form role="form" method="POST" action="[[~[[*id]]]]" enctype="multipart/form-data">
<input type="hidden" name="pagetitle" value="[[*pagetitle]] ([[*id]])">
<input type="text" name="name" value="[[!+fi.name]]" placeholder="Ваше имя" required="required" />
<span class="error error_name">[[+fi.error.name]]</span>
<input type="tel" name="phone" value="[[!+fi.phone]]" placeholder="Ваш телефон" required="required" />
<span class="error error_phone">[[+fi.error.phone]]</span>
<div class="privacy">Отправляя заявку, Вы соглашаетесь на обработку персональных данных
согласно <a href="{29|url}">Пользовательскому соглашению</a></div>
<button class="button-yellow" type="submit" value="{md5(rand())}" name="submit">Отправить</button>
</form>
</div>
Если нужно вызвать похожую форму но с другим заголовком и другими параметрами, то создадим чанк question, который наследует чанк callback:
{extends 'callback'}
{block 'id'}question{/block}
{block 'title'}Задать вопрос{/block}
{block 'desc'}Задайте свой вопрос, и мы с вами свяжемся{/block}
И вызов этой формы в шаблоне страницы:
{include 'ajaxForms'
form='question'
emailSubject='Задать вопрос'
validate='name:required,phone:required'
formFields='name,phone,pagetitle'
formName='Задать вопрос'
}
Таким образом, для всех форм мы можем задать единые параметры вызова сниппета ajaxForm, и при необходимости перезаписать их. А также под каждую форму либо создать отдельные чанки, либо унаследовать от одного чанка кучу других с индивидуальными параметрами.
Дополнительно я скрываю уведомления jgrowl js-скриптом:
$(document).on('af_complete', function(event, response) {
if (response.success) {
$.fancybox.close();
$.fancybox.open({
src : '#popup-success',
type : 'inline',
opts : {
afterShow : function( instance, current ) {
// console.info( 'done!' );
}
}
});
} else {
for (var prop in response.data) {}
}
response.message='';
});
Все уведомления об ошибках показываются под полями форм, в коде span c классом error. А если форма успешно отправлена, то с помощью скрипта выше мы скрываем popup-форму, если она была открыта, и показываем другое popup-окно:
<div style="display: none;" id="popup-success" class="popup">
<div class="title">Данные успешно отправлены</div>
<div class="success">
<img src="/assets/img/success.png" alt="">
</div>
</div>
Оно же всплывает и при отправке обычных не popup-форм. Да, насчёт popup-окон, актуально при использовании flexbox3.
Позвольте переписать ваш чанк ajaxForms:
Так ведь намного лучше
{'!AjaxForm' | snippet : [
'form' => $form,
'hooks' => $hooks !: 'email,FormItSaveForm',
'emailSubject' => $emailSubject !: 'Тема письма',
'emailTo' => $emailTo !: $_modx->config.callback_email,
'validationErrorMessage' => $validationErrorMessage !: 'В форме содержатся ошибки!',
'successMessage' => $successMessage !: '<div class="name">Спасибо</div><p>Ваше сообщение успешно отправлено</p>',
'vTextMaxLength' => $vTextMaxLength !: '<div>Проверьте правильность заполнения</div>',
'vTextMinLength' => $vTextMinLength !: '<div>Проверьте правильность заполнения</div>',
'vTextRequired' => $vTextRequired !: 'Это поле обязательно для заполнения',
'validate' => $validate !: 'name:required',
'formName' => $formName !: 'Имя формы',
'formFields' => $formFields !: 'name,pageId,pagetitle,email,message,file',
]}
Так ведь намного лучше
Согласен, так красивее :)
И чанк callback также:
<div style="display: none;" id="{block 'id'}callback{/block}" class="popup">
<div class="title">{block 'title'}Заказать обратный звонок{/block}</div>
<div class="desc">{block 'desc'}Оставьте заявку, и мы свяжемся с вами в самое ближайшее время{/block}</div>
<form role="form" method="POST" action="{('id' | resource) | url}" enctype="multipart/form-data">
<input type="hidden" name="pagetitle" value="{'pagetitle' | resource} ({'id' | resource})">
<input type="text" name="name" value="" placeholder="Ваше имя" required="required" />
<span class="error error_name"></span>
<input type="tel" name="phone" value="" placeholder="Ваш телефон" required="required" />
<span class="error error_phone"></span>
<div class="privacy">Отправляя заявку, Вы соглашаетесь на обработку персональных данных
согласно <a href="{29 | url}">Пользовательскому соглашению</a></div>
<button class="button-yellow" type="submit" value="{md5(rand())}" name="submit">Отправить</button>
</form>
</div>
Если используется хук FormItSaveForm, то можно обойтись без лишнего сниппета:
{foreach $_pls['savedForm.values'] | json_decode as $label => $value}
{$label}: {$value | e}
{/foreach}
немного схитрить используя свойство fieldNames сниппета FormItА разве прописав кастомный параметр при вызове FormIt (или AjaxForm) он не попадает прямиком в чанк формы?
Я делаю примерно так:
{'!AjaxForm' | snippet : [
...
'formFields' => [
'name' => [
'type' => 'text',
'label' => '',
'placeholder' => 'Ваше имя',
'required' => true,
],
'email' => [
'type' => 'email',
'label' => '',
'placeholder' => 'Ваш email',
],
'phone' => [
'type' => 'text',
'label' => '',
'placeholder' => 'Контактный телефон',
'required' => true,
],
],
...
]}
И в чанке формы:
{foreach $formFields as $fk => $fv}
{if $fv['label']?}
<div class="form__label-w">
<label class="form__label">
{$fv['label']}
</label>
</div>
{/if}
{switch $fv['type']}
{case 'text'}
<div class="form__input-w">
<input class="form__input" type="text" name="{$fk}" placeholder="{$fv['placeholder']}">
</div>
{case 'email'}
<div class="form__input-w">
<input class="form__input" type="email" name="{$fk}" placeholder="{$fv['placeholder']}">
</div>
{case 'textarea'}
<div class="form__input-w">
<textarea class="form__input" name="{$fk}" placeholder="{$fv['placeholder']}"></textarea>
</div>
{/switch}
<div class="form__error error_{$fk}"></div>
{/foreach}
А разве прописав кастомный параметр при вызове FormIt (или AjaxForm) он не попадает прямиком в чанк формы?Паша, попадёт конечно, отличная реализация, только в заметке речь о чанках писем или я что-то не понял?)
Ой, точно! Видимо я что-то упустил)) Кстати, хорошая статья! =)
Кстати, хорошая статья! =)Спасибо большое :)
Можно модифицировать параметр &fieldNames так чтобы можно было выводить разные типы инпутов и задать обязательные параметры, возможно что-нибудь ещё
&fieldNames=`name:text:required==Имя, email:email:required==E-mail`
ребят, а кто-нибудь делал зависимую валидацию полей в formit?
нужно так: если поле name_1 имеет значени true, то поле name_2 становится обязательным.
родной параметр validate вроде как по одному полю работает, а хуки не выводят сообщение об ошибке.
нужно так: если поле name_1 имеет значени true, то поле name_2 становится обязательным.
родной параметр validate вроде как по одному полю работает, а хуки не выводят сообщение об ошибке.
хук сделать, не?
Разве не выводят?
$hook->addError('field_name', 'lexicon_key');
return !$hook->hasErrors();
Для универсальных писем, с проверкой
Телефон: {$phone}
{set $data = $fields | fromJSON}
{if count($data)}
<em>Текст сообщения</em>
<ul style="list-style: none">
{foreach $data as $field}
<li>— <b>{$field['name']}</b>: {$field['value']}</li>
{/foreach}
</ul>
{/if}
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.