Laravel 9 – 永夜 https://www.shuijingwanwq.com 没有不值得去解决的问题,也没有不值得去学习的技术! Sat, 25 Apr 2026 03:57:21 +0000 zh-Hans hourly 1 https://wordpress.org/?v=7.0 在 Spatie\QueryBuilder 中,查询 SQL 强制使用索引 https://www.shuijingwanwq.com/2026/04/25/9583/ https://www.shuijingwanwq.com/2026/04/25/9583/#comments Sat, 25 Apr 2026 03:56:41 +0000 https://www.shuijingwanwq.com/?p=9583 浏览量: 214

1、参考:列表接口响应超时的优化 。在 Spatie\QueryBuilder 中,查询 SQL 强制使用索引 。实现如下

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Arr;

        // 当筛选字段包含:交运状态、交运类型、操作来源 时,强制使用组合索引 order_shipping_logs_sag_os_ss_st_index
        if (Arr::hasAny($criteria['filter'], ['shipping_status','shipping_type', 'operated_source'])) {
            Log::info(
                '$criteria1',
                $criteria['filter']
            );
            $builder->from(DB::raw('order_shipping_logs FORCE INDEX (order_shipping_logs_sag_os_ss_st_index)'));
        }

2、生成的 SQL 如下,符合预期。如图1

生成的 SQL 如下,符合预期
select
  count(*) as aggregate
from
  order_shipping_logs FORCE INDEX (order_shipping_logs_sag_os_ss_st_index)
where
  `order_shipping_logs`.`shipping_type` = 2
  and `shipping_at_gmt` >= '2024-07-22 03:39:54'
  and `shipping_at_gmt` <= '2024-10-22 03:39:54';


select
  count(*) as aggregate
from
  order_shipping_logs FORCE INDEX (order_shipping_logs_sag_os_ss_st_index)
where
  `order_shipping_logs`.`operated_source` in (3)
  and `order_shipping_logs`.`shipping_type` = 2
  and `order_shipping_logs`.`shipping_status` = 3
  and `shipping_at_gmt` >= '2024-07-22 03:47:16'
  and `shipping_at_gmt` <= '2024-10-22 03:47:16'
]]>
https://www.shuijingwanwq.com/2026/04/25/9583/feed/ 1
在 Laravel 9 中报错:Indirect modification of overloaded property ReturnOrder::$order has no effect https://www.shuijingwanwq.com/2024/10/24/8836/ https://www.shuijingwanwq.com/2024/10/24/8836/#respond Thu, 24 Oct 2024 01:53:45 +0000 https://www.shuijingwanwq.com/?p=8836 浏览量: 188 1、在 Laravel 9 中报错:Indirect modification of overloaded property ReturnOrder::$order has no effect。如图1
在 Laravel 9 中报错:Indirect modification of overloaded property ReturnOrder::$order has no effect

图1



{
    "status_code": 500,
    "code": 0,
    "message": "Indirect modification of overloaded property ReturnOrder::$order has no effect",
    "trace": {
        "line": 648,
        "file": "E:\\object\\ReturnOrderService.php",
        "class": "ErrorException"
    }
}


2、代码实现如下


$item->order = $keyedOrders[$item->order_id] ?? null;
$item->order->returnOrders = $keyedOrderReturnOrders[$item->order_id] ?? new EloquentCollection();


2、调整后的代码实现如下,不再报错


$item->order = $keyedOrders[$item->order_id] ?? null;
if ($item->order) {
	$item->order->returnOrders = $keyedOrderReturnOrders[$item->order_id] ?? new EloquentCollection();
}


]]>
https://www.shuijingwanwq.com/2024/10/24/8836/feed/ 0
每天定时执行命令,在命令中同步一张表中的某个字段值的汇总至另一张表中 https://www.shuijingwanwq.com/2024/10/12/8824/ https://www.shuijingwanwq.com/2024/10/12/8824/#respond Sat, 12 Oct 2024 08:28:13 +0000 https://www.shuijingwanwq.com/?p=8824 浏览量: 80 1、现在场景如下,在一张表中,有一个付款方式的字段,现在有需要,将付款方式定时汇总至另一张表中。为了防止每次定时执行时,重复查询之前的记录,所以需要记录一个上次的执行时间。 2、决定先查询出所有的不重复的付款方式,如果存在上次的执行时间,则加入条件,记录的创建时间小于等于上次的执行时间。
<pre class="wp-block-syntaxhighlighter-code">

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $startTime = time();
        $this->line('同步付款方式到选项');

        $now = Carbon::now();
        // 上一次的执行时间
        // $lastTime = Carbon::now();
        $builder = Model::query()
            ->select('payment_type')
            ->where('table.created_at_gmt', '<', $now)
            ->distinct();
        if (!empty($lastTime)) {
            $builder->where('table.created_at_gmt', '>=', $now);
        }
        $paymentTypes = $builder->pluck('payment_type')->filter()->unique()->toArray();
        print_r($paymentTypes);exit;

        $endTime = time();
        Log::info("同步付款方式到选项; time:" . Carbon::now('GMT+8')->toDateTimeString() . ' 耗时:' . $endTime - $startTime . '秒');
        $this->line('同步完成' . ' 耗时:' . $endTime - $startTime . '秒');
        return Command::SUCCESS;
    }

