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