【问题标题】:Laravel pivot table and Model EventsLaravel 数据透视表和模型事件
【发布时间】:2014-06-04 12:00:23
【问题描述】:

我正在开发一款游戏,目前正在管理部分添加游戏和关卡。

我通过带有“订单”列的数据透视表引用了游戏和关卡。

我的游戏模型:

class Game extends Eloquent {

    public function levels() {
        return $this->belongsToMany('Level')->withPivot('order');
    }
}

我的关卡模型:

class Level extends Eloquent {

    public function game() {
        return $this->belongsToMany('Game')->withPivot('order');
    }
}

我想要实现的是在添加关卡时自动填充订单列,并创建 moveup() 和 movedown() 函数。

我尝试在关卡模型上设置模型事件来实现这一点,但没有成功。

【问题讨论】:

  • “在创建关卡时自动增加顺序”是什么意思?您的数据透视表是否有超过两列?
  • 嗨,我已经习惯了 symfony 的“可排序”行为。我的意思是,当我添加一个新关卡时,它会根据添加到的游戏以及已经属于该游戏的关卡数量自动填充顺序列。我的数据透视表有四列(ID、game_id、level_id、order)。
  • 我明白...我相信你必须手动处理这个问题,也许在你关卡的create 操作上使用过滤器。希望有人提出更好的解决方案,因为这看起来像是一个常见问题......

标签: php laravel laravel-4 eloquent


【解决方案1】:

首先,这种顺序是单向的。您不能将同一列用于游戏的关卡顺序,反之亦然。

现在,您可以通过覆盖关系上的attach 方法来实现所需的功能,以检查当前最大位置并相应地添加下一个以进行插入(不创建事件或其他任何内容,因为这与level 无关创建本身,而不是将其附加到游戏中)。

话虽如此,您需要扩展 Eloquent Model 或使用 trait 才能使用您的自定义 BelongsToMany 关系。像下面这样的东西应该可以完成这项工作(没有 cmets 等使其可能简洁)。

// Game model
class Game extends Eloquent {

  use SortablePivotModelTrait;

  public function levels()
  {
    return $this->sortableBelongsToMany('Level', 'order')->withPivot('order');
  }

  //...
}

// SortablePivotModelTrait.php
use Illuminate\Database\Eloquent\Model;

trait SortablePivotModelTrait {

    public function sortableBelongsToMany($related, $orderColumn = null, $table = null, $foreignKey = null, $otherKey = null, $relation = null)
    {
        // all the code of belongsToMany method needs to go here
        // ...

        // then just:
        return new SortableBelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation, $orderColumn);
    }

}

// SortableBelongsToMany.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class SortableBelongsToMany extends BelongsToMany {

    protected $orderColumn;

    public function __construct(Builder $query, Model $parent, $table, $foreignKey, $otherKey, $relationName = null, $orderColumn = 'order')
    {
        $this->orderColumn = $orderColumn;

        parent::__construct($query, $parent, $table, $foreignKey, $otherKey, $relationName);
    }

    public function attach($id, array $attributes = array(), $touch = true)
    {
        $attributes = array_merge($attributes, [$this->getOrderColumnName() => $this->getNextPosition()]);

        return parent::attach($id, $attributes, $touch);
    }

    public function getNextPosition()
    {
        return 1 + $this->newPivotQuery()->max($this->getOrderColumnName());
    }

    public function getOrderColumnName()
    {
        return $this->orderColumn;
    }

}

这样你就可以做到:

$level = new Level;
// do some stuff with the created level

$game = Game::find($id);

// then
$game->save($level);

// or
$level->save();
$game->levels()->attach($level);

【讨论】:

  • 那里有几个错别字.. 但除此之外,似乎工作得很好。
  • @UnamataSanatarai 感谢您的这一有益而有见地的评论,我真的很高兴您发布了它!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-29
  • 2021-08-17
  • 2014-01-12
  • 2015-01-22
相关资源
最近更新 更多