</pre>
3、查看生成的 SQL
<pre class="wp-block-syntaxhighlighter-code">

select
  distinct `payment_type`
from
  `table`
where
  `table`.`created_at_gmt` < '2024-09-12 02:56:29'

</pre>
4、打印出 $paymentTypes,结果符合预期。


Array
(
    [1] => PP
    [2] => credit
    [3] => payssion
    [4] => 网上支付
    [5] => 1
    [6] => 21
    [7] => paypal_cluster_rest
    [8] => asiabill
    [9] => nspayment_local
    [10] => worldpay
    [11] => paypal_rest
    [12] => srpay
    [13] => nspayment
    [14] => 1000
    [15] => worldpay_googlepay
)


5、先尝试设置当前时间为 6 个月前,最终代码实现如下
<pre class="wp-block-syntaxhighlighter-code">


    private string $cachedLastTimeKey = 'table:sync-payment-type-to-option:last_time';

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $startTime = time();
        $this->line('同步付款方式到选项');

        $now = Carbon::now()->subMonths(3);
        $optionRepository = app(IOptionRepository::class);
        $title = 'payment_types';
        // 选项中的付款方式
        $optionPaymentType = $optionRepository->fetchBy(['title' => $title]);
        // 上一次的执行时间
        $lastTime = Cache::store('redis')->get($this->cachedLastTimeKey);
        $builder = Model::query()
            ->select('payment_type')
            ->where('table.created_at_gmt', '<', $now)
            ->distinct();
        if (isset($lastTime)) {
            $builder->where('table.created_at_gmt', '>=', $lastTime);
        }
        $paymentTypes = $builder->pluck('payment_type')->filter()->unique()->values()->all();

        if (!empty($optionPaymentType)) {
            // 获取两个数组的并集
            $optionPaymentTypes = array_values(array_unique(array_merge($optionPaymentType->value, $paymentTypes)));
        } else {
            $optionPaymentTypes = $paymentTypes;
        }

        $optionRepository->updateOrCreateOptionBy(['title' => $title], ['value' => $optionPaymentTypes]);
        Cache::store('redis')->put($this->cachedLastTimeKey, $now->toDateTimeString());

        $endTime = time();
        Log::info("同步付款方式到选项; time:" . Carbon::now('GMT+8')->toDateTimeString() . ' 耗时:' . $endTime - $startTime . '秒');
        $this->line('同步完成' . ' 耗时:' . $endTime - $startTime . '秒');
        return Command::SUCCESS;
    }

</pre>
第1次执行同步命令:
<pre class="wp-block-syntaxhighlighter-code">



select
  *
from
  `options`
where
  `title` = 'payment_types'
limit
  1;

select
  distinct `payment_type`
from
  `table`
where
  `table`.`created_at_gmt` < '2024-03-12 05:59:02';

select
  *
from
  `options`
where
  `title` = 'payment_types'
limit
  1;

insert into
  `options` (`title`, `value`)
values
  (
    'payment_type',
    '[\"PP\",\"credit\",\"payssion\",\"\u7f51\u4e0a\u652f\u4ed8\",\"1\",\"21\",\"paypal_cluster_rest\",\"asiabill\",\"nspayment_local\",\"worldpay\",\"paypal_rest\",\"srpay\",\"nspayment\",\"1000\",\"worldpay_googlepay\"]'
  );

</pre>
6、再尝试设置当前时间为 3 个月前,第2次执行同步命令,执行的 SQL 如下。符合预期。


$now = Carbon::now()->subMonths(3);


<pre class="wp-block-syntaxhighlighter-code">

select
  *
from
  `options`
where
  `title` = 'payment_types'
limit
  1;

select
  distinct `payment_type`
from
  `table`
where
  `table`.`created_at_gmt` < '2024-06-12 06:00:50'
  and `table`.`created_at_gmt` >= '2024-03-12 05:59:02';

select
  *
from
  `options`
where
  `title` = 'payment_types'
limit
  1;

update
  `options`
set
  `value` = '[\"PP\",\"credit\",\"payssion\",\"\u7f51\u4e0a\u652f\u4ed8\",\"1\",\"21\",\"paypal_cluster_rest\",\"asiabill\",\"nspayment_local\",\"worldpay\",\"paypal_rest\",\"srpay\",\"nspayment\",\"1000\",\"worldpay_googlepay\"]'
