I am using the Laravel Framework and this question is directly related to using Eloquent within Laravel.

我试图建立一个Eloquent 的模型,可以在多个不同的表格中使用.原因是我有多个基本相同但每年都不同的表,但我不想重复代码来访问这些不同的表.

  • gamedata_2015_nations
  • gamedata_2015_leagues
  • gamedata_2015_团队
  • gamedata_2015_players

我当然可以有一个带有年份栏的大桌子,但由于每年有超过35万行,而且要处理很多年,我决定最好将它们拆分为多个表格,而不是在每个请求上都有额外"where"的4个大桌子.

So what I want to do is have one class for each and do something like this within a Repository class:

public static function getTeam($year, $team_id)
    {
        $team = new Team;

        $team->setYear($year);

        return $team->find($team_id);
    }

I have used this discussion on the Laravel forums to get me started: http://laravel.io/forum/08-01-2014-defining-models-in-runtime

So far I have this:

class Team extends \Illuminate\Database\Eloquent\Model {

    protected static $year;

    public function setYear($year)
    {
        static::$year= $year;
    }

    public function getTable()
    {
        if(static::$year)
        {
            //Taken from https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L1875
            $tableName = str_replace('\\', '', snake_case(str_plural(class_basename($this))));

            return 'gamedata_'.static::$year.'_'.$tableName;
        }

        return Parent::getTable();
    }
}

这似乎有效,但我担心它没有以正确的方式工作.

因为我使用的是静电关键字,所以属性$Year保留在类中,而不是每个单独的对象中,所以每当我创建新对象时,它仍然持有基于上次在不同对象中设置的$Year属性.我宁愿$Year与单个对象相关联,并且需要在每次创建对象时进行设置.

Now I am trying to track the way that Laravel creates Eloquent models but really struggling to find the right place to do this.

For instance if I change it to this:

class Team extends \Illuminate\Database\Eloquent\Model {

    public $year;

    public function setYear($year)
    {
        $this->year = $year;
    }

    public function getTable()
    {
        if($this->year)
        {
            //Taken from https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L1875
            $tableName = str_replace('\\', '', snake_case(str_plural(class_basename($this))));

            return 'gamedata_'.$this->year.'_'.$tableName;
        }

        return Parent::getTable();
    }
}

当试图组建单个团队时,这一点工作得很好.然而,在人际关系中,这是行不通的.这是我在人际关系中try 过的:

public function players()
{
    $playerModel = DataRepository::getPlayerModel(static::$year);

    return $this->hasMany($playerModel);
}

//This is in the DataRepository class
public static function getPlayerModel($year)
{
    $model = new Player;

    $model->setYear($year);

    return $model;
}

Again this works absolutely fine if i'm using static::$year, but if I try and change it to use $this->year then this stops working.

The actual error stems from the fact that $this->year is not set within getTable() so that the parent getTable() method is called and the wrong table name returned.

我的下一步是try 找出为什么它使用静电属性,而不是非静态属性(不确定用于该属性的正确术语).当我试图建立球员关系时,我假设它只是使用团队类中的静电::$Year.然而,情况并非如此.如果我try 使用类似于以下内容的命令强制执行错误,请执行以下操作:

public function players()
{
    //Note the hard coded 1800
    //If it was simply using the old static::$year property then I would expect this still to work
    $playerModel = DataRepository::getPlayerModel(1800);

    return $this->hasMany($playerModel);
}

现在发生的是,我得到一个错误,说找不到gamedata_1800_玩家.也许这并不奇怪.但它排除了Eloquent只是使用Team类中的static::$year属性的可能性,因为它明确地设置了我发送给getPlayerModel()方法的自定义年份.

所以现在我知道,当$Year在关系中设置并且是静态设置的时候,gettable()可以访问它,但是如果它是非静态设置的,那么它就会丢失在某个地方,并且对象在调用gettable()时不知道这个属性.

(note the significance of it working different when simply creating a new object and when using relationships)

我意识到我已经给出了很多细节,所以为了简化和澄清我的问题:

1) Why does static::$year work but $this->year not work for relationships, when both work when simply creating a new object.

2) Is there a way that I can use a non static property and achieve what I am already achieving using a static property?

理由:静态属性将保留在类中,即使我已经完成了一个对象,并试图用该类创建另一个对象,这似乎是不对的.

Example:

    //Get a League from the 2015 database
    $leagueQuery = new League;

    $leagueQuery->setYear(2015);

    $league = $leagueQuery->find(11);

    //Get another league
    //EEK! I still think i'm from 2015, even though nobodies told me that!
    $league2 = League::find(12);

