Laravel Eloquent: доступ к свойствам и динамическим именам таблиц
Я использую структуру Laravel, и этот вопрос напрямую связан с использованием Eloquent в Laravel.
Я пытаюсь сделать красноречивую модель, которая может использоваться в нескольких разных таблицах. Причина этого в том, что у меня есть несколько таблиц, которые по существу идентичны, но меняются из года в год, но я не хочу дублировать код для доступа к этим различным таблицы.
- gamedata_2015_nations
- gamedata_2015_leagues
- gamedata_2015_teams
- gamedata_2015_players
я мог бы, конечно, иметь одну большую таблицу со столбцом года, но с более чем 350 000 строк каждый год и много лет, чтобы иметь дело с я решил, что было бы лучше разделить их на несколько таблиц, а не 4 огромные таблицы с дополнительным " где " по каждому запросу.
так что я хочу сделать имейте по одному классу для каждого и сделайте что-то вроде этого в классе репозитория:
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 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();
}
}
это, кажется, работает, однако я беспокоюсь, что это не работает в правильном направлении.
поскольку я использую ключевое слово static, свойство $year сохраняется в классе вместо каждого отдельного объекта, поэтому всякий раз, когда я создаю новый объект, он все еще содержит свойство $year на основе последнего времени, когда он был установлен в другом объекте. Я бы предпочел, чтобы $year был связан с одним объектом и должен был устанавливаться каждый раз, когда я создавал объект.
теперь я пытаюсь отслеживать, как Laravel создает красноречивые модели, но действительно изо всех сил пытается найти правильное место для этого.
например, если я изменю его на это:
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();
}
}
это работает просто отлично, когда пытается получить одну команду. Однако с отношениями это не работает. Это то, что я пробовал с отношениями:
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() вызывается и неправильно возвращено имя таблицы.
моим следующим шагом было попытаться выяснить, почему он работает со статическим свойством, но не с нестатическим свойством (не уверен в правильном термине для этого). Я предположил, что он просто использует static::$year из класса Team при попытке построить отношения с игроком. Однако это не так. Если я попытаюсь заставить ошибку с чем-то вроде этого:
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 просто использует свойство static::$year из класса Team, поскольку оно явно устанавливает пользовательский год, который я отправляю методу getPlayerModel ().
Итак, теперь я знаю, что когда $year установлен в отношениях и установлен статически, то getTable() имеет доступ к нему, но если он установлен нестатически, то он где-то теряется, и объект не знает об этом свойстве вызывается time 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);
это может быть не самое худшее в мире, и, как я уже сказал, он на самом деле работает с использованием статических свойств без критических ошибок. Однако для приведенного выше примера кода опасно работать в таким образом, я хотел бы сделать это правильно и избежать такой опасности.
3 ответов
я предполагаю, что вы знаете, как перемещаться по API / кодовой базе Laravel, так как вам нужно будет полностью понять этот ответ...
отказ от ответственности: хотя я тестировал некоторые случаи, я не могу гарантировать, что он всегда работает. Если вы столкнетесь с проблемой, дайте мне знать, и я сделаю все возможное, чтобы помочь вам.
я вижу, у вас есть несколько случаев, когда вам нужно такое динамическое имя таблицы, поэтому мы начнем с создания 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()
. Этот метод используется my 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')
) не поддерживают передачу объектов. Они берут класс, а затем создать его экземпляр. Самое простое решение, которое я мог найти, - это создать объект отношения вручную. (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
(без года) я полагаю, это то, что вы хотите.
к сожалению, вам придется сделать это для каждого отношения вы определяете. Для других типов отношений посмотрите код в Illuminate\Database\Eloquent\Model
. Вы можете в основном скопировать вставить его и внести несколько изменений. Если вы используете много отношений на вашем зависит от года модели вы также можете переопределить методы отношений в своем BaseModel
.
посмотреть полный BaseModel
на сайт Pastebin
Ну это не ответ, а просто мое мнение.
Я думаю, вы пытаетесь масштабировать приложения в зависимости отphp
часть. Если вы ожидаете, что ваше приложение будет расти со временем, тогда будет разумно распределить ответственность между всеми другими компонентами. Часть, связанная с данными, должна обрабатываться RDBMS
.
Например, если вы используете mysql
, вы можете легко partitionize
данные YEAR
. И есть много других тем, которые помогут вам управлять вашим данные эффективно.возможно, пользовательский конструктор-это путь.
поскольку все, что меняется, - это год в имени соответствующей БД, ваши модели могут реализовать такой конструктор, как:
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);