where
  `id` = 75;

</pre>
6、再尝试设置当前时间为现在,第3次执行同步命令,执行的 SQL 如下。符合预期。如图1
再尝试设置当前时间为现在,第3次执行同步命令,执行的 SQL 如下。符合预期

图1



$now = Carbon::now();


<pre class="wp-block-syntaxhighlighter-code">

select
  *
from
  `options`
where
  `title` = 'payment_types'
limit
  1;

select
  distinct `payment_type`
from
  `table`
where
  `table`.`created_at_gmt` < '2024-09-12 06:04:35'
  and `table`.`created_at_gmt` >= '2024-06-12 06:00:50';

select
  *
from
  `options`
where
  `title` = 'payment_types'
limit
  1;

update
  `options`
set
  `value` = '[\"PP\",\"credit\",\"payssion\",\"\u7f51\u4e0a\u652f\u4ed8\",\"1\",\"21\",\"paypal_cluster_rest\",\"asiabill\",\"nspayment_local\",\"worldpay\",\"paypal_rest\",\"srpay\",\"nspayment\",\"1000\",\"worldpay_googlepay\",\"paypal\",\"stripe\"]'
where
  `id` = 75;

</pre>
]]>
https://www.shuijingwanwq.com/2024/10/12/8824/feed/ 0
在 Laravel 9 中,嵌套预加载时,同时为预加载添加约束 https://www.shuijingwanwq.com/2024/09/20/8815/ https://www.shuijingwanwq.com/2024/09/20/8815/#respond Fri, 20 Sep 2024 07:47:32 +0000 https://www.shuijingwanwq.com/?p=8815 浏览量: 49 1、现有的实现如下,是一个嵌套预加载,SQL 如下


return $builder->distinct()->with('items.orderItem')->get();


2、现在需要为 items 添加约束,最终实现如下
<pre class="wp-block-syntaxhighlighter-code">

        // return $builder->distinct()->with('items.orderItem')->get();
        return $builder->distinct()->with(['items' => function ($query) {
            $query->whereRaw('(signed_quantity + signed_damaged_quantity) < return_quantity');
        }, 'items.orderItem'])
            ->get();

</pre>
3、生成的 SQL 如下,符合预期。如图1
生成的 SQL 如下,符合预期

图1

<pre class="wp-block-syntaxhighlighter-code">

select
  *
from
  `items`
where
  `items`.`return_order_id` in (230)
  and (signed_quantity + signed_damaged_quantity) < return_quantity
  and `items`.`deleted_at_gmt` is null

</pre>
]]>
https://www.shuijingwanwq.com/2024/09/20/8815/feed/ 0
在 Spatie\QueryBuilder ,报错:Call to undefined method Spatie\\QueryBuilder\\AllowedFilter::endsWithStrict() https://www.shuijingwanwq.com/2024/09/14/8810/ https://www.shuijingwanwq.com/2024/09/14/8810/#respond Sat, 14 Sep 2024 02:15:55 +0000 5.7.0]]> https://www.shuijingwanwq.com/?p=8810 浏览量: 106 1、在 Spatie\QueryBuilder ,报错:Call to undefined method Spatie\\QueryBuilder\\AllowedFilter::endsWithStrict()。如图1
在 Spatie\QueryBuilder ,报错:Call to undefined method Spatie\\QueryBuilder\\AllowedFilter::endsWithStrict()

图1



{
    "status_code": 500,
    "code": 0,
    "message": "Call to undefined method Spatie\\QueryBuilder\\AllowedFilter::endsWithStrict()"
}


2、在 vendor/spatie/laravel-query-builder/src/AllowedFilter.php 中搜索:endsWithStrict,确认 endsWithStrict 方法不存在。 3、查看 composer.json ,”spatie/laravel-query-builder”: “^5.2”, 。使用 Composer 自动更新到该包的最新兼容版本,5.2.0 => 5.7.0。如图2
查看 composer.json ,"spatie/laravel-query-builder": "^5.2", 。使用 Composer 自动更新到该包的最新兼容版本,5.2.0 => 5.7.0

图2



