Laravel数据透视表和模型事件


Laravel pivot table and Model Events

我正在制作一款游戏,我目前正在做添加游戏和关卡的管理部分。

我将游戏和关卡通过带有"顺序"列的数据透视表进行引用。

我的游戏模型:

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()函数。

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

首先,这种顺序是单向的。你不能使用相同的列来显示游戏的关卡顺序,反之亦然。

现在,你可以实现你所需要的,例如通过重写attach方法来检查当前的最大位置,并相应地添加next for insert(不是创建事件或任何东西,因为这与level创建本身无关,而是将其附加到游戏中)。

也就是说,为了使用自定义的BelongsToMany关系,您需要扩展Eloquent Model或使用一个trait。像下面这样的内容应该可以完成这项工作(没有注释等,使其简洁)。

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