php - Laravel Eloquent : Accessing properties and Dynamic Table Names

标签 php laravel laravel-4 eloquent

我正在使用 Laravel 框架,这个问题与在 Laravel 中使用 Eloquent 直接相关。

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

  • gamedata_2015_nations
  • gamedata_2015_leagues
  • gamedata_2015_teams
  • gamedata_2015_players

  • 我当然可以有一个带有年份列的大表,但是每年有超过 350,000 行并且需要处理很多年,我决定最好将它们拆分为多个表,而不是 4 个带有额外“位置”的大表在每个请求上。

    所以我想做的是为每个类创建一个类,并在 Repository 类中执行以下操作:
    public static function getTeam($year, $team_id)
        {
            $team = new Team;
    
            $team->setYear($year);
    
            return $team->find($team_id);
        }
    

    我在 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();
        }
    }
    

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

    因为我使用的是 static 关键字,属性 $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();
        }
    }
    

    这在尝试获得单个团队时效果很好。但是,对于关系,它不起作用。这是我尝试过的关系:
    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;
    }
    

    如果我使用的是 static::$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);
    }
    

    现在发生的是我收到一条错误消息,说找不到 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);
    

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

    最佳答案

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

    免责声明:即使我测试了一些情况,我也不能保证它总是有效。如果您遇到问题,请告诉我,我会尽力帮助您。

    我看到您有多种情况需要这种动态表名,因此我们将从创建 BaseModel 开始。所以我们不必重复自己。

    class BaseModel extends Eloquent {}
    
    class Team extends BaseModel {}
    

    到目前为止没有什么令人兴奋的。接下来,我们来看一看静态 Illuminate\Database\Eloquent\Model 中的函数并编写我们自己的静态函数,我们称之为 year .
    (把这个放在 BaseModel 中)
    public static function year($year){
        $instance = new static;
        return $instance->newQuery();
    }
    

    这个函数现在除了创建当前模型的一个新实例,然后在它上面初始化查询构建器之外什么都不做。与 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
        }
    }
    

    现在我们必须更改 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;
    }
    

    这就是基础知识。以下是如何使用它:
    $team = Team::year(2015)->find(1);
    
    $newTeam = new Team();
    $newTeam->setTable(2015);
    $newTeam->property = 'value';
    $newTeam->save();
    

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

    关系的方法(如: 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);
    }
    

    注意:外键仍会被称为 team_id (没有年份)我想这就是你想要的。

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

    查看完整版 BaseModelPastebin

    关于php - Laravel Eloquent : Accessing properties and Dynamic Table Names,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26757452/

    相关文章:

    php - 想要使用 composer 添加新插件而不是更新所有插件

    mysql - Laravel groupBy 排名导致多个分组行

    mysql - SQL格式DATETIME统计月份

    php - Laravel Eloquent 不只更新插入

    php - 拉拉维尔。 toArray() 转换后不能将 StdClass 类型的对象用作数组

    php - Laravel 5.6 永久更改运行时中的数据库连接

    php - 如何使用不在表单中的复选框和按钮从数据库中删除记录

    php - mysql 中非常奇怪的错误。与搜索匹配是有效的,但对它来说是无效的

    php - 如何让 PHP-GA 停止在谷歌分析中增加实时访问者?

    PHP检查对象或类中是否存在属性