I'm trying to add a second condition inside the WHERE in my foreach loop.
$customers = Customers::find()->where(['status' => 1])->orderBy('vip DESC, id ASC')->all();
Currently, I have it filtering where status=1 but I need to also add where 'rank' does not equal the text "Can".
The reason I need StackOverflow is that I've tried to find the answer on the web but I have a text comparison and a new query method I haven't used before so I can't research well.
How do I add the additional condition in the WHERE clause "AND 'rank' != 'Can'?
If I had to guess at it, I'd write it like this (which likely wrong)
$customers = Customers::find()->where(['status' => 1])
->andwhere (['rank' !=> 'Can'])
->orderBy('vip DESC, id ASC')->all();
According to the amazing ActiveQuery documentation, you have to use andWhere() method:
$customers = Customers::find()->where(['status' => 1])
->andwhere (['not', ['rank' => 'Can']])
->orderBy('vip DESC, id ASC')->all();
OR:
->andwhere (['<>', 'rank', 'Can'])
Please remember, that PHP don't have !=> comparision operator!
In Yii i think you need change andwhere to andWhere.
or try this
$customers = Customers::find()->where("status = 1 and rank!='Can'"])->orderBy('vip DESC, id ASC')->all();
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 have this following Yii 2 query
$find = People::find()->where(['c_id' => $c_id])->orderBy('totals DESC, id DESC')->all();
So imagine this query was an array. Everything found by this query has an "id" attribute.
Since it's sorted by "totals", I essentially want to return the position in the array where I can find this specific id.
Currently, I'm using this code.
foreach ($find as $t) {
$arr[] = $t->id;
if ($t->id == $id) {
break;
}
}
$key = count($arr);
return $key;
However, this code is vany wayow on a 100k+ result query.
Is there anyway to speed this up?
You could get the result as an array (instead of object) as
$find = People::find()->where(['c_id' => $c_id])
->orderBy('totals DESC, id DESC')
->asArray()
->all();
then you could find your value using array_search()
$my_index = array_search($id,$find);
but for 100k+ you should find using a direct select in db...instead tha looping on php or load all in php and scan with array_search()
To get array from query in YII, you can use queryAll();
$find = People::find()->where(['c_id' => $c_id])->orderBy('totals DESC, id DESC')->queryAll();
OR, another way to convert the object into an array is:
$find = json_decode(json_encode($find), true); // to convert all data into array.
And once you get results in array, you can implement the actual code for your requirement as given below.
You can use array_search() function to get index of your value.
$a=array("a"=>"red","b"=>"green","c"=>"blue");
echo array_search("red",$a);
The array_search() function search an array for a value and returns the key.
Maybe I didn't understand you correctly but I assume that you are trying to detect the index or key for your desired id inside an array returned from an SQL query that is sorted by some other column like total.
So let us fetch records from the database with your query with a little change asArray() like this
$find = People::find()
->where(['c_id' => $c_id])
->orderBy('totals DESC, id DESC')
->asArray()
->all();
in the result, let us assume the People table returns you an array with the following dataset ordered by columns total and id DESC.
[
0 => [
'id' => 2 ,
'c_id'=>2,
'name' => 'John' ,
'age'=>18,
'totals'=>100,
],
1=>[
'id'=>1,
'c_id'=>55,
'name'=>'Bob',
'age'=>20,
'totals'=>80,
],
2=>[
'id'=>3,
'c_id'=>85,
'name'=>'Peter',
'age'=>15,
'totals'=>75,
]
];
Now if you look into \yii\helpers\ArrayHelper you will find ArrayHelper::getColumn().
Let us use this on the array we received from the query, I assume that you are searching $id inside the column id so we will first filter out the id column like below.
$idsArray = ArrayHelper::getColumn($find, 'id');
this will give us the ids in the following sequence which is in the same order as the initial result set.
[2,1,3]
then lets use the built-in php function array_search()
$key=array_search($yourId,$idsArray);
Hope this is what you are looking for.
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 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'm using readbeanphp as ORM for my php project. I'm trying to load a bean with an additional where clasue. But i'm not sure how to do this.
Normally i'd get a 'bean' like this:
$book = R::load('book', $id);
Which is basically like saying:
SELECT * FROM book WHERE id = '$id'
But i need to add another condition in the where clause:
SELECT * FROM book WHERE id = '$id' AND active = 1
How can i do this with redbeanphp?
R::find() and R::findOne() is what you are looking for:
$beans=R::find("books","active=? AND id=?",array(1,1));
Or if you want a single bean only:
$bean=R::findOne("books","active=? AND id=?",array(1,1));
R::find() will return multiple beans, while R::findOne() returns just a single bean.
You technically could use R::load() but would have to use PHP to check the active field after the bean is loaded to test if it is valid:
$bean=R::load("book",$id);
if($bean->active==1){//valid
//do stuff
}else{//not valid
//return error
}
Hope this helps!
RedBean_Facade::load is using for primary key only.
if you want get beans by a complex query
use
R::find like,
$needles = R::find('needle',' haystack = :haystack
ORDER BY :sortorder',
array( ':sortorder'=>$sortorder, ':haystack'=>$haystack ));
Read more about R::find
http://www.redbeanphp.com/manual/finding_beans
try to use getAll to write your query directly with parameters
R::getAll( 'select * from book where
id= :id AND active = :act',
array(':id'=>$id,':act' => 1) );
Read more about queries,
http://www.redbeanphp.com/manual/queries
these answers are fine but can be simplified. What you should use is:
$book = R::find('books', id =:id AND active =:active, array('id' => $id, 'active' => 1));
And then you can do your standard foreach to loop through the returned array of data.