【问题标题】:Laravel eager loading sort by relationshipLaravel 急切加载按关系排序
【发布时间】:2015-05-14 08:37:34
【问题描述】:

我有一些关系(希望我可以正确解释)并且需要按照本质上是远距离的关系对输出进行排序。

我有一个数据透视表,其中包含许多关系的详细信息,包括我想要排序的关系。

--用户.php

public function players()
{
    return $this->belongsToMany('App\Models\Player', 'league_player_user')->withPivot('position_id');
}

--Player.php

public function position()
{
    return $this->belongsToMany('App\Models\Position', 'league_player_user');
}

我会渴望像这样加载关系;

$user = User::with('players')->where('id', Auth::user()->id)->first();

所以,我想我想做这样的事情(不起作用)。

$user = User::with(['players' => function($q){
    $q->orderBy('position.sort_id', 'asc');
}])->where('id', Auth::user()->id)->first();

关键的Table结构看起来有点像这样;

league_player_user.
...league_id
...player_id
...user_id
...position_id

Positions 表包含 sort_id

希望这是足够的信息,如果需要,请请求更多信息。非常感谢。

【问题讨论】:

  • User 模型中的 belongsToMany 将自动加入数据透视表(和远表 (players)),但不会加入 position 表(即使你说您想要position_id 列。因此,您需要确保自己添加额外的连接。我认为您可以使用User::with('players', 'players.position') 执行此操作(尽管position_id 是来自枢轴关系的虚拟字段,您可能不会能这样,你得玩代码)。关键是,position 不会自动加入,所以你不能点它。
  • 谢谢@alexrussell。我看过这个,是的 - 也渴望加载嵌套关系。然后我可以在 player.position 中使用 orderBy() 查询,虽然它没有错误,但它也没有对结果进行正确排序..
  • 而不是使用->first() 使用->toSql() 来返回它生成的SQL - 这将帮助您准确调试ORM 的内容。
  • 感谢@alexrussell,但在这种情况下,这对我来说并没有任何结果——它似乎没有提供任何对我有帮助的细节!
  • 好吧,我想我明白你在做什么了 - 获得一个用户,但他们的玩家已经填充并按位置排序。我是否可以建议,为了使您的查询更简单,您不要急于加载播放器。因此,您需要预先做的就是$user = Auth::user();。然后,在关系上(players() 方法)你做特殊的排序工作:return $this->belongsToMany('App\Models\Player', 'league_player_user')->withPivot('position_id')->join('position', 'league_player_user.position_id', '=', 'position.id')->orderBy('position.sort_id');

标签: php laravel laravel-5 eager-loading


【解决方案1】:

好的,所以您正在尝试获取单个用户,但他们的玩家(用户 bTM 玩家)已经填充并按位置(枢轴 bT 位置)排序。

在这种情况下,如果不进行修改,您将无法使用 Laravel 的内置关系方法,因为 Laravel 并不是为处理其他关系的数据透视表上的关系而构建的。幸运的是,ORM 足够灵活,可以让您通过“手动”连接来做您想做的事情。

所以要直接回答你的问题,这里是你需要的代码(你真的很接近!):

$user = User::with(['players' => function ($q) {
    $q->join('position', 'league_player_user.position_id', '=', 'position.id')
      ->orderBy('position.sort_id', 'asc');
}])->where('id', Auth::user()->id)->first();

但是,在我看来,这不是很好的代码,原因如下:

  1. 您正在手动获取授权用户(当Auth::user() 如此方便时)
  2. 实际上,您必须从模型中获取特定于实现的逻辑(事实上,数据透视表称为 league_player_user 并将其放在......无论它在哪里(您的控制器?)
  3. 这只会影响这一个查询 - 如果您碰巧通过其他方式获得了 User(例如 Auth::user() 或 User::find(1) 或其他方式),您将无法正确订购玩家

因此,我是否可以建议,为了使您的查询更简单,您不要急于加载播放器。因此,您需要预先做的就是:

$user = Auth::user();

现在,关于关系(User 类中的 player() 方法),您需要进行特殊的排序工作:

public function players()
{
    return $this->belongsToMany('App\Models\Player', 'league_player_user')
                ->withPivot('position_id')
                ->join('position', 'league_player_user.position_id', '=', 'position.id')
                ->orderBy('position.sort_id');
}

这样,无论何时您致电$user->players,您都会得到正确的订购。

我必须补充一点,由于 Laravel 执行急切加载查询的方式,这样做可能不允许您急切加载播放器,因为 Laravel 的急切加载(即在 ORM 链中使用 ->with())用于主要查询(即用户)和一个用于关系(即玩家),但它执行此查询以获取所有结果,因此可能无法使用特殊的排序系统。你必须看看你是否真的关心急切加载玩家。在您的上述情况下(您获得的是单个授权用户),在我看来,急切加载并不那么重要。


编辑以添加有关预加载的说明:

我建议急切加载可能不起作用的原因是 Laravel 中的急切加载有点像这样:假设您有类别和产品:Category hM Product, Product bT Category。要获得一个类别,你可以使用$category = Category::find(1),Laravel 将其转换为类似这样的查询:SELECT * FROM `categories` WHERE `id` = '1'。如果你随后调用$category->products,Laravel 将发出SELECT * FROM `products` WHERE `category_id` = '1'。这是明智的。但是如果你有下面的代码那就更糟了:

<?php $categories = Category::all(); ?>

<ul>
    @foreach ($categories as $category)
        <li>Category {{{ $category->name }}} contains {{{ $category->products->count() }}} products.</li>
    @endforeach
</ul>

在这种情况下,您会有以下查询:

SELECT * FROM `categories`;
SELECT * FROM `products` WHERE `category_id` = '1';
SELECT * FROM `products` WHERE `category_id` = '2';
SELECT * FROM `products` WHERE `category_id` = '3';
... as many categories as you had

但是,如果您要将第一行更改为:

<?php $categories = Category::with('products')->get(); ?>

现在您只有两个查询:

SELECT * FROM `categories`;
SELECT * FROM `products` WHERE `category_id` IN ('1', '2', '3', ...);

然后,在调用第二个查询之后,Laravel 将根据它知道您拥有的类别 ID 为您创建各种集合。

但是,这是简单的关系案例。在您的情况下,products() 方法不仅仅是return $this-&gt;hasMany('Product');,它还包括枢轴和手动连接等,并且第二个查询(即急切加载的查询)可能无法执行应对并正确地进行排序。

正如我所说,我不确定这是如何工作的,这对我来说只是一个危险信号。一定要试一试,看看你会得到什么——你可能会发现它对你有用。

【讨论】:

  • 这完全是正确的答案。但是,您提到如果不进行一些修改,则可能无法以这种方式进行预加载。在这个特定的查询中,它会导致更多的数据库查询......这不是我应该担心的吗? (一页最多 30 个..)
  • 其实我认为你混淆了我所说的两件事(但这是我的错——我不是很清楚)。我说如果不修改,你将无法做你想做的事。然后我给你这些修改。然后最后我建议用急切加载来做这件事可能行不通。我现在将我的推理添加到答案的底部,因为它有点深入的解释。但最后我并不真正知道 Laravel 是如何进行这种预加载的,因为我只在更简单的情况下使用过它,所以如果你想使用预加载,试试看是否可行工作。
猜你喜欢
  • 2020-12-15
  • 2017-03-01
  • 2014-06-21
  • 1970-01-01
  • 2017-01-25
  • 2021-12-01
  • 2021-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多