Yii - Работа с субдоменами и несколькими конфигурациями

23.09.2011

Задача: нужно реализовать регистрацию пользователя, сразу после которой новому пользователю динамически назначается суб-домен идентичный логину. Также создается отдельная база данных и соответственно свой файл конфигурации приложения.

Предисловие.

Про динамическое создание субдоменов писать не буду, просто намекну - в настройках DNS ставим звездочки и в настройках Apache в директиве ServerName - также звездочку. Подробнее - google в помощь, это просто.

НЕ ПОЛЬЗУЕМСЯ решением yii framework для параметризации имен хостов.

Решение.

1) Начнем по порядку, сперва нам нужна регистрация и авторизация пользователя. Для этих целей я использовал yii-user немного допиленный под конкретные нужды. Устанавливаем его, находим RegistrationController.php и добавляем новый метод класса createNewDb($subdomain) - который создает новую базу данных, файл конфигурации пользователя, генерирует случайные пароли для сайта и базы данных, ну и создает директории пользователя.

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

Из особенностей - создаю новое подключение к серверу mysql, создаю пользователя и базу, заполняю базу начальными таблицами со значениями и последнее - генерирую конфиг пользователя.

Небольшой кусок кода с самыми важными моментами (полностью приводить код - не имеет смысла, сами додумаете, появятся вопросы - пишите):

private function createNewDb($subdomain) {
  $sql1 = "CREATE USER $subdomain@localhost IDENTIFIED BY '$pass'";
  $sql2 = "CREATE DATABASE IF NOT EXISTS users_" . $subdomain;

  // Создаем новое подключение
  $conn2 = new CDbConnection('mysql:host=localhost', 'root', 'pass');
  $conn2->active = true;
  // Добавляем пользователя и создаем БД
  $conn2->createCommand($sql1)->execute();
  $conn2->createCommand($sql2)->execute();
  $conn2->active = false;

  // Заливаем в новую базу таблицы для yii-user
  $conn3 = new CDbConnection("mysql:host=localhost;dbname=users_$subdomain", 'root', 'admin`1');
  $conn3->active = true;
  $sql = "CREATE TABLE `sss_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(128) NOT NULL,
`email` varchar(128) NOT NULL,
`activkey` varchar(128) NOT NULL DEFAULT '',
`createtime` int(10) NOT NULL DEFAULT '0',
`lastvisit` int(10) NOT NULL DEFAULT '0',
`superuser` int(1) NOT NULL DEFAULT '0',
`status` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`),
KEY `status` (`status`),
KEY `superuser` (`superuser`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;";
  $conn3->createCommand($sql)->execute();
  // Тут должны быть ещё как минимум 2 траблицы для yii-user

  // Создание директории и файла конфига
  $pathConfFile = 'protected/users/' . $subdomain;
  $dbName = 'users_' . $subdomain;
  $confString = " 'mysql:host=localhost;dbname=$dbName',
'emulatePrepare' => true,
'username' => '$subdomain',
'password' => '$pass',
'charset' => 'utf8',
'tablePrefix'=>'udd_',
);";
  mkdir($pathConfFile, '0755');
  mkdir($pathConfFile . '/import', '0755');
  $fp = fopen($pathConfFile . '/config.php', 'w');
  fwrite($fp, $confString);
  fclose($fp);
}

Вставляем вызов этого метода куда нибудь перед отправкой письма с подтверждением регистрации. $subdomain получаем из имени пользователя.

2) Теперь нужно сделать проверку на главной странице сайта - если пользователь заходит через субдомен - проверяем авторизован ли пользователь, если нет - перенаправляем на страницу логина, да - редиректим на главную страницу сайта.

user->isGuest)
        $this->redirect(array('/user/login'));
    else
        $this->redirect(array('/site/list'));
}
?>

3) Последнее что нужно сделать - подключать конфиг, соответствующий пользователю. Править будим /protected/config/main.php, также в нём сделаем проверку на существование такого пользователя. Если такой пользователь есть (и соответственно ему нужна работа с субдоменом) всё ок - грузим его конфиг, нет - отправляем регистрироваться.

Суть в том чтобы не переписывать конфиг полностью, а изменять только подключение к БД и определять дополнительные модули.

Делается это следующим образом: сначала присваиваем весь конфигурационный массив какой-нибудь переменной:

$config_array = array(
    'basePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '..',
    'name' => 'My Web Application',
    'language' => 'ru',
    // preloading 'log' component
    'preload' => array('log'),
    // autoloading model and component classes
    'import' => array(
        'application.models.*',
        'application.components.*',
    ),
    'modules' => array(
        'user',
    ),
    ...
    // Дефолтное подключение к БД и тому подобное, всё как в стандартном конфиге
);

/*
* Определяет субдомен ли это?
* Если да - проверяем длину имени субдомена
* Если больше 5 символов - всё ок, подгружаем нужный конфиг и добавляем необходимые модули
* Если же меньше 5 - делает редирект на главную страницу
*/
$domainName = explode('.', $_SERVER['HTTP_HOST']);
if (count($domainName) == 3) {
    if (strlen($domainName[0]) > 5) {
        $clientSite = trim(strtolower($domainName[0]));
        $root = dirname(__FILE__);
        $root = str_replace("/config", "", $root);

        /*
         * Проверяем существует ли такой субдомен впринципе?
         * Если нет - редирект на страницу регистрации
         */
        if (file_exists($root . '/subscribers/' . $clientSite . '/config.php')) {
            include_once($root . '/subscribers/' . $clientSite . '/config.php');
            // Переопределяем в конфиге подключение к БД и добавляем модуль
            $config_array['components']['db'] = $client_config;
            $config_array['modules'][] = 'newModule';
        } else {
            header('Location: http://test/index.php?r=user/registration');
        }
    } else {
        header('Location: http://test/');
    }
}

return $config_array;

Я думаю смысл понятен, просто берём, допиливаем под себя и всё будет работать. Далее подготовлю статью по работе с Ajax'ом рассмотрим на примерах как вообще это делается в Yii framework.

blog comments powered by Disqus
Наверх