Laravel Eloquent:访问属性和动态表名称

Joh*_*lor 13 php laravel eloquent laravel-4

我使用的是Laravel框架,这个问题与在Laravel中使用Eloquent直接相关.

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

  • gamedata_2015_nations
  • gamedata_2015_leagues
  • gamedata_2015_teams
  • gamedata_2015_players

我当然可以有一个带有年份列的大表,但是每年有超过350,000行和多年处理我决定将它们分成多个表更好,而不是4个带有额外'where'的大表在每个请求上.

所以我想要做的是为每个类创建一个类,并在Repository类中执行类似的操作:

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

        $team->setYear($year);

        return $team->find($team_id);
    }
Run Code Online (Sandbox Code Playgroud)

我在Laravel论坛上使用了这个讨论让我开始:http://laravel.io/forum/08-01-2014-defining-models-in-runtime

到目前为止我有这个:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

因为我使用静态关键字,所以属性$ year保留在类中而不是每个单独的对象中,因此每当我创建一个新对象时,它仍然保持$ year属性,该属性基于上次在另一个对象中设置的属性.我宁愿$ year与单个对象相关联,每次创建对象时都需要设置.

现在我试图追踪Laravel创建Eloquent模型的方式,但真的很难找到合适的地方来做到这一点.

例如,如果我将其更改为:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这在尝试获得单个团队时效果很好.然而,通过关系,它不起作用.这是我尝试过的关系:

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;
}
Run Code Online (Sandbox Code Playgroud)

再次,如果我使用静态:: $ year,这非常好,但如果我尝试将其更改为使用$ this-> year,那么这将停止工作.

实际错误源于$ this-> year未在getTable()中设置,因此调用了父getTable()方法并返回了错误的表名.

我的下一步是试图找出它为什么使用静态属性而不是非静态属性(不确定在正确的术语上).我假设在尝试构建Player关系时,它只是使用Team类中的static :: $ year.然而,这种情况并非如此.如果我尝试用这样的东西强制错误:

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);
}
Run Code Online (Sandbox Code Playgroud)

现在发生的事情是我收到错误,说找不到gamedata_1800_players.也许并不令人惊讶.但是它排除了Eloquent只是使用Team类中的static :: $ year属性的可能性,因为它明确地设置了我发送到getPlayerModel()方法的自定义年份.

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

(注意在简单地创建新对象和使用关系时,它的工作意义不同)

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

1)为什么static :: $ year工作但是$ this-> year不适用于关系,当两者都在简单地创建一个新对象时工作.

2)有没有办法可以使用非静态属性并使用静态属性实现我已经实现的目标?

对此的理由:即使在我完成了一个对象并且我正在尝试使用该类创建另一个对象之后,静态属性仍将保留在类中,这似乎不对.

例:

    //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);
Run Code Online (Sandbox Code Playgroud)

这可能不是世界上最糟糕的事情,就像我说的,它实际上是使用静态属性而没有严重错误.但是上面的代码示例以这种方式工作是危险的,所以我想正确地做到并避免这样的危险.

luk*_*ter 31

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

免责声明:即使我测试了一些案例,但我无法保证它始终有效.如果您遇到问题,请告诉我,我会尽力帮助您.

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

class BaseModel extends Eloquent {}

class Team extends BaseModel {}
Run Code Online (Sandbox Code Playgroud)

到目前为止没什么令人兴奋 接下来,我们来看看其中一个静态函数Illuminate\Database\Eloquent\Model并编写我们自己的静态函数,让我们调用它year.(把它放进去BaseModel)

public static function year($year){
    $instance = new static;
    return $instance->newQuery();
}
Run Code Online (Sandbox Code Playgroud)

此函数现在不执行任何操作,只创建当前模型的新实例,然后在其上初始化查询构建器.与Laravel在Model类中的方式类似.

下一步是创建一个实际在实例化模型上设置表的函数.我们称之为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
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们必须改变year实际调用setYear

public static function year($year){
    $instance = new static;
    $instance->setYear($year);
    return $instance->newQuery();
}
Run Code Online (Sandbox Code Playgroud)

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

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

这是基础知识.以下是如何使用它:

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

$newTeam = new Team();
$newTeam->setTable(2015);
$newTeam->property = 'value';
$newTeam->save();
Run Code Online (Sandbox Code Playgroud)

下一步是关系.这就是它变得非常棘手.

关系方法(如hasMany('Player'):)不支持传入对象.他们接受一个类,然后从中创建一个实例.我能找到的最简单的解决方案是手动创建关系对象.(中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);
}
Run Code Online (Sandbox Code Playgroud)

注意:仍会调用外键team_id(没有年份)我想这就是你想要的.

不幸的是,您必须为您定义的每个关系执行此操作.对于其他关系类型,请查看代码Illuminate\Database\Eloquent\Model.您基本上可以复制粘贴它并进行一些更改.如果您在依赖年份的模型上使用了很多关系,那么您也可以覆盖您的关系方法BaseModel.

BaseModelPastebin上查看完整内容