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'
)
);
Related
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.
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 been working with cakephp paginations options for 2 days. I need to make a INNER Joins to list a few fields, but I have to deal with search to filter results.
This is portion of code in which I deal with search options by $this->passedArgs
function crediti() {
if(isset($this->passedArgs['Search.cognome'])) {
debug($this->passedArgs);
$this->paginate['conditions'][]['Member.cognome LIKE'] = str_replace('*','%',$this->passedArgs['Search.cognome']);
}
if(isset($this->passedArgs['Search.nome'])) {
$this->paginate['conditions'][]['Member.nome LIKE'] = str_replace('*','%',$this->passedArgs['Search.nome']);
}
and after
$this->paginate = array(
'joins' => array(array('table'=> 'reservations',
'type' => 'INNER',
'alias' => 'Reservation',
'conditions' => array('Reservation.member_id = Member.id','Member.totcrediti > 0' ))),
'limit' => 10);
$this->Member->recursive = -1;
$this->paginate['conditions'][]['Reservation.pagamento_verificato'] = 'SI';
$this->paginate['fields'] = array('DISTINCT Member.id','Member.nome','Member.cognome','Member.totcrediti');
$members = $this->paginate('Member');
$this->set(compact('members'));
INNER JOIN works good, but $this->paginations ignore every $this->paginate['conditions'][] by $this->passedArgs and I cannot have idea how I can work it out.
No query in debug, just the original INNER JOIN.
Can someone helps me ?
Thank you very much
Update:
No luck about it.
I've been dealing with this part of code for many hours.
If I use
if(isset($this->passedArgs['Search.cognome'])) {
$this->paginate['conditions'][]['Member.cognome LIKE'] = str_replace('*','%',$this->passedArgs['Search.cognome']);
}
$this->paginate['conditions'][]['Member.sospeso'] = 'SI';
$this->Member->recursive = 0;
$this->paginate['fields'] = array(
'Member.id','Member.nome','Member.cognome','Member.codice_fiscale','Member.sesso','Member.region_id',
'Member.district_id','Member.city_id','Member.date','Member.sospeso','Region.name','District.name','City.name');
$sospesi = $this->paginate('Member');
everything goes well, and from debug I receive the first condition and the conditions from $this->paginate['conditions'][]['Member.cognome LIKE'], as you can see
array $this->passedArgs
Array
(
[Search.cognome] => aiello
)
Array $this->paginate['conditions'][]
(
[0] => Array
(
[Member.cognome LIKE] => aiello
)
[1] => Array
(
[Member.sospeso] => NO
)
But, if I write the joins with paginate , $this->paginate['conditions'][] will ignore all the stuff, and give me from debug, just $this->paginate['conditions'][]['Reservation.pagamento_verificato'] = 'SI';
Another bit of information.
If I put all the stuff dealing with $this->paginate['conditions'][]['Reservation.pagamento_verificato'] = 'SI';
before the $this->paginate JOIN, nothing will be in $this->paginate['conditions'][].
This is an old question, so I'll just review how to do a JOIN in a paginate for others who got here from Google like I did. Here's the sample code from the Widget's Controller, joining a Widget.user_id FK to a User.id column, only showing the current user (in conditions):
// Limit widgets shown to only those owned by the user.
$this->paginate = array(
'conditions' => array('User.id' => $this->Auth->user('id')),
'joins' => array(
array(
'alias' => 'User',
'table' => 'users',
'type' => 'INNER',
'conditions' => '`User`.`id` = `Widget`.`user_id`'
)
),
'limit' => 20,
'order' => array(
'created' => 'desc'
)
);
$this->set( 'widgets', $this->paginate( $this->Widget ) );
This makes a query similar to:
SELECT widgets.* FROM widgets
INNER JOIN users ON widgets.user_id = users.id
WHERE users.id = {current user id}
And still paginates.
I'm not sure if you need those [] - try just doing this:
$this->paginate['conditions']['Reservation.pagamento_verificato'] = 'SI';
I use the conditions when I call paginate method.
$this->paginate($conditions)
This works ok for me, I hope it works for you!
If you have setted previous params, you may use:
$this->paginate(null,$conditions)
This might be help full to someone....
This is how I did complicated joins with pagination in cakephp.
$parsedConditions['`Assessment`.`showme`'] = 1;
$parsedConditions['`Assessment`.`recruiter_id`'] = $user_id;
$this->paginate = array(
'conditions' => array($parsedConditions ),
'joins' => array(
array(
'alias' => 'UserTest',
'table' => 'user_tests',
'type' => 'LEFT',
'conditions' => '`UserTest`.`user_id` = `Assessment`.`testuser_id`'
),
array(
'alias' => 'RecruiterUser',
'table' => 'users',
'type' => 'LEFT',
'conditions' => '`Assessment`.`recruiter_id` = `RecruiterUser`.`id`'
)
,
array(
'alias' => 'OwnerUser',
'table' => 'users',
'type' => 'LEFT',
'conditions' => '`Assessment`.`owner_id` = `OwnerUser`.`id`'
)
),
'fields' => array('Assessment.id', 'Assessment.recruiter_id', 'Assessment.owner_id', 'Assessment.master_id', 'Assessment.title', 'Assessment.status', 'Assessment.setuptype','Assessment.linkkey', 'Assessment.review', 'Assessment.testuser_email', 'Assessment.counttype_2', 'Assessment.bookedtime', 'Assessment.textqstatus', 'Assessment.overallperc', 'UserTest.user_id', 'UserTest.fname', 'UserTest.lname', 'RecruiterUser.company_name', 'OwnerUser.company_name'),
'limit' => $limit,
'order'=> array('Assessment.endtime' => 'desc')
);