拉维尔(Laravel)

发布于 2025-01-21 18:49:45 字数 1570 浏览 3 评论 0原文

我正在使用Laravel 8构建应用程序。此应用程序具有表顺序和表跟踪。订单和跟踪具有一对一关系,模型如下:

class Order extends Model
{
    protected $with = ['tracking'];

    public function tracking() 
    {
        return $this->hasMany(Tracking::class);
    }
}

class Tracking extends Model
{
    public function order() 
    {
        return $this->belongsTo(Order::class);
    }
}

现在我想查询订单在最后插入的跟踪中通过状态过滤它们的订单。对于以下数据,对于出现:

Order 
ID         VALUE
1          100.00
2          200.00
3          300.00

Tracking
ID     ORDER_ID  STATUS     CREATED_AT
1      1         Accept     2022-03-01 00:00:00
2      1         Paid       2022-03-02 00:00:00
3      2         Accept     2022-03-01 00:00:00
4      2         Paid       2022-03-02 00:00:00
5      2         Cancel     2022-03-03 00:00:00
6      3         Accept     2022-03-01 00:00:00
7      3         Paid       2022-03-02 00:00:00

如果子句中的param status status =已付费,我希望带有ID 1和3的返回订单,并且所有跟踪都相关。

我尝试使用这样的地方:

$query = Order::query();

$query->whereHas('tracking', function ($query) use ($request) {
    return $query->where('status', '=', 'Paid');
});
$orders = $query->paginate(10);

但是,这在所有跟踪中都会进行查询,并且只有在最后一个状态付费时,我才需要查询。

我需要这样的数据:

Order ID   VALUE     TRACKING
1          100.00    [[ID: 1, STATUS: Accept], [ID: 2, STATUS: Paid]]
3          300.00    [[ID: 6, STATUS: Accept], [ID: 7, STATUS: Paid]]

请注意,ID 2的订单已在2022-03-02付款,但在2022-03-03-03 00:00:00取消了该订单,因此该订单已取消,因此不会出现。

欢迎任何帮助。

谢谢。

I’m build a app with laravel 8. This app has table order and table tracking. The order and tracking has a one to many relation, the models are like below:

class Order extends Model
{
    protected $with = ['tracking'];

    public function tracking() 
    {
        return $this->hasMany(Tracking::class);
    }
}

class Tracking extends Model
{
    public function order() 
    {
        return $this->belongsTo(Order::class);
    }
}

Now I want query the orders filtering them by status in the last tracking inserted. For exemple, with the data below:

Order 
ID         VALUE
1          100.00
2          200.00
3          300.00

Tracking
ID     ORDER_ID  STATUS     CREATED_AT
1      1         Accept     2022-03-01 00:00:00
2      1         Paid       2022-03-02 00:00:00
3      2         Accept     2022-03-01 00:00:00
4      2         Paid       2022-03-02 00:00:00
5      2         Cancel     2022-03-03 00:00:00
6      3         Accept     2022-03-01 00:00:00
7      3         Paid       2022-03-02 00:00:00

If the param in where clause is status = Paid, I want return order with id 1 and 3, with all tracking related.

I try to use whereHas like this:

$query = Order::query();

$query->whereHas('tracking', function ($query) use ($request) {
    return $query->where('status', '=', 'Paid');
});
$orders = $query->paginate(10);

but this make a query in all tracking, and I need query only if the last status is like Paid.

And I need data like this:

Order ID   VALUE     TRACKING
1          100.00    [[ID: 1, STATUS: Accept], [ID: 2, STATUS: Paid]]
3          300.00    [[ID: 6, STATUS: Accept], [ID: 7, STATUS: Paid]]

Notice, that order with id 2 has Paid in 2022-03-02 but in 2022-03-03 00:00:00 its canceled, so this wont appear.

Any help is welcome.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

只怪假的太真实 2025-01-28 18:49:45

我现在无法再现这种情况。因此,建议无需进行任何性能测试的解决方案。我希望它能解决您的问题。

DB::table("tracking")
->join("order", function($join){$join;})
->select("tracking.order_id", "order.val", "max (tracking.created_at) as status_updated")
->where("order.id", "=", tracking.order_id)
->groupBy("tracking")
->get();

I cannot reproduce the situation right now. So, suggesting the following solution without any performance testing. I hope it will solve your issue.

DB::table("tracking")
->join("order", function($join){$join;})
->select("tracking.order_id", "order.val", "max (tracking.created_at) as status_updated")
->where("order.id", "=", tracking.order_id)
->groupBy("tracking")
->get();
半边脸i 2025-01-28 18:49:45