This may not be the worst thing in the world, and like I said, it is actually working using the static properties with no critical errors. However it is dangerous for the above code sample to work in that way, so I would like to do it properly and avoid such a danger.

推荐答案

我假设你知道如何浏览Laravel API/代码库,因为你需要它来完全理解这个答案...

Disclaimer:尽管我测试了一些 case ,但我不能保证它总是有效的.如果你遇到问题,请告诉我,我会尽力帮助你.

我发现有很多情况需要这种动态表名,所以我们首先创建一个BaseModel,这样我们就不必重复了.

class BaseModel extends Eloquent {}

class Team extends BaseModel {}

到目前为止还没有什么令人兴奋的事情.接下来,我们来看一下Illuminate\Database\Eloquent\Model中的static个函数中的一个,并编写我们自己的静电函数,让我们称它为year. (把这个放在BaseModel)

public static function year($year){
    $instance = new static;
    return $instance->newQuery();
}

该函数现在只需创建当前模型的一个新实例,然后在该实例上初始化查询构建器.其方式类似于Laravel在Model类中的操作方式.

下一步是创建一个函数,在instantiated模型上实际设置表格.我们把这个叫做setYear.我们还将添加一个实例变量,将年份与实际表名分开存储.

protected $year = null;

public function setYear($year){
    $this->year = $year;
    if($year != null){
        $this->table = 'gamedata_'.$year.'_'.$this->getTable(); // you could use the logic from your example as well, but getTable looks nicer
    }
}

现在我们必须把year改成实际拨打setYear

public static function year($year){
    $instance = new static;
    $instance->setYear($year);
    return $instance->newQuery();
}

最后但并非最不重要的是,我们必须覆盖newInstance().例如,我的Laravel在使用find()时使用了这种方法.

public function newInstance($attributes = array(), $exists = false)
{
    $model = parent::newInstance($attributes, $exists);
    $model->setYear($this->year);
    return $model;
}

That's the basics. Here's how to use it:

$team = Team::year(2015)->find(1);

$newTeam = new Team();
$newTeam->setTable(2015);
$newTeam->property = 'value';
$newTeam->save();

The next step are relationships. And that's were it gets real tricky.

关系的方法(比如:hasMany('Player'))不支持传入对象.他们获取一个类,然后从中创建一个实例.我能找到的最简单的解决方案是手动创建relationship对象.(Team年)

public function players(){
    $instance = new Player();
    $instance->setYear($this->year);

    $foreignKey = $instance->getTable.'.'.$this->getForeignKey();
    $localKey = $this->getKeyName();

    return new HasMany($instance->newQuery(), $this, $foreignKey, $localKey);
}

Note:外键仍然会被称为team_id(没有年份),我想这就是你想要的.

不幸的是,对于你定义的每一段关系,你都必须这样做.对于其他关系类型,请查看Illuminate\Database\Eloquent\Model中的代码.你基本上可以复制粘贴它并做一些修改.如果你在year-dependent个模型上使用了很多关系,你也可以覆盖BaseModel中的关系方法.

Pastebin上查看完整的BaseModel

Laravel相关问答推荐

Inertiajs - 使用链接组件添加记录但无法在不使用表单的情况下清除文本区域

如何通过 Eloquent 获取单个集合中每位教师的学生人数

如何从@foreach 循环中捕获所有数据并将其传递给 值? - Laravel

(1/1) ErrorException ReflectionFunction::__construct() ex

Laravel 连接表

如何将用户对象绑定到中间件中的请求

如何在 Laravel Passport 中获取刷新令牌?

Laravel 5.1 是否与 PHP 7 兼容

Laravel 合集日期比较

在 laravel 如何将额外的数据传递给 mutators 和 accessors

laravel 中的 Http Post 使用 fetch api 给出 TokenMismatchException

Laravel 5.5 类型错误:传递给 Illuminate\Auth\EloquentUserProvider::validateCredentials() 的参数 1 必须是实例

SQLSTATE [01000]:警告:1265 列的数据被截断(truncated)

如果值存在于另一个字段数组中,Laravel 验证规则

为什么在 Laravel 的 DB::select 中使用 DB::raw?

Elastic search全文与mysql全文?

Laravel 接口

如何在 Laravel 5 中的每个相关输入字段旁边显示验证错误?

在laravel中删除确认

Laravel 4:分析器在哪里?