Possible to mix where() clause - yii2

I have the following code (it's shortened otherwise it would be too long to show):
Feedback::find()
->where(['feedback.fg_id' => $this->id])
->orWhere(['feedback.fg_id' => $this->id, 'feedback.closed_time' => NULL, '<' => ['feedback.survey_end_date', new Expression('NOW()')]])
->all();
This code do not work, because of the last statement in orWhere()
My question is now: How can I combine the syntax inside orWhere? It's because I need all the statements inside the or block.
The ambivalent sql code should be:
SELECT * FROM feedback WHERE
(feedback.fg_id = 1) OR (feedback.fg_id = 1 AND feedback.closed_time IS NULL AND feedback.survey_end_date < NOW())

Yes you can combine you condition as ...
Feedback::find()
->where(['feedback.fg_id' => $this->id])
->orWhere(['AND',
['feedback.fg_id' => $this->id],
['feedback.closed_time' => NULL],
['<' , 'feedback.survey_end_date', new Expression('NOW()')]
]);

Related

Not equal condition with findAll()

I am trying to pull record from a table using the following code
$userId = Yii::$app->user->id;
$lists = PromoLists::findAll(['user_id' => $userId, 'list_type' => 'custom']);
which outputs a query like below
select * from promo_lists where user_id ='$userId' and list_type='custom'
But i am unable to find any thing in the documentation that would help me achieve it with the following condition.
select * from promo_lists where user_id ='$userId' and list_type='custom' and status!='deleted'
as the status is an ENUM field and there are 4 different status
'active','pending','rejected','deleted'
currently i used the following approach
PromoLists::findAll(['user_id' => $userId, 'list_type' => 'custom', 'status'=>['active','pending','rejected']]);
which outputsthe following query
select * from promo_lists where user_id ='$userId' and list_type='custom' and status in ('active','pending','rejected')
which somehow achieves the same thing but this query would need to be edited every time when there is a new status type added to the table column status.
i know i can do this by using PromoLists::find()->where()->andWhere()->all()
but how to check with != / <> operator using findAll().
Simply like this:
PromoLists::find()->where(['and',
[
'user_id' => $userId,
'list_type' => 'custom',
],
['<>', 'status', 'deleted'],
])->all();
Using operator format in condition
http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html#operator-format
PromoLists::find()
->andWhere([
'user_id' => $userId,
'list_type' => 'custom',
['!=', 'status', 'deleted']
])
->all();

How to use AND and OR in MySQL query?

I want to get those records whose date_last_copied field is empty or less than the current date. I tried this, but it did not give me the desired result:
$tasks = $this->Control->query("
SELECT *
FROM
`controls`
WHERE
`owner_id` = ".$user_id."
AND `control_frequency_id` = ".CONTROL_FREQUENCY_DAILY."
OR `date_last_copied` = ''
OR `date_last_copied` < ". strtotime(Date('Y-m-d'))."
");
Current query looks something like this, I think. That is, find the records with the correct owner_id and frequency_id, where the date_last_copied is null or less than a certain date. Is that logic correct?
SELECT *
FROM controls
WHERE owner_id = ::owner_id::
AND control_frequency_id = ::frequency_id::
AND (
date_last_copied IS NULL
OR date_last_copied < ::date::
)
But we should really be using the CakePHP query builder, rather than running raw SQL. This article gives some details. If I were to take a stab at a solution, we'd want something like the following. But we ideally want someone from the CakePHP community to chime in here. EDIT: Note that this seems to be for CakePHP 3.0, only.
// Build the query
$query = TableRegistry::get('controls')
->find()
->where([
'owner_id' => $ownerId,
'control_frequency_id' => $frequencyId,
'OR' => [
['date_last_copied IS' => null],
['date_last_copied <' => $date]
]
]);
// To make sure the query is what we wanted
debug($query);
// To get all the results of the query
foreach ($query as $control) {
. . .
}
I'm suggesting this, rather than the raw SQL string you have above, because:
We can now leverage the ORM model of CakePHP.
We don't have to worry about SQL injection, which you're currently vulnerable to.
EDIT: OK, this is a guess at the syntax applicable for CakePHP 2.0... YMMV
$controls = $this->controls->find('all', [
'conditions' => [
'owner_id' => $ownerId,
'control_frequency_id' => $frequencyId,
'OR' => [
['date_last_copied IS' => null],
['date_last_copied <' => $date]
]
]
];
Otherwise, we just use the raw query as a prepared statement:
$result = $this->getDataSource()->fetchAll("
SELECT *
FROM controls
WHERE owner_id = ?
AND control_frequency_id = ?
AND (
date_last_copied IS NULL
OR date_last_copied < ?
)",
[$ownerId, $frequencyId, $date]
);
Not sure about your whole logic but your final query statement should be something like:
SELECT * FROM `controls` WHERE (`owner_id` = <some owner_id>)
AND (`control_frequency_id` = <some id value>)
AND (`date_last_copied` = '' OR
`date_last_copied` IS NULL OR
`date_last_copied` < CURDATE() )
Use parentheses carefully to match your logic.
Always specify the version of cakePHP you are using for your App.
This query should work fine in CakePHP 3.0 for SQL AND and OR.
$query = ModelName>find()
->where(['colunm' => 'condition'])
->orWhere(['colunm' => 'otherCondition'])
->andWhere([
'colunm' => 'anotherContion',
'view_count >' => 10
])
->orWhere(['colunm' => 'moreConditions']);

ISNULL not working in order in CAKEPHP

I am using LEFT join and as a result getting null values for is_read column in Messages table. I want to keep the nulls at bottom when ordering. I'm using this in paginator. Following is the my code for doing the same:
$this->Paginator->settings = array(
'fields' => array('User.*'),
'joins' => array(
array('table' => 'messages',
'alias' => 'Message',
'type' => 'LEFT',
'conditions' => array(
'User.id = Message.user_from_id'
)
),
),
'limit' => 20,
'group' => array('User.id'),
'order' => array('ISNULL(Message.is_read)' => 'asc','Message.is_read' => 'asc', 'Message.created' => 'asc'),
);
The query Cakephp generates for this is as follows:
SELECT `User`.*, (CONCAT(`User`.`first_name`, ' ', `User`.`last_name`)) AS `User__full_name` FROM `srs_development`.`users` AS `User` LEFT JOIN `srs_development`.`messages` AS `Message` ON (`User`.`id` = `Message`.`user_from_id`) WHERE 1 = 1 GROUP BY `User`.`id` ORDER BY `Message`.`is_read` asc, `Message`.`created` asc LIMIT 20
ISNULL function is getting omitted in the final query.
Also please suggest a way to accomplish this without using custom pagination() if possible.
Aggregate functions didn't work in the order clause when using Pagination component. I tried declaring a virtual field in Message model as:
public $virtualFields = array(
'sortme' => "ISNULL(Message.is_read)",
);
So finally, declaring it as virtual field in the Message model did the job.
Thank you everyone.

MySQL Equivalent of NULLS LAST deployed through CakePHP

This is what I'm working with:
$this->paginate = array(
'order' => array('Job.deadline' => 'ASC'),
'limit' => 500
);
Not all Jobs have a deadline, and at present, with this order, the NULLS appear above those with deadlines, so in effect I have this:
NULL
NULL
NULL
28/06/2012
29/06/2012
04/07/2012
I'm desperate to get this for clarity:
28/06/2012
29/06/2012
04/07/2012
NULL
NULL
NULL
Is there a way I can achieve this through the paginate 'order' options in CakePHP.
Has anyone managed this at all?
Untested, but something like this should do:
'order' => array('ISNULL(Job.deadline)' => 'asc', 'Job.deadline' => 'asc')
Basically this solution in Cake syntax.

MySQL short but highly complex results problem w/ two variations of search

My search is working as expected, but I have an added factor which I cannot sort out.
This is the breakdown of what I need to achieve in one query:
$ApplicantAge BETWEEN Age.Min_Age AND Age.Max_age.
NOT 'PlanDetail.company_id' => '27'
$PsSpouseAge BETWEEN Age.Min_Age AND Age.Max_age.
WHERE 'PlanDetail.company_id' => '27'
So as you can see with $ApplicantAge matches, I need to get all records that do not have company_id = 27, and then where $PsSpouseAge matches, only get records with the company_id = 27.
So all records merge together... So essentially there are two queries on the same data at the same time and then all records merged into the result.
As I said, my search is working fine, but I need to integrate this in and sort out the MySQL which I am having 0 luck with.
I've tried many AND / OR AND AND options, and the closest I can get is overwriting the first part of the query with the second part which of course does no good.
What I am trying now:
'conditions' => array(
'AND' => array(
array($ApplicantAge . ' BETWEEN Age.Min_Age AND Age.Max_age'),
'AND' => array('Zips.title' => $Zip),
'AND' => array('Applicant.amount' => array($comboType, $memberCount)),
'NOT' => array('PlanDetail.company_id' => '27'),
array('OR' =>
array($PsSpouseAge . ' BETWEEN Age.Min_Age AND Age.Max_age'),
'AND' => array('Zips.title' => $Zip),
'AND' => array('Applicant.amount' => array($comboType, $memberCount)),
'AND' => array('PlanDetail.company_id' => '27'))
)),
And my SQL output is so:
WHERE ((19 BETWEEN `Age`.`Min_Age` AND `Age`.`Max_age`) AND (`Applicant`.`amount` IN ('as', '2')) AND (NOT (`PlanDetail`.`company_id` = 27)) AND (((21 BETWEEN `Age`.`Min_Age` AND `Age`.`Max_age`) AND (`PlanDetail`.`company_id` = 27))))
It's a bit difficult to understand what you're looking for, but perhaps you want this?
([$ApplicantAge] BETWEEN Age.Min_Age AND Age.Max_age AND PlanDetail.company_id != 27) OR ([$PsSpouseAge] BETWEEN Age.Min_Age AND Age.Max_age AND PlanDetail.company_id = 27)
The simple answer to your question is just use UNION something like:
Select 'Applicant', Age
Where $ApplicantAge BETWEEN Age.Min_Age AND Age.Max_age. NOT 'PlanDetail.company_id' <> '27'
Union
Select 'Spouse', Age
Where $PsSpouseAge BETWEEN Age.Min_Age AND Age.Max_age. WHERE 'PlanDetail.company_id' = '27'
However I think we are missing some important details on the relationship between applicant and Spouse.