Reputation: 130
I'm not sure what do I need to do to get dynamic table names working.
Consider following Model (table 'test' does not exist):
<?php
// app/models/Test.php
class Test extends Eloquent {
}
and then if I do ('fields' table does exist):
<?php
// app/routes.php
$test = new \Test;
$test->setTable('fields');
$data = $test->find(1);
dd($data);
I get an error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.tests' doesn't exist (SQL: select * from `tests` where `id` = ? limit 1) (Bindings: array ( 0 => 1, ))"
Note that setting the table name from the Test model works just fine.
L4 actually uses setTable() method, very much the way I would want to, in vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations.Pivot.php constructor, though I just couldn't get it to work by following that example.
Thanks for help.
Upvotes: 2
Views: 5756
Reputation: 30575
Larvael's design doesn't fully support dynamic models. Although there is the setTable method its limitation is its an instance method. Furthermore within the framework a lot of queries begin with creating a model instance via "new static", thus there is no way for you to set the table on the internal queries. Yes you can call setTable on your own queries but as you develop more of your application using more of Laravel's features, e.g. SoftDeletes forceDelete you will gradually come up against a brick wall. A work around is to use PHP's eval function to generate model classes at runtime, that way new static will work the way it was designed.
// load all the models
$eval = "namespace App\\Records;";
foreach($container->tables()->get() as $table){
$recordType = $table->recordType;
$recordTable = $table->getRecordTableName();
$eval .= "class $recordType extends \\App\\Record { protected \$table = '$recordTable'; }";
}
eval($eval);
In the case of when I have a 'Venue' recordType that uses the 'venue' recordTable I now have a class \App\Records\Venue that uses the table "venue", thus I can now do this
$class = "\App\Records\Venue";
$venue = $class::find(1);
Now you are free to use Laravel as designed without any more hackery. This is a very powerful workaround that comes with a security risk, so be sure to sanitise any parameters used in eval. Hopefully someday Laravel will implement a way to support dynamic models.
Upvotes: 0
Reputation: 2676
You need to override Eloquent Model's static find
method as it's used for find:
public static function find($id, $columns = array('*'))
{
if (is_array($id) && empty($id)) return new Collection;
$instance = new static;
return $instance->newQuery()->find($id, $columns);
}
That method is creating new instance of your model class instead of reusing the instiated class. By default this "forgets" the table you've set. And probably you need to pass the table name for it. In my use case my code infers the used table based on the id
so I don't have to pass the whole table name. Hooray for legacy ;-)
Other option is to use some additional factory class like Daniel Cantarin suggested
You may need to override some additional static methods to handle all use cases. For example I just noticed that using eager loading breaks my table mods, so I need to copy with
method's behaviour without breaking it's calling API, yay -_____-
Upvotes: 0
Reputation: 319
Perhaps this is useful for you: http://laravel.io/forum/08-01-2014-defining-models-in-runtime
More exactly, from here: http://laravel.io/forum/08-01-2014-defining-models-in-runtime?page=1#reply-11779
class ModelBuilder extends Eloquent {
protected static $_table;
public static function fromTable($table, $parms = Array()){
$ret = null;
if (class_exists($table)){
$ret = new $table($parms);
} else {
$ret = new static($parms);
$ret->setTable($table);
}
return $ret;
}
public function setTable($table)
{
static::$_table = $table;
}
public function getTable()
{
return static::$_table;
}
}
$user = ModelBuilder::fromTable("users")->find(1);
That's not my final implementation, which's much more complex (and dirtier) than that because of my use cases. But i guess the example may lead you to what you need.
Upvotes: 7