Мультисайтинг на Друпале - это круто!

Импульсом к написанию этой статьи стала потребность в мультисайтинге, идеей чего я озаботился сразу после прочтения статей

Мультисайтинг – как и зачем , автор jerboa7,
Мультисайтинг - это просто, автор Макс Кириленко,
а чуть позже к ним присоединился и Mультисайтинг. В который раз. :) автора andron13

и усвоил мысль, что если хочешь потом сделать мультисайтинг, то лучше его делать сразу.

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

1. Установка Друпал
Паркуем на хостинг наш домен сайт1. Этот домен будет указывать на какую-то директорию на Вашем сайте, которая для него будет являться базовой. В эту директорию на хостинге копируем Друпал - распаковываем его из архива.
Заводим новую БД на хостинге. Её будет использовать Друпал. Завели? Теперь у нас есть База Данных:

Имя БД: БД_сайт1
Пользователь БД: юзер_БД_сайт1
Пароль пользователя: пароль_БД_сайт1

У пользователя юзер_БД_сайт1, само собой, должны быть выставлены привелегии, позволяющие ему полный доступ к базе данных БД_сайт1.

Также у Вас есть файл settings.php, находящийся в подпапке sites/default/settings.php. В нем прописываются все необходимые параметры базы данных, нужные для работы Друпал. Заполните его, в соответствии с инструкциями, расположенными в нем в комментариях, и описанными ниже.

Строка доступа к базе данных, выглядит так:

$db_url = 'mysql://юзер_БД_сайт1:пароль_БД_сайт1@localhost/БД_сайт1';

Еще мы должны задать префикс таблиц. Это означает, в самом простом случае, что к имени таблицы в БД будет приписываться в начале некий префикс:

$db_prefix = 'префикссайт1_';

т.е. таблица с именем, скажем, sequences будет префикссайт1_sequences. Я рекомендую в конце префикса ставить знак подчёркивания "_", чтобы отделять префикс от собственно имени таблицы.

Префикс можно не задавать:

$db_prefix = '';

однако я не рекомендую этого делать. Подумайте о возможной SQL атаке? Или просто о попытках взломать ваш сервер. Хакеру не трудно будет догадаться, что Вы используете Друпал. Ну а с какими именами Друпал создаёт таблицы, тоже хорошо всем известно. Поэтому Вы затрудните доступ к Вашим таблицам, если вначале каждой пропишите префикс из случайной последовательности символов.

А теперь информация для самых ретивых: задавая префикс таблиц не в виде

$db_prefix = 'префикссайт1_';

а в виде

$db_prefix = 'префикссайт1.';

Вы на самом деле указываете Друпал, что он должен обращаться к базе данных "префикссайт1", вместо БД_сайт1! Поэтому будьте предусмотрительны при выборе разделителя префикса и имени таблицы. Самое удобное, на мой взгляд, использовать "_" - знак подчёркивания.

Однако и это ещё не все о префиксах. Друпал позволяет для каждой из таблиц задать отдельный префикс. Выглядит это так:

$db_prefix = array (
'default'   => 'main_',
'users'     => 'shared_',
'sessions'  => 'shared_',
'role'      => 'shared_',
'authmap'   => 'shared_',
'sequences' => 'shared_',
);

Зачем это надо? Если Вы ставите 1 сайт и уверены, что больше Вам никогда не понадобиться, то не забивайте себе голову. Ну позволяет это Друпал, ну и позволяет, Вам-то что?

Однако если когда-нибудь Вы запланируете поставить ещё один сайт, (о чем и пойдёт дальше речь), да так, чтобы не копировать ещё раз все скрипты (ядро и модули), да чтобы использовать какую-нибудь информацию, которая у Вас уже есть в БД (например, переводы Друпала и модулей, или данные самописных модулей, или данные из общих таблиц, созданных вообще другими скриптами) - то эта возможность очень даже пригодится... Но обо всем по порядку.

Итак, сайт1 мы поставили, запустили, работает - все замечательно! И вот в один прекрасный день...

2. Возникает потребность в новом сайте на Друпале, хотим организовать его на том же хостинге. Имя сайта - сайт2.
Паркуем на хостинг наш новый домен сайт2. Домен должен указывать на ту директорию, в которую вы ранее скопировали/поставили Друпал (в нашем примере - на директорию с сайт1).

