How to get last month row sum in cakephp 3 - cakephp

I need to get last month transactions amount sum , I wrote query but getting error Error: Call to undefined function App\Controller\YEAR()
// sum of total received amount
$conditions['transaction_type']=1;
$conditions['AND']['YEAR(Transactions.created) >='] = YEAR('CURRENT_DATE - INTERVAL 1 MONTH');
$conditions['AND']['MONTH(Transactions.created) <='] = MONTH('CURRENT_DATE - INTERVAL 1 MONTH');
$query = $this->Transactions->find('all',['conditions'=>$conditions]);
$collection = new Collection($query);
$sumOfReceivedAmount = $collection->sumOf('amount');
$this->set('sumOfReceivedAmount',$sumOfReceivedAmount);
I have also tried using query builder
$query = $this->Transactions->find()
->where(function ($exp, $q) {
$year = $q->func()->year([
'created' => 'identifier'
]);
$month = $q->func()->month([
'created' => 'identifier'
]);
return $exp
->eq($year, $q->func()->year(['CURRENT_DATE - INTERVAL 1 MONTH']))
->eq($month, $q->func()->month(['CURRENT_DATE - INTERVAL 1 MONTH']));
});
For this code I am getting query like
SELECT
*
FROM
transactions Transactions
WHERE
(
year(created) = (
year(
'CURRENT_DATE - INTERVAL 1 MONTH'
)
)
AND month(created) = (
month(
'CURRENT_DATE - INTERVAL 1 MONTH'
)
)
)
Problem is this single quote 'CURRENT_DATE - INTERVAL 1 MONTH'
How can I remove this single quote ?

Similar to your other year() usage where you decalare the passed value to be an identifier, you'd have to declare your other values as literal values (or pass expression objects) so that they are inserted into the query as is, instead of being bound.
['CURRENT_DATE - INTERVAL 1 MONTH' => 'literal']
CakePHP also ships with methods to express similar functionality, specifically the FunctionsBuilder::dateAdd() method. Also when expressing CURRENT_DATE as a function the query builder will be able to convert the SQL for other DBMS like SqlServer, which has no CURRENT_DATE:
$q->func()->year(
[$q->func()->dateAdd($q->func()->CURRENT_DATE(), -1, 'MONTH')]
)
NOW() should work too:
$q->func()->dateAdd($q->func()->now(), -1, 'MONTH')
See also
Cookbook > Database Access & ORM > Query Builder > Using SQL Functions

Related

Without changing sql mode=only_full_group_by mode how can I execute group by in cakephp?

I am trying get month wise sum of amount from transactions table. I have written below cakephp function to get my desire output.
public function getLastSixMOnthsExpenses($since_date, $t_type)
{
$query = $this->find()->select([
'month' => $this->find()->func()->monthname([
'created' => 'identifier'
]),
'amount' => $this->find()->func()->sum('Transactions.amount')
])
->where([
'Transactions.created >='=> $since_date->modify('6 months ago')->startOfMonth(),
'Transactions.created <='=> $since_date->endOfMonth(),
'Transactions.transaction_type '=>$t_type
])
->group(['month'])
->order(['Transactions.created'=>'ASC'])
;
return $query;
}
I am getting below error
Syntax error or access violation: 1055 Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'kjoumaa_kamaljoumaa.Transactions.created' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Without change sql mode , How can I run group by here ?
Order on an aggregate instead.
Since you group by the month, all created fields in those groups will be of one and the same month, eg all created fields in one group will point to either an earlier or a later date than the fields of another group, so you could simply pick either the min or the max value out of a group:
->orderAsc(function (
\Cake\Database\Expression\QueryExpression $exp,
\Cake\ORM\Query $query
) {
return $query->func()->min(
$query->identifier('Transactions.created')
);
})
ORDER BY MIN(Transactions.created) ASC
Also if you would select the month as a number instead of as a name, you could order on that field.

CakePHP how to get the running total in an entity using a virtual field?

I have field called deposit, I am trying to create a virtual field called balance. Below my desire output, It's like chaining sum.
deposit balance
100 100
300 400
10 410
I have tried below code in entity
public $balance = 0;
protected function _getBalance()
{
$this->balance = $this->balance + $this->deposit;
return $this->balance;
}
I have got all 0 in balance.
I am getting result like below
deposit balance
100 0
300 0
10 0
How can I get desire result ?
An entity has no idea about other entities, but that would be required in order for it to be able to sum up the balance.
The two solutions that come to my mind here are a) iterating over all the results and modifying the data, or b) in case your DBMS supports them, using window functions to create the running total on SQL level.
If you iterate over all results you can access the previous result's balance and calculate the sum and populate the balance field accordingly, for example in a result formatter:
$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;
});
});
On SQL level window functions would allow you sum up previous rows:
$query->select(function (\Cake\ORM\Query $query) {
return [
'deposit',
'balance' => $query
->func()
->sum('deposit')
->over()
->order('id')
->rows(null)
];
});
This would create a SELECT clause like this:
SELECT
deposit,
(
SUM(deposit) OVER (
ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
) AS balance
where the sum is calculated over all previous rows up to and including the current row.
It should be noted that window functions on the builder are only supported as of CakePHP 4.1, in previous version you'd have to create custom expressions or pass raw SQL:
$query->select([
'deposit',
'balance' => 'SUM(deposit) OVER (
ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)'
]);
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
Cookbook > Database Access & ORM > Query Builder > Window Functions

