I am trying to use multiple where() with Zends delete(), but nothing is happening..!!!
$where=array('id' => $id,'likedbyID' => $likedbyID);
$this->delete($where);
The above code is written inside a Model.
Please help me.....
Thanks in advance.....
From zend documentation :
Since the table delete() method proxies to the database adapter delete() method, the argument can also be an array of SQL expressions. The expressions are combined as Boolean terms using an AND operator.
Knowing all that you can use it like this :
$this->delete(
array(
'id = ?' => $id,
'likedbyID = ?' => $likedbyID,
)
);
Allso you could use the > or < or IN or .. operators instead of =
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 have a very complex setup on my tables and achieving this via any of the find() methods is not an option for me, since I would need to fix relationships between my tables and I don't have the time right now, so I'm looking for a simple fix here.
All I want to achieve is run a query like this:
SELECT MAX( id ) as max FROM MyTable WHERE another_field_id = $another_field_id
Then, I need to assign that single id to a variable for later use.
The way I have it now it returns something like [{{max: 16}}], I'm aware I may be able to do some PHP on this result set to get the single value I need, but I was hoping there was already a way to do this on CakePHP.
Assuming you have a model for your table and your are using CakePHP 2.x, do:
$result = $this->MyTable->field('id', array('1=1'), 'id DESC');
This will return a single value.
see Model::field()
This example is directly from the CakePHP documentation. it seems you can use the find method of a model to get count
$total = $this->Article->find('count');
$pending = $this->Article->find('count', array(
'conditions' => array('Article.status' => 'pending')
));
$authors = $this->Article->User->find('count');
$publishedAuthors = $this->Article->find('count', array(
'fields' => 'DISTINCT Article.user_id',
'conditions' => array('Article.status !=' => 'pending')
));
I am trying to extract ONLY the PlanDetails where PlanDetail.company_id = Company.id AND PlanDetail.id' => $id.. ( you can see the conditions in my controller below)..
Controller:
function pd_list_by_company($id = null) {
$this->recursive = 2; // I am going to use containable to trim this.
return $this->PlanDetail->find('all',
array('conditions' =>
array('AND' =>
array('PlanDetail.company_id' => 'Company.id',
array('PlanDetail.id' => $id)))));
}
Test View:
$planDetailsByCompany = $this->requestAction('/planDetails/pd_list_by_company');
debug($planDetailsByCompany );
Output result of my debug??
Array()
If I remove the conditions and just have the find all, I get all PlanDetails as expected, so I know the data is being passed.. SQL debug dump even shows the query:
WHERE ((`PlanDetail`.`company_id` = 'Company.id') AND (`PlanDetail`.`id` IS NULL))
And yes, I did notice the $id is NULL, and I know the value needs to be there.. So maybe my question is why is the $id value not being passed to the controller even though I can see the PlanDetail.id value on a find('all') w/ out the conditions??
Thanks for any tips.
Since $id seems to be null, I would assume that you call the function without the parameter. And you don't get an error message, because as far as PHP is concerned the parameter is optional. In this case it's clearly required, so you should make it a required parameter in your function declaration:
function pd_list_by_company($id) {
Also you could simplify the return statement, you do not need the AND:
return $this->PlanDetail->find('all',
array('conditions' =>
array('PlanDetail.company_id' => 'Company.id','PlanDetail.id' => $id)
)
);
To answer the question why is the $id not being passed is because you're not passing it
To pass say $id of 2 you need to do the following in your requestAction
$this->requestAction('/planDetails/pd_list_by_company/2');
Seems to me that your code should just be
return $this->PlanDetail->find('array('PlanDetail.id' => $id));
Assuming you have the $this->PlanDetail->recursive flag set to > 0, your Model should already know about and return the associated data for any 'Company' table.....
I'm used to an old (1.3) version of CakePHP but the find() function is pretty basic and is designed to only return one row.
and yes, you definitely need to call the function with the id appended to the url, eg.
$planDetailsByCompany = $this->requestAction('/planDetails/pd_list_by_company/999');
I'd like to exclude results from a call to a Lithium model's find() method. I need to do this for models with both MongoDB and MySQL data sources, but in SQL I mean something like WHERE myfield NOT IN (1,2,3).
I'd like to just be able to pass a not clause in the conditions array like below, but that doesn't appear to be possible.
Item::all(array('conditions' => array('not' => array('myfield' => array(1,2,3))));
So my question is, is this possible in Lithium in a way that I've overlooked? And if not, what would be the most Lithium-ish way to implement it for my models?
Just to clarify, Lithium's MongoDB adapter supports most SQL comparison operators as a convenience, so for either Mongo or MySQL, you could simply write the query as follows:
Item::all(array('conditions' => array(
'myfield' => array('!=' => array(1,2,3))
)));
And it should give you the results you expect. For MySQL, the query should look something like:
SELECT * FROM items WHERE myfield NOT IN (1, 2, 3);
And in Mongo:
db.items.find({ myfield: { $nin: [1, 2, 3] }})
Merely filtering for MongoDB can easily be achieved like this:
Item::all(array('conditions' =>
array('myfield' => array(
'$nin' => array(1,2,3)
))
));
If this is something you do a lot you could even create a custom finder for it :
class MyModel extends \lithium\data\Model {
public static function __init()
{
parent::__init();
static::finder('notin', function($self, $params, $chain) {
// Take all array keys that are not option keys
$array = array_diff_key($params['options'],
array_fill_keys(array('conditions', 'fields','order','limit','page'),0));
// Clean up options leaving only what li3 expects
$params['options'] = array_diff_key($params['options'], $array);
$params['options']['conditions'] = array(
'myfield' => array(
'$nin' => $array
)
);
return $chain->next($self, $params, $chain);
});
}
}
And call it like this :
MyModel::notin(array(1,2,3));
In the same manner you could create a custom finder for MySQL sources.
As you probably can see this creates some issues if you pass something like array('fields'=>$array) as it would overwrite the option.
What happens is that ::notin() (finders in general) has a distinct behavior for the (array,null) signature. If that happens it thinks the first array is options and the finder took no arguments.
Using notin($array,array()) breaks the previous finder because the first argument ends up in $params['notin'] when the real second argument (options) is passed.
If you mix data sources on the fly here I would create a custom model that does not inherit \lithium\data\Model and have it delegate
to the different models and create the conditions based on the end models data source.
class MyFacadeModel {
public static function byNotIn($conditions, $source) {
return ($source == "mongodb")
? $source::find( $rewrittenConditions)
: $source::find( $rewrittenConditionsForMysql );
}
}
(Code might be slightly incorrect as its mostly taken from the top of my head)