Changing model table using setTable() does not reflect in returned Collection
A simple Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Region;
class Country extends MergedModel
{
protected $connection = 'common';
protected $table = 'countries';
}
Now set the table to the second table using setTable() and check it:
$model = new \\App\\Models\\Country;
$org_collection = $model->setTable('merge_countries')->get();
dd($model->getTable()); // THIS RETURNS "merge_countries" AS EXPECTED
Now return the collection and note that even though the data is form the 2nd table as expected, the table attribute is still pointing to the first table:
Collection {#296
#items: array:1 [
0 => Country {#297
#connection: "common"
#table: "countries" <---**** THE DATA IS FROM "merged_countries" table, not "countries" table
#hidden: array:2 [
0 => "created_at"
1 => "updated_at"
]
#appends: array:1 [
0 => "level"
]
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:7 [
"id" => 3
"organization_id" => 1
"region_id" => 2
"name" => "Southpark"
"active" => 1
"created_at" => "2018-06-21 14:05:36"
"updated_at" => "2018-06-21 13:25:27"
]
#original: array:7 [
"id" => 3
"organization_id" => 1
"region_id" => 2
"name" => "Southpark"
"active" => 1
"created_at" => "2018-06-21 14:05:36"
"updated_at" => "2018-06-21 13:25:27"
]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#visible: []
#fillable: []
#guarded: array:1 [
0 => "*"
]
}
]
}
The #table attribute in the Collection is incorrect (even though the actual data is correct). This is a vital issue for us.
Seems legit.
Though it has nothing to do with Collections, really. The title and example are a bit misleading. In short, the hydrated models already have the "wrong" table:
$model = new \\App\\Models\\Country;
$modelOnOtherTable = $model->setTable('merge_countries')->first();
dd($modelOnOtherTable->getTable());
The reason is that newly hydrated models are created via \Illuminate\Database\Eloquent\Model::newInstance and when we look at the code, we see it respects the connection but not the table (I've shorted the code for readability):
public function newInstance($attributes = [], $exists = false)
{
$model = new static((array) $attributes);
$model->exists = $exists;
$model->setConnection(
$this->getConnectionName()
);
return $model;
}
I tried it locally by adding these lines:
$model->setTable(
$this->getTable()
);
Seems easy to fix but I'm not able to do a PR with tests right now, maybe someone can?
You can workaround until it's fixed by just calling setTable on all the models in your collection.
Well the title and description are indicative of what a user sees, so they may be best left as is to help others find this. It's also not likely a very common use case, but we have some very complex business requirements and use annual multi-tentant databases combined with a shared set of tables from another database - which BTW of the plethora of server frameworks we initially evaluated, Laravel was the ONLY one to even come close to meeting our requirements pretty much out of the box.
In any event, after debugging further, we came to the same conclusion and we can work around it by overriding newInstance in our trait until the upgrade with the fix. Thanks for the rapid responses! Here is the override in case anyone else needs it:
public function newInstance($attributes = [], $exists = false)
{
// Overridden in order to allow for late table binding.
$model = parent::newInstance($attributes, $exists);
$model->setTable($this->table);
return $model;
}
A PR to fix this has already been created => https://github.com/laravel/framework/pull/26085
Saw the PR and thanks! However when in development linking many thing together, teams often cannot just go out and grab things off the cuff. So, until this is in a formal released build that can be upgraded to and tested before integrating, the work-around may still be needed.
Most helpful comment
Seems legit.
Though it has nothing to do with Collections, really. The title and example are a bit misleading. In short, the hydrated models already have the "wrong" table:
The reason is that newly hydrated models are created via
\Illuminate\Database\Eloquent\Model::newInstanceand when we look at the code, we see it respects the connection but not the table (I've shorted the code for readability):I tried it locally by adding these lines:
Seems easy to fix but I'm not able to do a PR with tests right now, maybe someone can?
You can workaround until it's fixed by just calling
setTableon all the models in your collection.