Magento using operators in where clauses

My products have date_created and expires_in attributes. date_created is Magento date backend format while expires_in is a select that contains options as 1 week, 1 month etc.
Using those two attributes I'm trying to determine the total number of expired products. My idea was to write a query that selects:
products where date created + 1 week < now() AND expires_in = 1 week
AND
products where date created + 1 month < now() AND expires_in = 1 month
...
But I don't seem to be able to make even the first part of the first step to work:
$currentTimestamp = Mage::getModel('core/date')->timestamp(time());
$oneWeekTimestamp = Mage::getModel('core/date')->timestamp(strtotime("+1 week"));
$products = Mage::getResourceModel('catalog/product_collection')
->setStoreId(Mage::app()->getStore()->getId())
->addAttributeToSelect('')
...
//these are what i have tried so far:
$products->addAttributeToFilter('date_updated + ' . $oneWeekTimestamp , array('gt' => $currentTimestamp));
$products->addAttributeToFilter(new Zend_Db_Expr('date_updated + ' . $oneWeekTimestamp), array('gt' => $currentTimestamp));
$products->addAttributeToFilter("DATEDIFF($currentTimestamp, 'date_updated')" , array('gt' => 7));
$countExpired = $products -> count();
None of them seem to work. If possible I'd like the cross RDBMS solution.
You can do this in following way:
$currentTimestamp = Mage::getModel('core/date')->timestamp(time());
$oneWeekTimestamp = Mage::getModel('core/date')->timestamp(strtotime("+1 week"));
$oneweekDate=date("Y-m-d H:i:s",$oneWeekTimestamp);
$products->addAttributeToFilter('date_updated',array('date'=>true,'gt',$oneweekDate);

CakePHP : Search date from form helper

I use form helpers to show drop down select date. My problem is, i can't compare date from ms sql with form data.
My View :
<?php echo $this->Form->input('collect_date', array('label' => 'Collect Date','type'=>'date','dateFormat'=> 'DMY','minYear' => date('Y'),'maxYear' => date('Y')+1));?>
My Controller
$status =array(0,2,4);
$find_date = $this->data['Transaction']['collect_date'];
$field = array('Transaction.status','Catalogue.title', 'Catalogue.author', 'Catalogue.isbn', 'Location.rack');
$condition = array('OR' => array('AND' =>array('Transaction.return_date <'=> $find_date,'Transaction.status '=> '3'),array('Transaction.status'=> $status)));
$data = $this->Catalogue->Transaction->find('count',array('fields' => $field,'conditions'=>$condition,'order'=>array('Transaction.id desc') ));
$this->set('Found', $data);
And the sql dump
SELECT COUNT(*) AS [count] FROM [transactions] AS [Transaction]
LEFT JOIN [catalogues] AS [Catalogue] ON ([Transaction].[catalogue_id] = [Catalogue].[id])
LEFT JOIN [users] AS [User] ON ([Transaction].[user_id] = [User].[id])
WHERE (((([Transaction].[return_date] < ('01', '09', 2013))
AND ([Transaction].[status] = 3))) OR ([Transaction].[status] IN (0, 2, 4)))
As you can see the date format ('01', '09', 2013). But when try to convert use this
'Transaction.return_date <'=> date('Y-m-d', strtotime($find_date))
it show error:
Warning (2): strtotime() expects parameter 1 to be string, array given [APP\Controller\CataloguesController.php, line 66]
and the sql show:
[Transaction].[return_date] < '1970-01-01'
You could use:
$date = DateTime::createFromFormat('d/m/y', implode('/', $find_date));
'Transaction.return_date <'=> $date->format('Y-m-d');
Edit:
I think the way its meant to be 'flattened' is with
$this->Model->deconstruct('find_date', $find_date);
However, last time i tried to use this, i couldn't get it to work properly, so i went with the above.
http://api21.cakephp.org/class/model#method-Modeldeconstruct
Try converting return date using UNIX_TIMESTAMP in sql query and then comparing it with
strtotime($find_date).

How can I query data with two date ranges in CakePHP?

I'm trying to execute this query to fetch 'Banners' between a date_start and a date_end.
$current_date = date('Y-m-d');
$banners = $this->Banner->find('all',
array('conditions' =>
array("date_start >= " => $current_date,
"date_end <= " => $current_date)
));
I've attempted to use NOW() which seems to cause problems, I've tried using an "AND" condition and I've also concatenated the query with $current_date (eg. "date_start =>".$current_date)
Any ideas where I'm going wrong?
Edit
Managed to get it working by switching the conditions around:
$banners = $this->Banner->find('all', array('conditions' => array("'$current_date' >=" >= "date_start", "'$current_date' <=" => 'date_end')));
you should change your equation
date_start <= $current_date and
date_end >= $current date
lets say date_start is 1/5/2011
and date_end is 3/5/2011
and curr_date is 2/5/2011
notice that curr_date is bigger than start_date and smaller than end_date, in your condition you check the opposite

Resources