Как динамически использовать несколько баз данных для одной модели в CakePHP
Хорошо, мой первый вопрос изменялся так много раз, что я решил удалить его и переформулировать свой вопрос. Я сделал небольшой тестовый проект с разными названиями моделей, чтобы найти правильное решение моей проблемы.
Предупреждение: Не смешивайте базы данных с таблицами
Мотивация: Я разделил пользовательские данные на несколько баз данных по юридическим вопросам и вопросам производительности.
В настоящее время я работаю над CakePHP проект, который имеет несколько User
, и каждый User
имеет свою собственную базу данных с несколькими таблицами (cars
является одной из таблиц ). Теперь мне нужно сначала кое-что объяснить:
У каждого User
есть своя база данных (не таблица, а база данных ), поэтому имена баз данных следующие.
- [БАЗА ДАННЫХ]
app
(Это основная база данных приложения)- [ТАБЛИЦА]
users
- [ТАБЛИЦА]
permissions
( Не имеет отношения к делу для этого вопроса)
- [ТАБЛИЦА]
- [БАЗА ДАННЫХ]
app_user1
(User.id
1 владеет всей этой базой данных )- [ТАБЛИЦА]
cars
(таблица полностью принадлежитUser.id
1)
- [ТАБЛИЦА]
- [БАЗА ДАННЫХ]
app_user2
(User.id
2 владеет всей этой базой данных )- [ТАБЛИЦА]
cars
(таблица полностью принадлежитUser.id
2)
- [ТАБЛИЦА]
- и т.д....
Я сделал небольшой рисунок, который мог бы прояснить базу данных / таблица - определения и их связь с моделями:
В чем проблема!!!
Я не знаю, к какой базе данных подключиться, пока User
не войдет в систему. User
и их базы данных создаются динамически, поэтому я не могу использовать app/Config/database.php
.
Поэтому в настоящее время я пишу расширение для классов Model
и ConnectionManager
, чтобы обойти базовое поведение базы данных CakePHP. Таким образом, модель Car
знает, какую базу данных использовать. Но у меня просто такое чувство, что это можно было бы сделать проще!
Так что, я думаю, все сводится к одному вопросу:
Есть ли более простой способ сделать это?!
Спасибо всем, кто потратит время и усилия на чтение и понимание моей проблемы!
5 answers
Этот джентльмен ( У Оливье) была та же проблема! (Год назад) Он написал небольшую адаптацию для Controller
s! Он довольно маленький, и, оказывается, он работает в 1.3 и 2.x.
В любом случае, это мое окончательное решение, которое я вложил в app/Model/AppModel.php
:
class AppModel extends Model
{
/**
* Connects to specified database
*
* @param String name of different database to connect with.
* @param String name of existing datasource
* @return boolean true on success, false on failure
* @access public
*/
public function setDatabase($database, $datasource = 'default')
{
$nds = $datasource . '_' . $database;
$db = &ConnectionManager::getDataSource($datasource);
$db->setConfig(array(
'name' => $nds,
'database' => $database,
'persistent' => false
));
if ( $ds = ConnectionManager::create($nds, $db->config) ) {
$this->useDbConfig = $nds;
$this->cacheQueries = false;
return true;
}
return false;
}
}
И вот как я использовал его в своем app/Controller/CarsController.php
:
class CarsController extends AppController
{
public function index()
{
$this->Car->setDatabase('cake_sandbox_client3');
$cars = $this->Car->find('all');
$this->set('cars', $cars);
}
}
Держу пари, я не первый и не последний, кто столкнулся с этой проблемой. Поэтому я действительно надеюсь, что эта информация найдет людей и Сообщество CakePHP.
Мне не нравится идея записывать фактическое имя базы данных в коде. Для нескольких баз данных у вас есть database.php файл, в котором вы можете установить столько баз данных, сколько вам нужно.
Если вы хотите "переключить" базу данных для определенной модели на лету , используйте метод setDataSource. (см. здесь)
Например, если у вас есть две базы данных, вы можете определить их в database.php файл как "по умолчанию" и "песочница", как пример.
Затем в вашем коде:
$this->Car->setDataSource('sandbox');
Изолированная среда - это имя конфигурации, а фактическое имя базы данных записывается только один раз в database.php файл.
Вы всегда можете запросить любые базы данных, используя полную нотацию в mysql. F.например:
SELECT * FROM my_schema_name.table_name;
Способ приготовления торта:
$db = $this->getDataSource();
$query = array(
'fields' => array('*'),
'table' => 'my_schema_name.table_name'
);
$stmt = $db->buildStatement($query, $this);
$result = $db->execute($stmt);
В вашем database.php
public $mongo = array(
'datasource' => 'Mongodb.MongodbSource',
'database' => 'database_name_mongo',
'host' => 'localhost',
'port' => 27017,
);
В вашем контроллере вы можете использовать
$this->Organisation->setDataSource('mongo');
Затем примените запрос, подобный
this->Organisation->find('all');
Это народ принятого ответа. Это предотвратит ошибку, если новый источник данных уже существовал:
public function setDatabase($database, $datasource = 'default')
{
$newDatasource = $datasource . '_' . $database;
try {
// return the new datasource if it's already existed
$db = ConnectionManager::getDataSource($newDatasource);
$this->useDbConfig = $newDatasource;
$this->cacheQueries = false;
return true;
} catch (Exception $e) {
// debug($e->getMessage());
}
$db = ConnectionManager::getDataSource($datasource);
$db->setConfig(array(
'name' => $newDatasource,
'database' => $database,
'persistent' => false
));
if ( $ds = ConnectionManager::create($newDatasource, $db->config) ) {
$this->useDbConfig = $newDatasource;
$this->cacheQueries = false;
return true;
}
return false;
}