PS E:\wwwroot\object> composer update spatie/laravel-query-builder
Loading composer repositories with package information
Updating dependencies
Lock file operations: 0 installs, 1 update, 0 removals
  - Upgrading spatie/laravel-query-builder (5.2.0 => 5.7.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 1 update, 1 removal
  - Downloading spatie/laravel-query-builder (5.7.0)
  - Removing laravel/telescope (v5.2.2)
  - Upgrading spatie/laravel-query-builder (5.2.0 => 5.7.0): Extracting archive
Generating optimized autoload files


4、在 Spatie\QueryBuilder ,不再报错。查看生成的 SQL,符合预期。如图3
在 Spatie\QueryBuilder ,不再报错。查看生成的 SQL,符合预期

图3

<pre class="wp-block-syntaxhighlighter-code">

select
  `table`.*
from
  `table`
where
  `table`.`shipping_type` = 2
  and `shipping_at_gmt` >= '2024-06-14 02:00:51'
  and `shipping_at_gmt` <= '2024-09-14 02:00:51'
  and (
    `table`.`plat_order_no` LIKE '%GM'
    or `table`.`plat_order_no` LIKE '%20'
  )
order by
  `operated_at_gmt` desc
limit
  100 offset 0

</pre>
]]>
https://www.shuijingwanwq.com/2024/09/14/8810/feed/ 0
在 Spatie\QueryBuilder ,请求参数中包含 “,” 报错:SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens https://www.shuijingwanwq.com/2024/09/03/8800/ https://www.shuijingwanwq.com/2024/09/03/8800/#respond Tue, 03 Sep 2024 01:54:58 +0000 toSql()]]> getBindings()]]> https://www.shuijingwanwq.com/?p=8800 浏览量: 79 1、在 Spatie\QueryBuilder ,请求参数中包含 “,” 报错:SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens (SQL: select count(*) as aggregate from `table` where `table`.`shipping_type` = 2 and `shipping_at_gmt` >= 2024-05-21 08:54:14 and `shipping_at_gmt` <= 2024-08-21 08:54:14 and `table`.`shipping_error_message` in (aaa))。如图1
在 Spatie\QueryBuilder ,请求参数中包含 "," 报错:SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens (SQL: select count(*) as aggregate from `table` where `table`.`shipping_type` = 2 and `shipping_at_gmt` >= 2024-05-21 08:54:14 and `shipping_at_gmt` <= 2024-08-21 08:54:14 and `table`.`shipping_error_message` in (aaa))。

图1

2、当请求参数 从 “aaa,bbbb” 修改为 “aaa” 后,SQL 不再报错。如图2
当请求参数 从 "aaa,bbbb" 修改为 "aaa" 后,SQL 不再报错

图2

3、查看生成的 SQL
<pre class="wp-block-syntaxhighlighter-code">

select
  count(*) as aggregate
from
  `table`
where
  `table`.`shipping_type` = 2
  and `shipping_at_gmt` >= '2024-05-21 09:00:21'
  and `shipping_at_gmt` <= '2024-08-21 09:00:21'
  and `table`.`shipping_error_message` in ('aaa')

</pre>
4、打印出处理后的查询和绑定的参数,检查是否所有参数都匹配 SQL 中的占位符。确认占位符 ? 有 3 个,绑定的参数有 4 个。根源在于 “aaa,bbbb” 因为是逗号间隔的缘故,被拆分为了 2 个绑定参数。如图3
打印出处理后的查询和绑定的参数,检查是否所有参数都匹配 SQL 中的占位符。确认占位符 ? 有 3 个,绑定的参数有 4 个。根源在于 "aaa,bbbb" 因为是逗号间隔的缘故,被拆分为了 2 个绑定参数。

图3



        print_r($builder->toSql());
        print_r($builder->getBindings());
        exit;


<pre class="wp-block-syntaxhighlighter-code">

