【问题标题】:(Laravel) How to filter table by newest pivot value(Laravel)如何按最新的枢轴值过滤表
【发布时间】:2018-06-25 03:19:49
【问题描述】:

我有一个订单表 (orders)、一个状态表 (order_statuses) 和一个用作状态日志的数据透视表 (order_order_status)。

当订单状态发生变化时,会在数据透视表中添加一个条目。该订单的数据透视表中的最新条目将是它的当前状态。

我需要能够显示当前具有给定状态的所有订单。例如,所有处于“报价”状态的订单。有没有一种雄辩的方式来构建这个查询?

(编辑,附加说明:订单的当前状态是状态日志中具有最新“created_at”日期的条目。)

以下是模式的一些示例:

mysql> SELECT * FROM orders WHERE id = 2;
+----+---------+--------------+---------------+----------------------+---------------------+---------------------+
| id | user_id | order_number | job_reference | accounting_reference | created_at          | updated_at          |
+----+---------+--------------+---------------+----------------------+---------------------+---------------------+
|  2 |      73 | 37-5         | Janis Joplin  | NULL                 | 2018-06-25 02:27:21 | 2018-06-25 02:27:21 |
+----+---------+--------------+---------------+----------------------+---------------------+---------------------+

mysql> SELECT * FROM order_order_status WHERE order_id = 2 ORDER BY created_at;
+------+----------+-----------------+---------+---------------------+---------------------+
| id   | order_id | order_status_id | user_id | created_at          | updated_at          |
+------+----------+-----------------+---------+---------------------+---------------------+
|    2 |        2 |               2 |     753 | 2012-06-27 09:47:00 | 2012-06-27 09:47:00 |
|    3 |        2 |               3 |     753 | 2012-06-27 09:56:00 | 2012-06-27 09:56:00 |
|    4 |        2 |               4 |     753 | 2012-06-27 09:56:00 | 2012-06-27 09:56:00 |
|    5 |        2 |               5 |    1153 | 2012-06-27 10:13:00 | 2012-06-27 10:13:00 |
|    6 |        2 |               6 |    1153 | 2012-06-27 10:13:00 | 2012-06-27 10:13:00 |
|    7 |        2 |              10 |    1153 | 2012-06-27 10:13:00 | 2012-06-27 10:13:00 |
|    8 |        2 |               7 |    1153 | 2012-06-27 10:13:00 | 2012-06-27 10:13:00 |
|    9 |        2 |              10 |    1153 | 2012-06-27 10:42:00 | 2012-06-27 10:42:00 |
|   10 |        2 |               7 |    1153 | 2012-06-27 10:42:00 | 2012-06-27 10:42:00 |
|   11 |        2 |               8 |     753 | 2012-06-27 10:44:00 | 2012-06-27 10:44:00 |
|   12 |        2 |               9 |     753 | 2012-06-27 10:45:00 | 2012-06-27 10:45:00 |
| 2222 |        2 |              10 |      54 | 2013-01-03 12:08:00 | 2013-01-03 12:08:00 |
+------+----------+-----------------+---------+---------------------+---------------------+

mysql> SELECT * FROM order_statuses;
+----+----------------+----------------+---------------------+---------------------+
| id | title          | tag            | created_at          | updated_at          |
+----+----------------+----------------+---------------------+---------------------+
|  1 | Archived Quote | archived_quote | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
|  2 | Quote          | quote          | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
|  3 | Order          | order          | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
|  4 | Confirmed      | confirmed      | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
|  5 | Manufacturing  | manufacturing  | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
|  6 | Painting       | painting       | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
|  7 | Dispatched     | dispatched     | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
|  8 | Invoiced       | invoiced       | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
|  9 | Paid           | paid           | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
| 10 | Closed         | closed         | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
| 11 | Archived       | archived       | 2018-06-25 02:25:28 | 2018-06-25 02:25:28 |
+----+----------------+----------------+---------------------+---------------------+

编辑:进一步澄清。这是返回所需结果的 SQL 查询。我正在寻找一种获得相同结果的雄辩方法:

SELECT a.order_status_id, c.* 
FROM order_order_status a
INNER JOIN (
    SELECT order_id, MAX(updated_at) last_date
    FROM order_order_status
    GROUP BY order_id
) b ON a.order_id = b.order_id AND a.updated_at = b.last_date 
INNER JOIN 
        orders c 
    ON c.id = a.order_id 
WHERE a.order_status_id = (SELECT id from order_statuses where tag="closed")

