cakephp and SQL_CALC_FOUND_ROWS - php

I am trying to add the SQL_CALC_FOUND_ROWS into a query (Please note this isn't for pagination)
please note I am trying to add this to a cakePHP query the code I currently have is below:
return $this->find('all', array(
'conditions' => $conditions,
'fields'=>array('SQL_CALC_FOUND_ROWS','Category.*','COUNT(`Entity`.`id`) as `entity_count`'),
'joins' => array('LEFT JOIN `entities` AS Entity ON `Entity`.`category_id` = `Category`.`id`'),
'group' => '`Category`.`id`',
'order' => $sort,
'limit'=>$params['limit'],
'offset'=>$params['start'],
'contain' => array('Domain' => array('fields' => array('title')))
));
Note the 'fields'=>array('SQL_CALC_FOUND_ROWS',' this obviously doesn't work as It tries to apply the SQL_CALC_FOUND_ROWS to the table e.g. SELECTCategory.SQL_CALC_FOUND_ROWS,
Is there anyway of doing this? Any help would be greatly appreciated, thanks.

You may want to look at cakephp paginate using mysql SQL_CALC_FOUND_ROWS. The person had similar syntax as you have and it worked for him.
If that doesn't help you can always use $this->find('count', $params); (http://book.cakephp.org/view/1020/find-count) or $this->query('YOUR SQL QUERY HERE'); (http://book.cakephp.org/view/1027/query).
Besides that you should not use 'joins' together with 'contain'. According to the documentation that "could lead to some SQL errors (duplicate tables), so you need to use the joins method as an alternative for Containable".

Maybe you can make your field parameter as below:
'fields'=>array('SQL_CALC_FOUND_ROWS *','COUNT(`Entity`.`id`) as `entity_count`')

This is a horrible, horrible hack to get an unescaped SQL_CALC_FOUND_ROWS into the query, but it works:
$categories = $this->Category->find('all', array(
'fields' => array('SQL_CALC_FOUND_ROWS 0.0 AS dummy_field,1', 'Category.*', ...),
'limit' => 42,
...
));
$totalCategories = $this->Category->query('SELECT FOUND_ROWS() as `total_categories`');
All credit goes to "Kani" from http://mogura.in/blog/2011/06/17/cakephp-1-3-sql_calc_found_rows.

I found a way to realize it with cake built in functions.
$dbo = $this->User->getDataSource();
//buildStatement() creates a Standard SQL Statement
$subQuery = $dbo->buildStatement(
array(
'fields' => $fields,
'table' => $dbo->fullTableName($this->User),
'alias' => 'User',
'limit' => null,
'offset' => null,
'joins' => array(),
'conditions' => $conditions,
'order' => null,
'group' => null
),
$this->User
);
//Add the SQL_CALC_FOUND_ROWS part
$subQuery = str_replace('SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $subQuery);
$Users = $this->User->query($subQuery);
//Get FOUND ROWS
$foundRows = $this->User->query("SELECT FOUND_ROWS()");
$count = intval($foundRows[0][0]['FOUND_ROWS()']);

Related

Group By within contain cakephp

Hellow , I want to use group by within contain in cakephp. In the following case i want to take only distinct organization within organizationUser array..
$options = array(
'conditions' => array('User.' .$this->User->primaryKey => $userId),
'contain' => array(
'OrganizationUser'=>array(
'conditions'=>['status'=>3],
'group'=> array( 'OrganizationUser.organization_id')),
'OrganizationUser.Organization',
'OrganizationUser.Organization.Noticeboard',
'OrganizationUser.Organization.Newsboard',
'OrganizationUser.Organization.Noticeboard.Branch',
),
'page'=>$page,
'limit'=>$limit
);
$org = $this->User->find('all', $options);
But this is throwing error like 'Column not found', and 'conditions' is working fine within OrganizationUser but 'group' not working.I am using cakephp version 2.Thanks in advance.
I don't think cakephp 2+ offer something like you are doing to make field distinct within contain. So better to try following..
Replace :
'group'=> array( 'OrganizationUser.organization_id')
By
'fields'=> array( 'DISTINCT OrganizationUser.organization_id')
that might work for you.
In my case, I'm using cake version 4+.
In my table the relation I've made
$this->belongsTo('CreatedOperator')
->setClassName(USERS_PLUGIN . '.Users')
->setForeignKey('created_by')
->setJoinType('INNER')
;
and I'm calling the relation like
$query
->disableHydration()
->select([
'CreatedOperator.id',
'CreatedOperator.first_name',
'CreatedOperator.last_name',
'full_name' => $query->func()->concat(['CreatedOperator.first_name' => 'identifier',' ','CreatedOperator.last_name' => 'identifier']),
'total' => $query->func()->count('CreatedOperator.id')])
->contain(['CreatedOperator'])
->group(['CreatedOperator.id'])
;
return $query->toList();

Adding query clause to cakephp find operation

I have a standard find query on my user model that looks as followed:
$user = $this->User->find( 'all', array( 'conditions' => array( 'User.id' => $user_id ) ) );
I also have some extensions I would like to make to the where clause of this function call like so:
$query_extension = 'AND users.id IN ( complex join between a few tables )';
I want to add this complex WHERE clause on to the end of that user find condition, but I'm not sure how to do this. I'm looking into the ConnectionManager class, but I'm still not sure how to append this extra clause:
http://api.cakephp.org/2.5/class-ConnectionManager.html#_getDataSource
Check sample code I used, you can also set dynamic content like joins Will have array What you built Or
will have blank array.
$courseNames = $this->UsersCourse->Course->find('list',
array('fields'=> array('Course.course_name'),
'order'=> array('Course.course_name'),
'conditions'=>array('Course.is_active'=>1 ,
'CourseCategory.is_active'=>1
),
"joins" => array(
array(
"table" => "course_categories",
"alias" => "CourseCategory",
"type" => "INNER",
"conditions" => array(
"CourseCategory.id = Course.course_category_id"
)
)
)
)
);
This is how this operation can be preformed. Notice that the clause being added to the main query includes an AND This means that it is actually part of the where clause and can be added as a string to a find as followed.
// Note here that including the $complex_subselect_string as an un-keyed term in the conditions automatically ANDs this extra query clause
$complete_user_list = $this->find( 'all', array( 'conditions' => array( $complex_subselect_string ), 'limit' => $limit, 'contain' => array() ) );

CakePHP paginate and order by

It feels like I've tried everything so I now come to you.
I am trying to order my data but it isn't going so well, kinda new to Cake.
This is my code:
$this->set('threads', $this->paginate('Thread', array(
'Thread.hidden' => 0,
'Thread.forum_category_id' => $id,
'order' => array(
'Thread.created' => 'desc'
)
)));
It generates an SQL error and this is the last and interesting part:
AND `Thread`.`forum_category_id` = 12 AND order = ('desc') ORDER BY `Thread`.`created` ASC LIMIT 25
How can I fix this? The field created obviously exists in the database. :/
You need to pass in the conditions key when using multiple filters (i.e. order, limit...). If you just specify conditions, you can pass it as second parameter directly.
This should do it:
$this->set('threads', $this->paginate('Thread', array(
'conditions' => array(
'Thread.hidden' => 0,
'Thread.forum_category_id' => $id
),
'order' => array(
'Thread.created' => 'desc'
)
)));
or perhaps a little clearer:
$this->paginate['order'] = array('Thread.created' => 'desc');
$this->paginate['conditions'] = array('Thread.hidden' => 0, ...);
$this->paginate['limit'] = 10;
$this->set('threads', $this->paginate());
if you get an error, add public $paginate; to the top of your controller.
Try
$this->set('threads', $this->paginate('Thread', array(
'Thread.hidden' => 0,
'Thread.forum_category_id' => $id
),
array(
'Thread.created' => 'desc'
)
));
I'm not a Cake master, just a guess.
EDIT. Yes, thats right. Cake manual excerpt:
Control which fields used for ordering
...
$this->paginate('Post', array(), array('title', 'slug'));
So order is the third argument.
try
$all_threads = $this->Threads->find('all',
array(
'order' => 'Threads.created'
)
);
$saida = $this->paginate($all_threads,[
'conditions' => ['Threads.hidden' => 0]
]);
There are a few things to take note of in paginate with order. For Cake 3.x, you need :
1) Ensure you have included the fields in 'sortWhitelist'
$this->paginate = [
'sortWhitelist' => [
'hidden', 'forum_category_id',
],
];
2) for 'order', if you put it under $this->paginate, you will not be able to sort that field in the view. So it is better to put the 'order' in the query (sadly this wasn't stated in the docs)
$query = $this->Thread->find()
->where( ['Thread.hidden' => 0, 'Thread.forum_category_id' => $id, ] )
->order( ['Thread.created' => 'desc'] );
$this->set('threads', $this->paginate($query)

CakePHP nesting two select queries

I have two tables in cakePHP.
competencies
------------
id
name
competenceRatings
-----------------
id
competence_id
user_id
rating
I need a way to write the following query in the cake way:
SELECT * FROM competencies WHERE id NOT IN (SELECT competence_id FROM competence_ratings WHERE employee_id = $userId)
Someone please help me!!
What i did before going to this subquery method:
I tried competencies->hasMany->competenceRatings, competenceRatings->belongsTo->competencies relations.
$competencies = $this->Competence->CompetenceRating->find('all',array('CompetenceRating.user_id' => $userId,'CompetenceRating.competence_id !=' => 'Competence.id'));
I want to be able to get the names of competencies for which a user have NOT made any ratings into competenceRatings table. i.e., I need list of names from competencies table for which there are no entries in comptenceRatings table(for given user_id).
EDIT
I tried table join also:
$options['joins'] = array(
array(
'table' => 'competence_ratings',
'alias' => 'CompetenceRating',
'type' => 'LEFT OUTER',
'conditions' => array(
'Competence.id = CompetenceRating.competence_id'
)
)
);
$options['conditions'] = array( 'CompetenceRating.employee_id' => $employee['Employee']['id'] );
$competencies = $this->Competence->find('all',$options);
you would probably have to use a subquery():
$subqueryOptions = array('fields' => array('competence_id'), 'conditions' => array('employee_id'=>$user_id));
$subquery = $this->Competence->CompetenceRating->subquery('all', $subqueryOptions);
$res = $this->Competence->CompetenceRating->find('all', array(
'conditions' => array('id NOT IN '. $subquery)
));
the source for subquery is here:
https://github.com/dereuromark/tools/blob/2.0/Lib/MyModel.php#L405
you need to put this in your AppModel.php
BUT I think the subquery is not necessary. You can probably make a single and easy query out of it:
$this->Competence->CompetenceRating->find('all', array(
'group' => 'competence_id',
'conditions' => array('NOT' => 'employee_id'=>$user_id)),
'contain' => array('Competence')
));
dont forget to include Competence via "contain" if you have recursive set to -1.

Cakephp returns empty but sql query has results

I have been fighting with this code:
function getNextActionFObyBalance($when) {
$theQuery = $this->find('first', array(
'fields' => array(
'Contract.id',
'Contract.start_balance'
),
'conditions' => array(
'AND' => array(
'Status.next_action_by' => 'frontoffice',
'Status.status_type' => 'active',
'Status.visibility' => 'frontoffice',
'OR' => array(
'Contract.next_action_on' => null,
'Contract.next_action_on <=' => $when
)
)),
'order' => 'Contract.start_balance DESC',
'recursive' => 0,
));
return $theQuery;
}
I have enabled logging on the MySQL server at this is what the server indicates that CakePHP is requesting:
SELECT `Contract`.`id`, `Contract`.`start_balance` FROM `contracts` AS `Contract` LEFT JOIN `statuses` AS `Status` ON (`Contract`.`status_id` = `Status`.`id`) LEFT JOIN `users` AS `User` ON (`Contract`.`user_id` = `User`.`id`) WHERE ((`Status`.`next_action_by` = 'frontoffice') AND (`Status`.`status_type` = 'active') AND (`Status`.`visibility` = 'frontoffice') AND (((`Contract`.`next_action_on` IS NULL) OR (`Contract`.`next_action_on` <= '2010-09-13 10:13:04')))) ORDER BY `Contract`.`start_balance` DESC LIMIT 1
if I use that in the phpmyadmin tool, I get exactly what I was expecting 1 record with two fields. BUT CakePHP just gives me an empty result set.
Can anyone enlighten me?
PS the code was working but I can figure out what changed!
The problem was with a stub to do some post processing afterFind. The problem is that I have completely forgotten to return $results;
I found the error by doing a step by step debugging down the find method in model.php. Found that the after find was called at some point and went to check my afterFind.
Took my about 4 hours for a simple error but I am learning!
Presumably this method is defined in models/contract.php?
The recursive = 0 statement looks a bit suspect to me. Are the models correctly related in their respective model files?
Have you tried loadModel in case the associations aren't working properly?
It would be useful to see the relationship definitions from the respective models.
--EDIT--
I've formatted the code from your comment here as I can't edit your OP
var $belongsTo = array(
'Status' => array(
'className' => 'Status',
'foreignKey' => 'status_id',
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
)
);
var $hasMany = array(
'Transaction' => array(
'className' => 'Transaction',
'foreignKey' => 'contract_id',
'dependent' => false,
)
);

Categories