select `table`.* from `table` where `shipping_at_gmt` >= ? and `shipping_at_gmt` <= ? and `table`.`shipping_error_message` in (?) order by `operated_at_gmt` descArray
(
    [0] => Illuminate\Support\Carbon Object
        (
            [endOfTime:protected] => 
            [startOfTime:protected] => 
            [constructedObjectId:protected] => 000000000000115d0000000000000000
            [localMonthsOverflow:protected] => 
            [localYearsOverflow:protected] => 
            [localStrictModeEnabled:protected] => 
            [localHumanDiffOptions:protected] => 
            [localToStringFormat:protected] => 
            [localSerializer:protected] => 
            [localMacros:protected] => 
            [localGenericMacros:protected] => 
            [localFormatFunction:protected] => 
            [localTranslator:protected] => 
            [dumpProperties:protected] => Array
                (
                    [0] => date
                    [1] => timezone_type
                    [2] => timezone
                )

            [dumpLocale:protected] => 
            [dumpDateProperties:protected] => 
            [date] => 2024-05-29 03:03:53.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [1] => Illuminate\Support\Carbon Object
        (
            [endOfTime:protected] => 
            [startOfTime:protected] => 
            [constructedObjectId:protected] => 000000000000115e0000000000000000
            [localMonthsOverflow:protected] => 
            [localYearsOverflow:protected] => 
            [localStrictModeEnabled:protected] => 
            [localHumanDiffOptions:protected] => 
            [localToStringFormat:protected] => 
            [localSerializer:protected] => 
            [localMacros:protected] => 
            [localGenericMacros:protected] => 
            [localFormatFunction:protected] => 
            [localTranslator:protected] => 
            [dumpProperties:protected] => Array
                (
                    [0] => date
                    [1] => timezone_type
                    [2] => timezone
                )

            [dumpLocale:protected] => 
            [dumpDateProperties:protected] => 
            [date] => 2024-08-29 03:03:53.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [2] => aaa
    [3] => bbbb
)


</pre>
5、决定在 Spatie\QueryBuilder 中禁用逗号(,)自动转换为数组的行为。参考:Is it possible to disable a delimiter for an allowed filter?  打印出处理后的查询和绑定的参数,检查是否所有参数都匹配 SQL 中的占位符。确认占位符 ? 有 3 个,绑定的参数有 3 个。符合预期。如图4
决定在 Spatie\QueryBuilder 中禁用逗号(,)自动转换为数组的行为。参考:Is it possible to disable a delimiter for an allowed filter? https://github.com/spatie/laravel-query-builder/discussions/756 打印出处理后的查询和绑定的参数,检查是否所有参数都匹配 SQL 中的占位符。确认占位符 ? 有 3 个,绑定的参数有 3 个。符合预期。

图4



$allowedFilters[] = AllowedFilter::exact($field, null, true, false);


<pre class="wp-block-syntaxhighlighter-code">

select `table`.* from `table` where `shipping_at_gmt` >= ? and `shipping_at_gmt` <= ? and `table`.`shipping_error_message` in (?) order by `operated_at_gmt` descArray
(
    [0] => Illuminate\Support\Carbon Object
        (
            [endOfTime:protected] => 
            [startOfTime:protected] => 
            [constructedObjectId:protected] => 000000000000115d0000000000000000
            [localMonthsOverflow:protected] => 
            [localYearsOverflow:protected] => 
            [localStrictModeEnabled:protected] => 
            [localHumanDiffOptions:protected] => 
            [localToStringFormat:protected] => 
            [localSerializer:protected] => 
            [localMacros:protected] => 
            [localGenericMacros:protected] => 
            [localFormatFunction:protected] => 
            [localTranslator:protected] => 
            [dumpProperties:protected] => Array
                (
                    [0] => date
                    [1] => timezone_type
                    [2] => timezone
                )

            [dumpLocale:protected] => 
            [dumpDateProperties:protected] => 
            [date] => 2024-05-29 05:54:56.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [1] => Illuminate\Support\Carbon Object
        (
            [endOfTime:protected] => 
            [startOfTime:protected] => 
            [constructedObjectId:protected] => 000000000000115e0000000000000000
            [localMonthsOverflow:protected] => 
            [localYearsOverflow:protected] => 
            [localStrictModeEnabled:protected] => 
            [localHumanDiffOptions:protected] => 
            [localToStringFormat:protected] => 
            [localSerializer:protected] => 
            [localMacros:protected] => 
            [localGenericMacros:protected] => 
            [localFormatFunction:protected] => 
            [localTranslator:protected] => 
            [dumpProperties:protected] => Array
                (
                    [0] => date
                    [1] => timezone_type
                    [2] => timezone
                )

            [dumpLocale:protected] => 
            [dumpDateProperties:protected] => 
            [date] => 2024-08-29 05:54:56.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [2] => aaa,bbbb
)


</pre>
6、当请求参数 是 “aaa,bbbb”,SQL 不再报错,符合预期。查看生成的 SQL。如图5
当请求参数 是 "aaa,bbbb",SQL 不再报错,符合预期。查看生成的 SQL

图5

<pre class="wp-block-syntaxhighlighter-code">

select
  count(*) as aggregate
from
  `table`
where
  `shipping_at_gmt` >= '2024-05-29 05:57:18'
  and `shipping_at_gmt` <= '2024-08-29 05:57:18'
  and `table`.`shipping_error_message` in ('aaa,bbbb')

</pre>
]]>
https://www.shuijingwanwq.com/2024/09/03/8800/feed/ 0
在 Laravel 9 中,基于 chunkById 分块查询时报错:SQLSTATE[23000]: Integrity constraint violation: 1052 Column ‘id’ in where clause is ambiguous https://www.shuijingwanwq.com/2024/07/27/8730/ https://www.shuijingwanwq.com/2024/07/27/8730/#respond Sat, 27 Jul 2024 01:39:37 +0000 https://www.shuijingwanwq.com/?p=8730 浏览量: 199 1、在 Laravel 9 中,基于 chunkById 分块查询时报错:SQLSTATE[23000]: Integrity constraint violation: 1052 Column ‘id’ in where clause is ambiguous 。如图1
在 Laravel 9 中,基于 chunkById 分块查询时报错:SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous

图1



PS E:\wwwroot\object> php artisan upgrade:v0.26.0:fix-split-order-amount
所有已拆分订单金额的历史数据修复开始
第1批处理完毕

   Illuminate\Database\QueryException

  SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous (SQL: select distinct `orders`.`id`, `orders`.`amount_receivable`, `orders`.`amount_postage`, `orders`.`amount_insurance`, `orders`.`refund_amount`, `orders`.`discount_amount`, `orders`.`other_income_cny`, `orders`.`other_expense_cny`, `orders`.`rate` from `orders` inner join `orders` as `split_orders` on `orders`.`id` = `split_orders`.`parent_split_id` where `split_orders`.`create_type` = 50 and `split_orders`.`parent_split_id` is not null and `orders`.`id` in (3174, 3237, 3268) and `id` > 3174 order by `id` asc limit 1)

  at E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Database\Connection.php:759
    755▕         // If an exception occurs when attempting to run a query, we'll format the error
    756▕         // message to include the bindings with SQL, which will make this exception a
    757▕         // lot more helpful to the developer instead of just the database's errors.
    758▕         catch (Exception $e) {
  ➜ 759▕             throw new QueryException(
    760▕                 $query, $this->prepareBindings($bindings), $e
    761▕             );
    762▕         }
    763▕     }

  1   E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Database\Connection.php:418
      PDOException::("SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous")

  2   E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Database\Connection.php:418
      PDOStatement::execute()
PS E:\wwwroot\object>


2、代码实现如下



    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle(): void
    {
        $this->comment('所有已拆分订单金额的历史数据修复开始');
        $i = 1;

        Order::query()
            ->select('orders.id', 'orders.amount_receivable', 'orders.amount_postage', 'orders.amount_insurance', 'orders.refund_amount', 'orders.discount_amount', 'orders.other_income_cny', 'orders.other_expense_cny', 'orders.rate')
            ->join('orders as split_orders', 'orders.id', '=', 'split_orders.parent_split_id')
            ->where('split_orders.create_type', '=', Order::SPLIT_CREATION)
            ->whereNotNull('split_orders.parent_split_id')
            ->distinct()
            ->with('orderItems:id,order_id,quantity,price_sale,is_discard')
            ->whereIn('orders.id', [3174,3237,3268])
            ->chunkById(
                1,
                function ($orders) use (&$i) {

//                    $parentSplitIds = $orders->pluck('id')->all();
//                    $splitOrders = Order::query()
//                        ->select('id', 'amount_receivable', 'amount_postage', 'amount_insurance', 'refund_amount', 'discount_amount', 'other_income_cny', 'other_expense_cny', 'rate', 'parent_split_id')
//                        ->where('create_type', '=', Order::SPLIT_CREATION)
//                        ->whereIn('parent_split_id', $parentSplitIds)
//                        ->with('orderItems:id,order_id,quantity,price_sale,is_discard')
//                        ->get();
//
//                    // 根据 parent_split_id 对集合项进行分组
//                    $groupedSplitOrders = $splitOrders->groupBy('parent_split_id')->all();

                    foreach ($orders as $order) {
                        // $this->calculateSplitOrderAmount($order, $groupedSplitOrders[$order->id]);
                    }

                    $this->line(sprintf('第%s批处理完毕', $i));
                    $i++;
                }
            );
        $this->comment('所有已拆分订单金额的历史数据修复结束');
    }



3、添加了 chunkById 的 column 参数后,报错:The chunkById operation was aborted because the [orders.id] column is not present in the query result.如图2
添加了 chunkById 的 column 参数后,报错:The chunkById operation was aborted because the [orders.id] column is not present in the query result.

图2



PS E:\wwwroot\object> php artisan upgrade:v0.26.0:fix-split-order-amount
所有已拆分订单金额的历史数据修复开始
第1批处理完毕

   RuntimeException

  The chunkById operation was aborted because the [orders.id] column is not present in the query result.

  at E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Database\Concerns\BuildsQueries.php:148
    144▕
    145▕             $lastId = data_get($results->last(), $alias);
    146▕
    147▕             if ($lastId === null) {
  ➜ 148▕                 throw new RuntimeException("The chunkById operation was aborted because the [{$alias}] column is not present in the query result.");
    149▕             }
    150▕
    151▕             unset($results);
    152▕

  1   E:\wwwroot\object\app\Console\Commands\Upgrades\v0_26_0\FixSplitOrderAmount.php:66
      Illuminate\Database\Eloquent\Builder::chunkById(Object(Closure), "orders.id")

  2   E:\wwwroot\object\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php:36
      App\Console\Commands\Upgrades\v0_26_0\FixSplitOrderAmount::handle()
PS E:\wwwroot\object>





        Order::query()
            ->select('orders.id', 'orders.amount_receivable', 'orders.amount_postage', 'orders.amount_insurance', 'orders.refund_amount', 'orders.discount_amount', 'orders.other_income_cny', 'orders.other_expense_cny', 'orders.rate')
            ->join('orders as split_orders', 'orders.id', '=', 'split_orders.parent_split_id')
            ->where('split_orders.create_type', '=', Order::SPLIT_CREATION)
            ->whereNotNull('split_orders.parent_split_id')
            ->distinct()
            ->with('orderItems:id,order_id,quantity,price_sale,is_discard')
            ->whereIn('orders.id', [3174,3237,3268])
            ->chunkById(
                1,
                function ($orders) use (&$i) {

//                    $parentSplitIds = $orders->pluck('id')->all();
//                    $splitOrders = Order::query()
//                        ->select('id', 'amount_receivable', 'amount_postage', 'amount_insurance', 'refund_amount', 'discount_amount', 'other_income_cny', 'other_expense_cny', 'rate', 'parent_split_id')
//                        ->where('create_type', '=', Order::SPLIT_CREATION)
//                        ->whereIn('parent_split_id', $parentSplitIds)
//                        ->with('orderItems:id,order_id,quantity,price_sale,is_discard')
//                        ->get();
//
//                    // 根据 parent_split_id 对集合项进行分组
//                    $groupedSplitOrders = $splitOrders->groupBy('parent_split_id')->all();

                    foreach ($orders as $order) {
                        // $this->calculateSplitOrderAmount($order, $groupedSplitOrders[$order->id]);
                    }

                    $this->line(sprintf('第%s批处理完毕', $i));
                    $i++;
                },
                'orders.id'
            );


4、再次添加了 chunkById 的 alias 参数后,不再报错。且生成的 SQL 符合预期。



        Order::query()
            ->select('orders.id', 'orders.amount_receivable', 'orders.amount_postage', 'orders.amount_insurance', 'orders.refund_amount', 'orders.discount_amount', 'orders.other_income_cny', 'orders.other_expense_cny', 'orders.rate')
            ->join('orders as split_orders', 'orders.id', '=', 'split_orders.parent_split_id')
            ->where('split_orders.create_type', '=', Order::SPLIT_CREATION)
            ->whereNotNull('split_orders.parent_split_id')
            ->distinct()
            ->with('orderItems:id,order_id,quantity,price_sale,is_discard')
            ->whereIn('orders.id', [3174,3237,3268])
            ->chunkById(
                1,
                function ($orders) use (&$i) {

//                    $parentSplitIds = $orders->pluck('id')->all();
//                    $splitOrders = Order::query()
//                        ->select('id', 'amount_receivable', 'amount_postage', 'amount_insurance', 'refund_amount', 'discount_amount', 'other_income_cny', 'other_expense_cny', 'rate', 'parent_split_id')
//                        ->where('create_type', '=', Order::SPLIT_CREATION)
//                        ->whereIn('parent_split_id', $parentSplitIds)
//                        ->with('orderItems:id,order_id,quantity,price_sale,is_discard')
//                        ->get();
//
//                    // 根据 parent_split_id 对集合项进行分组
//                    $groupedSplitOrders = $splitOrders->groupBy('parent_split_id')->all();

                    foreach ($orders as $order) {
                        // $this->calculateSplitOrderAmount($order, $groupedSplitOrders[$order->id]);
                    }

                    $this->line(sprintf('第%s批处理完毕', $i));
                    $i++;
                },
                'orders.id',
                'id'
            );
        $this->comment('所有已拆分订单金额的历史数据修复结束');
    }





