高级Laravel合并数据/模型 - 可以在模型级别完成吗?

时间:2018-06-13 01:44:46

标签: php laravel

我们有一个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!

2 个答案:

答案 0 :(得分:1)

如果您将此功能放在Widget模型中,您将获得2倍的查询次数。您需要将Widget视为一个实例,我想说的是当前方法最少进行2次查询,如果tenantmodified_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);

无论行来自哪个表,您都会看到正确的数据更新。

这导致每个租户能够选择性地覆盖和/或添加基表数据,而不会影响基表数据本身或其他租户,同时最大限度地减少数据重复,从而简化维护(显然,表数据和人口在其他地方管理就像任何其他表)。如果租户没有覆盖,则返回基表数据。合并和自定义集合的东西有很少的文档,所以这需要一些时间来弄清楚。希望有一天能帮助别人!