CakePHP как получить промежуточную сумму в сущности с помощью виртуального поля?

У меня есть поле под названием депозит, я пытаюсь создать виртуальное поле под названием баланс. Ниже моего желаемого результата это похоже на цепную сумму.

deposit   balance   
100       100
300       400
10        410

Я пробовал код ниже в сущности

public $balance = 0;
protected function _getBalance()
{
    $this->balance = $this->balance + $this->deposit;
    return $this->balance;
}

У меня есть все 0 в балансе.

Я получаю результат, как показано ниже

deposit   balance   
    100       0
    300       0
    10        0

Как добиться желаемого результата?

1 ответ

Решение

Сущность не имеет представления о других сущностях, но это необходимо для того, чтобы она могла подвести итоги баланса.

Здесь мне приходят на ум два решения: а) перебор всех результатов и изменение данных или б) в случае, если ваша СУБД их поддерживает, использование оконных функций для создания промежуточной суммы на уровне SQL.

Если вы перебираете все результаты, вы можете получить доступ к балансу предыдущего результата, вычислить сумму и заполнить balance соответствующее поле, например, в средстве форматирования результатов:

$query->formatResults(function (\Cake\Collection\CollectionInterface $results) {
    $previous = null;
    return $results->map(function ($row) use (&$previous) {
        if ($previous === null) {
            $row['balance'] = $row['deposit'];
        } else {
            $row['balance'] = $previous['balance'] + $row['deposit'];
        }
        
        $previous = $row;
        
        return $row;
    });
});

На уровне SQL оконные функции позволяют суммировать предыдущие строки:

$query->select(function (\Cake\ORM\Query $query) {
    return [
        'deposit',
        'balance' => $query
            ->func()
            ->sum('deposit')
            ->over()
            ->order('id')
            ->rows(null)
    ];
});

Это создало бы SELECT предложение вроде этого:

SELECT
    deposit,
    (
        SUM(deposit) OVER (
            ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        )
    ) AS balance

где сумма вычисляется по всем предыдущим строкам до текущей строки включительно.

Следует отметить, что оконные функции в построителе поддерживаются только в CakePHP 4.1, в предыдущей версии вам нужно было создавать собственные выражения или передавать необработанный SQL:

$query->select([
    'deposit',
    'balance' => 'SUM(deposit) OVER (
        ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    )'
]);

Смотрите также

Другие вопросы по тегам