I know how find-all works and I wrote a search query which will search an expression in different columns of a datatable. all works well.
But now, I want to search for the expression not only in a flat data-array, I want to search in a child-array. I have the following structure:
[Company] => Array
(
[id] => 1
[user_id] => 3
[name] => TEAM-Security
[address1] => Dorfstrasse 9
)
[Service] => Array
(
[0] => Array
(
[id] => 2
[cat] => Reinigung
[name] => Baureinigung
[created] => 2014-07-13 00:00:00
[modified] => 2014-07-13 00:00:00
)
[1] => Array
(
[id] => 3
[cat] => Reinigung
[name] => Wohnungsreinigung
[created] => 2014-07-13 00:00:00
[modified] => 2014-07-13 00:00:00
)
)
Now, I am able to search in the different "Company-columns". Now, I want to search also in the columns of the Service-childs. I read a lot of blogs, webpages etc., but I couldn't find any hints how to work that out.
My simple solution so far:
$searchterm = 'TEAM';
$pending = $this->Company->find('all', array(
'conditions' => array('Company.name like' => '%$searchterm%')
));
The troubles I have, I want to search now in a child with subarrays. I want to search for "Baureinigung" etc. Do I have to do a 'foreach' or something manually? Or is there a cake-way which I don't know so far?
This is one of the biggest pains for me in working with Cake - trying to search on deeper columns and use anything other than LEFT joins. Here is what's worked for me. I'm assuming that you are working in the Company Model - if not, you should be. This can be slightly modified to work in the Controller, but it belongs in the Model. I'm also assuming that your HABTM is using a join table called companies_services
First unbind the current associations to the table you want to inner join for the query:
$this->unbindModel(array('hasAndBelongsToMany' => array('Service')));
Then you need to explicitly join up your tables using INNER joins. To do this in CakePHP 2.x, you use the join key in the options argument of the find method:
$this->find('all', array(
'joins' => array(
array(
'table' => 'companies_services',
'alias' => 'CompanyServices',
'type' => 'INNER',
'conditions' => array(
'CompanyServices.company_id = Company.id'
)
),
array(
'table' => 'services',
'alias' => 'Service',
'type' => 'INNER',
'conditions' => array(
'Service.id = CompanyServices.id',
'Service.name LIKE ' => '%' . $service_search_term . '%'
)
),
),
'conditions' => array('Company.name LIKE ' => '%'. $company_search_term . '%',
'recursive' => -1
));
One other thing I'll note is that I have run into situations where I had to explicitly declare the fields that I wanted returned after doing this. To do so you would add a 'fields' key to the options array above, so for instance (following on from the 'recursive' line above):
...
'recursive' => -1,
'fields' => array(
'Company.id',
'Company.name',
'CompanyServices.company_id',
'CompanyServices.service_id',
'Service.id',
'Service.name'
)
....
You can use the containable behavior to do some searches on associated data. Example:
$this->find(
'all',
array(
'conditions' => array('Company.name like' => '%$searchterm%'),
'contain' => array(
'Service' => array(
'conditions' => array(
'Service.name like' => '%$searchterm%'
)
)
)
)
);
Be aware though that you have to pay particular attention to join types when doing searches on multiple tables at the same time like this. By default, model associations behave as a left join.
In your Company Model you have define direct association like
class Company extends AppModel
{
var $name = 'Company';
var $hasAndBelongsToMany = array('Service' =>
array(
'fields' => array('name')
)
);
}
After that you can try following
$pending = $this->Company->find('all', array(
'conditions' => array("OR" => array(
'Company.name like' => '%$searchterm%',
'Service.name like' => '%$searchterm%'
)))
);
This solution is a bit complex and probably not optimized but will work:
$data = $this->Company->find('all', array(
'conditions' => array(
"OR" => array(
'Company.name like' => '%$searchterm%',
'Service.name like' => '%$searchterm%'
)
),
'contain' => array(
'Service' => array(
'conditions' => array(
'Service.name like' => '%$searchterm%'
)
)
),
'joins' => array(
array(
'table' => 'companies_services',
'alias' => 'CompanyServices',
'type' => 'INNER',
'conditions' => array(
'CompanyServices.company_id = Company.id'
)
),
array(
'table' => 'services',
'alias' => 'Service',
'type' => 'INNER',
'conditions' => array(
'Service.id = CompanyServices.id'
)
)
)
)
);
Related
I created a query, but now i need to refactor it to the CakePHP standard. Only i can't seem to get it working.
This is my working query:
$query = $this->Transfer->query( "SELECT DISTINCT emailaddresses.emailaddress
FROM transfers
JOIN emailaddresses
ON (emailaddresses.transfer_id = transfers.transfer_id AND emailaddresses.type <> 'SENDER' AND emailaddresses.received_state = 'DELIVERED')
WHERE transfers.created_user_id = $created_user_id " );
This query is working, but when i try to Cakeify it i get access denied for table emailaddress. This is the CakePHP query:
$query = $this->Transfer->find('all', array(
'fields' => 'DISTINCT Emailaddress.emailaddress ',
'conditions' => array(
'transfers.created_user_id' => $created_user_id
),
'joins' => array(
array(
'table' => 'Emailaddress.emailaddress',
'type' => 'INNER',
'conditions' => array(
'Emailaddress.transfer_id' => 'Transfer.transfer_id',
'Emailaddress.type' => 'SENDER',
'Emailaddress.received_state' => 'DELIVERED'
)
)
)
));
What do i need to change to get this query working with the Cake standards?
Your query would be like:
$query = $this->Transfer->find('all', array(
//'fields' => 'DISTINCT Emailaddress.emailaddress ',
'fields' => array('DISTINCT Emailaddress.emailaddress '),
'conditions' => array(
//'transfers.created_user_id' => $created_user_id
'Transfer.created_user_id' => $created_user_id
),
'joins' => array(
array(
//'table' => 'Emailaddress.emailaddress',
'table' => 'emailaddresses',
'alias' => 'Emailaddress', // add alias
'type' => 'INNER',
'conditions' => array(
'Emailaddress.transfer_id' => 'Transfer.transfer_id',
//'Emailaddress.type' => 'SENDER',
'Emailaddress.type <>' => 'SENDER',
'Emailaddress.received_state' => 'DELIVERED'
)
)
)
));
Read more:
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#find
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
I have 3 models (artist, media item, accent)
Artist hasMany media items,
Media items hasAndBelongsToMany accents,
Now what I am having issues with is how to search with pagination, I need to get pages of 8 artists and the media_items which match the accent entered. Hopefully that makes sense.
This is what I have currently
$this->Paginator->settings = array(
'limit' => 8,
'fields' => array("Artist.first_name",
"Artist.last_name", "Artist.image",
"Artist.image", "Artist.slug"),
'order' => "Artist.last_name",
'url' => $this->passedArgs,
'contain' => array(
'MediaItem'=> array(
"fields" => array("MediaItem.file",
"MediaItem.title",
"MediaItem.accent",)
),
'joins' => array(
array(
'table' => 'media_items',
'type' => 'INNER',
'alias' => 'MediaItem',
'conditions' => array(
"AND" => array('Artist.id = MediaItem.artist_id')
)
),
array(
'table' => 'media_accents_media_items',
'type' => 'RIGHT',
'alias' => 'MediaAccentsMediaItems',
'conditions' => array(
"AND" => array('MediaAccentsMediaItems.media_item_id = MediaItem.id')
)
),
array(
'table' => 'media_accents',
'type' => 'RIGHT',
'alias' => 'MediaAccent',
'conditions' => array(
"AND" => array('MediaAccent.id = MediaAccentsMediaItems.media_accent_id'),
"OR" => array('MediaAccent.name LIKE' => "%".$searchFor ."%")
)
)
),
'group' => 'Artist.id',
'update' => '.results-container',
'evalScripts' => true
);
it gets the right artists but returns all their tracks rather than the ones I need. Is there a better way rather than looping through the results pruning them? Which is my current backup plan.
Remove joins and just use Containable Behavior, here is docs for deeper associations.
I have this inner join query in a paginated model called Application
Application has many AssignedExploration
AssignedExploration belongs to Exploration
Exploration belongs to ExplorationCategory
ExplorationCategory belongs to Season
My query so far is:
$session_condition = array(
'table' => 'assigned_explorations',
'alias' => 'AssignedExploration',
'type' => 'INNER',
'conditions' => array(
'AssignedExploration.application_id = Application.id',
'OR' => array(
array('AssignedExploration.label' => $this->request->named['filter_session'])
)
)
);
I would like to add another query inside the OR that will get applications which has a Season.name = fall
I've tried adding array('Season.name' => 'fall') :
$session_condition = array(
'table' => 'assigned_explorations',
'alias' => 'AssignedExploration',
'type' => 'INNER',
'conditions' => array(
'AssignedExploration.application_id = Application.id',
'OR' => array(
array('AssignedExploration.label' => $this->request->named['filter_session']),
array('Season.name' => 'fall')
)
)
);
But no luck. Seems like Season is not recognized by AssignedExploration.
I would like to nest from AssignedExploration to Exploration to ExplorationCategory and to Season
And I don't have any idea on how to make it thru inner join query via cakephp.
Thanks in advanced.
EDIT: query as of now:
INNER JOIN
`ntc_development`.`assigned_explorations` AS `AssignedExploration` ON (
`AssignedExploration`.`application_id` = `Application`.`id`
AND
`AssignedExploration`.`label` = 'fall'
)
$this->paginate['Application'] = array(
'recursive' => -1,
'joins' => array(
array(
'table' => 'assigned_explorations',
'alias' => 'AssignedExploration',
'type' => 'inner',
'foreignKey' => true,
'conditions' => array('AssignedExploration.application_id = Application.id')
),
array(
'table' => 'seasons',
'alias' => 'Season',
'type' => 'inner',
'foreignKey' => true,
'conditions' => array('Season.id = Application.season_id') # whats your foregin key
),
),
'fields' => array(),
'conditions' => array('Season.name' => 'fail'),
);
New to CakePHP etc and I'm massively confused with following problem so any guidance would be greatly appreciated. Essentially I'm having problems with hasAndBelongsToMany relationships and I don’t know if I’m going about it correctly as I’m doing most of the work inside one controller and one model.
I have a client’s pages, clients have many jobs (this works), clients belong to a client type (this works), clients also have many case studies (also works) and clients have jobs (fine).
Jobs have and belong to many disciplines – this doesn’t work, however, it appears as though the queries are being run (the SQL output in debug mode shows this, so I ran the SQL directly into MySQL - it queries fine) but Cake is not providing me the data into the clients array.
Here is the code for my Client Model and Controller.
Client.php (Model)
public $belongsTo = array(
'ClientType' => array(
'className' => 'ClientType',
'foreignKey' => 'type_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public $hasMany = array(
'CaseStudy' => array(
'className' => 'CaseStudy',
'foreignKey' => 'main_contractor',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Job' => array(
'className' => 'Job',
'foreignKey' => 'client_id',
'conditions' => '',
'fields' => '',
'order' => 'Job.id desc'
)
);
ClientsController.php (Controller)
$options['joins'] = array(
array('table' => 'case_studies',
'alias' => 'CaseStudy',
'type' => 'LEFT',
'conditions' => array(
'CaseStudy.client_id = Client.id',
)
),
array('table' => 'jobs',
'alias' => 'Job',
'type' => 'LEFT',
'conditions' => array(
'Job.client_id = Client.id',
)
),
array('table' => 'sectors',
'alias' => 'Sector',
'type' => 'LEFT',
'conditions' => array(
'Job.sector_id = Sector.id',
)
),
array('table' => 'disciplines_jobs',
'alias' => 'DisciplinesJobs',
'type' => 'LEFT',
'conditions' => array(
'Job.id = DisciplinesJobs.job_id',
)
),
array(
'table' => 'disciplines',
'alias' => 'Discipline',
'type' => 'LEFT',
'conditions' => array(
'DisciplinesJobs.discipline_id = Discipline.id'
)
)
);
$options['conditions'] = array('Client.id' => $client_id);
$clients = $this->Client->find('all', $options);
Output of $clients array above:
array(
(int) 0 => array(
'Client' => array(
'id' => '47',
'type_id' => '2',
'name' => 'Balfour Beatty',
'logo' => '1361786198_thumbnail_balfour beatty.jpg',
'website_url' => 'http://www.google.com',
'date_added' => '2013-02-25 10:56:38',
'date_modified' => '2013-02-25 10:56:38'
),
'ClientType' => array(
'id' => '2',
'name' => 'Constructors'
),
'CaseStudy' => array(
(int) 0 => array(
'id' => '23',
'client_id' => '47',
'sector_id' => '1',
'name' => 'Shoreham Academy',
'header_image' => '1365088787_thumbnail_1365088787_header copy.jpg',
'main_contractor' => '47',
'architect' => 'Architecture PLB',
'project_value' => '565000',
'scope_of_works' => '<table></table>',
'text' => '<p><</p>',
'type' => 'flooring',
'date_added' => '2013-04-04 11:19:47',
'date_modified' => '2013-04-04 11:19:47'
)
),
'Job' => array(
(int) 0 => array(
'id' => '1',
'client_id' => '47',
'sector_id' => '2',
'project' => 'Shoreham Academy (Project not case study)',
'date' => '2012-10-19',
'cost' => '£416k',
'quantity_of_flooring' => '7000m',
'date_added' => '2013-08-06 21:46:59',
'date_modified' => '2013-08-06 21:47:01'
)
)
)
Notice the above $clients array doesn’t have any disciplines data from the discipline_jobs table but the SQL output is which runs successfully:
SELECT `Client`.`id`, `Client`.`type_id`, `Client`.`name`, `Client`.`logo`, `Client`.`website_url`, `Client`.`date_added`, `Client`.`date_modified`, `ClientType`.`id`, `ClientType`.`name`
FROM `ar_flooring`.`clients` AS `Client`
LEFT JOIN `ar_flooring`.`case_studies` AS `CaseStudy` ON (`CaseStudy`.`client_id` = `Client`.`id`)
LEFT JOIN `ar_flooring`.`jobs` AS `Job` ON (`Job`.`client_id` = `Client`.`id`)
LEFT JOIN `ar_flooring`.`sectors` AS `Sector` ON (`Job`.`sector_id` = `Sector`.`id`)
LEFT JOIN `ar_flooring`.`disciplines_jobs` AS `DisciplinesJobs` ON (`Job`.`id` = `DisciplinesJobs`.`job_id`) LEFT JOIN `ar_flooring`.`disciplines` AS `Discipline` ON (`DisciplinesJobs`.`discipline_id` = `Discipline`.`id`)
LEFT JOIN `ar_flooring`.`client_types` AS `ClientType` ON (`Client`.`type_id` = `ClientType`.`id`)
WHERE `Client`.`id` = 47
I don’t have a discipline model or controller. I don’t have a jobs model or controller because I’m trying to do everything with the clients model and controller – is this correct?
Does anyone know why this is happening?
Hope this makes sense.
Cheers!
It appears that you want to run a
$this->Client->find('all')
at your ClientsController.
In that find all, you want to retrieve a list of Clients, the ClientType each Client belongsTo, the CaseStudy list each Client has, the Job list each Client has, and the Discipline each Job hasAndBelongsTo.
First, as a practitioner of CakePHP, I almost always avoid using the habtm relationship.
The reason is that it is almost always the case that I have extra fields in the join table. In this case, jobs_disciplines is likely to have more than just job_id and discipline_id. Maybe it will also have another field called status.
What I would do is that I will bake the JobsDiscipline model and the Discipline Model.
JobsDiscipline will belong to both Discipline and Job.
Both Discipline and Job will have many JobsDiscipline.
After this, the query to construct is basically just
$this->Client->find('all', array(
'contain' => array('ClientType', 'CaseStudy', 'Job' => array('JobsDiscipline'=>array('Discipline')))
));
This should work even if you set the recursive at your AppModel to be -1.
If this does not work, let me know again.
As the title says, I'm having troubles with joins in my find-query, with errors appearing like:
preg_match() expects parameter 2 to be string, array given [CORE/cake/libs/model/behaviors/containable.php, line 301]
I've tried adding joins to the array over there, but it didn't change anything.
These are the options I'm passing to the find method.
$options = array(
'contain' => array(
'Answer' => array(
'conditions' => array('Answer.type' => 'answer'),
'joins' => array(
$this->votesJoin()
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'joins' => array(
$this->votesJoin()
)
)
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'joins' => array(
$this->votesJoin()
)
),
'User',
'Tag' => array()
),
'joins' => array(
$this->votesJoin()
),
'conditions' => array(
'Question.id' => $id
)
);
return $this->find('first', $options);
with votesJoin() returning the following array.
(
[table] => (SELECT Vote.node_id, SUM(value) as total FROM votes AS `Vote` WHERE 1 = 1 GROUP BY `Vote`.`node_id` )
[alias] => Vote
[conditions] => Vote.node_id = Question.id
)
What I'm trying to do:
Each user can up/downvote a node (question/answer/comment). With the join I'm trying to add the sum of those votes.
database http://github.com/navale/QA/wiki/img/datamodel.png
You should use "joins" only for things that can't be done with Cake model relationship and Containable. I don't know the details of your database, but I think the find operation can be simplified. Why don't you post the schema for these tables on here?
try this:
$options = array(
'contain' => array(
'Answer' => array(
'conditions' => array('Answer.type' => 'answer'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
)
)
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
)
),
'User',
'Tag' => array()
),
'conditions' => array(
'Question.id' => $id
)
);
You get the Vote sum value for each answer, comment, and comment for answer. (You might need to add 'hasMany' Vote in the Node model if you haven't done that yet)
If instead you want to get one single total sum of Vote for the question, then I'd suggest:
get the list of the answers and comments of the question:
$lvl1 = find('list','fields'=>array('id'),'conditions'=>array('Node.parent_id'=>$id))
then get list of the comments of the answers
$lvl2 = find('list','fields'=>array('id'),'conditions'=>array('Node.parent_id'=>$lvl1))
then just combine the 2 array then do a sum over that.