Итак, настал момент, когда вы твердо решили монетизировать свой проект и выбрали для этого простой и легкий (на первый взгляд) способ - СМС. Допустим, ваш выбор пал на сайт SMSCoin и одну из его услуг - смс:ключ. Рассмотрим во всех деталях порядок действий, подводные камни и возможные решения.
Перед тем как начать извращения, давайте уточним для чего нам это нужно.
Например, на вашем сайте есть информация или услуга, за которую вы бы не против получить небольшое вознаграждение. Простейшим решением во все времена было создание Премиум-аккаунтов, VIP-аккаунтов и т.д. Попросту говоря, вам нужно взять деньги с пользователей и дать им больше возможностей, чем обычным пользователям. При этом автоматизация процесса может занимать много времени и средств (сам делал, знаю).
Повсеместное распространение сотовых телефонов и СМС агрегаторов в частности, делает возможным получить пользователю то что он хочет даже не регистрируясь на вашем сайте. Как это работает?
Часть 1. Операторы, агрегаторы, страны.
Основные участники системы:
- Пользователь с сотовым телефоном.
- Ваш сайт с информацией или услугой.
- Агрегатор с возможностью получать СМС с разных стран и операторов.
Действия участников системы:
- Желание пользователя получить заветный файл на вашем сайте приводит его на страницу с текстом: "отправь СМС на такой-то номер и получи пароль доступа". Пользователь отправляет СМС.
- Агрегатор получает СМС, генерирует ключ и передает его обработчику на вашем сайте и одновременно отправляет пользователю СМС с этим же ключом. Теперь ключ знает пользователь и ваш сайт.
- Вам остается только проверить идентичность ключей и дать пользователю то, зачем он пришел.
Simple as it get's. Теперь подводные камни и извращения.
Понятно, что основой взаимодействия вашего сайта и пользователя является форма ввода СМС-ключа. Что нас ждет здесь? Сама валидация ключа и обработчик сабмита формы ничего сложного не представляют. В валидаторе мы сравниваем то что ввел пользователь с тем что лежит в нашей базе. Если пользователь ввел невалидный ключ, то скорее всего он и не отправлял СМС и мы ничего не получали от SMSCoin - уведомляем пользователя, доступ не даем. Если ключ введен правильно - показываем закрытую информацию, делаем другие действия. Все хорошо, но как нам сказать пользователю куда слать СМС? Для этого нам нужно усложнить нашу форму.
Многие могут спросить: а почему бы не воспользоваться стандартными решениями выбора страны и оператора, ведь у многих агрегаторов уже есть готовые примеры и остается только вставить в шаблон формы и радоваться жизни? Ответ: причин несколько, начиная с того, что мы все-таки подходим к вопросу серьезно и раз уж пишем для друпала, то и будем пользоваться теми средствами, которые он предлагает... Заканчивая тем, что данное решение имеет место для одной услуги. А если на вашем сайте вы хотите принимать СМС разной стоимостью? Для каждого "закрытого" контента делать отдельную страницу? Не комильфо... Об этом далее.
Практически все агрегаторы работают со многими странами и с несколькими операторами сотовой связи. Поэтому, первым вопросом будет - а из какой ты страны? Если пользователь выбирает, допустим, Белоруссия - агрегатор говорит: "шли СМС с таким-то текстом на такой-то номер". Если пользователь выбирает Россию - агрегатор спрашивает - а какой у тебя оператор? - билайн! - шли сюда... К форме добавляются 3 динамических элемента: выбор страны, выбор оператора, текст с инструкцией отправки, при чем текст инструкции зависит как от оператора, так и от страны, и выбор оператора так же зависит от страны.
И это все динамически... Нда.
Ах да, чуть не забыл сказать о том, откуда мы сами берем информацию о стоимости СМС и номере отправки. Естественно от агрегатора... Агрегатор по крону дает нам информацию в виде XML, который мы парсим и складываем в 2 таблицы: страны, операторы. Вы можете ничего не парсить и в таблицы не складывать, а я буду, мне так удобнее потом брать нужную информацию.
Итак, форма имеет вид:
select - выбор страны
select - выбор оператора
item - инструкция по отправке СМС
textfield - ввод смс ключа
submit - это не знаю зачем
Нам нужно: а) в зависимости от страны показать оператора, б) в зависимости от оператора показать инструкцию по отправке
либо: а) в зависимости от страны показать инструкцию по отправке.
Часть 2. #prefix, #ahah, #suffix.
Как 2 селекта и item заменить одним селектом? При этом информация будет изменяться без перезагрузки страницы. Нам поможет #ahah.
'Россия' => array
(
'1-1' => 'Билайн',
'1-2' => 'MTS',
'1-3' => 'Megafon',
),
'2' => 'Украина',
'3' => 'Белоруссия',
'4' => 'Латвия',
);
$form['fieldset']['country'] = array(
'#type' => 'select',
'#title' => t('Страна и оператор'),
'#options' => $options,
'#prefix' => '<div id="edit-fieldset-country-select">',
'#suffix' => '</div>',
'#ahah' => array
(
'path' => 'path/to/your/country_js',
'wrapper' => 'edit-fieldset-country-select',
'event' => 'change',
'method' => 'replace',
),
);
Теперь построчно.
Сначала мы задаем массив с возможными странами и операторами (информация берется из таблиц стран и операторов).
Далее объявляем наш главный селект.
Обратите внимание, что #prefix
прописывает id="edit-fieldset-country-select" и #ahah[wrapper]
указывает на тот же самый идентификатор. Вот и вся магия - мы говорим AHAH, чтобы при изменении элемента edit-fieldset-country-select мы заменяли его же содержимое на то, что вернет нам обработчик по адресу 'path' => 'path/to/your/country_js'
. А что нам возвращает наш обработчик?
{
$country = $_POST['country'];
$options = array(
'Россия' => array
(
'1-1' => 'Билайн',
'1-2' => 'MTS',
'1-3' => 'Megafon',
),
'2' => 'Украина',
'3' => 'Белоруссия',
'4' => 'Латвия',
);
$form_element = array(
'#type' => 'select',
'#title' => t('Страна и оператор'),
'#options' => $options,
'#default_value' => $country,
// вот тут мы выводим инструкцию по отправке
'#suffix' => '<div class="form-item">Отправьте СМС на номер XXXX в зависимости от $country</div>',
);
drupal_alter('form', $form_element, array(), '_mymodule_form_get_operators');
$form_state = array('submitted' => FALSE);
$form_build_id = $_POST['form_build_id'];
if (!$form = form_get_cache($form_build_id, $form_state)) {
exit();
}
$form['fieldset']['country'] = $form_element;
form_set_cache($form_build_id, $form, $form_state);
$form += array(
'#post' => $_POST,
'#programmed' => FALSE,
);
// Rebuild the form.
$form = form_builder($form_build_id, $form, $form_state);
$program_form = $form['fieldset']['country'];
// нужно обязательно закомментировать следующую строку, чтобы #suffix не обнулился
//unset($program_form['#prefix'], $program_form['#suffix']);
$output = drupal_render($program_form);
drupal_json(array('status' => TRUE, 'data' => $output));
}
Вот собственно и вся магия. При изменении страны или оператора вызывается обработчик AHAH который к тому же самому элементу формы дописывает #suffix
с инструкцией. Так 2 селекта и item заменились на один селект и функцию обработки события.
Теперь вернемся к вопросу о нескольких услугах и разной стоимости СМС. Грубо говоря - это можно назвать тарифами. И нам добавляется ещё одна головная боль - выбор пользователем тарифа, который он хочет оплатить. Но, используя тот же AHAH - это делается быстро и легко. Сначала пользователь выбирает тариф, отрабатывает обработчик и в зависимости от тарифа формируется список стран и операторов. Т.е. мы замыкаем обработчик не на самого себя, а уже на другой элемент.
Вопрос, который я хотел решить изначально - сделать несколько селектов: выбор тарифа, выбор страны, выбор оператора, вывод инструкции с помощью одного только AHAH - не решился по одной простой причине. AHAH может обработать только один элемент формы (сам себя, либо любой другой), поэтому, выбрав тариф, затем страну, затем оператора - пользователь получит инструкцию. Но, допустим, пользователь захочет сменить тариф, при этом обновится только список стран, а список операторов и инструкция так и останутся видны пользователю, что понятно не катит. Пришлось искать другое решение и оно нашлось. Метод тыка - не только лучший способ что-либо узнать... Это единственный способ.
Комментарии
что-то тут не того в коде обработки AHAH. По уму надо бы взять готовый элемент из кэша формы, поменять ему суффикс и прочие свойства и вернуть в кэш дабы не плодить дубли. А иначе если что-то надо будет поменять в форме, с таким кодом придется лезть в два места и сравнивать. Плюс ко всему, последовательность - формирование элемента->drupal_alter()->и вдруг внезапный выход если "!$form = form_get_cache($form_build_id, $form_state)" логически неправильная.
Доля смысла в этом есть, но... Во всех примерах, которые я нашел в инете - было сделано именно так, везде пример модуля poll, никаких других примеров.
С другой стороны пусть я лучше залезу в 2 места своего кода, нежели буду шерстить функции друпала работы с кэшем формы.
Дубли плодиться не будут, так как #ahah['method'] = 'replace'.
и чего там шерстить-то? В коде уже все есть, всего лишь подкорректировать чуток.
... тут проверки всякие.....
}
$form['fieldset']['country']['#suffix'] = '<div class="form-item">Отправьте СМС на номер XXXX в зависимости от $country</div>';
form_set_cache($form_build_id, $form, $form_state);
... дальше друпал_альтер, вывод и прочее.......
Про дубли, я имел в виду дубли кусков кода в исходниках.
По поводу примеров - рекомендую посмотреть исходники CCK, там где реализуется вывод мультиполей, нюансов на самом деле много. Стандартные модули poll или тот же upload это довольно-таки узконаправленные варианты для изучения, IMHO конечно.