Laravel雄辩:访问属性和动态表名称 [英] Laravel Eloquent: Accessing properties and Dynamic Table Names

查看:424
本文介绍了Laravel雄辩:访问属性和动态表名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我正在努力使一个可以在整个多个不同的表。原因是我有多个基本相同的表,但是每年都有所不同,但是我不想复制代码来访问这些不同的表。




  • gamedata_2015_nations

  • gamedata_2015_leagues

  • gamedata_2015_teams

  • gamedata_2015_players



我当然可以有一个大桌子,一年一栏,但每年有超过35万行,多年来处理我决定会更好将它们分成多个表,而不是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



到目前为止,我有这样的:

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

protected static $ year;

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

public function getTable()
{
if(static :: $ year)
{
//取自https: /gar class_basename($本))));

return'gamedata _'。static :: $ year .'_'。$ tableName;
}

返回Parent :: getTable();
}
}

这似乎很有效,但是我很担心不正确的方式工作。



因为我正在使用静态关键字属性$ year保留在类中,而不是每个对象,所以每当我创建一个新对象根据最后一次设置在不同的对象中,仍然保存$ year属性。我宁愿$ year与单个对象相关联,并且每次创建一个对象时都需要设置。



现在我正在尝试跟踪Laravel创建的方式演绎模型,但真的很难找到正确的地方这样做。



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

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

public $ year;

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

public function getTable()
{
if($ this-> year)
{
//取自https :/gar/ (class_basename($本))));

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

返回Parent :: getTable();
}
}

当尝试获得一个团队时,这工作正常。然而关系不行。这是我已经尝试过的关系:

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

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

//这是在DataRepository类中
public static function getPlayerModel($ year)
{
$ model = new Player;

$ model-> setYear($ year);

return $ model;
}

再次,如果我使用static :: $ year,但是如果我尝试改变它使用$ this-> year,那么这将停止工作。



实际的错误来自于$ this-> year没有设置在getTable()中,以便调用父getTable()方法并返回错误的表名。



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

  public function players()
{
//注意硬编码1800
//如果它只是使用旧的static :: $ year属性,那么我会期望这仍然工作
$ playerModel = DataRepository :: getPlayerModel(1800);

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

现在发生了什么,我发现错误说gamedata_1800_players没有找到。也许并不奇怪但是它排除了Enloquent简单地使用Team类中的static :: $ year属性的可能性,因为它清楚地设置了我发送到getPlayerModel()方法的自定义年份。



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



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



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



1 )为什么static :: $年工作但是$ this-> year不适用于关系,当两者都工作时,只需创建一个新对象。



2)有没有我可以使用非静态属性并实现我已经使用静态属性实现的方式吗?



对于这个的理由:即使在我完成一个对象之后,静态属性仍然保持在类中,并且正在尝试使用该类创建另一个对象,这似乎不正确。 / p>

示例:

  //从2015数据库获取联盟
$ leagueQuery = new League;

$ leagueQuery-> setYear(2015);

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

//获取另一个联盟
// EEK!我仍然认为我是从2015年,即使​​没有人告诉我!
$ league2 = League :: find(12);

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

解决方案>

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



免责声明:即使我测试了一些情况,我不能保证它总是有效的。如果遇到问题,让我知道,我会尽力帮助你。



我看到你有多种情况需要这种动态表名字,所以我们先创建一个 BaseModel ,这样我们就不用重复了。

  class BaseModel extends Eloquent {} 

class Team extends BaseModel {}

到目前为止还没有什么令人兴奋的接下来,我们来看看 Illuminate\Database\Eloquent\Model 中的一个 static 函数,并写入我们自己的静态函数,我们称它为
(将其放在 BaseModel 中)

  public static函数年($ 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(); //你可以使用你的例子的逻辑,但getTable看起来更好
}
}

现在我们必须更改年份来实际调用 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'))不支持传递对象。他们拿一个类,然后从中创建一个实例。我可以找到的最简单的解决方案是手动创建关系对象。 (在团队

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

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

