MD
TRepositorySum - 4.0
Publicado previamente em : https://www.adianti.com.br/forum/pt/view_3819?soma-de-valores-no-banco-de-dados-
Publicado aqui de acordo com a dica do Willian Padilha. Realizado ajuste no exemplo de acordo com info do Felipe Cortez!
--
Olá, bom dia,
Disponibilizo aqui uma forma de trabalhar com a soma de valores no banco de dados via objeto, usando componentes do framework, espero que seja útil.
A algum tempo tenho a necessidade de trabalhar com relatórios complexos, para tal uso views de acordo com o que já foi tratado pelo próprio Pablo no passado (disponível aqui https://www.adianti.com.br/forum/pt/view_879?relatorios-com-queries-complexas-de).
Acontece que mais recentemente tive a necessidade de trabalhar com soma de valores ou mesmo a soma de colunas dos objetos, realizando isso através do TRepository e somando dentro da classe (através do PHP), isso funciona bem, mas o desempenho deixa a deseja quando se trabalha com alguns milhões de entradas no banco de dados.
Pensando nisso, e pensando em resolver meu problema de forma "elegante", evitando montar SQLs na mão para poder ter o sum e também podendo aproveitar objetos com TCriteria por exemplo, criei uma classe chamada TRepositorySum (baseada em TRepository tentei estender ela pois precisava de um único método, mas ela é uma classe final e não pode ser estendida), onde é possível somar colunas ou mesmo valores de mais de uma coluna. Abaixo segue a classe e alguns exemplos de uso:
Exemplos de uso:
Espero ter me feito entender e espero que a classe seja útil. No futuro talvez esse método seja um método interessante para ser adicionado a classe TRepository.
Um abraço!
Publicado aqui de acordo com a dica do Willian Padilha. Realizado ajuste no exemplo de acordo com info do Felipe Cortez!
--
Olá, bom dia,
Disponibilizo aqui uma forma de trabalhar com a soma de valores no banco de dados via objeto, usando componentes do framework, espero que seja útil.
A algum tempo tenho a necessidade de trabalhar com relatórios complexos, para tal uso views de acordo com o que já foi tratado pelo próprio Pablo no passado (disponível aqui https://www.adianti.com.br/forum/pt/view_879?relatorios-com-queries-complexas-de).
Acontece que mais recentemente tive a necessidade de trabalhar com soma de valores ou mesmo a soma de colunas dos objetos, realizando isso através do TRepository e somando dentro da classe (através do PHP), isso funciona bem, mas o desempenho deixa a deseja quando se trabalha com alguns milhões de entradas no banco de dados.
Pensando nisso, e pensando em resolver meu problema de forma "elegante", evitando montar SQLs na mão para poder ter o sum e também podendo aproveitar objetos com TCriteria por exemplo, criei uma classe chamada TRepositorySum (baseada em TRepository tentei estender ela pois precisava de um único método, mas ela é uma classe final e não pode ser estendida), onde é possível somar colunas ou mesmo valores de mais de uma coluna. Abaixo segue a classe e alguns exemplos de uso:
- <?php
- /**
- * Implements the Repository Pattern to deal with sum of Active Records
- *
- * @version 4.0
- * @package database
- * @author Pablo Dall'Oglio
- * @author Marco Driemeyer <marco@plenatech.com.br>
- * @copyright Copyright (c) 2006 Adianti Solutions Ltd. (http://www.adianti.com.br)
- * @license http://www.adianti.com.br/framework-license
- */
- class TRepositorySum
- {
- private $class; // Active Record class to be manipulated
- private $criteria; // buffered criteria to use with fluent interfaces
- /**
- * Class Constructor
- * @param $class = Active Record class name
- */
- public function __construct($class)
- {
- if (class_exists($class))
- {
- if (is_subclass_of($class, 'TRecord'))
- {
- $this->class = $class;
- $this->criteria = new TCriteria;
- }
- else
- {
- throw new Exception(AdiantiCoreTranslator::translate('The class ^1 was not accepted as argument. The class informed as parameter must be subclass of ^2.', $class, 'TRecord'));
- }
- }
- else
- {
- throw new Exception(AdiantiCoreTranslator::translate('The class ^1 was not found. Check the class name or the file name. They must match', $class));
- }
- }
- /**
- * Returns the name of database entity
- * @return A String containing the name of the entity
- */
- protected function getEntity()
- {
- return constant($this->class.'::TABLENAME');
- }
- /**
- * Return the sum of columns of objects that satisfy a given criteria
- * @param $criteria An TCriteria object, specifiyng the filters
- * @param $columns An indexed array with the name and the column to sum
- * @return An stdClass with the named property storing the sum of values
- */
- public function sum(TCriteria $criteria = NULL, array $columns)
- {
- if (!$criteria)
- {
- $criteria = isset($this->criteria) ? $this->criteria : new TCriteria;
- }
- // creates a SELECT statement
- $sql = new TSqlSelect;
- // Interact with the array and add the columns to sum
- foreach ($columns as $key => $column)
- {
- $sql->addColumn("sum ($column) as $key");
- }
- $sql->setEntity($this->getEntity());
- // assign the criteria to the SELECT statement
- $sql->setCriteria($criteria);
- // get the connection of the active transaction
- if ($conn = TTransaction::get())
- {
- // register the operation in the LOG file
- TTransaction::log($sql->getInstruction());
- $dbinfo = TTransaction::getDatabaseInfo(); // get dbinfo
- if (isset($dbinfo['prep']) AND $dbinfo['prep'] == '1') // prepared ON
- {
- $result = $conn-> prepare ( $sql->getInstruction( TRUE ) , array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
- $result-> execute ( $criteria->getPreparedVars() );
- }
- else
- {
- // executes the SELECT statement
- $result= $conn-> query($sql->getInstruction());
- }
- if ($result)
- {
- $row = $result->fetch();
- // Initiate the stdClass and interact with the return of the sum
- $stdClass = new stdClass();
- foreach ($columns as $key => $column)
- {
- if ($row["$key"])
- $stdClass->$key = $row["$key"];
- else
- $stdClass->$key = 0;
- }
- return $stdClass;
- }
- }
- else
- {
- // if there's no active transaction opened
- throw new Exception(AdiantiCoreTranslator::translate('No active transactions') . ': ' . __METHOD__ .' '. $this->getEntity());
- }
- }
- public function get(TCriteria $criteria = NULL, $callObjectLoad = TRUE)
- {
- return $this->load($criteria, $callObjectLoad);
- }
- }
- ?>
Exemplos de uso:
- <?php
- $repo = new TRepositorySum('ModelToSum');
- $criteria = new TCriteria();
- $criteria->add(new TFilter('column1', '>=', '1'));
- $criteria->add(new TFilter('column2', '=', '3'));
- // TRepositorySum retorna a soma de uma ou mais colunas dentro de um objeto stdClass, as colunas a serem somadas são informadas no momento da chamada do metodo
- // Para somar, basta chamar o metodo sum com um array indexado no qual informamos o nome da propriedade que deve receber a soma no objeto que será retornado e os campos a serem somados
- // Realizamos testes em PostgreSQL e MySQL, mas possivelmente funcione em outros bancos, se não funcionar a lógica para implementar a soma nesses banco esta exposta e pode ser ajustada
- $simpleSum = $repo->sum($criteria, array('return_name' => 'column_to_sum'));
- echo "$simpleSum->return_name"; // retorna a soma da coluna column_to_sum realizada no banco de dados
- $otherSum = $repo->sum($criteria, array('return_name' => 'column_to_sum', 'return_sum' => 'column_to_sum_1 + solumn_to_sum_2'));
- echo "$otherSum->return_name"; // retorna a soma da coluna column_to_sum realizada no banco de dados
- echo "$otherSum->return_sum"; // retorna a soma das colunas column_to_sum_1 + column_to_sum_2 realizada no banco de dados
- ?>
Espero ter me feito entender e espero que a classe seja útil. No futuro talvez esse método seja um método interessante para ser adicionado a classe TRepository.
Um abraço!
Parabéns amigo pela contribuição. Segui conforme suas instruções, e está somando as colunas conforme desejado.
Agora estou quebrando a cabeça para gravar os valores apurados. Segue código.
static function calcularEstoque($array_items)
{
try
{
TTransaction::open(self::$database); // open a transaction
foreach ($array_items as $item)
{
$dt_estoque = $item->data_estoque;
$dt_estoque = TDate::date2us($dt_estoque);
$produto_id = $item->produto_id;
$id = $item->id;
$repo = new TRepositorySum('Estoque');
$criteria = new TCriteria();
$criteria->add(new TFilter('produto_id', '=', $produto_id));
$criteria->add(new TFilter('data_estoque', '<=', $dt_estoque));
$somaEntrada = $repo->sum($criteria, array('soma_qtde' => 'entrada_qtde', 'soma_vr_total' => 'entrada_vr_total'));
$somaSaida = $repo->sum($criteria, array('soma_qtde' => 'saida_qtde', 'soma_vr_total' => 'saida_vr_total'));
$quantidade = 0;
$vrTotal = 0;
$precoMedio = 0;
if ($somaEntrada->soma_qtde > 0 and $somaSaida->soma_qtde > 0) {
$quantidade = $somaEntrada->soma_qtde - $somaSaida->soma_qtde;
}else{
if ($somaEntrada->soma_qtde > 0){
$quantidade = (double) $somaEntrada->soma_qtde;
}
}
if ($somaEntrada->soma_vr_total > 0 and $somaSaida->soma_vr_total > 0) {
$quantidade = $somaEntrada->soma_vr_total - $somaSaida->soma_vr_total;
}else{
if ($somaEntrada->soma_vr_total > 0){
$vrTotal = (double) $somaEntrada->soma_vr_total;
}
}
$quantidade = number_format($quantidade, 4 , ',', '.');
$vrTotal = number_format($vrTotal, 2 , ',', '.');
$precoMedio = ((double) $vrTotal / (double) $quantidade);
//$precoMedio = number_format($precoMedio, 4 , ',', '.');
$precoMedio = (double) $precoMedio;
$object = new Estoque($id);
$object->estoque_qtde = $quantidade;
$object->estoque_vr_total = $vrTotal;
$object->estoque_vr_unitario = $precoMedio;
$object->store();
}
TTransaction::close(); // close the transaction
//code here
}
catch (Exception $e)
{
new TMessage('error', $e->getMessage());
}
}