Using Medoo PHP database framework and trying to make a join
$users = $db->select(
'users',
[
'[>] tournaments_users' =>
[
'tournaments_users.user_id' => 'users.uid'
]
], [
'users.uid',
'users.name',
'users.modifier',
'users.handicap',
'tournaments_users.tournament_id'
], [
'tournaments_users.tournament_id' => 1
'ORDER' => 'users.username ASC'
]
);
foreach( $users as $u) {
echo $u['name'].'<br>';
}
The selection results in an invalid argument supplied for foreach().
Removing 'tournaments_users.tournament_id' from the column- and where-section makes the query work, but does not show the correct data.
Why is the query invalid?
Changing the join-selection to
'[>]tournaments_users' =>
[
'uid' => 'user_id'
]
solved the issue with invalid argument.
Concatenation of logical expressions using AND/OR in join clause is currently not supported by the medoo library (medoo 0.9.6.2). You can only use the query($query) method given by medoo to directly execute sql queries. For your example the query looks like this:
$data = $db->query("SELECT u.user_id, s.shirt_id FROM `cvs_users` u
LEFT JOIN `shirts` s
ON user_id = shirt_owner_id AND shirt_being_worn = 1
WHERE user_id = 1")->fetchAll();
Note the call to fetchAll() at the end to get the queried data. I ran into the same problem and debugging the medoo.php code revealed that AND/OR conditions are only considered within where clause. Maybe they'll put this feature in a future update.
Related
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.
I am trying to add OR SQL Query Statement to my CakePHP project, but notice I am getting AND instead of OR.
I am getting this string when I use SQL(), and no OR is include whereas OR is expected
SELECT Blacklist.id AS `Blacklist__id`,
Blacklist.provider AS `Blacklist__provider`,
Blacklist.receiver AS `Blacklist__receiver`,
Blacklist.platform AS `Blacklist__platform`,
Blacklist.brokers AS `Blacklist__brokers`
FROM blacklist Blacklist
WHERE (brokers = :c0 AND provider = :c1 AND platform=FXCMMetaTrader4)
I tried the code below, to but instead of OR I am getting AND.
Why is this working this way and how can I avoid this?
//fill blacklist data
$blacklistQuery = $blacklistModel->find()
->select(['id', 'provider', 'receiver', 'platform', 'brokers'])
->order(array('created' => 'desc'))
->where(function (QueryExp $exp, Query $q) use ($accountBroker, $accountLogin,$platform) {
$orBrokerCond = $exp->or_(['brokers' => $accountBroker]);
$orProviderCond = $exp->or_(['provider' => $accountLogin]);
$orPlatformCond = $exp->or_('platform='. $platform);
return $exp->add($orBrokerCond)
->add($orProviderCond)
->add($orPlatformCond);
});
// ->where("provider = $accountLogin");
echo $blacklistQuery->sql();
Inner expression objects usually do not affect outer expression objects, ie the main expression doesn't really care about what type of expression you are passing to it.
What you are doing there is creating 3 separate expression objects, each containing only a single condition. Each of those objects will compile itself to a single conditional statement, and those statements will then be concatenated using AND as you are passing them to the main expression that uses AND by default.
What you are trying to create could simply be expressed as:
return $exp->or_([
'brokers' => $accountBroker,
'provider' => $accountLogin,
'platform' => $platform,
]);
If you where to add OR conditions to another expression, then you'd pass such an object with multiple conditions, ie:
$or = $exp->or_([
'brokers' => $accountBroker,
'provider' => $accountLogin,
'platform' => $platform,
]);
return $exp->add($or);
ps, do NOT do stuff like 'platform='. $platform, that's a possible SQL injection vulnerability!
See also
Cookbook > Database Access & ORM > Query Builder > Advanced Conditions
I am trying to learn CakePHP 3, but I have run into a problem:
I have two tables languages and rich_text_elements, and want to join them in the following manner:
$all = $this->find()->
select(['i18n','Language.long_name'])->
innerJoin(['Language' => 'languages'], ['Language.i18n' => 'RichTextElements.i18n'])->
group('RichTextElements.i18n')->
order(['RichTextElements.i18n'])->all();
The following query is produced:
SELECT RichTextElements.i18n AS `RichTextElements__i18n`,
Language.long_name AS `Language__long_name`
FROM rich_text_elements RichTextElements
INNER JOIN languages Language ON Language.i18n = :c0
GROUP BY RichTextElements.i18n ORDER BY RichTextElements.i18n;
If I replace ":c0" with "RichTextElements.i18n", this query runs fine alone (in HeidiSql) and returns five rows of data, exactly as I expect it to.
But CakePHP returns an empty set!
The problem seem related to the innerJoin() because if I modify the query to select only from the RichTextElements table, it will return five rows as expected, in CakePHP:
Runs fine:
$all = $this->find()->
select(['i18n'])->
group('RichTextElements.i18n')->
order(['RichTextElements.i18n'])->all();
Anyone see what I don't see?
As stated in the API:
Conditions can be expressed [...] using a string for comparing columns, or string with already quoted literal values. Additionally it is possible to use conditions expressed in arrays or expression objects.
Taken from Query::join() | Using conditions and types.
Try the following:
$all = $this->find()
->select(['i18n','Language.long_name'])
->innerJoin(
['Language' => 'languages'],
['Language.i18n' => new \Cake\Database\Expression\IdentifierExpression('RichTextElements.i18n')])
->group('RichTextElements.i18n')
->order(['RichTextElements.i18n'])->all();
This should also work:
$all = $this->find()
->select(['i18n','Language.long_name'])
->innerJoin(
['Language' => 'languages'],
['Language.i18n = RichTextElements.i18n'])
->group('RichTextElements.i18n')
->order(['RichTextElements.i18n'])->all();
I have some problems with the limit using active record.
I've created a dataProvider with a limit of 5:
$dataProvider = new ActiveDataProvider([
'query' => Devicesdb::find()
->joinWith('score')
->where('devices.devicetype = :deviceType', [':deviceType' => $device])
->orderBy(['score' => SORT_DESC])
->limit(5),
'totalCount' => 5,
]);
And this is the resultant query in debug panel:
SELECT `devicesdb`.*
FROM `devicesdb`
LEFT JOIN `devices`
ON `devicesdb`.`id` = `devices`.`id`
WHERE devices.devicetype = 'phone'
ORDER BY `score`
DESC LIMIT 20
The query is fine , and retourns me the data as I want, but I only want 5 items, not 20.
First of all totalCount is a property of Pagination, not ActiveDataProvider. You need to nest it inside of a pagination configurational array. But this is not how you achieve this.
Because you don't want the pagination to appear you can disable it by passing false and now limit will be taken from query (otherwise it's ignored and calculated differently, you can see this related question):
$dataProvider = new ActiveDataProvider([
'query' => Devicesdb::find()
->joinWith('score')
->where('devices.devicetype = :deviceType', [':deviceType' => $device])
->orderBy(['score' => SORT_DESC])
->limit(5),
'pagination' => false,
]);
]);
One more thing - you don't have to write params manually, see this question for explanation and better understanding. So where part of the query can be reduced to just:
->where(['devices.devicetype' => $device])
Also I recommend to refactor model name to just Device and use this to resolve duplicate names conflicts (if any) in SQL query:
->where(Device::tableName() . 'devicetype' => $device])
That way if this model related table name will changed in the future, you don't have to refactor your code.
I have a stream/table named profiles. All of its column are stream-fields. I am trying to restrict the result returned by the the function, get_entries() depending on some criteria. Below is my code:
$data = [
'stream' => 'profiles',
'namespace' => 'users',
'where' => 'user_id = 3' // lets say, this is my criteria
];
$row = $this->streams->entries->get_entries($data); // returns empty
The varaible, $row resulted in empty array. Although there is one row in table, profiles where user_id is 3. I have read the documentation of pyrocms and it pretty much says the exact way to use the where clause (just like above).
NOTE: I have also tried writing like
'where' => 'profiles.user_id = 3'`
joy !to avoid table conflict. Still no
But when I write the code like this:
$row = $this->streams->entries->get_entries($query);
$query = [
'stream' => 'profiles',
'namespace' => 'users'
];
// No where clause this time
$row = $this->streams->entries->get_entries($query);
This time $row returns all rows including the row with user id 3.
I am unable to use the where clause in get_entries in a right way. I might have done some mistake. Help me out guyz
NOTE: I am using community edition.
I think this might be due to a bug (well, not a bug, but a feature that doesn't work as intended).
If I'm intentionally issue a wrong query, the sql query output is
SELECT [ ... ] LEFT JOIN `default_profiles` as `profiles` ON `profiles`.`user_id`=`default_profiles`.`created_by` WHERE (user_id` = 1) ORDER BY `default_profiles`.`created` DESC
Here you see that PyroCMS tries to lookup the data for the "created_by" field. And that doesn't work in this case.
If you disable the 'created_by' field, you should get the correct row:
$this->streams->entries->get_entries(
array(
'stream' => 'profiles',
'namespace' => 'users',
'where' => 'user_id = 3',
'disable' => 'created_by'
)
);
It would be great if you could file an issue on the pyrocms github page. If you won't I'll do it in the next few days.
Model
public function get_entries($table, $where) {
$this->db->select('*');
$this->db->from($table);
foreach ($where as $key => $value) {
$this->db->where($key, $value);
}
$this->query = $this->db->get();
foreach ($this->query->result_array() as $row) {
$array1[] = $row;
}
if ($this->query->num_rows() == 0)
return false;
else
return $array1;
}
call this model function as
$row = $this->streams->entries->get_entries('profiles',array('user_id '=>3));