Laravel渴望按关系排序

时间:2015-05-14 08:37:35

标签: php laravel laravel-5 eager-loading

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

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

- user.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();

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

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

Positions表包含sort_id

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

1 个答案:

答案 0 :(得分: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)或其他任何方式),您将无法获得球员正确订购
  4. 因此,我可以建议,为了使您的查询更简单,您不要急于加载玩家。因此,您需要预先做的就是:

    $user = Auth::user();
    

    现在,关于User类中的关系(players()方法),您可以进行特殊订购工作:

    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 ProductProduct 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->hasMany('Product');,还包括一个数据透视表和一个手动连接等,而第二个查询可能是一个急切的加载,只是无法正确应对并做到这一点。

    正如我所说,我不确定这是如何运作的,这对我来说只是一个红旗。一定要试一试,看看你得到了什么 - 你可能会发现它对你有用。