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.
Related
I'm facing a problem with cakephp associations in Models.
I have to Select records which have atleast one hasMany reation row
Model
class Category extends AppModel
{
public $hasMany = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'CategoryId',
)
);
}
Query
$categories = $this->Category->find('all');
I only needed the categories which have atleast one product entry
Categories Like : Shirts, Footwear, Glasses etc
Products like :
Small, medium, large (Shirts)
With Frame, UV protected (Glass)
So, i jus want to get Shirts and Glasses Categories only because for the above example there is no products for Footwear
Use counterCache or joins
Please refer to CakePHP - Find and count how many associated records exist
The most simple way with the best performance would be using a properly indexed counter cache field as shown in the linked answer.
Sice the linked answer is not an exact duplicate with respect to the join, here's some additional info, instead of using HAVING COUNT with the join you'd use a IS NOT NULL condition. Here's an (untested) example:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'LEFT',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'conditions' => array(
'Product.CategoryId IS NOT NULL'
)
'group' => 'Category.id'
));
Depending on the used DBMS and version you might get better performance using an inner join:
$this->Category->find('all', array(
'joins' => array(
array(
'table' => 'products',
'alias' => 'Product',
'type' => 'INNER',
'conditions' => array('Category.id = Product.CategoryId')
)
),
'group' => 'Category.id'
));
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.
cakephp I try to get a find('all'...) on a model with many associations in cakePHP 1.3, which does have the filter criteria for the query in the second level of the recursion within the schema. Simply, it looks like this and I want to filter for the UserId:
Delivery belongsTo Order, Order belongsTo User.
Here are the assocs:
Order:
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),....
Delivery:
var $belongsTo = array(
'Order' => array(
'className' => 'Order',
'foreignKey' => 'order_id',
'conditions' => '',
'fields' => '',
'order' => ''
),...
The resulting error is:
SQL Error: 1054: Unknown column 'User.id' in 'where clause' [CORE/cake/libs/model/datasources/dbo_source.php, line 684]
Here the full query, just for fun:
SELECT Delivery.id, Delivery.order_id, Delivery.delivery_address_id, Delivery.deliver_date, Delivery.created, Delivery.modified, Delivery.deliver_run, Delivery.product_mix_id1, Delivery.product_mix_id2, Delivery.product_mix_id3, Delivery.product_mix_id4, Delivery.assembled, Delivery.shipped, Delivery.rated, Delivery.price, Delivery.product_lines_id, Order.id, Order.user_id, Order.product_lines_id, Order.order_date, Order.deliver_monday, Order.deliver_tuesday, Order.deliver_wednessday, Order.deliver_thursday, Order.deliver_friday, Order.deliver_saturday, Order.delivery_address_id, Order.payment_delay, Order.active, Order.cancle_date, Order.replaced_order_id, Order.created, Order.modified, DeliveryAddress.id, DeliveryAddress.delivery_company, DeliveryAddress.delivery_title, DeliveryAddress.delivery_first_name, DeliveryAddress.delivery_last_name, DeliveryAddress.delivery_street, DeliveryAddress.delivery_house_nr, DeliveryAddress.delivery_postal_code, DeliveryAddress.delivery_town, DeliveryAddress.delivery_country, DeliveryAddress.created, DeliveryAddress.deleted, DeliveryAddress.modified, ProductLine.id, ProductLine.name, ProductLine.description, ProductMix1.id, ProductMix1.name, ProductMix1.description, ProductMix1.image_small_path, ProductMix1.image_normal_path, ProductMix1.product_categories_id, ProductMix1.depricated, ProductMix1.created, ProductMix1.modified, ProductMix2.id, ProductMix2.name, ProductMix2.description, ProductMix2.image_small_path, ProductMix2.image_normal_path, ProductMix2.product_categories_id, ProductMix2.depricated, ProductMix2.created, ProductMix2.modified, ProductMix3.id, ProductMix3.name, ProductMix3.description, ProductMix3.image_small_path, ProductMix3.image_normal_path, ProductMix3.product_categories_id, ProductMix3.depricated, ProductMix3.created, ProductMix3.modified, ProductMix4.id, ProductMix4.name, ProductMix4.description, ProductMix4.image_small_path, ProductMix4.image_normal_path, ProductMix4.product_categories_id, ProductMix4.depricated, ProductMix4.created, ProductMix4.modified FROM deliveries AS Delivery LEFT JOIN orders AS Order ON (Delivery.order_id = Order.id) LEFT JOIN delivery_addresses AS DeliveryAddress ON (Delivery.delivery_address_id = DeliveryAddress.id) LEFT JOIN product_lines AS ProductLine ON (Delivery.product_lines_id = ProductLine.id) LEFT JOIN product_mixes AS ProductMix1 ON (Delivery.product_mix_id1 = ProductMix1.id) LEFT JOIN product_mixes AS ProductMix2 ON (Delivery.product_mix_id2 = ProductMix2.id) LEFT JOIN product_mixes AS ProductMix3 ON (Delivery.product_mix_id3 = ProductMix3.id) LEFT JOIN product_mixes AS ProductMix4 ON (Delivery.product_mix_id4 = ProductMix4.id) WHERE User.id = 1
Does anyone know why cake does not pull the second level, in this case the User model, when even recursive is set to 5?
Many thanks.
EDIT: It just occurred to me that in your case you don't need 2nd level JOIN actually, as you can filter by Order.user_id (instead of User.id)! Do you see my point?
So probably you don't need solution below.
As far as I know, Cake never does 2nd level JOIN itself, so for filtering (conditions) on 2nd level (and deeper) I use joins.
For your example:
$options['joins'] = array(
array(
'table' => 'orders',
'alias' => 'Order',
'type' => 'LEFT',
'conditions' => array(
'Order.id = Delivery.order_id',
)
),
array(
'table' => 'users',
'alias' => 'User',
'type' => 'LEFT',
'conditions' => array(
'User.id = Order.user_id',
'User.some_field' => $someFilteringValue
)
)
);
$result = $this->Delivery->find('all', $options);
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'
)
);