Find the position inside this Yii 2 query - php

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.

Related

codeigniter fetch mysql table with where_in + keep order of key array in result array

I have a string with image ids (fetched from another mysql table)
and converted to an array:
$idstring = "12, 18, 3, 392, 0, 9, 44";
$idarray = explode(',', $idstring);
Based on this array of ids, I want get all the rows from my "media" mysql table.
$result = $this->db->select('*')
->from('media')
->where_in('id', $ids)
->get()->result_array();
The problem is the $result array's values are in a weird order like this:
$result's order : 44, 9 ,0 ,18 ,3 ,392 ,12 ...
But i need them to stay like in my $id string/array order...
I've tried 4 approaches to solve the issue so far:
Fetch rows in a loop without where_in() - what creates a lot of queries - but works for now ...
Reorder the $result array based on the order of the $idstring or the $idarray, though I could not manage to to find a working result and I don't get the point why this step is necessary at all
Try to get the query itself fixed. I've heard about ORDER_BY and FIND_IN_SET, $ids but I could not get it into my a working codeigniter query and don't know about the performance if this is really a help
So in conclusion, I think this should be a simple everyday task, i just want to fetch a bunch of pictures in a given order with codeigniter.
Am I missing a simple solution here?
Use Field() function of mysql
$result = $this->db->select('*')
->from('media')
->where_in('id', $ids)
->order_by("FIELD(id,".join(',',$ids).")")
->get()
->result_array()
it should be something like
FIELD(id,12, 18, 3, 392, 0, 9, 44)
Reference
Field() returns the index position of a comma-delimited list
The accepted answer should be M Khalid Junaid answer. But just in case, if you are generating from an array like I do, use it like this:
$filters = [
"order_by" => [
"title" => "DESC",
""FIELD(id,".join(',',$ids).")" => "" // The option should be empty...
]
];
foreach($filters["order_by"] as $attribute => $option){
$this->db->order_by( $attribute, $option );
}
The output will be:
...
ORDER BY `name` DESC, FIELD(id, 2017, 2031, 2032, 2034, 2035)
LIMIT 50
Just to for clarification.

Associative array from a database with codeigniter

On my models I try to write a php model that will get me a associative array from a database. But I don't quite know how to approach this.
So after I execute this SQL query:
SELECT balance_events.weight,balance_events.added_date,
balance_entries.mid FROM balance_events, balance_entries
WHERE balance_entries.added_date BETWEEN '2016-08-02' AND '2016-08-03'
AND balance_entries.ptid =12
AND balance_entries.beid = balance_events.id
I will get this table:
And from that table I want to extract a asociative array that it will look like this:
count = ['13'=>1, '6'=>4, '16'=>3, '4'=>3]
where 'mid'=>number of how many times that mid can be found in the table.
ex. mid '13'=>1 cause you can found it only once.
I think that I will have to use SQL COUNT function, but how I can aggregate all of this in a PHP model in codeigniter? I know how to configure controller and view, but I don't know how to actually do the actual php model that will get me the desired array.
Try this query may help you ,
$result = $this->db->select('balance_events.weight,balance_events.added_date,COUNT(balance_entries.mid) as mid_count')
->from('balance_events, balance_entries')
->where('balance_entries.added_date BETWEEN "2016-08-02" AND "2016-08-03" ')
->where('balance_entries.ptid','12')
->where('balance_entries.beid','balance_events.id')
->group_by('balance_entries.mid')
->get();
return $result->result_array();
I'm not sure how you would create this in SQL but since you tagged php, I wrote a function that would do just this.
<?php
$query = array(array("mid"=>13), array("mid"=>2), array("mid"=>13), array("mid" =>6), array("mid" => 13), array("mid" => 6));
function createMidArray($queryResult){
$returnArray = array();
foreach ($queryResult as $qr){
$returnArray[$qr['mid']]++;
}
return $returnArray;
}
print_r(createMidArray($query));
?>
The output of this was Array ( [13] => 3 [2] => 1 [6] => 2 ) which matches up to my inputted $query (which is a 2D array). I'm expecting the output of your query is stored in a similar array, but with more data and keys

Output all 'findbyattributes' in yii 1.1