PS E:\wwwroot\object> php artisan upgrade:v0.26.0:fix-split-order-amount
所有已拆分订单金额的历史数据修复开始
第1批处理完毕
第2批处理完毕
第3批处理完毕
所有已拆分订单金额的历史数据修复结束




select
  distinct `orders`.`id`,
  `orders`.`amount_receivable`,
  `orders`.`amount_postage`,
  `orders`.`amount_insurance`,
  `orders`.`refund_amount`,
  `orders`.`discount_amount`,
  `orders`.`other_income_cny`,
  `orders`.`other_expense_cny`,
  `orders`.`rate`
from
  `orders`
  inner join `orders` as `split_orders` on `orders`.`id` = `split_orders`.`parent_split_id`
where
  `split_orders`.`create_type` = 50
  and `split_orders`.`parent_split_id` is not null
  and `orders`.`id` in (3174, 3237, 3268)
order by
  `orders`.`id` asc
limit
  1


]]>
https://www.shuijingwanwq.com/2024/07/27/8730/feed/ 0
在 Laravel 9 中,使用 API 资源集合,禁用单个接口的最外层资源的包裹 data 键 https://www.shuijingwanwq.com/2024/07/13/8714/ https://www.shuijingwanwq.com/2024/07/13/8714/#respond Sat, 13 Jul 2024 02:13:41 +0000 https://www.shuijingwanwq.com/?p=8714 浏览量: 72 1、最初的响应结构如下:如图1  
接口的最外层资源的包裹 data 键

