Laravel Eloquent 5.1有效地更新相关模型

时间:2015-07-31 13:30:41

标签: php laravel orm eloquent laravel-5.1

我正在寻找一个干净的解决方案来查询和修改我的Eloquent模型的动态属性,存储在另一个表中。

我的主要模特是UserUser可能有多个UserVariable。通过UserRepository加载模型时,我可能会也可能不会加载变量。

我想要实现的是UserVariable可以在内存中修改,并且只有在保存User时才会自动保存。

这是我的解决方案(剥离了不相关的部分),它起作用,但也不优雅,也不可扩展:

/**
 * @property boolean $isCommentingEnabled
 */
class User extends \Illuminate\Database\Eloquent\Model
{
    public function variables()
    {
        return $this->hasMany('UserVariable', 'var_userid', 'user_id');
    }

    public function getIsCommentingEnabledAttribute()
    {
        foreach ($this->variables as $variable) {
            if ($variable->name == UserVariable::IS_COMMENTING_ENABLED) {
                return (boolean) $variable->value;
            }
        }
        return false;
    }

    public function setIsCommentingEnabledAttribute($enabled)
    {
        foreach ($this->variables as $variable) {
            if ($variable->name == UserVariable::IS_COMMENTING_ENABLED) {
                $variable->value = $enabled ? 1 : 0;
                return;
            }
        }
        $this->variables()->add(new UserVariable([
                'name'  => UserVariable::IS_COMMENTING_ENABLED,
                'value' => $enabled ? 1 : 0,
        ]));
    }
}

/**
 * @property-read int $id Unique ID of the record.
 * @property string $name Must be one of the constants in this class.
 * @property int $userId
 * @property string $value
 */
class UserVariable extends EloquentModel {}

class UserRepository
{
    public function findById($id)
    {
        return User::with('variables')->find($id);
    }

    public function saveUser(User $user)
    {
        return $user->push();
    }
}

这个解决方案显然无法扩展。如果用户有5个以上的变量,即使我提取了循环,代码也会很丰富。

我怀疑在Laravel中必须有一个简短而干净的解决方案,只是按名称弹出User UserVariable,或者如果它不存在则获取新的解决方案,修改它的价值并把它归还给模特。调用User::push()后,系统会自动保存。完成。

我正在寻找像

这样的东西
$user->variables()->where('name', UserVariable::IS_COMMENTING_ENABLED)->first()->value
        = $enabled ? 1 : 0;

但上述方法无法正常工作,因为它使用的是数据库,而不是模型。任何帮助表示赞赏。

注意:我正在处理大型遗留代码库,因此现在无法更改数据库结构。

1 个答案:

答案 0 :(得分:2)

不幸的是,没有内置的方法可以做到这一点。幸运的是,实施起来并不算太糟糕。

我们的模型具有regnr关系。

->variables

然后我们需要建立一种方法,告诉我们我们是否已经加载了所述关系,并且如果我们没有加载它们就会加载它们。

class User extends \Illuminate\Database\Eloquent\Model {
    public function variables() {
        return $this->hasMany('UserVariable', 'var_userid', 'user_id');
    }

这些方法允许我们为 protected function hasLoadedVariables() { $relations = $this->getRelations(); return array_key_exists('variables', $relations); } protected function loadVariablesIfNotLoaded() { if(!$this->hasLoadedVariables()) { $this->load('variables'); } } s。

构建通用的getter和setter

getter确保加载UserVariable关系,然后使用匹配的键返回第一个值。

variables

setter稍微复杂一点,因为我们想要更新现有的 protected function getVariable($key) { $this->loadVariablesIfNotLoaded(); foreach($this->variables as $variable) { if($variable->key === $key) { return $variable->value; } } return null; } 模型(如果存在的话)并创建一个(如果不存在),并且在任何一种情况下保存{{1}中的更改关系但不是数据库。 (UserVariable保存到关系但不保存到数据库。)

->variables

Laravel的$this->variables->push($variable)方法默认情况下不保存数据库中不存在的记录,但我们可以覆盖它来执行此操作。

    protected function setVariable($key, $value) {
        $this->loadVariablesIfNotLoaded();
        foreach($this->variables as $k => $variable) {
            if($variable->key === $key) {
                $variable->value = $value;
                return;
            }
        }
        // We didn't find an existing UserVariable so we create one.
        $variable = new UserVariable;
        $variable->user_id = $this->id;
        $variable->key = $key;
        $variable->value = $value;
        $this->variables->push($variable);
    }

最后,有了所有这些,我们可以为单个属性添加特定的getter和setter。

->push()

要使用它,只需按照通常的方式编写代码。

    public function push(array $options = []) {
        if($this->hasLoadedVariables()) {
            foreach($this->variables as $variable) {
                if(!$variable->exists) {
                    $this->variables()->save($variable);
                } else if($variable->isDirty()) {
                    $variable->save();
                }
            }
        }
        parent::push();
    }