I have two tables
coins summary_coins
-------- -------------
id | name id| date | get_count | coin_id
===|==== ==============================================
1 |lira 1 | 2020-02-16 16:55:50 | 20 | 1
2 |A 1 | 2020-03-16 16:55:50 | 12 | 1
3 |B 1 | 2020-03-16 16:55:50 | 20 | 1
My Expected result
name get_count previous_month_count
Lira 32 20
I have tried below cakephp method to get expected result
public function getCount($start_date,$end_date,$previous_month_start_date,$previous_month_end_date)
{
$query = $this->find();
$query
->select([
'Coins.id',
'Coins.name',
'curr_get_count' => $query->func()->sum('SummaryCoins.get_count'),
'prev_get_count' => $query->func()->sum('SummaryCoins.get_count'),
])
->matching('SummaryCoins')
->where([
'SummaryCoins.user_id' => 1,
'SummaryCoins.date >' => '2021-04-01',
'SummaryCoins.date <' => '2021-05-31'
])
->group([
'Coins.id',
]);
return $query;
}
Here I am getting present month range, how I will get previous month count ?
There's many ways to achieve this, subqueries, multiple custom joins, case expressions, etc...
I would suggest that you try case expressions first, where depending on your date conditions you either return SummaryCoins.get_count, NULL (which is being ignored by SUM) or 0 (in case you want the sum to be 0 instead of NULL if no rows are matching your conditions), eg generate SQL like:
SUM(
CASE WHEN
SummaryCoins.date >= '2020-03-01' AND
SummaryCoins.date <= '2020-03-31'
THEN
SummaryCoins.get_count
ELSE
0
END
)
$query
->select([
'Coins.id',
'Coins.name',
'curr_get_count' => $query->func()->sum(
$query
->newExpr()
->addCase(
[
$query->newExpr()->add([
'SummaryCoins.date >=' => $start_date,
'SummaryCoins.date <=' => $end_date,
])
],
[$query->identifier('SummaryCoins.get_count'), 0]
[null, 'integer']
)
),
'prev_get_count' => $query->func()->sum(
$query
->newExpr()
->addCase(
[
$query->newExpr()->add([
'SummaryCoins.date >=' => $previous_month_start_date,
'SummaryCoins.date <=' => $previous_month_end_date,
]),
],
[$query->identifier('SummaryCoins.get_count'), 0]
[null, 'integer']
)
),
])
Also note that you should use >= and <= to make the conditions inclusive, otherwise the first day and the last day of the month would be ignored in case the time part is 00:00:00.
See also
Cookbook > Database Access & SQL > Query Builder > Case Statements
Related
I'd like to request advice for an approach to use the Query Builder to get multiple sums from associated models.
There are three tables:
invoices invoice_items payment_receipts
-------- ------------- -------------
id | name id| invoice_id | invoice_qty unit_price id| invoice_id | receipt_amount
===|====== ========================================== ================================
1 |INV01 1| 1 | 1300 |12.00 1 | 1 | 1000
2 |INV02 2| 1 | 2600 |9.00 2 | 1 | 2000
3 |INV03 3| 2 | 1100 |15.00 3 | 3 | 900
4| 3 | 900 |12:00
For each invoice, I want the sum of the items' total amount (qty * price), and also the sum of payment receipts.
This query (with subqueries) correctly gets the result I'm looking for:
SELECT Invoices.id, Invoices.invoice_name, InvoiceItemSum.SumOfAmount, PaymentSum.SumOfPaymentAmount
FROM Invoices
INNER JOIN (
SELECT invoice_id, SUM(Invoice_items.invoice_qty * Invoice_items.unit_price) AS SumOfAmount
FROM Invoice_items
GROUP BY Invoice_id
) InvoiceItemSum ON InvoiceItemSum.Invoice_id = Invoices.id
LEFT JOIN (
SELECT Invoice_id, SUM(Payment_receipts.receipt_amount) AS SumOfPaymentAmount
FROM Payment_receipts
GROUP BY Invoice_id
) PaymentSum ON PaymentSum.Invoice_id = Invoices.id
WHERE Invoices.invoice_id = 33
I can execute this query directly in my CakePhp app and get the results I need, so it works that way.
However I'd like advice on a more elegant CakePHP way to do this via the Query Builder.
I have tried this:
$query = $this->Invoices->find()->contain(['InvoiceItems', 'PaymentReceipts']);
$query->select([
'Invoices.id',
'Invoices.invoice_name',
]);
$query->select([
'total_inv_amt' => $query->func()->sum('InvoiceItems.invoice_qty * InvoiceItems.unit_price'),
'total_paid_amt' => $query->func()->sum('PaymentReceipts.receipt_amount')
])
->innerJoinWith('InvoiceItems')
->leftJoinWith('PaymentReceipts')
->group(['Invoices.id']);
$query->where(['Invoices.id' => 33]);
But this results in doubling the two sums via creating this query:
SELECT
Invoices.id AS Invoices__id,
Invoices.invoice_name AS Invoices__invoice_name,
(
SUM(
InvoiceItems.invoice_qty * InvoiceItems.unit_price
)
) AS total_inv_amt,
(
SUM(PaymentReceipts.receipt_amount)
) AS total_paid_amt
FROM
invoices Invoices
INNER JOIN invoice_items InvoiceItems ON Invoices.id = (InvoiceItems.invoice_id)
LEFT JOIN payment_receipts PaymentReceipts ON Invoices.id = (PaymentReceipts.invoice_id)
WHERE
Invoices.id = 33
GROUP BY
Invoices.id
I've tried subqueries following the documentation with myriad unsuccessful results. I've also played with joins but still no dice.
My question is: what is a good approach to write this query using the Query Builder?
Thanks in advance for any advice!
You can easily do it by cakephp JOIN. Check this solution it may help you.
Direct written in editor , not guaranty without syntax error !
$this->Invoices->find()
->select([
'Invoices.id',
'Invoices.invoice_name',
'total_inv_amt' => $query->func()->sum('InvoiceItems.invoice_qty * InvoiceItems.unit_price'),
'total_paid_amt' => $query->func()->sum('PaymentReceipts.receipt_amount')
])
->join([
'table' => 'InvoiceItems',
'alias' => 'InvoiceItems',
'type' => 'Inner',
'conditions' => [
// your condition for InvoiceItems
],
])
->join([
'table' => 'PaymentReceipts',
'alias' => 'PaymentReceipts',
'type' => 'LEFT',
'conditions' => [
// condition for PaymentReceipts
],
])
OK, finally after reviewing Alimon's answer and several other questions on subqueries such as this and this, I've arrived at the correct Query Builder solution for this. Here it is:
$subquery_a = $this->Invoices->InvoiceItems->find('all');
$subquery_a
->select(['totalinvoiceamt' => $subquery_a->func()->sum('invoice_qty * unit_price') ])
->where([
'InvoiceItems.invoice_id = Invoices.id'
]);
$subquery_b = $this->Invoices->PaymentReceipts->find('all');
$subquery_b
->select(['totalpaymentamt' => $subquery_b->func()->sum('receipt_amount') ])
->where([
'PaymentReceipts.invoice_id = Invoices.id'
]);
$query = $this->Invoices->find('all')
->select([
'Invoices.id',
'Invoices.invoice_name',
'InvoiceItems__total_invoice_amount' => $subquery_a,
'PaymentReceipts__total_payments_amount' => $subquery_b
])
->join([
[
'table' => 'invoice_items',
'alias' => 'InvoiceItems',
'type' => 'INNER',
'conditions'=> [
'Invoices.id = InvoiceItems.invoice_id'
]
]
])
->join([
[
'table' => 'payment_receipts',
'alias' => 'PaymentReceipts',
'type' => 'LEFT',
'conditions'=> [
'Invoices.id = PaymentReceipts.invoice_id'
]
]
])
->group('InvoiceItems.invoice_id');
$query->where(['Invoices.id' => 33]);
The results are the same as the direct query, though the SQL looks a bit different from the manual one, but the results are identical.
Thanks Alimon et al for the assistance.
What I have
In my OrdersTable.php:
$this->hasOne('Total', [
'className' => 'App\Model\Table\TotalsTable',
'foreignKey' => 'order_id',
'propertyName' => 'Total'
]);
Actual totals table:
| id | order_id | type | value |
|----|----------|----------|-------|
| 1 | 1 | total | 100 |
| 2 | 1 | tax | 20 |
| 3 | 1 | shipping | 5 |
The structure and logic come from opencart/opencart and I have no control over that.
What I want
This is a non-functional concept:
$query = $ordersTable->find('all', array(
'contain' => array(
'TotalTax' => [
'associationName' => 'Total',
'conditions' => function($query) {
return $query->where([
'TotalTax.type' => 'tax',
]);
},
],
'TotalShipping' => [
'associationName' => 'Total',
'conditions' => function($query) {
return $query->where([
'TotalShipping.type' => 'shipping',
]);
},
],
),
));
Do you guys think something like this is possible?
UPD: Creating an association for each type isn't an option since there may be too many of them
If this functionality is something that will be reused with your codebase, I would implement this logic at the table level and have two different conditional associations:
In OrdersTable.php
$this->hasOne('TotalTax', [
'className' => 'Totals'
])
->setConditions(['TotalTax.type' => 'tax'])
->setDependent(true);
$this->hasOne('TotalShipping', [
'className' => 'Totals'
])
->setConditions(['TotalShipping.type' => 'shipping'])
->setDependent(true);
Then you can simply contain them in the query:
$query = $ordersTable->find()->contain(['TotalTax', 'TotalShipping'];
An example of this can be found in the CakePHP documentation
How to get result of below tables.
catTbl
catId, name
11 fruit
12 vegetable
prodTbl
pId | catTbl_catId | name
1 11 apple
2 11 orange
3 12 slag
I want to get result for
cat name total count
fruit 2
vegetable 1
order by with total count--
How to use order by total below cakephp 3 query..
$cats=$this->catTbl->find()
->where(['catTbl.status'=>'1','is_deleted'=>'0'])
->select(['name','catId','modified'])
->contain([
'prodTbl' => function($q) {
$q->select([
'prodTbl.catTbl_catId',
'total' => $q->func()->count('prodTbl.catTbl_catId')
])
->where(['prodTbl.status'=>'1','prodTbl.is_deleted'=>'0'])
->group(['prodTbl.catTbl_catId']);
return $q;
}
]);
I managed to get the results
+------------+---------+
| Cat | Total |
+------------+---------+
| fruit | 2 |
| vegetable | 1 |
+------------+---------+
using this query
$query = $this->Prod->find();
$cats = $query->select([
'Cat.name',
'total' => $query->func()->count('catId')
])
->contain([
'Cat'
])
->group([
'catId'
])
->order([
'total' => 'DESC'
])
->all();
I'm have quite a problem while trying to make a list (for the admin section) of HABTM relations. Here's the deal:
permissions: id, name;
users: id, username;
permissions_users: permission_id, user_id
Permission HasAndBelongsToMany User
I want to make a listing like such:
User.id | User.username | Permission.id | Permission.name
1 | Jack | 1 | posts
1 | Jack | 2 | comments
2 | Mark | 1 | posts
3 | Kate | 3 | tags
Stuff like: $this->Permission->User->find('all'); (or the other way around) doesn't really work, because it will fetch many permissions for Jack, also the other way around it will fetch many users for the posts permission, thus making it impossible to list in the view.
What I want is to get a array like:
[0] = > array(
[User] => array([id] => 1 [username] => Jack)
[Permission] => array([id] => 1 [name] => posts)
)
[1] = > array(
[User] => array([id] => 1 [username] => Jack)
[Permission] => array([id] => 2 [name] => comments)
)
...
Any ideas?
I think you would need to use the foreach and loop through your result to reconstruct a new array like that.
$user = array('id' => '1', 'name' => 'Jack');
$data = array();
foreach($permission as $per) {
$data[] = array($user, $per['Permission'])
}
I'm using cakephp and would like to display all Submissions which are part of Category 'X'
I have 4 tables with a HABTM relationship.
Users -> (haveMany) -> Submissions <-> (hasAndBelongsToMany) <-> Categories
but I would like to do so using the $this->paginate() and for each submission I would like to display the user who posted the submission.
User Table
Id | Name
-----+-------------------
1 | User 1
2 | User 2
Submission Table
Id | Name | User_id
-----+-------------------+--------------
1 | Submission 1 | 1
2 | Submission 2 | 2
Category Table
Id | Name
-----+-------------------
1 | Category 1
2 | Category 2
SubmissionCategory Table
Id | Submission_id | Category_id
-----+-------------------+-------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
I am having really trouble creating a paginate which can do this, I'm starting to think its not possible unless I'm missing something.
If I was not using cakephp this is the query I would want to do
SELECT
*
FROM
submissions_categories,
submissions,
users
WHERE
submissions_categories.category_id = 8
AND
submissions_categories.submission_id = submissions.id
AND
submissions.user_id = users.id
HABTM relationships are very unwieldy in CakePHP I find. You are going to need to set up the joins manually using the $paginate variable. Then you can pass in the optional conditions array to the paginate() function. Example:
<?php
class SubmissionsController extends AppController {
var $name = 'Submissions';
var $helpers = array('Html', 'Form');
var $paginate = array('joins' => array(
array(
'table' => 'submissions_categories',
'alias' => 'SubmissionsCategory',
'type' => 'inner',
'conditions'=> array('SubmissionsCategory.submission_id = Submission.id')
),
array(
'table' => 'categories',
'alias' => 'Category',
'type' => 'inner',
'conditions'=> array(
'Category.id = SubmissionsCategory.category_id'
)
)));
function index() {
$this->Submission->recursion = 1;
$this->set('submissions', $this->paginate(array('Category.id'=>1)));
}
}
?>