How to compare against fields that are `null` (empty)? - php

I have a table in a CakePHP 3 application called downloads which has a column called master. The field type is set to TINYINT(1)
I can find any records where downloads.master == 1 like this:
$query = $this->Downloads->find()->where(['master' => true]);
But Cake won't let me query for ones where downloads.master !== 1. None of these work, and all return an empty array/object when the query is executed:
$query = $this->Downloads->find()->where(['master' => false]);
$query = $this->Downloads->find()->where(['master' => 0]);
$query = $this->Downloads->find()->where(['master' => null]);
$query = $this->Downloads->find()->where(['master' => '']);
What do you use as the condition to make this possible? My thinking was that it should be false since that's the opposite to true, but as with most things in CakePHP 3 they like to make it more complicated than necessary...
I've examined the records in my table using phpMyAdmin and there are indeed both records where master == 1 and master == null so it's not a case of there's zero results to return.

A column being NULL is not the same as being 0 (ie false-ish from the point of view of the ORM in case of a boolean-ish column type). If you want to compare against NULL, then you must issue a query with IS NULL, which is a SQL/DBMS requirement, not a CakePHP requirement.
CakePHP however requires you to be specific about what you want to do, as passing null does not neccesarily have to mean that you want to compare against SQL NULL, depending on the context.
Long story short, use the IS operator:
where(['master IS' => null])
Similarly use IS NOT for a negated condition. You can also pass user input as the value, the ORM will test the value and convert the IS and IS NOT operators into = and != respectively in case a non-null value is being passed.
See also
Cookbook > Database Access & ORM > Query Builder > Automatic IS NULL Creation

Related

CakePHP 3 putting un-necessary parentheses in SQL causing error

