CakePHP 分页将值变为空

CakePHP paginate turns values to null

我有这个函数可以获取所有 "feedings" 及其 "feedingsupps"。在函数中,我计算了一些字段的总和:

/* FeedingTable.php */
public function feedings_with_totals($herd_id)
{
    $feedings = $this->find('all',[
            'contain' => [  
                'Herds',
                'Herds.Users',
                'Feedingsupps'
            ]
    ])
    ->where(['Feedings.herd_id'=>$herd_id])
    ->order(['Feedings.id'=>'desc']);

    foreach($feedings as $f)
    {
        $collection = new Collection($f->feedingsupps);
        $f->sum_weight = $collection->sumOf('weight');
        $f->sum_dryweight = $collection->sumOf('dryweight_total');
        $f->sum_price = $collection->sumOf('price_total');
    }
    return $feedings;
}

当我调用这个函数并调试它时,它看起来像我想要的:

[
    (int) 0 => object(App\Model\Entity\Feeding) {

        'id' => (int) 19,
        ...
        'sum_weight' => (float) 109,
        'sum_dryweight' => (float) 92.71,
        'sum_price' => (float) 17.1775,

然后我分页:

$feedings = $this->paginate($feedings);

之后 "sum" 字段变为空:

[
    (int) 0 => object(App\Model\Entity\Feeding) {

        'id' => (int) 19,
        ...
        'sum_weight' => null,
        'sum_dryweight' => null,
        'sum_price' => null,

为什么分页会这样?

分页器不会修改您的字段,它所做的是再次 运行查询,因为应用分页器选项会使查询处于脏状态,并删除可能缓冲的结果集。因此分页器返回的结果集与您在方法中修改的结果集不同。

即使您在那里做的事情可行,您也不应该那样做,因为您将 a) 运行 查询两次,并且 b) 与您正在获取的分页器查询不同,并且处理 table 的 all 行(这可能是一个非常昂贵的操作,无论如何都是不必要的)。

您的计算应该在结果格式化程序中,在请求结果集时应用一次:

$feedings->formatResults(function (\Cake\Collection\CollectionInterface $results) {
    return $results->map(function ($row) {
        $collection = collection($row['feedingsupps']);

        $row['sum_weight'] = $collection->sumOf('weight');
        $row['sum_dryweight'] = $collection->sumOf('dryweight_total');
        $row['sum_price'] = $collection->sumOf('price_total');

        return $row;
    });
});

或者应该在 SQL 级别完成:

$feedings
    ->select([
        'sum_weight' => $feedings->func()->sum('Feedingsupps.weight'),
        'sum_dryweight' => $feedings->func()->sum('Feedingsupps.dryweight_total'),
        'sum_price' => $feedings->func()->sum('Feedingsupps.price_total'),
    ])
    ->enableAutoFields(true)
    ->leftJoinWith('Feedingsupps')
    ->group('Feedings.id');

在这两种情况下,只会对实际获取的记录进行计算,查询只会 运行 一次。

另见