CakePHP 3.x case statement on order - cakephp

I am trying to use a CASE statement on the order of a MySQL statement in CakePHP 3.x app. The simple select is as follows:
$articles = $this->Articles->find()
->where($conditions)
->order(function ($exp, $q) {
return $exp->addCase(
[
$q->newExpr()->gt('Articles.modified', (new Time())->subDays(365)) // article has been updated in the last x days
],
['priority'], # values matching conditions
['string'] # type of each value
);
})
->limit(15)
->all();
The following SQL is generated:
SELECT `Articles`.`id` AS `Articles__id`, ....
FROM `articles` `Articles`
WHERE (`publish` < :c0 AND `Articles`.`publish` > :c1)
ORDER BY CASE WHEN `Articles`.`modified` > :c2 THEN :param3 END LIMIT 15
The case statement is not correct because it is missing the DESC order which should come after the 'END' - see this fiddle:
http://sqlfiddle.com/#!9/8df161/5
I'm not sure if this is a limitation with how CakePHP handles CASE?
Further I require a second order after the case statement to order by 'publish' desc.

Expressions passed to Query::order() must generate everything required by the ORDER BY clause, including the direction keyword.
If the expression that you're using doesn't support that, then you can use Query::oderAsc() or Query::oderDesc(), which will append the respective direction keyword accordingly.
$query = $this->Articles->find();
$query
->where($conditions)
->orderDesc(
$query->newExpr()->addCase(/* ... */)
)
// ...
See also
Cookbook > Database Access & ORM > Query Builder > Selecting Data

Related

CakePHP query: where condition with calculated fields

I have the followwing problem with creating a query in CakePHP 3:
For the Entity Recordings I want to calculate the age as the difference between the year of the two dates "collection date" and "birthdate"
$query = $this->Recordings->find()
->select(['age' => 'Year(Recordings.collection_date) - Year(Athletes.birthdate)'])
->select($this->Recordings)
->select($this->Recordings->Athletes);
When I try to filter with the following where clause
$query = $query->where(['Year(Recordings.collection_date) - Year(Athletes.birthdate) =' => $age]);
The SQL Code which is created has the Athletes beeing modified to lowercase.
WHERE
( Year(Recordings.collection_date) - year(athletes.birthdate) = '17' )
How do I have to write the where clause correctly? I havent found a way to use Identifiers or query functions like
$year = $q->func()->year(['Athletes.birthdate' => 'identifier']);
to bild the conditions.
Any hints are welcome.

CakePHP doing auto lower casing in Query

Below is a part of my query which filters based on like operator in a specific format
$baseQuery->where(
["DATE_FORMAT(`CallRequests`.`updated_date`, '%m/%d/%Y %l:%i %p') like" => $string . '%'],
['updated_date' => 'string']
);
But for some reason CakePHP is auto-lowercasing %Y before query execution.
Below is the related part in the query from the debug dump:
DATE_FORMAT(`CallRequests`.`created_date`, '%m/%d/%y %l:%i %p') like :c0'
I am aware that this can be avoided using raw queries.
But is there a workaround for this behaviour without using raw queries?
You're not supposed to put SQL snippets in the key of a key => value condition. The key side is ment to hold an identifier, and optionally an operator, separated by whitespace. When processed, the right hand side of the whitespace, ie the operator part, is being lowercased, hence what you're experiencing.
You're already using raw SQL, so it's not a big leap to going a step further and use a single value condition to provide a complete raw SQL snippet, and use bindings to inject your value:
$baseQuery
->where([
"DATE_FORMAT(`CallRequests`.`updated_date`, '%m/%d/%Y %l:%i %p') like :date",
])
->bind(':date', $string . '%', 'string');
Alternatively you can use expressions, both for the DATE_FORMAT() function and the LIKE comparison:
$baseQuery->where(
function (
\Cake\Database\Expression\QueryExpression $exp,
\Cake\ORM\Query $query
) use ($string) {
$dateFormat = $query->func()->DATE_FORMAT([
$query->identifier('CallRequests.updated_date'),
'%m/%d/%Y %l:%i %p'
]);
return [
$exp->like($dateFormat, $string . '%')
];
}
);
See also
Cookbook > Database Access & ORM > Query Builder > Binding Values
Cookbook > Database Access & ORM > Query Builder > Using SQL Functions
Cookbook > Database Access & ORM > Query Builder > Advanced Conditions

CakePHP 3.x Contains GroupBy

