i'm working on a project in CakePHP but stuck at a strange problem. I'm writing a query:
$brands = $this->Product->find('all', array(
'fields'=> array('DISTINCT Product.brand as brand'),
'order'=>'Product.brand ASC',
'conditions'=> array('Product.subcategory_id'=>$subcategory_id)
));
It is picking Product.id along with Product.brand which I do not want.
The query it generates is:
SELECT DISTINCT `Product`.`brand`, `Product`.`id` FROM `ecom`.`products` AS `Product` LEFT JOIN `ecom`.`subcategories` AS `SubCategory` ON (`Product`.`subcategory_id` = `SubCategory`.`id`) LEFT JOIN `ecom`.`categories` AS `Category` ON (`Product`.`category_id` = `Category`.`id`) WHERE `Product`.`subcategory_id` = 13 ORDER BY `Product`.`brand` ASC
How can I skip Product.id from select?
Thanks
Adding group solved my issue:
$brands = $this->Product->find('all', array(
'fields'=> array('DISTINCT Product.brand as brand'),
'order'=>'Product.brand ASC',
'conditions'=> array('Product.subcategory_id'=>$subcategory_id),
'group' => 'brand'
));
I think it's because cake needs the id to create the joins
try
$brands = $this->Product->find('all', array(
'fields'=> array('DISTINCT Product.brand as brand'),
'order'=>'Product.brand ASC',
'conditions'=> array('Product.subcategory_id'=> $subcategory_id),
'recursive' => -1
));
Related
I am Cakephp Beginner.
I want this query with cakephp..
Original Query..
SELECT `Advertisement`.*, `Gallery`.`image` FROM `moenatmi_moen`.`advertisements`
AS `Advertisement` LEFT JOIN `moenatmi_moen`.`galleries` AS `Gallery` ON
(`Gallery`.`advertise_id` = `Advertisement`.`id` AND `Gallery`.`main_ad_image`
= 1) WHERE `Advertisement`.`status` = 1 AND `Advertisement`.`category_id` = 14 AND
((`Advertisement`.`expiry_date` >= '2014-11-18') OR (`Advertisement`.`expiry_date`
IS NULL)) GROUP BY `Advertisement`.`id` ORDER BY `Advertisement`.`id` desc
LIMIT 10
I Want Like this query..
SELECT `Advertisement`.*, `Gallery`.`image` FROM `moenatmi_moen`.`advertisements`
AS `Advertisement` LEFT JOIN `moenatmi_moen`.`galleries` AS `Gallery` ON
(`Gallery`.`advertise_id` = `Advertisement`.`id` AND `Gallery`.`main_ad_image` = 1)
WHERE `Advertisement`.`status` = 1 AND `Advertisement`.`category_id` IN (select id from
categories where id=14 or parent_id=14) AND ((`Advertisement`.`expiry_date` >=
'2014-11-18') OR (`Advertisement`.`expiry_date` IS NULL)) GROUP BY
`Advertisement`.`id` ORDER BY `Advertisement`.`id` desc LIMIT 10
Original Cakephp Code..
$this->paginate['conditions'] = array(
'Advertisement.status'=>1,
'Advertisement.category_id'=>$cat_id,
'OR'=>array(
'Advertisement.expiry_date >='=>date('Y-m-d'),
'Advertisement.expiry_date'=>null
)
);
Help me..
Thanks You..
this should be a painless switch. This first query calls the category ids that you're wanting within your main query.
$cat_id is the same variable you're using in the original query.
$ids = $this->Category->find('all', array(
'conditions' => array(
'id' => $cat_id,
'parent_id' => $cat_id
)
)
);
The important thing to note here, this query will output your typical nested array..
array(
array(),
array(),
array()
);
The classicExtract function will sift through the mess for you and give you a single array with a list of 'id' which is exactly what you want for your query. EX:
array(14, 15, 16, 18, 19);
Finally, your main query.
$this->paginate['conditions'] = array(
'Advertisement.status'=>1,
'Advertisement.category_id'=> Set::classicExtract($ids, '{n}.Category.id'),
'OR'=>array(
'Advertisement.expiry_date >=' => date('Y-m-d'),
'Advertisement.expiry_date'=>null
)
);
Conclusion
All together your new conditions will look just like this:
$ids = $this->Category->find('all', array(
'conditions' => array(
'id' => $cat_id,
'parent_id' => $cat_id
)
)
);
$this->paginate['conditions'] = array(
'Advertisement.status'=>1,
'Advertisement.category_id'=> Set::classicExtract($ids, '{n}.Category.id'),
'OR'=>array(
'Advertisement.expiry_date >=' => date('Y-m-d'),
'Advertisement.expiry_date'=>null
)
);
I have these tables and the relationships between them:
1 project hasmany configurationcontext 1 issuetype hasmany
optionconfiguration hasmany configurationcontext hasmany
optionconfiguration (does not exist intermediate table)
My goal is to get information like this query.
SELECT IT.id, IT.pname
FROM configurationcontext CC
LEFT OUTER JOIN optionconfiguration OC ON OC.fieldconfig = CC.fieldconfigscheme
LEFT OUTER JOIN issuetype IT ON IT.id = OC.optionid
WHERE CC.project = 10000
my doubts:
- which the controller to use to create a function that return me this information?
- How do I get this information?
thanks :)
Assuming I have your model names correct, this should do the trick:
$this->ConfigurationContext->find('all', array(
'joins' => array(
array(
'table' => 'optionconfiguration',
'alias' => 'OptionConfiguration',
'type' => 'LEFT OUTER JOIN',
'conditions' => array(
'OptionConfiguration.fieldconfig = ConfigurationContext.fieldconfigscheme'
)
),
array(
'table' => 'issuetype',
'alias' => 'IssueType',
'type' => 'LEFT OUTER JOIN',
'conditions' => array(
'IssueType.id = OptionConfiguration.optionid'
)
)
),
'conditions' => array(
'ConfigurationContext.project' => 10000
),
'fields' => array(
'IssueType.id',
'IssueType.pname'
)
));
I haven't tried type "LEFT OUTER JOIN" but I don't see why it wouldn't work, maybe look into using containable to avoid using joins if you can.
Task
I'm trying to return a set of data based on a condition in the related model.
The problem
Currently the closest I can get is using Containable to return all matching model data, but only returning child data if it matches the contain condition. This isn't ideal as my data still contains the primary model data, rather than it being removed.
I am using a HABTM relationship, between, for example, Product and Category, and I want to find all products in a specific category.
Inital idea
The basic method would be using containable.
$this->Product->find('all', array(
'contain' => array(
'Category' => array(
'conditions' => array(
'Category.id' => $categoryId
)
)
)
));
Although this will return all products, and just remove the Category dimension if it doesn't match the contain condition.
Closest so far
$this->Product->find('all', array(
'contain' => false,
'joins' => array(
array(
'table' => 'categories_products',
'alias' => 'CategoriesProduct',
'type' => 'LEFT',
'conditions' => array(
'CategoriesProduct.product_id' => 'Product.id'
)
),
array(
'table' => 'categories',
'alias' => 'Category',
'type' => 'LEFT',
'conditions' => array(
'Category.id' => 'CategoriesProduct.category_id'
)
)
),
'conditions' => array(
'Product.status_id' => 1,
'Category.id' => $categoryId
),
));
Which generates the following query,
SELECT `Product`.`id`, `Product`.`name`, `Product`.`intro`, `Product`.`content`, `Product`.`price`, `Product`.`image`, `Product`.`image_dir`, `Product`.`icon`, `Product`.`icon_dir`, `Product`.`created`, `Product`.`modified`, `Product`.`status_id`
FROM `skyapps`.`products` AS `Product`
LEFT JOIN `skyapps`.`categories_products` AS `CategoriesProduct` ON (`CategoriesProduct`.`product_id` = 'Product.id')
LEFT JOIN `skyapps`.`categories` AS `Category` ON (`Category`.`id` = 'CategoriesProduct.category_id')
WHERE `Product`.`status_id` = 1
AND `Category`.`id` = 12
This query is correct, except that the join conditions are being quoted ' instead of `, which breaks the query.
Manual query
SELECT *
FROM products
JOIN categories_products ON categories_products.product_id = products.id
JOIN categories ON categories.id = categories_products.category_id
WHERE categories.id = 12
The problem lay in the way I was defining my join conditions. It's not an associative array but rather a string.
'conditions' => array(
'CategoriesProduct.product_id' => 'Product.id'
)
Changes to
'conditions' => array(
'CategoriesProduct.product_id = Product.id'
)
Im trying to replicate the following query in cakephp:
SELECT *
FROM uploads, proposals
WHERE proposals.id = uploads.proposal_id AND proposals.tender_id = 10
Im using the find method in the Upload model with the following conditions:
$conditions = array(
'Proposal.id' => $id,
'AND' => array(
'Upload.proposal_id' => 'Proposal.id'
)
);
return($this->find('list', array('conditions' => $conditions)));
but im getting this query instead
SELECT `Upload`.`id`, `Upload`.`title`
FROM `kumalabs_lic`.`uploads` AS `Upload`
WHERE `Proposal`.`id` = 10 AND `Upload`.`proposal_id` = 'Proposal.id'
as you can see, the proposals table is missing, can somebody explain me how can i make this query?
Thanks :)
I would recommend you use the linkable behaviour for this. It is much easier than the default way of doing joins in CakePHP. It works with the latest version of CakePHP, as well as 1.3.
CakePHP Linkable Behavior
You would then modify your find to look like this:
return($this->find('list', array(
'link' => array('Proposal'),
'conditions' => array(
'Proposal.id' => $id,
),
'fields' => array(
'Upload.*',
'Proposal.*',
),
)));
CakePHP will automatically join on your primary / foreign key, so no need to have the
'Upload.proposal_id' => 'Proposal.id'
condition.
Though you don't need that condition, I also want to point out that you are doing your AND wrong. This is how you do AND and OR in CakePHP
'conditions' => array(
'and' => array(
'field1' => 'value1', // Both of these conditions must be true
'field2' => 'value2'
),
'or' => array(
'field1' => 'value1', // One of these conditions must be true
'field2' => 'value2'
),
),
If the model have association, CakePHP automatically join the table by 'contain' keyword. Try code bellow:
public function getProposalsFromTender($id){
$data = $this->find('all', array(
'conditions' => array('Proposal.id' => $id),
'fields' => array('Upload.*', 'Proposal.*'),
'contain' => array('Proposal')
));
return($data);
}
Note:
CakePHP use explicit join instead of implicit join like ...from proposals, uploads...
I'm not familiar with that JOIN syntax but I believe it equals this:
SELECT *
FROM uploads
INNER JOIN proposals ON proposals.id = uploads.proposal_id
WHERE proposals.tender_id = 10
... so you need something similar to this:
// Untested
$conditions = array(
'Proposal.id' => $id,
'joins' => array(
array(
'alias' => 'Proposal',
'table' => 'proposals',
'type' => 'INNER',
'conditions' => 'Proposal.id = Upload.proposal_id',
),
),
);
Of course, this is a direct translation of your JOIN. If your models are properly related, it should all happen automatically.
I've completely confused myself now.
I have three tables: applicants, applicants_qualifications, and qualifications.
In the index view for applicants, I have a form with a dropdown of qualifications. The results should be a list of applicants with that qualification.
So I need the table of applicants on the index view to be based on a join, right?
If I add this to my applicants_controller:
$options['joins'] = array(
array(
'table' => 'applicants_qualifications',
'alias' => 'q',
'type' => 'left outer', // so that I get applicants with no qualifications too
'conditions' => array('Applicant.id = q.applicant_id',)
)
);
$this->Applicant->find('all', $options);
I get an additional sql statement at the bottom of the page, with the left outer join but the sql without the join is there too.
I think this line:
$this->set('applicants', $this->paginate());
calls the sql statement without the join.
Looks like I need to combine the join $options with the paginate call. Is that right?
If I use the search form, I get: Unknown column 'Qualifications.qualification_id' in 'where clause'
So the page is obviously not yet 'using' my sql with the join.
Sorry - I'm still a noob. Any help appreciated...
In order to set conditions, joins, etc for your model's pagination, you must do it as follows:
function admin_index() {
$this->Applicant->recursive = 0;
$this->paginate = array(
'Applicant' => array(
// 'conditions' => array('Applicant.approved' => true),
'joins' => array(
array(
'table' => 'applicants_qualifications',
'alias' => 'ApplicationsQualification',
'type' => 'left outer',
'conditions' => array('Applicant.id = ApplicationsQualification.applicant_id')
)
)
// 'order' => array('Applicant.joined DESC')
)
);
$this->set('applicants', $this->paginate());
}
I've commented out some sample keys that you can include later on - just to give you an idea of how it works.
Hope that helps!
You can use inner join instead of left outer join.For eg.
in cource controller
$cond = array(
array(
'table' => 'colleges',
'alias' => 'Colleges',
'type' => 'inner',
'conditions'=> array('Colleges.college_id = Courses.college_id')
)
) ;
$courses = $this->Courses->find('all',
array('joins' =>$cond,'conditions' => array("Courses.status ='1' AND Colleges.status='1' "),
'order'=>array('course_name'),
'fields' => array('course_id','course_name'),'group'=>'Courses.course_id'
)
);