I have a findByAttributes in yii and I was wondering how you output all of the data that meets the criteria specified
I have an example below:
Assuming that in my Test table I have 3 rows which has the value 1 in test1 attribute then I have this code in my view
$a= Test::model()->findByAttributes(array('test1'=> '1'));
$b= $a-> id;
print_r($b);
I've noticed that this code would print the id '1' instead of '1 2 3'.
What code can I use so that it would output ALL of the ids with 1 in their test1 attribute?
sorry for the beginner question. I hope anyone can help...
If you want to get all records you have to use method findAllByAttributes (http://www.yiiframework.com/doc/api/1.1/CActiveRecord#findAllByAttributes-detail) instead of findByAttributes. Method findAllByAttributes will return all rows which meets requirements, then you should iterate over it to get an id.
Example,
foreach( $models as $model) {
print_r($model->id);
}
But if you want to get only ids it is better tou use CDbCommand and querycolumn
Yii::app()->db
->createCommand()
->select('id')
->from('Test')
->where('test1=:value', array(':value'=> 1))
->queryColumn()
It will return an array of ids
For retrive all the rows the meet a specific criteria you can use
findAllByAttributes();
And for obtain all the result you must loop eg ever this result:
$a= Test::model()->findAllByAttributes(array('test1'=> '1'));
foreach($a as $key => $value) {
echo $value->id
}

How to KeyBy where multiple items have the same key

I am using Laravel Collections methods and am trying to key my query results (which are a collection) by the id. The problem is I have multiple entries with the same id, but point to different countries and I want to have all of the values, not just the last one.
Here is my code that i am using so far:
$allCountries = new Collection($allCountries);
$offerCountries = $allCountries->keyBy('id');
dd($offerCountries);
foreach ($offer as $o) {
$o->countries = $allCountries->get($o->id);
}
To explain, my query puts the results in $allCountries which contains ids and countries and those results looks something like this
id=>225, country=>US
id=>225, country=>IT
id=>3304, country=>NZ
Just to give you a quick idea. I want to key this by the id which results in $offerCountries. I then loop thru a previous Collection that contains offers which have a certain ID that relates to the country result by id. So for the offer 225, the countries it contains are US and IT. I loop thru each offer and set the countries object equal to all the $allCountries id that it equals. The problem I have here is keyBy overwrites the value and only takes the last one. I am hoping to get some results like this:
[
225 => countries: {'id' => 225, 'country' => 'US'}, {'id' =>
'225', 'country' => 'IT'}
3304 => ['id' => 3304, 'country' => 'NZ'],
]
Is there a laravel method to do this, or do I need to write my own keyBy so it does not overwrite. If so, how can I get started to write this method?
Thanks
Instead of using keyBy, use groupBy:
$countriesById = collect($allCountries)->groupBy('id');
You could use filter and create a custom filter
$filtered = $allCountries->filter(function ($item) use ($id) {
return $item->id == $id;
});
$filtered->all();

Symfony 1.4 + Pager : Ignore order array

I have a symfony problem: The functionally works good, but this does not work the way I want.
$res = array("4","2","1","3"); // LIST ID (a.id)
$paginas = new sfDoctrinePager('TbArticle', 2);
$paginas->setQuery(Doctrine::getTable('TbArticle')->createQuery('a')->where('a.ifactive = 1')->andWhere('a.dirimage=1')->andWhere('a.stock<>0')->whereIn("a.id", $res));
$paginas->setPage($page);
$paginas->init();
It works okay, but when I call getResults(), the array order is incorrect. For instance, this sort returns: 1,2,3,4. And I like to get: 4, 2, 1, 3 ($res)
Can you help me?
Unfortubately this cannot be done with the query.
The MySQL queries can be returned ordered using the ORDER BY clause in ascending or descending order. Elements in your array use none. When you pass the array as a parameter for the WHERE IN clause MySQL doesn't care about the order of the elements as you can see.
Fortunately there is a solution :)
First you will have to use Doctrine's ability to create a table of results indexed with what you want. Use this:
Doctrine::getTable('TbArticle')->createQuery('a INDEX BY id')->...;
This will return an array of results where the array keys are the id's of the rows. Then you can rearange the results array to match your $res (assuming that $rows has the rows returned by Doctrine):
foreach ($res as $i) {
$new_array[] = $rows[$i];
}
The tricky part is to make it work with the paginator. But I'm sure you can do that as well (try to retrieve the results from the paginator and rearange them before displaying).

Categories