【问题标题】:Eloquent Relationship with a conditional scope有条件作用域的雄辩关系
【发布时间】:2019-04-29 21:47:50
【问题描述】:

我正在开发一款 Fantasy 体育应用。我使用的模型是 FantasyPlayer、PlayerGame、TeamGame

FantasyPlayer 可以有多个 PlayerGame,也可以有多个 TeamGame

public function PlayerGame()
{
    return $this->hasMany('App\Models\PlayerGame','player_id','player_id');
}

public function TeamGame()
{
    return $this->hasMany('App\Models\FantasyData\TeamGame','team','fantasy_player_key');
}

当我加载数据时,我当前使用的是预先加载:

FantasyPlayer::with(['PlayerGame', 'TeamGame'])->take(1)->get();

加载两个关系然后加载哪个关系变得乏味。理想情况下,我想让模型处理这个逻辑。所以我可以做这样的事情:

FantasyPlayer::with(['FantasyGame'])->take(1)->get();

然后我的 FantasyGame 范围将包含我需要基于位置的 FantasyPlayer 值的 PlayerGame 或 TeamGame 记录。像这样的东西是我想要的......但它对我不起作用:

public function scopeFantasyGame($query)
{
    if($this->position == "DEF"){
      return $this->TeamGame();
    }
    else{
      return $this->PlayerGame();
    }
 }

有谁知道我可以使用预加载并让 FantasyGame 根据 FantasyPlayer 位置属性返回正确的关系?:

FantasyPlayer::with(['FantasyGame'])->take(1)->get();

【问题讨论】:

  • 你没有提供足够的信息来确定,但这听起来可能是使用 Laravel polymorphic relationships 的好候选人。
  • 如果只返回 1 个结果,是否需要预先加载?

标签: php laravel eloquent eloquent-relationship


【解决方案1】:

#1

您不能根据结果元素有条件地预先加载关系,这是因为预先加载发生在您检索记录之前,这就是为什么这不起作用:

# FantasyPlayer.php

public function scopeFantasyGame($query)
{
    if($this->position == "DEF") // <--- Laravel doens't retrieve the records yet,
    {                            //      so this won't work
      //
    }
    else
    {
      // 
    }
 }

#2

Local Query scopes 用于约束查询,在您的情况下,您希望加载与此范围的关系,而不是它的一般用途,但确定您可以做到:

# FantasyPlayer.php

public function scopeFantasyGame($query)
{
    return $query->with(['PlayerGame', 'TeamGame']);
}

然后像这样使用它:

# YourController.php

public function myFunction($query)
{
    $fantasyPlayers = FantasyPlayer::fantasyGame()->get();
}

#3

但是,如果你想总是急切地加载关系,为什么要使用查询范围而不是告诉 laravel 默认加载你想要的关系呢?你可以在你的模型中指定它(检查 Eager Loading By Default of this section of the docs):

# FantasyPlayer.php

protected $with = ['PlayerGame', 'TeamGame'];

更新

如果要检索始终具有给定关系的元素,则有两条路径。对于第一个,您可以使用查询范围仅加载这些元素:

# FantasyPlayer.php

public function scopeHasFantasyGame($query)
{
    return $query
              ->has('PlayerGame')
              ->has('TeamGame');
}

然后:

# YourController.php

public function myFunction($query)
{
    $fantasyPlayers = FantasyPlayer::hasFantasyGame()->get();
}

第二个选项是检索元素,然后根据关系的存在过滤集合(使用Map() 函数):

# YourController.php

public function myFunction($query)
{
    $fantasyPlayers = FantasyPlayer::all()
                          ->map(function ($fantasyPlayer) {
                              return $fantasyPlayer->PlayerGame()->exists()
                                     && $fantasyPlayer->TeamGame()->exists();
                          });
}

【讨论】:

  • 感谢您的解释 - 现在更清楚了。默认的急切加载听起来不错,从长远来看会有所帮助。在那种情况下,我仍然需要做一些逻辑检查来查看哪个关系有数据(PlayerGame 或 TeamGame)。但是,我的最终目标是能够调用 FantasyPlayer->FantasyGame() 并且始终知道 FantasyGame 包含我需要的记录(PlayerGame 或 TeamGame)。但是,急切加载可能无法实现。
  • 嗯,你有两种方法可以做到这一点。我会更新我的答案。
  • 再次感谢您提供出色的示例代码和信息!这将极大地帮助我!
  • @thindery 很高兴为您提供帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-15
  • 2019-05-10
相关资源
最近更新 更多