【问题讨论】:

    标签: php mysql laravel eloquent pivot-table


    【解决方案1】:

    您必须使用Join。 Eloquent 在获取多对多关系时会创建两个查询。不幸的是,在这种情况下,不能通过雄辩的方式使用 order by。

    更新

    查询生成器将是您最好的选择,不幸的是,接受的答案效率极低,至少可以说不能很好地扩展。在我的脑海中,查询将类似于:

    $orders = DB::table('orders')
        ->leftJoin('order_order_status', 'orders.id', '=', 'order_order_status.order_id')
        ->leftJoin('order_statuses', function ($join) use ($title) {
             $join->on(
                 'order_order_status.order_status_id',
                 '=',
                 DB::raw('SELECT id FROM order_statuses WHERE title = ' . $title . ' AND order_order_status.order_status_id = order_statuses.id ORDER BY created_at DESC LIMIT 1')
             );
        })
        ->select('orders.*')
        ->groupBy('orders.id')
        ->get();
    

    【讨论】:

    • +1 用于了解潜在问题。如果没有其他人提出一个雄辩的答案,我将其标记为正确。
    【解决方案2】:

    您需要在每个模型中添加一些关系才能加入它们

    订单

    namespace App\Model;
    use Illuminate\Database\Eloquent\Model;
    
    class orders extends Model 
    {
        protected $table = 't_orders';
        protected $primaryKey = 'id';
    
        // join t_orders with t_order_order_status but get only the latest matching row out of order_order_status
        public function status_log()
        {
            return $this->hasMany('App\Model\order_order_status', 'order_id','id')->orderBy('t_order_order_status.id', 'desc')->limit(1);
        }
    
    }
    

    order_order_status

    namespace App\Model;
    use Illuminate\Database\Eloquent\Model;
    
    class order_order_status extends Model 
    {
        protected $table = 't_order_order_status';
        protected $primaryKey = 'id';
    
        // join t_orders with t_order_order_status
        public function status_name()
        {
            return $this->hasOne('App\Model\order_statuses', 'id','order_status_id');
        }
    
    }
    

    订单状态

    namespace App\Model;
    use Illuminate\Database\Eloquent\Model;
    
    class order_statuses extends Model 
    {
        protected $table = 't_order_statuses';
        protected $primaryKey = 'id';
    
    }
    

    然后你会做这样的事情

    App\Model\orders::with('status_log.status_name')->get();
    

    应该得到这样的东西

    [  
       {  
          "id":2,
          "user_id":73,
          "order_number":"37-5",
          "job_reference":"Janis Joplin",
          "accounting_reference":null,
          "created_at":"2018-06-25 02:27:21",
          "updated_at":"2018-06-25 02:27:21",
          "status_log":[{  
            "id":4,
            "order_id":2,
            "order_status_id":4,
            "user_id":753,
            "created_at":"2012-06-27 09:56:00",
            "updated_at":"2012-06-27 09:56:00",
            "status_name":{  
               "id":4,
               "title":"Confirmed",
               "tag":"confirmed",
               "created_at":"2018-06-25 02:25:28",
               "updated_at":"2018-06-25 02:25:28"
            }
          }]
       }
    ]
    

    请注意,为了便于阅读,我在表名前加上了 t_,但您可以将它们命名为与您的模型相同。

    更新

    为此,您可以使用类似这样的方法传递一个基于状态 id 进行过滤的集群

    $status = 3;
    
    $result =  App\Model\orders::with(['status_log'=>function($query) use($status) {
    
        $query->where('t_order_order_status.order_status_id','=', $status);
    
        $query->with('status_name');
    
    }])->get();
    
    return $result;
    

    会给

    [{  
          "id":2,
          "user_id":73,
          "order_number":"37-5",
          "job_reference":"Janis Joplin",
          "accounting_reference":null,
          "created_at":"2018-06-25 02:27:21",
          "updated_at":"2018-06-25 02:27:21",
          "status_log":[{                       // will be empty [] for order not in t_order_order_status
            "id":3,
            "order_id":2,
            "order_status_id":3,
            "user_id":753,
            "created_at":"2012-06-27 09:56:00",
            "updated_at":"2012-06-27 09:56:00",
            "status_name":{  
                   "id":3,
                   "title":"Order",
                   "tag":"order",
                   "created_at":"2018-06-25 02:25:28",
                   "updated_at":"2018-06-25 02:25:28"
            }
         }]
    
       }]
    

    它将返回所有订单,但您可以使用status_log 来确定它在t_order_order_status 表中是否有任何记录,但如果您想删除“额外”并仅保留那些已经存在的订单在记录器表中,您可以在$result 上使用过滤器。

    return $result->filter(function ($item) {
        return !empty($item->status_log && count($item->status_log));
    });
    

    【讨论】:

    • 更新了说明。
    • 这个解决方案有效,但它真的很慢。过滤 7832 个订单的数据库的查询需要 763 毫秒,而问题中引用的 SQL 只需要 55 毫秒。尽管如此,它确实有效。
    【解决方案3】:

    php artisan make:model 用户

    在您的用户模型中:

       protected $table='users';
       public $primaryKey='id';
    
     public function orders(){
        return $this->belongsToMany('App\Model\Orders','order_order_status','user_id','order_id')->orderby('created_at','desc');
     }
    

    php artisan make:model 命令

    在您的订单模型中:

      protected $table='orders';
      public $primaryKey='id';
    
      public function user(){
        return $this->belongsTo('App\Model\User','user_id');
      }
    
     public function order_status()
      {
       return $this->belongsToMany('App\Model\OrderStatus','order_order_status','order_id','order_status_id')->withTimestamps();
      }
    

    php artisan make:model OrderStatus

    在您的 OrderStatus 模型中:

      protected $table='order_statuses';
      public $primaryKey='id';
    
     public function orders()
        {
            return $this->belongsToMany('App\Model\Order','order_order_status')->with('user')->orderBy('created_at','desc');
        }
    

    php artisan make:controller OrdersController

    现在您想按 OrderStatus title='Quote' 获取订单

    在您的 OrdersController 中:

    use Illuminate\Http\Request;
    
    
    
     public function get_order(Request $request,$title){
             // $title='Quote';
             $orders=OrderStatus::where('title',$title)->with('orders')->get();
             return response()->json($orders);
        }
    

    【讨论】:

    • 很遗憾,get_order 函数将随时返回状态为“报价”的订单,而不是当前状态设置为“报价”的订单
    • 你需要在 $title 上传递不同的值
    • 通过将静态值传递给$title='Confirmed'进行检查
    • 标题不是问题。一个订单会同时在 order_order_status 表中有很多状态。当前状态是 order_order_status 表中的最新行。您的解决方案将随时返回所有状态为“报价”的订单(例如),而不仅仅是当前状态为“报价”的订单。
    • 一个订单可以同时有多个状态。 -- 怎么可能,请编辑您的问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 2012-05-23
    • 2021-08-12
    • 2020-07-29
    • 1970-01-01
    相关资源
    最近更新 更多