CakePHP 3.7. Trying to use the ORM to write a query which contains a MySQL COALESCE condition.
Followed advice on CakePHP 3 - How to write COALESCE(...) in query builder? and ended up having to write it manually using newExpr() as this was the given solution.
The code I have is as follows:
$TblRegulatoryAlerts = TableRegistry::getTableLocator()->get('TblRegulatoryAlerts');
$subscribed_to = $TblRegulatoryAlerts->getUserRegulations($u_id, $o_id, false);
$query = $this->find()
->contain('Filters.Groups.Regulations')
->select(['id', 'date', 'comment', 'Filters.label', 'Filters.anchor', 'Groups.label']);
$query->select($query->newExpr('COALESCE((SELECT COUNT(*)
FROM revision_filters_substances
WHERE revision_filter_id = RevisionFilters.id), 0) AS count_substances'));
$query->where(['date >=' => $date_start, 'date <=' => $date_end, 'Regulations.id' => $regulation_id, 'Filters.id IN' => $subscribed_to]);
$query->enableHydration(false)->orderDesc('date');
This produces the following SQL (output of debug($query->sql()):
SELECT RevisionFilters.id AS `RevisionFilters__id`, RevisionFilters.date AS `RevisionFilters__date`, RevisionFilters.comment AS `RevisionFilters__comment`, Filters.label AS `Filters__label`, Filters.anchor AS `Filters__anchor`, Groups.label AS `Groups__label`, (COALESCE((SELECT COUNT(*) FROM revision_filters_substances WHERE revision_filter_id = RevisionFilters.id), 0) AS count_substances) FROM revision_filters RevisionFilters INNER JOIN dev_hub_subdb.filters Filters ON Filters.id = (RevisionFilters.filter_id) INNER JOIN dev_hub_subdb.groups Groups ON Groups.id = (Filters.group_id) INNER JOIN dev_hub_subdb.regulations Regulations ON Regulations.id = (Groups.regulation_id) WHERE ...
Unfortunately this doesn't execute because Cake is putting in un-necessary parentheses surrounding the COALESCE statement, which changes the SQL.
In the above code it generates:
(COALESCE((SELECT COUNT(*) FROM revision_filters_substances WHERE revision_filter_id = RevisionFilters.id), 0) AS count_substances)
Whereas it needs to omit the parentheses surrounding COALESCE so it's just:
COALESCE((SELECT COUNT(*) FROM revision_filters_substances WHERE revision_filter_id = RevisionFilters.id), 0) AS count_substances
Is this possible?
Don't specify the alias in the expression, instead specify it using the key => value syntax of the Query::select() method, like this:
$query->select([
'count_substances' => $query->newExpr('...')
]);
It would still wrap the expression in parentheses, but that would then be valid as it doesn't include the alias.
That being said, using the function builders coalesque() method should work fine, the problem described in the linked question can be fixed by using the key => value syntax too, where the value can specify the kind of the argument, like ['Nodes.value' => 'identifier'], without that it would bind the value as a string.
However there shouldn't be any such problem with your example, using the function builders coalesce() method should work fine.
$query->select([
'count_substances' => $query->func()->coalesce($countSubquery, 1, ['integer'])
]);
The type argument is kinda optional, it would work with most DBMS without it, but for maximum compatibility it should be specified so that the integer value is being bound properly, also it will automatically set the return type of the function (the casting type) to integer too.

Yii2 - Ignore where clause if variable is empty

I have yii2 query with conditional where clause. this is my query :
$query = new Query();
$query->select
(['pengeluaran.id'])
->from('surat_jalan, pengeluaran')
->where('surat_jalan.no_bus=:no_bus',['no_bus'=>$no_bus])
//this is my conditional query
->andWhere(['between', 'pengeluaran.tgl_pengeluaran', $awal, $akhir])
->andWhere('pengeluaran.nama_toko=:nama_toko',['nama_toko'=>$nama_toko])
->andWhere('pengeluaran.metode_pembayaran=:metode_pembayaran',['metode_pembayaran'=>$metode_pembayaran])
->andWhere('pengeluaran.waktu_pembayaran=:waktu_pembayaran',['waktu_pembayaran'=>$waktu_pembayaran])
//this is my conditional query
->andWhere('surat_jalan.id_surat_jalan=pengeluaran.id_surat_jalan');
$command = $query->createCommand();
$data_id_pengeluaran = $command->queryAll();
I read some question from others and what I found is for mysql table field :
->andWhere(['not', ['activated_at' => null]]);
what I want is if the variable is empty, the conditional where() clause is ignore. Is it possible in yii2??
The filterWhere is the perfect tool to achieve your goals.
For example,
// This will not appear in the finally statement if $val is empty.
$query->andFilterWhere(['activated_at' => $val])
Note: A value is considered empty if it is null, an empty array, an empty string or a string consisting of whitespaces only.

CakePHP 3.x: Query to exclude records in which a field can be either NULL or empty ('')

I am using Cakephp 3 and trying to get some rows/records from a table having a field as non empty.
For example:-
I have a areas table with a field named Area_block. I need to fetch records for those rows which have something inside Area_block. In simple words, I don't need empty or NULL Area_block rows.
Area_block is not a NULL field by default. So it remains empty/blank.
I have tried
$conditions = ['Area_Block IS NOT NULL'];
$conditions = ['NOT' => array('Area_Block' => '')];
$conditions = ['Area_Block <>' => ''];
$conditions = ['Area_Block !=' => ''];
but nothing works !!
My environment includes MSSQL server 2008 and PHP 5.6 on apache.
Please help !!
I understand your table holds records with Area_Block set both to NULL and '' (empty string).
Try the following:
$conditions = [['Area_Block IS NOT' => null], ['Area_Block IS NOT' => '']];
This should also work:
$conditions = ['Area_Block IS NOT' => [null,'']]; //haven't tested it
However, in order to reduce complexity, in the database you may want to set the Area_Block field either to default to NULL, or default to '' and prevent the field from accepting NULL values.
Working exclusively with NOT NULL values might be easier. If you alter the table to reflect this, you would only need to check for '' values:
$conditions = ['Area_Block IS NOT' => ''];
See Automatic IS NOT NULL Creation in the 3.x Cookbook.
I think the solution provided by #InigoFlores worked to some extend but was failing for some records.
Here's what I did to make it work perfectly.
$conditions = ['DATALENGTH(Area_Block) >' => 0];
I Hope it helps someone.

addBetweenCondition in YII

SO I want to find that if value x is exits between the values of 2 columns or not, For that i have run the query in phpmyadmin :
Normal Approch :-
SELECT * FROM `traits_versions` WHERE 16 BETWEEN `trait_value_lower` and `trait_value_upper` and `style_id` = 1
and it is giving me fine result.But when the same approach i want to find achieve in YII that it is not running and giving the sql error :
YII apprroch :-
$details = array();
$criteria = new CDbCriteria();
$criteria->addCondition('style_id='.$style_id);
$criteria->addCondition('version='.$version);
$criteria->addBetweenCondition($style_contribution,$this->trait_value_lower,$this->trait_value_upper);
$trait_details= $this->find($criteria);
When i debug the query in log than it shows in case of yii :
SELECT * FROM `traits_versions` `t` WHERE ((style_id=1) AND (version=1)) AND (16 BETWEEN NULL AND NULL) LIMIT 1
Why it is giving NULL value in query while i'm passing the name of the column in it.
So please guide me where i'm going wrong in yii.
Add compare condition like below
$criteria->compare('trait_value_lower', 16, false, '>');
$criteria->compare('trait_value_upper',16, false, '<');
instead of between condition
$criteria->addBetweenCondition($style_contribution,$this->trait_value_lower,$this->trait_value_upper);
because between condition will apply on one column as per Yii doc.
public static addBetweenCondition(string $column, string $valueStart, string $valueEnd, string $operator='AND')

SQL where value combination of 0 and sha1(value, true) in CodeIgniter's Active Records returns all rows

So this is my conclusion of the real problem from a question I asked earlier here:
SQL Select Query with CodeIgniter's active records and 'where sha1' returns first row entry
I'm also double posting in the CodeIgniter forum:
http://codeigniter.com/forums/viewthread/194502/ (latest progress...)
You can get several code snippets from both.
In short, the problem I am having is the following and I'm not sure who's really the culprit:
I am using an array as shown below to define the 'where' in a select query for an active records function. The query gives me all the rows in the table if used in the certain combination outlined below.
pasting---
it actually looks like this combination of error only happens when sha1 returns raw output data and email < 1 For instance:
$where = array(
'email' => 0,
'password' => sha1(false, true), # sha1($this->input>post('password'), true);
);
if sha1(false, true) is changed to sha1(whatever) there is no error. (‘whatever’ includes strings, booleans, etc.)
if array is unchanged and 'email' => 0 is changed to 'email' => '0' which happens in the SQL QueryA, then it works correctly. If I remove the ‘‘s from the value in QueryA as in Active Records then I get all the rows again …
Also, I do not get any rows (which is correct) when 'email' is > 0. i.e. 'email' => 1 (2,3,etc) and even when 'email' => null.
The combination of ‘email’ = 0 and sha1(‘any value’, true) results in returning EVERY row in the table in the Active Records because there’s no quotes added to the value 0. Quotes are however added around the ‘password’ = ‘value’. If there are no quotes around the raw hash, then SQL returns an Error and prevents the script from running.. (which is better than returning ever row…)
This could be an SQL error since it only happens on a value of 0... but is it user error to not quote a where value? If so, then should Active Records should quote the value for me automatically if it is an integer or not? It seems to do it for string values, but not integers (false = 0, true = 1)...
If you want the same result in your queries, write the two equal. In QUERY1 you define your terms as follows:
WHERE `email` = \''.$data['email'].'\'
AND `password` = \''.$data['password'].'\'
in QUERY2
# data for sql query
$where = array(
'email' => $this->input->post('email'),
'password' => sha1($this->input->post('password'), true);
);
For QUERY1 and QUERY2 are equal, QUERY2 would be:
# data for sql query
$where = array(
'email' => "'".$this->input->post('email')."'",
'password' => sha1($this->input->post('password'), true);
);
Codeigniter can not determine by itself that 0 should not be a number. You do it yourself as QUERY1.

Categories