图1



{
    "data": [
        {
            "id": 1,
            "return_order_id": 61,
            "return_order_item_id": 86
        },
        {
            "id": 2,
            "return_order_id": 61,
            "return_order_item_id": 86
        }
    ]
}


2、由于不需要分页,前端希望去掉最外层资源的包裹 data 键,设置「数据」包装器 为 null。修改资源集合类如下


class ReturnOrderItemReceiptRecordResourceCollection extends ResourceCollection
{
    /**
     * 应该应用的「数据」包装器。
     *
     * @var string
     */
    public static $wrap = null;

    /**
     * Transform the resource into an array.
     *
     * @param Request $request
     * @return array
     */
    public function toArray($request): array
    {
        return parent::toArray($request);
    }
}


3、结果符合预期,接口响应一个数组。如图2
结果符合预期,接口响应一个数组

图2



[
    {
        "id": 1,
        "return_order_id": 61,
        "return_order_item_id": 86
    },
    {
        "id": 2,
        "return_order_id": 61,
        "return_order_item_id": 86
    }
]


]]>
https://www.shuijingwanwq.com/2024/07/13/8714/feed/ 0
在 Laravel 9 中,在数据库迁移中,修改字段类型 unsignedTinyInteger,报错:Unknown column type “tinyinteger” requested. https://www.shuijingwanwq.com/2024/07/09/8704/ https://www.shuijingwanwq.com/2024/07/09/8704/#respond Tue, 09 Jul 2024 01:34:51 +0000 https://www.shuijingwanwq.com/?p=8704 浏览量: 153 1、在 Laravel 9 中,在数据库迁移中,修改字段类型 unsignedTinyInteger,报错:Unknown column type “tinyinteger” requested.。如图1
在 Laravel 9 中,在数据库迁移中,修改字段类型 unsignedTinyInteger,报错:Unknown column type "tinyinteger" requested.