返回新的HasMany($ instance-> newQuery(),$ this,$ foreignKey,$ localKey);
}

注意:外键仍然会被调用 team_id (没有一年)我想这就是你想要的。



不幸的是,你必须这样做为您定义的每一个关系。对于其他关系类型,请查看 Illuminate\Database\Eloquent\Model 中的代码。您可以基本上复制粘贴它并进行一些更改。如果您在年龄相关的模型中使用了很多关系,您也可以覆盖 BaseModel 中的关系方法。



查看 Pastebin 上的完整 BaseModel / a>


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

I am trying to make an Eloquent model that can be used across the multiple different tables. The reason for this is that I have multiple tables that are essentially identical but vary from year to year, but I do not want to duplicate code to access these different tables.

  • gamedata_2015_nations
  • gamedata_2015_leagues
  • gamedata_2015_teams
  • gamedata_2015_players

I could of course have one big table with a year column, but with over 350,000 rows each year and many years to deal with I decided it would be better to split them into multiple tables, rather than 4 huge tables with an extra 'where' on each request.

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

This seems to work, however i'm worried it's not working in the right way.

Because i'm using the static keyword the property $year is retained within the class rather than each individual object, so whenever I create a new object it still holds the $year property based on the last time it was set in a different object. I would rather $year was associated with a single object and needed to be set each time I created an object.

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

This works just fine when trying to get a single Team. However with relationships it doesn't work. This is what i've tried with relationships:

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.

My next step was to try and figure out why it was working with the static property but not with the nonstatic property (not sure on the right term for that). I assumed that it was simply using the static::$year from the Team class when trying to build the Player relationship. However this is not the case. If I try and force an error with something like this:

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

Now what happens is that I get an error saying gamedata_1800_players isn't found. Not that surprising perhaps. But it rules out the possibility that Eloquent is simply using the static::$year property from the Team class since it is clearly setting the custom year that i'm sending to the getPlayerModel() method.

So now I know that when the $year is set within a relationship and is set statically then getTable() has access to it, but if it is set non-statically then it gets lost somewhere and the object doesn't know about this property by the time getTable() is called.

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

I realise i've given alot of detail now, so to simplify and clarify my question:

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?

Justification for this: The static property will stay with the class even after I have finished with one object and am trying to create another object with that class, which doesn't seem right.

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.

解决方案

I assume you know how to navigate the Laravel API / codebase since you will need it to fully understand this answer...

Disclaimer: Even though I tested some cases I can't guarantee It always works. If you run into a problem, let me know and I'll try my best to help you.

I see you have multiple cases where you need this kind of dynamic table name, so we will start off by creating a BaseModel so we don't have to repeat ourselves.

class BaseModel extends Eloquent {}

class Team extends BaseModel {}

Nothing exciting so far. Next, we take a look at one of the static functions in Illuminate\Database\Eloquent\Model and write our own static function, let's call it year. (Put this in the BaseModel)

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

This function now does nothing but create a new instance of the current model and then initialize the query builder on it. In a similar fashion to the way Laravel does it in the Model class.

The next step will be to create a function that actually sets the table on an instantiated model. Let's call this one setYear. And we'll also add an instance variable to store the year separately from the actual table name.

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
    }
}

Now we have to change the year to actually call setYear

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

And last but not least, we have to override newInstance(). This method is used my Laravel when using find() for example.

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.

The methods for relations (like: hasMany('Player')) don't support passing in objects. They take a class and then create an instance from it. The simplest solution I could found, is by creating the relationship object manually. (in 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: the foreign key will still be called team_id (without the year) I suppose that is what you want.

Unfortunately, you will have to do this for every relationship you define. For other relationship types look at the code in Illuminate\Database\Eloquent\Model. You can basically copy paste it and make a few changes. If you use a lot of relationships on your year-dependent models you could also override the relationship methods in your BaseModel.

View the full BaseModel on Pastebin

这篇关于Laravel雄辩:访问属性和动态表名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