Laravel口才:访问属性和动态表名

cnwbcb6i  于 2022-12-24  发布在  其他
关注(0)|答案(4)|浏览(124)

我正在使用Laravel框架,这个问题与在Laravel中使用Eloquent直接相关。
我正在尝试创建一个可以跨多个不同表使用的Eloquent模型,原因是我有多个表,它们本质上是相同的,但每年都有变化,但我不想重复代码来访问这些不同的表。

  • 游戏数据_2015_国家
  • 游戏数据_2015_联赛
  • 游戏数据_2015_团队
  • 游戏数据_2015_玩家

我当然可以有一个带有年份列的大表,但是由于每年有超过350,000行,而且要处理很多年,我决定最好将它们分成多个表,而不是4个大表,每个请求都有一个额外的"where"。
所以我想做的是为每个类创建一个类,并在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与单个对象相关联,并且需要在每次创建对象时设置。
现在我正试图跟踪的方式,拉拉维尔创造雄辩的模型,但真的很难找到合适的地方这样做。
例如,如果我把它改成这样:

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,这就不起作用了。
实际的错误源于在getTable()中没有设置$this-〉year,因此调用父getTable()方法并返回错误的表名。
我的下一步是试着找出为什么它使用static属性而不是nonstatic属性(不确定这个术语是否正确)。我假设它只是在尝试构建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()的时候,对象不知道这个属性。
(note简单地创建新对象和使用关系时其工作的重要性不同)
我意识到我现在已经给出了很多细节,所以为了简化和澄清我的问题:
1)为什么static::$year对关系有效,而$this-〉year对关系无效,而这两种方法在简单地创建一个新对象时都有效。
2)有没有一种方法可以让我使用非静态属性,并实现我已经实现使用静态属性?
理由:static属性将保留在类中,即使我已经完成了一个对象,并试图用该类创建另一个对象,这似乎不正确。
示例:

//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);

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

63lcw9qa

63lcw9qa1#

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

  • 免责声明 *:虽然我测试了一些案例,但我不能保证它总是有效。如果你遇到问题,让我知道,我会尽我所能帮助你。

我知道您有多种需要这种动态表名的情况,因此我们将从创建一个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()。例如,当使用find()时,我的Laravel使用了这个方法。

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中的关系方法。
Pastebin上查看完整的BaseModel

ubof19bj

ubof19bj2#

也许,一个定制的构造函数是一条可行之路。
因为变化的只是相应数据库名称中的年份,您的模型可以实现如下构造函数:

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

    public function __construct($attributes = [], $year = null) {
        parent::construct($attributes);

        $year = $year ?: date('Y');

        $this->setTable("gamedata_$year_teams");
    }

    // Your other stuff here...

}

我还没测试过...就这么说吧:

$myTeam = new Team([], 2015);
6ioyuze2

6ioyuze23#

这不是答案,只是我的看法。

我猜,您正试图仅仅依靠php部分来扩展您的应用程序。如果您希望您的应用程序随时间增长,那么将责任分配给所有其他组件将是明智的。数据相关部分应由RDBMS处理。例如,如果您正在使用mysql,你可以很容易地用YEARpartitionize你的数据。还有很多其他的主题可以帮助你有效地管理你的数据。

ttcibm8c

ttcibm8c4#

我有一个非常简单的解决这个问题的方法。我正在我的项目中使用。
必须使用Model Scope来定义名称动态
Model文件中编写代码

public function scopeDefineTable($query)
  {
    $query->from("deviceLogs_".date('n')."_".date('Y'));
  }

现在在您的Controller类

function getAttendanceFrom()
 {
   return  DeviceLogs::defineTable()->get();  
 }

但是如果你想管理表名表单控制器,那么你可以按照下面的代码。

Model类中

public function scopeDefineTable($query,$tableName)
  {
     $query->from($tableName);
  }

Controller类中

function getAttendanceFrom()
 {
    $table= "deviceLogs_".date('n')."_".date('Y');
   return  DeviceLogs::defineTable($table)->get();
 }

您的输出

[
  {
    DeviceLogId: 51,
    DownloadDate: "2019-09-05 12:44:20",
    DeviceId: 2,
    UserId: "1",
    LogDate: "2019-09-05 18:14:17",
    Direction: "",
    AttDirection: null,
    C1: "out",
    C2: null
   },
   ......
 ]

相关问题