我在Laravel中有一个应用程序堆栈,我们将继续切换到SaaS模型。为了做到这一点,我只是假设我可以用动态域属性将所有路由封装在一个组中,启动一个过滤器,然后观察$route参数来实现这一点。
我应该注意到,这实际上是一个多租户应用程序,但我们实际上已经决定为这个应用程序分离数据库。
所以我们开始了:
在我的routes.php
文件中,我得到了以下内容:
Route::group(array('domain' => '{domain}.{tld}', 'before' => 'database.setup'), function()
{
Route::group(array('prefix' => 'backend', 'before' => 'auth'), function () {
//all of my routes
});
});
从上面可以看出,当请求任何路由时,它都会进入我在filters.php
:中定义的database.setup
过滤器
Route::filter('database.setup', function($route, $request){
$domain = $route->getParameter('domain').'.'.$route->getParameter('tld');
$details = DB::table('my_table')->where('domain', '=', $domain)->first();
if($details){
Config::set('database.connections.account', [
'driver' => 'mysql',
'host' => 'my_host',
'database' => Encryption::decrypt($details->db_hash, 'my_salt'),
'username' => 'my_username',
'password' => 'my_password',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'charset' => 'utf8',
]);
//these are things I was doing to get the URL-permalink working.
Config::set('app.url', 'http://' . $domain);
Config::set('app.domain', $domain);
Config::set('session.domain', '.' . $domain);
//This actually works exactly as I've intended
Config::set('database.connections.default', 'account');
DB::setDefaultConnection('account');
}
});
现在一开始我觉得这很好。从表中提取了正确的记录,数据库在销毁前一个实例时迅速切换,没有出现问题太棒了。
然而,我注意到我在路由中丢失了所有的模型绑定关系。
这样的路线:
Route::get('/shipping/packages/{package}', 'PackageController@get');
使用这样定义的模型:
Route::model('package', 'Package');
不幸的是,结果总是这样:
No query results for model [Package].
现在,如果我从Route中删除我的过滤器,一切都很好,但默认数据库将被使用,这对我的应用程序来说是一个很大的nono。
最后,所有的永久链接结构似乎都被彻底打破了。而不是当我悬停在链接上时看到我的域,例如:
http://example.com/shipping/packages/package
我看到的是:
%7Bdomain%7D.%7Btld%7D/shipping/packages/package
我不知道为什么会发生这种事。
我尝试过重载响应对象,更改过滤器中站点配置的设置,以及许多其他事情,但我总是以某种方式遇到同样的问题。
如果有人知道如何解决这个问题,我将不胜感激。
好吧,我已经弄清楚问题出在哪里了。
我显然没有很好地阅读这些文件。如果你遍历路由器组调用,它最终会调用mergeGroup(),在这个特定的函数中,你可以观察到以下代码:
$new['where'] = array_merge(array_get($old, 'where', []), array_get($new, 'where', []));
在这里,我们可以看到他们只是使用一个堆栈来跟踪这些值,所以{domain}.{tld}
的字面解释被推到了堆栈上。
这一开始运行得很好,因为我的过滤器实际上明确地捕捉到了它们:
$domain = $route->getParameter('domain').'.'.$route->getParameter('tld');
为了解决这个问题,我只需要创建自己的助手函数来获取$host(这是一个非常初级的实现,但应该有助于澄清)
$domain = get_domain();
Route::group(array('domain' => $domain, 'before' => 'database.setup'), function()
然后在我的助手文件中,我添加了get_domain()
函数:
if ( ! function_exists('get_domain'))
{
function get_domain()
{
$url_parts = parse_url(Request::root());
return $url_parts['host'];
}
}
然后我也可以简单地在过滤器中调用get_domain(),它将始终同步:
Route::filter('database.setup', function($route, $request){
$domain = get_domain();
现在这个效果很好。