問題描述
我正在使用 Laravel 框架,這個(gè)問題與在 Laravel 中使用 Eloquent 直接相關(guān).
I am using the Laravel Framework and this question is directly related to using Eloquent within Laravel.
我正在嘗試制作一個(gè)可在多個(gè)不同表中使用的 Eloquent 模型.這樣做的原因是我有多個(gè)基本相同但每年都不同的表,但我不想重復(fù)代碼來訪問這些不同的表.
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
我當(dāng)然可以有一個(gè)帶有年份列的大表,但是每年有超過 350,000 行并且需要處理很多年,我決定將它們拆分為多個(gè)表會(huì)更好,而不是 4 個(gè)帶有額外表的大表每個(gè)請(qǐng)求的位置".
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.
所以我想做的是為每個(gè)類創(chuàng)建一個(gè)類,并在 Repository 類中執(zhí)行類似的操作:
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);
}
我在 Laravel 論壇上使用了這個(gè)討論來讓我開始:http://laravel.io/forum/08-01-2014-defining-models-in-runtime
I have used this discussion on the Laravel forums to get me started: http://laravel.io/forum/08-01-2014-defining-models-in-runtime
到目前為止我有這個(gè):
class Team extends IlluminateDatabaseEloquentModel {
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();
}
}
這似乎有效,但我擔(dān)心它沒有以正確的方式工作.
This seems to work, however i'm worried it's not working in the right way.
因?yàn)槲沂褂玫氖?static 關(guān)鍵字,屬性 $year 保留在類中而不是每個(gè)單獨(dú)的對(duì)象中,所以每當(dāng)我創(chuàng)建一個(gè)新對(duì)象時(shí),它仍然根據(jù)上次設(shè)置的 $year 屬性保存在一個(gè)不同的對(duì)象.我寧愿 $year 與單個(gè)對(duì)象相關(guān)聯(lián),并且每次創(chuàng)建對(duì)象時(shí)都需要設(shè)置.
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.
現(xiàn)在我試圖追蹤 Laravel 創(chuàng)建 Eloquent 模型的方式,但真的很難找到合適的地方來做到這一點(diǎn).
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 IlluminateDatabaseEloquentModel {
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();
}
}
這在嘗試獲得單個(gè)團(tuán)隊(duì)時(shí)效果很好.但是,對(duì)于關(guān)系,它不起作用.這是我在人際關(guān)系中嘗試過的:
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;
}
如果我使用的是 static::$year,這同樣可以正常工作,但是如果我嘗試將其更改為使用 $this->year,那么這將停止工作.
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.
實(shí)際的錯(cuò)誤源于 $this->year 沒有在 getTable() 中設(shè)置,因此調(diào)用了父 getTable() 方法并返回了錯(cuò)誤的表名.
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.
我的下一步是嘗試弄清楚為什么它可以使用靜態(tài)屬性而不是非靜態(tài)屬性(不確定是否正確).我認(rèn)為它只是在嘗試建立 Player 關(guān)系時(shí)使用 Team 類中的 static::$year .然而,這種情況并非如此.如果我嘗試強(qiáng)制出現(xiàn)這樣的錯(cuò)誤:
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);
}
現(xiàn)在發(fā)生的情況是我收到一條錯(cuò)誤消息,指出找不到 gamedata_1800_players.也許并不那么令人驚訝.但它排除了 Eloquent 只是使用 Team 類中的 static::$year 屬性的可能性,因?yàn)樗宄卦O(shè)置了我發(fā)送到 getPlayerModel() 方法的自定義年份.
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.
所以現(xiàn)在我知道當(dāng) $year 在關(guān)系中設(shè)置并靜態(tài)設(shè)置時(shí),getTable() 可以訪問它,但是如果它是非靜態(tài)設(shè)置的,那么它會(huì)在某處丟失并且對(duì)象不知道調(diào)用 getTable() 時(shí)關(guān)于此屬性的信息.
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.
(注意在簡(jiǎn)單地創(chuàng)建新對(duì)象和使用關(guān)系時(shí)它的工作意義不同)
(note the significance of it working different when simply creating a new object and when using relationships)
我意識(shí)到我現(xiàn)在已經(jīng)提供了很多細(xì)節(jié),所以為了簡(jiǎn)化和澄清我的問題:
I realise i've given alot of detail now, so to simplify and clarify my question:
1) 為什么 static::$year 工作但 $this->year 不適用于關(guān)系,當(dāng)兩者都在簡(jiǎn)單地創(chuàng)建新對(duì)象時(shí)工作.
1) Why does static::$year work but $this->year not work for relationships, when both work when simply creating a new object.
2) 有沒有辦法可以使用非靜態(tài)屬性并實(shí)現(xiàn)我已經(jīng)使用靜態(tài)屬性實(shí)現(xiàn)的目標(biāo)?
2) Is there a way that I can use a non static property and achieve what I am already achieving using a static property?
理由:即使在我完成一個(gè)對(duì)象并嘗試使用該類創(chuàng)建另一個(gè)對(duì)象后,靜態(tài)屬性仍將保留在類中,這似乎不正確.
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.
示例:
//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);
這可能不是世界上最糟糕的事情,就像我說的,它實(shí)際上使用靜態(tài)屬性工作,沒有嚴(yán)重錯(cuò)誤.然而,上面的代碼示例以這種方式工作是危險(xiǎn)的,所以我想正確地做到這一點(diǎn),避免這種危險(xiǎn).
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.
推薦答案
我假設(shè)你知道如何瀏覽 Laravel API/代碼庫,因?yàn)槟阈枰鼇硗耆斫膺@個(gè)答案......
I assume you know how to navigate the Laravel API / codebase since you will need it to fully understand this answer...
免責(zé)聲明:即使我測(cè)試了一些情況,我也不能保證它總是有效.如果您遇到問題,請(qǐng)告訴我,我會(huì)盡力幫助您.
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.
我看到您有多種情況需要這種動(dòng)態(tài)表名,所以我們將從創(chuàng)建一個(gè) BaseModel
開始,這樣我們就不必重復(fù)自己了.
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 {}
到目前為止沒有什么令人興奮的.接下來我們看一下IlluminateDatabaseEloquentModel
中的一個(gè)static函數(shù),編寫我們自己的靜態(tài)函數(shù),姑且稱之為year
代碼>.(把這個(gè)放在BaseModel
)
Nothing exciting so far. Next, we take a look at one of the static functions in IlluminateDatabaseEloquentModel
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();
}
這個(gè)函數(shù)現(xiàn)在除了創(chuàng)建當(dāng)前模型的一個(gè)新實(shí)例,然后在它上面初始化查詢構(gòu)建器之外什么都不做.與 Laravel 在 Model 類中的做法類似.
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.
下一步是創(chuàng)建一個(gè)函數(shù),在實(shí)例化模型上實(shí)際設(shè)置表.我們稱之為 setYear
.我們還將添加一個(gè)實(shí)例變量來將年份與實(shí)際表名分開存儲(chǔ).
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
}
}
現(xiàn)在我們必須改變year
來實(shí)際調(diào)用setYear
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();
}
最后但并非最不重要的是,我們必須覆蓋 newInstance()
.例如,在使用 find()
時(shí),此方法用于我的 Laravel.
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;
}
這是基礎(chǔ)知識(shí).使用方法如下:
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();
下一步是關(guān)系.這就是它變得非常棘手.
The next step are relationships. And that's were it gets real tricky.
關(guān)系的方法(如:hasMany('Player')
)不支持傳入對(duì)象.他們接受一個(gè)類,然后從中創(chuàng)建一個(gè)實(shí)例.我能找到的最簡(jiǎn)單的解決方案是手動(dòng)創(chuàng)建關(guān)系對(duì)象.(在團(tuán)隊(duì)
中)
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);
}
注意:外鍵仍將被稱為 team_id
(沒有年份)我想這就是您想要的.
Note: the foreign key will still be called team_id
(without the year) I suppose that is what you want.
不幸的是,您必須為您定義的每個(gè)關(guān)系執(zhí)行此操作.對(duì)于其他關(guān)系類型,請(qǐng)查看 IlluminateDatabaseEloquentModel
中的代碼.您基本上可以復(fù)制粘貼它并進(jìn)行一些更改.如果您在 year-dependent 模型上使用大量關(guān)系,您還可以覆蓋 BaseModel
中的關(guān)系方法.
Unfortunately, you will have to do this for every relationship you define. For other relationship types look at the code in IlluminateDatabaseEloquentModel
. 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
.
在 Pastebin
這篇關(guān)于Laravel Eloquent:訪問屬性和動(dòng)態(tài)表名的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!