Cakephp retrieving HABTM which Conditions - cakephp

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)));
}
}
?>

Related

Query Builder: Subquery with multiple SUMs on associated data

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.

How get data from Relationship tables

I have 2 table in my database that second(orders) table has foreign_key of primary key of first(books) table like these
Books
----+---------+-------------
id | slug | name |
----+---------+--------------
1 | math | mathematics |
----+---------+--------------
2 | physics | holidays |
-----------------------------
Orders
----+---------+-------+--------
id | book_id | count | price |
----+---------+-------+--------
1 | 2 | 12 | 100000 |
--------------------------------
I want result like below
result
----+---------+----------+----------+----------+------------------
id | book_id | slug | name | order_id | count | price |
----+---------+----------+----------+-----------------------------
1 | 2 | physics | holidays | 1 | 12 | 100000 |
------------------------------------------------------------------
I believe each product has a price. To that start with relating PRODUCT table to PRICE.
In cake this is done very easily:
in your Model (Product.php)
class Product extends AppModel{
public $hasMany = array('Price');
}
in Price.php
class Price extends AppModel{
public $hasBelongTo = array('Product');
}
in your controller if you query any of these Models the returned array will contain data from both models.
make the relationships as follow
Product.php
....
public $hasMany = array(
'Price' => array(
'className' => 'Price',
'foreignKey' => 'product_id',
'dependent' => true,
'conditions' => '',
'group' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
....
Price.php
.....
public $belongsTo = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
....
then in your controller use this query
$this->Product->find('all',array('contain'=>array('Price')));

CakePHP How to get Photo from different table?

Hi i have two table friends and galleries I want to use CakePHP model Association between two table which Friend.friend_id = 1 and Gallery.user_id = 1
friends
id friend_id
-------------
1 5
2 1
3 6
galleries
id user_id photo
------------------------
1 1 photo1.jpg
2 6 photo2.jpg
3 5 photo3.jpg
Above two table i want to join. But i am unable to join. Please help me.
After lots of research finally i got my answer. This code helps to other-
//$user_id=$this->Session->read('Auth.User.id');
$user_id=1;
$this->Friend->bindModel(
array('hasOne' => array(
'Gallery' => array(
'className' => 'Gallery',
'foreignKey' => FALSE,
'conditions' => array('Friend.friend_id=Gallery.user_id'),
'type' => 'LEFT'
)
)
)
);
$this->Friend->bindModel(array('belongsTo' => array('User' => array('className' => 'User', 'foreignKey' => 'friend_id', 'conditions'=>array('Friend.user_id'=>$user_id)))));
$data = $this->Friend->find('all');
pr($data);

CakePHP - Customer has multiple Country fields. How do I link the Country model to Customer?

The countries table looks like this:
iso | name
--------------------------
AD | Andorra
AE | United Arab Emirates
AF | Afghanistan
...etc
My customers table has the following fields which all store a country code:
id | country_origin | current_country_study | address_country
--------------------------------------------------------
54 | BE | GB | GB
I'm not sure how to link the Country model so that I can retrieve the name of the countries when I am doing a find on the Customer model. If I had one field called country_id I would be okay, but not sure how to do it with multiple fields.
Aliases should do the trick:
public $belongsTo = array(
'CountryOrigin' => array(
'className' => 'Country',
'foreignKey' => 'country_origin_id'
),
'CurrentCountry' => array(
'className' => 'Country',
'foreignKey' => 'current_country_id'
),
....
);
You would need to update your customers table to get the new foreign key fields.

CakePHP HABTM relations listing

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'])
}

Resources