I am attempting to do a GroupBy on an associated table via contains -> conditions, however I am getting the following error...
Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'group = 'BrandUsers.user_id' AND BrandUsers.brand_id in (1,2,3,4,5,6))' at line 1
with the following query
SELECT
BrandUsers.id AS `BrandUsers__id`,
BrandUsers.user_id AS `BrandUsers__user_id`,
BrandUsers.brand_id AS `BrandUsers__brand_id`,
Users.id AS `Users__id`,
Users.username AS `Users__username`,
Users.email AS `Users__email`,
Users.password AS `Users__password`,
Users.first_name AS `Users__first_name`,
Users.last_name AS `Users__last_name`,
Users.token AS `Users__token`,
Users.token_expires AS `Users__token_expires`,
Users.api_token AS `Users__api_token`,
Users.activation_date AS `Users__activation_date`,
Users.secret AS `Users__secret`,
Users.secret_verified AS `Users__secret_verified`,
Users.tos_date AS `Users__tos_date`,
Users.active AS `Users__active`,
Users.is_superuser AS `Users__is_superuser`,
Users.role AS `Users__role`,
Users.created AS `Users__created`,
Users.modified AS `Users__modified`
FROM
brand_users BrandUsers
INNER JOIN
users Users
ON Users.id =
(
BrandUsers.user_id
)
WHERE
(
group = :c0
AND BrandUsers.brand_id in
(
:c1,
:c2,
:c3,
:c4,
:c5,
:c6
)
)
I have taken a look at the following links, but the above error persists
Group By within contain cakephp
cakephp GROUP BY within containable
Here is my code
$this->paginate = [
'contain' => [
'BrandUsers' => [
'conditions' => [
'group' => 'BrandUsers.user_id'
]
],
'BrandUsers.Users'
]
];
$brands = $this->paginate(
$this->Brands
->find('all')
->where(['Brands.user_id' => $this->Auth->user('id')])
);
As mentioned in the answers/comments to the questions that you've linked, there is no group option for containments, that's true for CakePHP 2.x as well as 3.x, and if there was such an option you would have placed it wrong, as you've nested it inside the conditions option, hence it is being compiled into the queries WHERE clause.
If you need to modify the query used for obtaining containments on the fly, then you can for example pass a callable as known from other query builder methods:
'BrandUsers' => function (\Cake\ORM\Query $query) {
return $query->group('BrandUsers.user_id');
}
or use the finder option to point to a finder that modifies the passed query accordingly:
'BrandUsers' => [
'finder' => 'groupedByUser'
]
It should be noted that grouping only works for HasMany and BelongsToMany associations, as they are not being joined into the main/parent query.
See also
Cookbook > Database Access & ORM > Query Builder > Passing Conditions to Contain
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Custom Finder Methods

CAKEPHP 3 : Select * and sum() in one statement

I am trying to fetch result from database table with SELECT * and SUM() function.
The sql query is :
SELECT * ,SUM(msg_send) AS msg_send FROM msg_campaigns
Now how to write this query in cakephp3.
I am trying this :
$this->loadModel('MsgCampaigns');
$SmsDetails = $this->MsgCampaigns->find('all',[
'conditions'=>['YEAR(date_time)'=>date('Y')],
'fields'=>['msg_send'=>'SUM(msg_send)','msg_failed'=>'SUM(msg_failed)']
]);
But I do not know how to use SELECT * . Please help
Check the CakePHP Query Builder on how to use SQL functions and how to select all fields.
$query = $this->MsgCampaigns->find();
$query
->select([
'sum_msg_send' => $query->func()->sum('msg_send'),
'sum_msg_failed' => $query->func()->sum('msg_failed')
])
// passing the table instance to the `select` function, selects all fields
->select($this->MsgCampaigns);
$query->execute();

Pagination of query ignoring the LIMIT clause in CakePHP 3.x

I am trying to create a query that would only return 2 results, and by following the documentation I get the query to run, however the limit is still set to 20 by default.
Here is how the query is built:
$upcomingMeetings = $this->Meetings->find('all')
->where(['Meetings.user_id' => $this->Auth->User('user_id')])
->andWhere(["Meetings.date >= " => date('Y-m-d') ])
->order(['Meetings.date' => 'ASC'])
->limit(2);
The result is being passed to the view like following:
$this->set('upcomingMeetings', $this->paginate($upcomingMeetings));
Here is the query that is being run on the database:
SELECT
Meetings.id AS `Meetings__id`,
Meetings.date AS `Meetings__date`,
Meetings.user_id AS `Meetings__user_id`,
FROM
meetings Meetings
WHERE
(
Meetings.group_id = 7
AND Meetings.date >= '2016-01-14'
)
ORDER BY
Meetings.date ASC
LIMIT
20 OFFSET 0
Any help or guidance is much appreciated.
When paginating a Query object, CakePHP will ignore the limit() clause and use the value defined in the $paginate configuration array instead.
This is what can be concluded after inspecting the source code.
Try adding the following to your controller:
public $paginate = [
'limit' => 2,
];

Resources