Laravel Eloquent:访问属性和动态表名
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26757452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Laravel Eloquent: Accessing properties and Dynamic Table Names
提问by John Mellor
I am using the Laravel Framework and this question is directly related to using Eloquent within Laravel.
我正在使用 Laravel 框架,这个问题与在 Laravel 中使用 Eloquent 直接相关。
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.
我正在尝试制作一个可以跨多个不同表使用的 Eloquent 模型。这样做的原因是我有多个基本相同但每年都不同的表,但我不想复制代码来访问这些不同的表。
- gamedata_2015_nations
- gamedata_2015_leagues
- gamedata_2015_teams
- gamedata_2015_players
- 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.
我当然可以有一个带有年份列的大表,但是每年有超过 350,000 行并且需要处理很多年,我决定将它们拆分为多个表会更好,而不是 4 个带有额外“位置”的大表在每个请求上。
So what I want to do is have one class for each and do something like this within a Repository class:
所以我想要做的是为每个类设置一个类,并在 Repository 类中执行以下操作:
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
我在 Laravel 论坛上使用了这个讨论来让我开始: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.
因为我使用的是 static 关键字,属性 $year 保留在类中而不是每个单独的对象中,所以每当我创建一个新对象时,它仍然根据上次在不同对象中设置的 $year 属性。我宁愿 $year 与单个对象相关联,并且每次创建对象时都需要设置。
Now I am trying to track the way that Laravel creates Eloquent models but really struggling to find the right place to do this.
现在我试图追踪 Laravel 创建 Eloquent 模型的方式,但真的很难找到合适的地方来做到这一点。
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.
如果我使用的是 static::$year,这同样可以正常工作,但是如果我尝试将其更改为使用 $this->year,那么这将停止工作。
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.
实际错误源于这样一个事实,即 $this->year 未在 getTable() 中设置,因此调用了父 getTable() 方法并返回了错误的表名。
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:
我的下一步是尝试弄清楚为什么它可以使用静态属性而不是非静态属性(不确定是否正确)。我认为它只是在尝试建立 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);
}
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.
现在发生的是我收到一条错误消息,说找不到 gamedata_1800_players。也许并不那么令人惊讶。但它排除了 Eloquent 只是使用 Team 类中的 static::$year 属性的可能性,因为它清楚地设置了我发送到 getPlayerModel() 方法的自定义年份。
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.
所以现在我知道,当 $year 在关系中设置并静态设置时,getTable() 可以访问它,但如果它是非静态设置的,那么它会在某处丢失并且对象不知道这个属性到 getTable() 被调用时。
(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.
1) 为什么 static::$year 工作但 $this->year 不适用于关系,当两者都在简单地创建新对象时工作。
2) Is there a way that I can use a non static property and achieve what I am already achieving using a static property?
2)有没有办法可以使用非静态属性并使用静态属性实现我已经实现的目标?
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.
这可能不是世界上最糟糕的事情,就像我说的,它实际上使用静态属性工作,没有严重错误。但是,上面的代码示例以这种方式工作是危险的,所以我想正确地做到这一点并避免这种危险。
回答by lukasgeiter
I assume you know how to navigate the Laravel API / codebase since you will need it to fully understand this answer...
我假设您知道如何浏览 Laravel API/代码库,因为您需要它来完全理解这个答案......
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.
我看到您有多种情况需要这种动态表名,所以我们将从创建一个开始,这样我们BaseModel
就不必重复自己了。
class BaseModel extends Eloquent {}
class Team extends BaseModel {}
Nothing exciting so far. Next, we take a look at one of the staticfunctions in Illuminate\Database\Eloquent\Model
and write our own static function, let's call it year
.
(Put this in the BaseModel
)
到目前为止没有什么令人兴奋的。接下来,我们看一下其中的一个静态函数,Illuminate\Database\Eloquent\Model
并编写我们自己的静态函数,我们称之为year
. (把这个放在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.
这个函数现在除了创建当前模型的一个新实例,然后在它上面初始化查询构建器之外什么都不做。与 Laravel 在 Model 类中的做法类似。
The next step will be to create a function that actually sets the table on an instantiatedmodel. Let's call this one setYear
. And we'll also add an instance variable to store the year separately from the actual table name.
下一步是创建一个函数,在实例化模型上实际设置表。让我们称之为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
}
}
Now we have to change the year
to actually call setYear
现在我们必须改变year
实际调用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.
最后但并非最不重要的是,我们必须覆盖newInstance()
. 例如,此方法在我的 Laravel 中使用find()
。
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
)
关系的方法(如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);
}
Note:the foreign key will still be called team_id
(without the year) I suppose that is what you want.
注意:外键仍然会被调用team_id
(没有年份)我想这就是你想要的。
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-dependentmodels you could also override the relationship methods in your BaseModel
.
不幸的是,您必须为您定义的每个关系执行此操作。对于其他关系类型,请查看 中的代码Illuminate\Database\Eloquent\Model
。您基本上可以复制粘贴它并进行一些更改。如果您在与年份相关的模型上使用大量关系,您还可以覆盖BaseModel
.
View the full BaseModel
on Pastebin
BaseModel
在Pastebin上查看完整内容
回答by nozzleman
Maybe, a custom Constructor is the way to go.
也许,自定义构造函数是要走的路。
Since all that varies is the year in the name of the corresponding db, your models could implement a constructor like the following:
由于所有不同的是相应数据库名称中的年份,因此您的模型可以实现如下构造函数:
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...
}
Haven't tested this though... Call it like that:
不过还没有测试过……这样称呼它:
$myTeam = new Team([], 2015);
回答by Fazal Rasel
Well its not an answer but just my opinion.
好吧,这不是答案,而只是我的意见。
我想,您正试图仅根据php
php
部分来扩展您的应用程序。如果您期望您的应用程序会随着时间的推移而增长,那么将职责分配给所有其他组件将是明智之举。数据相关部分应由RDBMS
RDBMS
. 例如,如果您正在使用mysql
mysql
,您可以轻松地partitionize
partitionize
通过YEAR
YEAR
. 还有很多其他主题可以帮助您有效地管理数据。回答by pankaj kumar
I have a very simple solution to this problem. I am being used in my projects.
我有一个非常简单的解决方案来解决这个问题。我正在我的项目中使用。
you have to use Model Scope
for define TableName Dynamic.
您必须Model Scope
用于定义TableName Dynamic。
write code in your Model
File
在您的Model
文件中编写代码
public function scopeDefineTable($query)
{
$query->from("deviceLogs_".date('n')."_".date('Y'));
}
Now in your Controller Class
现在在您的控制器类中
function getAttendanceFrom()
{
return DeviceLogs::defineTable()->get();
}
But If you want to manage Table Nameform Controllerthen you can follow this code.
但是如果你想管理Table Name表单Controller那么你可以按照这个代码。
In Model
Class
在Model
类
public function scopeDefineTable($query,$tableName)
{
$query->from($tableName);
}
In Controller
Class
在Controller
类
function getAttendanceFrom()
{
$table= "deviceLogs_".date('n')."_".date('Y');
return DeviceLogs::defineTable($table)->get();
}
Your Output
你的输出
[
{
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
},
......
]