我们有一个COMMON数据库,然后是每个使用我们应用程序的组织的租户数据库。我们在COMMON数据库中为某些表格提供了基本值,例如
COMMON.widgets。然后在租户数据库中,如果存在名为modified_widgets的表并且具有值,则它们将与COMMON.widgets表合并。
现在我们在控制器中执行此操作:
public function index(Request $request)
{
$widgets = Widget::where('active', '1')->orderBy('name')->get();
if(Schema::connection('tenant')->hasTable('modified_widgets')) {
$modified = ModifiedWidget::where('active', '1')->get();
$merged = $widgets->merge($modified);
$merged = array_values(array_sort($merged, function ($value) {
return $value['name'];
}));
return $merged;
}
return $countries;
}
正如您所看到的,我们为每个表都有模型,这样就行了。我们从控制器获得这样的GET请求的预期结果,但是如果可能的话,我们想要在Laravel MODEL级别进行合并。这样,当使用这些值填充表单时,id会链接到正确的表。合并意味着在BOTH表中可以存在相同的id。如果存在,我们总是希望对合并的数据采取行动。所以看起来模型级别就是这个地方,但我们会尝试任何有助于满足需求的建议。希望一切都有意义。
任何人都可以帮忙解决这个问题,或者有没有人有任何想法尝试?我们已经与最重要的模型构造者一起玩过,但是还没有能够解决这个问题。感谢任何想法和TIA!
答案 0 :(得分:1)
如果您将此功能放在Widget
模型中,您将获得2倍的查询次数。您需要将Widget视为一个实例,我想说的是当前方法最少进行2次查询,如果tenant
有modified_widgets
表,则为+1。现在假设你在模型中执行此操作,每个Widget
实例都将引入,在最好的情况下,它的等效来自不同的数据库,因此对于一堆小部件,你将做1(->all()
)+ n( n = ModifiedWidgets
个)查询的数量 - 因为每个Widget
实例都会拉出自己的镜像,如果它存在,不会有急切加载。
您可以通过以下方式改进代码:
$widgets = Widget::where('active', '1')->orderBy('name')->get();
if(Schema::connection('tenant')->hasTable('modified_widgets')) {
$modified = ModifiedWidget::where('active', '1')->whereIn('id', $widgets->pluck('id'))->get(); // remove whereIn if thats not the case
return $widgets->merge($modified)->unique()->sortBy('name');
}
return $widgets;
答案 1 :(得分:-1)
好的,这就是我们想出来的。
我们现在使用单个模型,并且两个数据库中的表名必须相同(即使在数据库/ Eloquent / Model基本源代码中存在,setTable似乎也不起作用 - 这可能就是为什么它'没有记录)。无论如何=只使用常规模型并确保表格相同(或者至少是您正在使用的字段):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Widget extends Model
{
}
然后我们有一个通用的&#39;合并控制器&#39;在请求中传递模型和可选的排序(我们在这里硬编码&#39; where&#39;和键,但它们也可以变为动态)。注意这不会使用静态方法创建新的实例,例如$ model :: all(),因此在这种情况下你需要使用$ model-&gt; get():
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class MergeController extends Controller
{
public function index(Request $request)
{
//TODO: add some validations to ensure model is provided
$model = app("App\\Models\\{$request['model']}");
$sort = $request['sort'] ? $request['sort'] : 'id';
$src_collection = $model->where('active', '1')->orderBy('name')->get();
// we setup the tenants connection elsewhere, but use it here
if(Schema::connection('tenant')->hasTable($model->getTable())) {
$model->setConnection('tenant');
$tenant_collection = $model->get()->where('active', '1');
$src_collection = $src_collection->keyBy('id')->merge($tenant_collection->keyBy('id'))->sortBy('name');
}
return $src_collection;
}
}
如果你dd($ src_collection);在返回它之前,您将看到每行的连接是正确的(取决于表中的数据)。如果您更新一行:
$test = $src_collection->find(2); // this is a row from the tenant db in our data
$test->name = 'Test';
$test->save();
$test2 = $src_collection->find(1); // this is a row from the tenant db in our data
$test2->name = 'Test2'; // this is a row from the COMMON db in our data
$test2->save();
dd($src_collection);
无论行来自哪个表,您都会看到正确的数据更新。
这导致每个租户能够选择性地覆盖和/或添加基表数据,而不会影响基表数据本身或其他租户,同时最大限度地减少数据重复,从而简化维护(显然,表数据和人口在其他地方管理就像任何其他表)。如果租户没有覆盖,则返回基表数据。合并和自定义集合的东西有很少的文档,所以这需要一些时间来弄清楚。希望有一天能帮助别人!