想要的结果有点复杂,因此我们需要注意我们将如何实现它。

为此,让我们将获取过程分解为各个部分:

  1. 我们要选择以下列:
    • order_id
    • tracking_id,<代码>状态create_at从相关跟踪记录。这些相关的记录将使用内部加入 on tracking.ording.order_id = orders.id
    • 上获取这些相关记录

  2. 说我们需要通过status ='付费'过滤,因此在查询中,我们可能会在procking.status ='付费'中使用之类的东西。
  3. 所需的结果可以描述如下:获取我的订单,其中最新的跟踪状态为“已付费”,这意味着如果特定订单>最新的tracking.status /code>不是'付费',我们不希望该顺序,也不希望其跟踪 s。
  4. 要实现上述语句,我们需要知道最新 跟踪order> order相关的记录,并且如果该相关记录具有状态< /代码>等于付费然后获取该order的所有相关记录。

这是一个sql查询,当我们需要tracking.status ='付费'

-- selects the needed columns
SELECT `orders`.`id` AS `order_id`, `value`, `t`.`id` AS `tracking_id`, `t`.`status`
FROM `orders`
-- fetch the related "tracking" records for each order in the set. The table "tracking" is aliased as "t" for simplicity
INNER JOIN `tracking` `t` ON `t`.`order_id` = `orders`.`id`
-- we need to get only the related records ONLY when the latest "tracking" saved for the current "order" is the status we want (in our case "paid")
WHERE (
    -- this subquery gets the status of the latest added "tracking" of each "order"
    -- check below sub queries
    SELECT `status`
    FROM `tracking`
    WHERE `order_id` = `orders`.`id` AND `id` = (
        -- this subquery gets the "id" of the latest added "tracking"
        -- check below sub query
        SELECT `id`
        FROM `tracking`
        WHERE `order_id` = `orders`.`id` AND `created_at` = (
            -- this subquery filters the records and returns the "created_at" values of the latest added "tracking"
            SELECT `created_at`
            FROM `tracking`
            WHERE `order_id` = `orders`.`id`
            -- instead of using "max" function we can simply get the first record in the reverse order (DESC)
            ORDER BY `created_at` DESC LIMIT 1
        )
    )
) = 'paid'

让我尝试将上述查询转换为Laravel的查询构建器:

use Illuminate\Support\Facades\DB

/** inform the query builder that the "orders" table is main table in the query */
DB::table('orders')
    /** instruct to select those columns */
    ->select('orders.id as order_id', 'orders.value', 't.status', 't.created_at') 
    /** have an INNER JOIN clause to get the related "tracking" records */
    ->join('tracking as t', 't.order_id', '=', 'orders.id')
    ->where('t.status', function ($q) {
        /** creates a subquery to get the status of the last inserted related "tracking" */
        $q->select('status')
            ->from('tracking')
            ->whereColumn('order_id', 'orders.id')
            ->where('created_at', function ($q) {
                /** a subquery to get the latest inserted tracking record (only the last will be returned) */
                $q->select('created_at')
                    ->from('tracking')
                    ->whereColumn('order_id', 'orders.id')
                    ->orderByDesc('created_at')
                    ->limit(1);
            });
    })->get(); /** execute the query */

未测试上述查询,因此最好花一些时间对其进行测试并按照所需的方式进行调整。

基于问题中提供的数据样本,上述sql查询应返回:

order_idvaluetracking_id状态
1100.001接受
1100.002付费
3300.006接受
3300.007付费

7 。

The wanted result is a bit complex to achieve, so we need to pay attention to how we'll achieve it.

To do so, let's decompose the fetching process into parts :

  1. we want to select the following columns :
    • order_id and value from orders table.
    • tracking_id, status and created_at from the related tracking records. Those related records will be fetched using an INNER JOIN clause on tracking.order_id = orders.id.
  2. Say we need to filter by status = 'paid' so in the query we'll likely to have something like where tracking.status = 'paid'.
  3. The wanted result can be described as follows: GET ME orders WHERE LATEST TRACKING STATUS IS 'paid' which means if the latest tracking.status for a specific order is not 'paid' then we don't want that order, nor its trackings, at all.
  4. To achieve the above statement, we need to know the latest tracking record related to order and if that related record has a status equals to paid then fetch all the related records for that order.

Here's an SQL query that fetches the wanted results when we need tracking.status = 'paid' :