Если после парковки домена при переходе на него мы попадаем на сайт1. (Так должно быть, если этого не происходит, значит что-то сделано не так.)

Создаём директорию sites/сайт2, копируем в него файл settings.php и правим его в соответствии с нуждами нашего сайт2:

задаем параметры доступа к БД,
прописываем другой (не такой как у сайт1) префикс таблиц для сайт2.

Переходим опять по нашему новому адресу сайт2 - и видим, что Друпал сыпется ошибками. И правильно, ведь никаких таблиц в БД сейчас нет. В БД сейчас есть только таблицы для сайт1. Друпал думает, что у нас уже установлен сайт2, и пытается сгенерировать главную страницу, но обламывается. Вот так вот, сгенерив страницу с ошибками, Друпал кэширует её. Если вы попробуете перейти опять по новому адресу, то увидите страницу с ошибками опять. Однако нам пока главная страница не нужна.

Перейдём на страницу установки Друпала для сайта2, т.е. перейдем на сайт2/install.php - скрипт установки отработает, создав нужные таблицы и прописав все необходимые связи между ними. Если какие-то таблицы уже существуют, они перезаписаны не будут, и никаких повреждений в них не будет, однако Друпал сообщит об ошибке при создании этих таблиц.

Перейдём на главную страницу свежесозданного сайта - сайт2. Видим опять всяческие сообщения об ошибке. Это отображается закэшированная ранее страница. Обновите ее - и о чудо! - видим первую страницу Друпала для сайта2 с инструкциями.

Т.е. у нас получилось два сайта, использующие одни и те же скрипты и одну и ту же БД, но разные таблицы в БД. Очень удобно.

А теперь помните, что если бы в качестве разделителя префикса таблиц указали не знак подчёркивания, "_", а точку - ".", то Друпал обращался бы не к базе данных "БД_сайт1", а к базе "префикссайт2"? Это можно использовать, если вы хотите чтобы для разных сайтов использовалась разная БД, но был один и тот же пользователь БД и пароль (такая задача часто возникает при объединении таблиц, о чем речь пойдёт дальше). Конечно можно просто задать разные параметры для БД для сайт2.

Но давайте представим, что у Вас...

3. Возникает понимание, что некоторую информацию сайта1 и сайта2 неплохо бы объединить. Например сделать общих пользователей.

Вот здесь-то и проявляется вся гибкость и мощь Друпала, здесь-то мы и осознаем в полной мере, насколько удобно задавать разным таблицам разные префиксы, и как Друпал умеет работать с разными БД.

Сделать общим можно все! Начиная от пользователей и заканчивая документами (нодами). Но если общих пользователей я ещё могу понять и принять, то делать общие документы между сайтами я не вижу смысла - тогда чем будет отличаться один сайт от другого? Только дизайном? Поэтому, после некоторого тестинга, я пришёл к выводу, что объединить без ущерба нарушения целостности БД и психики можно:

  1. Пользователей,
  2. Роли пользователей и права ролей
  3. Профили пользователей
  4. Переводы ядра и модулей
  5. Словари
  6. Термины в словарях
  7. Фильтры сообщений - насчет этого уже не уверен, т.к. таблица filter_formats привязывается к кэшу (поле cash), а таблица filters использует id фильтра из таблицы filter_formats, таким образом, обе таблицы фильтров привязаны к конкретному сайту.

Т.е. вообще все таблицы и модули, в которых нет жёсткой привязки к документам (нодам).

Также сразу скажу, что задача объединения словарей и терминов в них (модуль таксономии) может возникнуть только на очень тесных и близких по тематике сайтах. Я объединил словари и попробовал использовать в таком режиме документы, это оказалось очень неудобным и нецелесообразным. Поэтому мы не будем это рассматривать в этой статье. Остальные же вещи, удалось объединить очень даже удобно.

Итак, чтобы сделать общих пользователей, нам нужно сделать для сайтов общие таблицы пользователей. Допустим, что у нас уже есть сайт1, и новый сайт сайт2, и мы осознали что неплохо было бы, если пользователь зарегистрировался на каком-либо из этих сайтов, имел бы доступ и к другому.

За пользователей у нас в Друпале отвечают следующие таблицы:

'authmap'
'sessions'
'sequences'
'users'