图1



$table->unsignedTinyInteger('status')->default(1)->comment('状态,1:待处理;2:处理中;3:可换单;4:已完成')->change();




Unknown column type "tinyinteger" requested. Any Doctrine type that you use has to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database introspection then you might have forgotten to register all database types for a Doctrine Type. Use AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement Type#getMappedDatabaseTypes(). If the type name is empty you might have a problem with the cache or forgot some mapping information.


2、由于 Laravel 的 Schema 构建器更新字段属性,不支持字段类型:unsignedTinyInteger,因此,需要使用原生 SQL 语句


use Illuminate\Support\Facades\DB;

DB::statement("ALTER TABLE return_orders MODIFY status tinyint unsigned NOT NULL DEFAULT 1 COMMENT '状态,1:待处理;2:处理中;3:可换单;4:已完成'");


3、执行迁移,更新成功,如图2
执行迁移,更新成功

图2



`status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态,1:待处理;2:处理中;3:可换单;4:已完成',


]]>
https://www.shuijingwanwq.com/2024/07/09/8704/feed/ 0
在 Lavavel 9 中 验证 符合 MySQL 字段类型 decimal(8,2) 的字段 https://www.shuijingwanwq.com/2024/07/05/8699/ https://www.shuijingwanwq.com/2024/07/05/8699/#respond Fri, 05 Jul 2024 07:55:56 +0000 https://www.shuijingwanwq.com/?p=8699 浏览量: 60 1、在 Lavavel 9 中 验证 符合 MySQL 字段类型 decimal(8,2) 的字段。如图1
在 Lavavel 9 中 验证 符合 MySQL 字段类型 decimal(8,2) 的字段

图1



`return_package_length` decimal(8,2) NOT NULL DEFAULT '0.00' COMMENT '退货包裹长度(厘米)'


2、最终实现如下


        $validator = Validator::make(
            $params,
            [
                'return_package_length' => [
                    'numeric',
                    'min:0.00',
                    'max:999999.99',
                    'regex:/^\d{0,6}(\.\d{1,2})?$/'
                ],
            ],
            [

            ]
        );
        if ($validator->stopOnFirstFailure()->fails()) {
            throw new BusinessException(BusinessException::MODULE_ORDER, $validator->getMessageBag()->first());
        }



3、分别尝试:-4(失败)、0(成功)、5(成功)、999999.99(成功)、999999.990(成功)、999999.991(失败)、9999990.99(失败)、0.09(成功)、0.009(失败)、0.0000001(失败),符合预期。如图2
分别尝试:-4(失败)、0(成功)、5(成功)、999999.99(成功)、999999.990(成功)、999999.991(失败)、9999990.99(失败)、0.09(成功)、0.009(失败)、0.0000001(失败),符合预期

图2

]]>
https://www.shuijingwanwq.com/2024/07/05/8699/feed/ 0