-- selects the needed columns
SELECT `orders`.`id` AS `order_id`, `value`, `t`.`id` AS `tracking_id`, `t`.`status`
FROM `orders`
-- fetch the related "tracking" records for each order in the set. The table "tracking" is aliased as "t" for simplicity
INNER JOIN `tracking` `t` ON `t`.`order_id` = `orders`.`id`
-- we need to get only the related records ONLY when the latest "tracking" saved for the current "order" is the status we want (in our case "paid")
WHERE (
    -- this subquery gets the status of the latest added "tracking" of each "order"
    -- check below sub queries
    SELECT `status`
    FROM `tracking`
    WHERE `order_id` = `orders`.`id` AND `id` = (
        -- this subquery gets the "id" of the latest added "tracking"
        -- check below sub query
        SELECT `id`
        FROM `tracking`
        WHERE `order_id` = `orders`.`id` AND `created_at` = (
            -- this subquery filters the records and returns the "created_at" values of the latest added "tracking"
            SELECT `created_at`
            FROM `tracking`
            WHERE `order_id` = `orders`.`id`
            -- instead of using "max" function we can simply get the first record in the reverse order (DESC)
            ORDER BY `created_at` DESC LIMIT 1
        )
    )
) = 'paid'

Let me try to translate the above query into Laravel's query builder:

use Illuminate\Support\Facades\DB

/** inform the query builder that the "orders" table is main table in the query */
DB::table('orders')
    /** instruct to select those columns */
    ->select('orders.id as order_id', 'orders.value', 't.status', 't.created_at') 
    /** have an INNER JOIN clause to get the related "tracking" records */
    ->join('tracking as t', 't.order_id', '=', 'orders.id')
    ->where('t.status', function ($q) {
        /** creates a subquery to get the status of the last inserted related "tracking" */
        $q->select('status')
            ->from('tracking')
            ->whereColumn('order_id', 'orders.id')
            ->where('created_at', function ($q) {
                /** a subquery to get the latest inserted tracking record (only the last will be returned) */
                $q->select('created_at')
                    ->from('tracking')
                    ->whereColumn('order_id', 'orders.id')
                    ->orderByDesc('created_at')
                    ->limit(1);
            });
    })->get(); /** execute the query */

The above query was not tested so it's better that you take some time to test it and tweak it the way you want.

Based on the data sample provided in the question, the above SQL query should return :

order_idvaluetracking_idstatus
1100.001Accept
1100.002Paid
3300.006Accept
3300.007Paid

Hope i have pushed you further.

音栖息无 2025-01-28 18:49:45

编辑:我以前的答案是完全错误的,所以这是另一种尝试:

更轻松的方法可能是在订单模型中定义扩展的关系:

// App\Models\Order
public function active_tracking() {
    return $this->tracking()->where('status', 'Paid');
}

那么,您可以使用Active的订单来获取订单跟踪记录:

Order::with('active_tracking')->whereHas('active_tracking')->get();

Edit: My previous answer was totally wrong, so here's another attempt:

An easier way to do this probably would be to define an extended relationship in the Order model:

// App\Models\Order
public function active_tracking() {
    return $this->tracking()->where('status', 'Paid');
}

Then you can just fetch your orders with with active Tracking records:

Order::with('active_tracking')->whereHas('active_tracking')->get();
孤蝉 2025-01-28 18:49:45

更新1.2

根据我的评论 ,我已更新了答案。我也是一个好主意,如果订单的最后一个track没有取消值,我将返回此曲目,我不会退货。

use Illuminate\Pagination\LengthAwarePaginator;

$orders = Order::with('tracks')->withCount('tracks')->get();
$filteredOrders = [];

$orders->each(function ($order) use (&$filteredOrders) {
    $order->tracks->each(function ($track) use ($order, &$filteredOrders) {
        if ($order->tracks[$order->tracks_count - 1]->STATUS != "Cancel") {
            array_push($filteredOrders, $order);
        }
    });
});

// Paginate the filtered array
return new LengthAwarePaginator($filteredOrders, count($filteredOrders), 10);

Update 1.2

According to my comment, I have updated my answer. I've a nice idea also is that if the last track of the order has not has a Cancel value I'll return this track else, I'll not return it.

use Illuminate\Pagination\LengthAwarePaginator;

$orders = Order::with('tracks')->withCount('tracks')->get();
$filteredOrders = [];

$orders->each(function ($order) use (&$filteredOrders) {
    $order->tracks->each(function ($track) use ($order, &$filteredOrders) {
        if ($order->tracks[$order->tracks_count - 1]->STATUS != "Cancel") {
            array_push($filteredOrders, $order);
        }
    });
});

// Paginate the filtered array
return new LengthAwarePaginator($filteredOrders, count($filteredOrders), 10);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文