Таблица users - непосредственно хранит данные пользователя.
Таблица sequences - хранит служебную информацию Друпал. Для каждой таблицы, в sequences сохраняется её имя, и следующее значение первичного ключа. (Если вообще планируете сделать что-то общее для каких-то двух сайтов, таблицу sequences рекомендую делать общей при любом раскладе).
Таблица sessions хранит информацию текущих сессий пользователя.
Таблица authmap я не знаю пока чего хранит.

Помимо вышеперечисленных таблиц, для пользователей существуют роли:

'role'
'users_roles'

При этом в таблице role хранится для каждого сайта информация о ролях пользователей, а в таблице users_roles - информация, какой пользователь к какой группе (роли) принадлежит. Скажу сразу, что если Вы хотите сделать так, чтобы роли для каждого сайта были абсолютно независимы, то Вам не нужно делать общей таблицу role. Таблицу users_roles можно сделать общей, если Вы делаете общих пользователей. Я же, потестив немного, нашёл, что очень удобно когда роли пользователей общие для всех сайтов, т.к. не нужно по 10, или сколько у вас там сайтов, раз заводить одни и те же роли: вот этим пользователям можно то, а этим это.

Profile module. Если у Вас включён модуль профилей для пользователей, тогда его таблицы тоже имеет смысл сделать общими:

'profile_fields' - хранит информацию о полях профиля
'profile_values' - хранит информацию поля.

Locale module. Модуль локализации. Одна из удобнейших возможностей - использовать общую таблицу локализации, т.к. переводы всего и вся можно залить один раз и потом просто выбирать из админки нужного сайта.
'locales_meta'
'locales_source'
'locales_target'

BUEditor Module. Это я привел чисто для примера. Любой модуль, не использующий привязку к документам, может быть объединён. Строго говоря, то же верно и для любого модуля, жёстко привязанного к документам, просто в нем надо найти таблицу связей для модуля и документов, и не делать её общей. Для модуля BUEditor таких таблиц нет, поэтому смело объединяем:
'bueditor_buttons'
'bueditor_editors'

Что нужно, чтобы использовать на сайт2 эти таблицы, которые уже поставлены и работают для сайт1? Ответ очень прост: надо просто прописать префикс сайт1 для этих таблиц: "префикссайт1_", в файле настроек settings.php для сайт2:
$db_prefix = array (
'default'   => 'main_',
'authmap' => 'префикссайт1_',
'sessions' => 'префикссайт1_',
'sequences' => 'префикссайт1_',
'users' => 'префикссайт1_' и т.д. если объединять будете что либо кроме пользователей.

Также, сайт1 и сайт2 должны использовать одну и ту же БД.

Теперь из админки сайт2 доступны пользователи (и еще что-нибудь, если еще что-нибудь объединяли) сайт1. И пользователи на сайтах общие.

Всё!

Ах да... еще одна деталь. Если Вы будете мультисайтить большое количество сайтов, то для этого может быть целесообразно разделить сайты по разным базам данных. В этом случае схема будет такой: создаем столько баз данных, сколько у нас сайтов, и еще одну общую (shared) и при этом пользователь для всех этих БД должен быть один и тот же. В качестве префиксов указываем префиксы с точкой, например так: "сайт1.". Т.е. в нашем случае можно сделать так:

БД с именем shared (общая для всех сайтов) -

'authmap'
'sessions'
'sequences'
'users'
'role'
'users_roles'
'profile_fields'
'profile_values'
'locales_meta'
'locales_source'
'locales_target'
'bueditor_buttons'
'bueditor_editors'

БД с именем сайт1 (БД для сайт1) -
содержит все таблицы, кроме тех, что в таблице shared
БД с именем сайт2 (БД для сайт2) -
содержит все таблицы, кроме тех, что в таблице shared

В файле settings.php Вы прописываете значения (для сайт1):

$db_url = 'mysql://юзер_БД:пароль_БД@localhost/сайт1';
$db_prefix = array (
'default'   => 'сайт1.',
'authmap' => 'shared.',
'sessions' => 'shared.',
'sequences' => 'shared.',
'users' => 'shared.',
//Роли общие
'role'  => 'shared.',
'users_roles' => 'shared.',
//Profile module
'profile_fields' => 'shared.',
'profile_values' => 'shared.',

//Locale module :
'locales_meta' => 'shared.',
'locales_source' => 'shared.',
'locales_target' => 'shared.',
//BUEditor Module :
'bueditor_buttons' => 'shared.',
'bueditor_editors' => 'shared.'
);