Когда приходит понимание того, что технология, любимая многими, тебя настолько устарела, что ты готов к кардинальным изменениям. Многие разработчики крутили свои велосипеды, писали на чистом PHP, создавали свои CMS. Но в какой-то момент надоедает заниматься извращениями и хочется создавать большие проекты с меньшими затратами времени.
Понять Drupal-way задача не из лёгких. Тем более, что эта CMS не использовала ООП, от слова совсем. А использовала систему хуков, которые вызывались в момент формирования страницы. Подключать jQuery скрипты нельзя в шаблонах, а нужно подключать в коде своего модуля! Вынести сами скрипты в футер, тоже нельзя, потому что поломается нормальная работа функционала со скриптами.
Просто стандартный jQuery скрипт, тоже нельзя подключить. Его нужно оборачивать в специальный друпаловский обработчик. И если в обычной жизни разработчика, скрипт получает в себя настройки, через вызов аякса, к примеру, то в Друпал при подключении скрипта из своего модуля, в него можно передать параметры с бэкенда.
Все эти штучки и приколы были не просто необычны, но еще и очень тяжелы для понимания и восприятия. На картинке ниже можно увидеть кривую изучения разных CMS.
Давайте разберем ряд примеров, как реализовать что-то на Друпал, и повторим это же на Laravel. Для начала напишем роут страницы добавления объявления для посуточной аренды недвижимости на Drupal:
/**
* Implementation of hook_menu().
*/
function itp_menu() {
$items = array();
// Add new announcement Daily rent.
$items['add_new_daily_rent'] = array(
'title' => t('Add: Daily rent'),
'description' => '',
'page callback' => 'itp_add_new_daily_rent',
'page arguments' => array(),
'access arguments' => array('seller user'),
'type' => MENU_CALLBACK,
// Add widget for upload files to form.
'file path' => drupal_get_path('module', 'node'),
'file' => 'node.pages.inc',
);
}
В коде выше мы видим хук меню, который заменяет систему роутинга во фреймворках. Именно в этом хуке и нужно описывать все ссылки. При чём, каждый модуль использует этот хук. В голом виде он выглядит так: hook_menu(). В каждом модуле, слово hook заменяется названием своего модуля. Весь этот ужас, в случае ошибки, отлавливать крайне непросто.
- $items[‘add_new_daily_rent’] — это адрес, вида http://mysite/add_new_daily_rent
- ‘page callback’ — функция, которая будет вызвана при переходе на эту страницу
- ‘page arguments’ — директива, через которую можно передать переменную из адресной строки, например, так: array(1). Это означает, что если бы адрес выглядел так: add_new_daily_rent/10, то array(1) — это получение первого аргумента, то есть 10, и передача его в функцию вызова.
Аргументы считаются с нуля.
- ‘access arguments’ — проверка прав. Только пользователи с ролью продавцы и с правами на добавление объявления, могут вызвать функцию
- ‘type’ — тип действия. Можно создать ссылку в меню или закладку, но в нашем случае, это просто вызов колбека.
Две последних строчки дают возможность работать с загрузкой изображений через стандартные механизмы, по сути это костыль.
А теперь все тоже, но уже на Laravel:
// Show form.
Route::get('add_new_daily_rent', 'DailyRentController@create')->name('daily.create');
Теперь заглянем в колбек Drupal:
/**
* Output form for add new Daily rent.
*
* @global object $user
* @return object form.
*/
function itp_add_new_daily_rent() {
// Machine name content.
$node_type = 'daily_rent';
$form_id = $node_type . '_node_form';
// Add current users info.
global $user;
global $language;
// Create empty node.
$node = new stdClass();
$node->uid = $user->uid;
$node->name = (isset($user->name) ? $user->name : '');
$node->type = $node_type;
$node->language = $language->language;
// Ready object node for editing.
node_object_prepare($node);
return drupal_get_form($form_id, $node);
}
Если в двух словах, то определяем название формы, создаем новый пустой stdClass ноды, передаём туда пользователя, локализацию, тип ноды, подготавливаем саму ноду и вызываем отображение формы, куда и передаем пустую ноду.
Сама форма построена в конструкторе форм, и хранится в базе данных. Но можно строить и свои формы в коде. Вот пример построения формы для связи с владельцем недвижимости:
// Create form for send message author's.
function itp_send_mail_form() {
if (arg(0) == 'modal_forms') {
// Load options node.
$node = node_load(arg(3));
}
else {
$node = node_load(arg(1));
}
// If isset node.
if (!empty($node)) {
$user_name = '';
$user_tel = '';
$user_email = '';
$form['nid'] = array(
'#type' => 'hidden',
'#value' => $node->nid,
);
$form['user_name'] = array(
'#type' => 'textfield',
'#title' => t('Your name'),
'#description' => t('Enter your name'),
'#default_value' => $user_name,
'#size' => 50,
'#maxlength' => 50,
'#required' => TRUE,
);
$form['user_tel'] = array(
'#type' => 'textfield',
'#title' => t('Your phone'),
'#description' => t('Enter your phone (with code), example, +380(67)-321-21-21'),
'#default_value' => $user_tel,
'#size' => 50,
'#maxlength' => 30,
'#required' => TRUE,
);
$form['user_email'] = array(
'#type' => 'textfield',
'#title' => t('Your e-mail'),
'#description' => t('Enter your e-mail address'),
'#default_value' => $user_email,
'#size' => 50,
'#maxlength' => 30,
'#required' => TRUE,
);
$form['user_text'] = array(
'#type' => 'textarea',
'#resizable' => FALSE,
'#rows' => 3,
'#title' => t('Message'),
'#description' => t('Enter your message'),
'#required' => TRUE,
);
$form['user_submit'] = array(
'#type' => 'submit',
'#value' => t('Send'),
'#weight' => 20,
'#submit' => array('itp_send_mail_form_submit'),
);
$form['cancel'] = array(
'#markup' => l(t('Cancel'), '#',
array(
'attributes' => array(
'class' => 'ctools-close-modal'
),
'external' => TRUE
)
),
'#weight' => 21,
);
return $form;
}
else {
drupal_set_message(t('This announcement does not exist!'), 'error');
return '';
}
}
// Check entered data.
function itp_send_mail_form_validate($form, &$form_state) {
$email = $form_state['values']['user_email'];
if (!valid_email_address($email)) {
form_set_error('user_email', t('E-mail is not correct!'));
}
}
// Submit form.
function itp_send_mail_form_submit($form, &$form_state) {
$nid = $form['nid']['#value'];
$user_name = $form['user_name']['#value'];
$user_tel = $form['user_tel']['#value'];
$user_email = $form['user_email']['#value'];
$user_text = $form['user_text']['#value'];
$sent_mess = itp_send_mail($nid, $user_name, $user_tel, $user_email, $user_text);
// If message don't send.
if (!$sent_mess) {
$sent_mess_text = '
' . t('Error sending message. Message not sent.') . '
';
}
else {
$sent_mess_text = '
' . t('Your message successful sent.') . '
';
}
$sent_mess_text .= '
' . l(t('Close'), '#',
array(
'attributes' => array(
'class' => 'ctools-close-modal',
),
'external' => TRUE
)
) . '
';
// Reload dialog page and show message status.
$form_state['ajax_commands'][] = ctools_modal_command_display(t('Message status'), $sent_mess_text);
}
Даже не хочется это комментировать. Авот так выглядит вызов метода для Ларавел:
/**
* Show the form for creating a new daily announcement.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
// Check permission.
$this->authorize('content-users');
return view('daily.create');
}
А создание формы — это простой процесс в шаблоне. Именно там, где ей и место.
Следующий пример. Drupal — это CMS, а если точнее, то CMF система. Поэтому в нём запилено огромнейшее количество готовых решений. Но мы не хотим кушать то, что нам подсовывают, мы хотим изменить вывод, например формы, так, как нужно нам. Для этого существуют хуки-альтеры:
/**
* Implementation of hook_form_FORM_ID_alter().
*/
function itp_form_user_profile_form_alter(&$form, &$form_state) {
$form['account']['name']['#title'] = t('Login');
if (!user_access('administer users')) {
unset($form['path']);
unset($form['field_my_favorites']);
$form['field_user_status'][LANGUAGE_NONE]['#disabled'] = TRUE;
}
$form['account']['mail']['#prefix'] = '
' .
t("Attention! Messages can get into the SPAM folder. If you don't see post into the folder INBOX, please check SPAM folder.
Find a message and change status on the NOT SPAM. Please don't use e-mail from hotmail.com!") .
'
';
$form['field_user_status']['#prefix'] = '
' .
t("Only seller can add announcements!") .
'
';
// Delete field progress.
unset($form['field_progress']);
}
/**
* Implementation of hook_form_FORM_ID_alter().
*/
function itp_form_user_register_form_alter(&$form, &$form_state) {
global $language;
$path = url('agreement', array('absolute' => TRUE));
$form['account']['name']['#title'] = t('Login');
$form['path']['#disabled'] = TRUE;
$form['path']['#prefix'] = '
';
$form['agree'] = array(
'#type' =>
'checkbox',
'#title' => t('User Agreement'),
'#description' => t('You must read and agree to the User Agreement',
array('@agree' => $path)),
'#weight' => 14,
'#required' => TRUE,
);
$form['account']['mail']['#prefix'] = '
' .
t("Attention! Messages can get into the SPAM folder. If you don't see post into the folder INBOX, please check SPAM folder.
Find a message and change status on the NOT SPAM. Please don't use e-mail from hotmail.com!") .
'
';
$form['field_user_status']['#prefix'] = '
' .
t("Only seller can add announcements!") .
'
';
// Add own validator.
$form['#validate'][] = 'itp_register_form_validate';
}
В Ларавел же просто допишите в шаблон что вам нужно за две минуты!
В итоге, поддерживать fпроект на Друпале не просто трудно — это может превратиться в самоистязание. Любое новое обновление ядра приводит к каким-то глюкам. Все эти удобные роуты и формы, плюс типы контента которые создавались из админки Друпала, хранятся в базе данных. Для того, чтобы всё это не потерять и хранить в git, нужен жестокий костыль — модуль Features. Кто понимает, как он работает и заглядывал в тот хаос, который он создает в экспортированных файлах, тот должен меня понять.
Когда выходит новая версия одного из сотен, установленных в Drupal модулей, начинает бить мелкая дрожь. Начинается полный бэкап сервера и базы. А это, между прочим, терабайты данных! И восстановится из бэкапа — это порой очень непростая задача.
Очень часто бывает, что при обновлении чужого модуля Drupal, сайт падает на продакшне. Первым делом отваливается база, где хранятся все системные настройки самой CMS! Что делает отладку сложной задачей.
Если вы умеете алгоритмы и писать код, то фреймворк — это единственно правильное для вас решение. CMS зло на крупных проектах это зло! Но лучше вообще полностью отказаться от использования CMS, даже на мелких проектах. Да, с CMS быстро, но это путь в никуда. Тратить время на борьбу с CMS нет никакого желания. Лучше сосредоточится на своих идеях и написать код, почти на голом PHP, используя удобный механизм, который даёт Laravel.
Те, кто обожает писать свой код и свои решения, должен оценить фреймворк. А кто ленивый и ищет уже готовые решения из коробки, тот не оценит, до поры до времени.
Однажды меня просили отремонтировать сайт на Битрикс. Я его скачал и начал изучать код. Я был в шоке! Такой помойки я не видел! Куча чудовищного разнородного кода! Подход к формированию HTML просто варварский. И я отказался. Как сказано ещё первобытными программистами: чем в чужом коде разбираться, легче свой написать.
Никогда не использовал CMS. Скачивал, смотрел что это такое. Но когда видел, что простую страницу с простым текстом формируют 28 запросов к базе данных, как куски HTML хранятся в огромном количестве таблиц! Всякое желание что-то с ними делать отпадало. Я очень долго писал на голом php и признал фреймворки только когда понял, что иначе вымру как мамонт.
Но, к огромному сожалению, подавляющее большинство заказчиков хотят на CMS. Так как дёшево и быстро. Качество многих вообще не волнует. А фреймворки — это уже корпоративный сегмент. Найти независимому программисту такого заказчика задача не простая.