Multiple Fields with a GroupBy Statement in Laravel - mysql

Already received a great answer at this post
Laravel Query using GroupBy with distinct traits
But how can I modify it to include more than just one field. The example uses pluck which can only grab one field.
I have tried to do something like this to add multiple fields to the view as such...
$hats = $hatData->groupBy('style')
->map(function ($item){
return ['colors' => $item->color, 'price' => $item->price,'itemNumber'=>$item->itemNumber];
});
In my initial query for "hatData" I can see the fields are all there but yet I get an error saying that 'colors', (etc.) is not available on this collection instance. I can see the collection looks different than what is obtained from pluck, so it looks like when I need more fields and cant use pluck I have to format the map differently but cant see how. Can anyone explain how I can request multiple fields as well as output them on the view rather than just one field as in the original question? Thanks!

When you use groupBy() of Laravel Illuminate\Support\Collection it gives you a deeper nested arrays/objects, so that you need to do more than one map on the result in order to unveil the real models (or arrays).
I will demo this with an example of a nested collection:
$collect = collect([
collect([
'name' => 'abc',
'age' => 1
]),collect([
'name' => 'cde',
'age' => 5
]),collect([
'name' => 'abcde',
'age' => 2
]),collect([
'name' => 'cde',
'age' => 7
]),
]);
$group = $collect->groupBy('name')->values();
$result = $group->map(function($items, $key){
// here we have uncovered the first level of the group
// $key is the group names which is the key to each group
return $items->map(function ($item){
//This second level opens EACH group (or array) in my case:
return $item['age'];
});
});
The summary is that, you need another loop map(), each() over the main grouped collection.

Related

Laravel: DB Query returns stdclass instead of array

I've got the following:
$srv = DB::table('ads')
->join('ad_service','ad_service.ad_id', '=', 'ads.id')
->select('ads.id')
->whereIn('ads.id',[45789,46531])
->get();
Log::info($srv);
and log info gives me
49 => stdClass::__set_state(array(
'id' => '46531',
)),
50 =>
stdClass::__set_state(array(
'id' => '46531',
)),
51 =>
stdClass::__set_state(array(
'id' => '46531',
)),
What I would like to have:
return the entry ONCE, there are multiple entries with this id in the "ad_service" table , and i would like to retrieve it once.
its a many to many relationship.
also biggest problem, what is this stdclass set state?
I was expecting something like:
array('id' => '46531', ...,)
thanks guys.
EDIT:
Solved it using Eloquent:
$srv = Ad::whereIn('id',[46696,48982,...MORE IDS HERE])->get();
this returns not an object but a collection itself. i do not really know what #ourmandave meant with "you get a collection with std class objects" i mean obviously i could see that but i do not really know why i got it, and why i get it as i wish using eloquent.
Any answer to that would be appreciated:
Why does DB:: return this std class stuff and Eloquent returns the "wanted format"?
If you just want one record you could try:
$srv = DB::table('ads')
->join('ad_service','ad_service.ad_id', '=', 'ads.id')
->select('ads.id')
->whereIn('ads.id',[45789,46531])
->first();
Change the get to a first(). The reason you are getting multiple records is because get() returns a laravel collection of your ads.
You're actually getting back a lararvel Collection.
From the docs (https://laravel.com/docs/5.8/queries#retrieving-results)
The get method returns an Illuminate\Support\Collection containing the
results where each result is an instance of the PHP stdClass object.
You may access each column's value by accessing the column as a
property of the object:
foreach ($users as $user) {
echo $user->name;
}
There's a lot of methods they provide for collections to treat them like arrays.
(https://laravel.com/docs/5.8/collections#available-methods)
You might also try the distinct() method to get back one of multiples.
$srv = DB::table('ads')
->join('ad_service','ad_service.ad_id', '=', 'ads.id')
->select('ads.id')
->whereIn('ads.id',[45789,46531])
->distinct()
->get();
To convert a model and its loaded relationships to an array, you should use the toArray method. This method is recursive, so all attributes and all relations (including the relations of relations) will be converted to arrays
$srv = DB::table('ads')
->join('ad_service','ad_service.ad_id', '=', 'ads.id')
->select('ads.id')
->whereIn('ads.id',[45789,46531])
->get()
->toArray();
Print array:
Log::info($srv);

laravel 5 update multiple columns in one line?

It's possible update multiple columns in one line with laravel 5.4?
I try to this
DB::table('Home_Content')->where('id',1)->update(
[$_POST['name'] => $_POST['content']],
[$_POST['title'] => $_POST['titleMsg']]
);
only 1st part is work([$_POST['name'] => $_POST['content']])
server not return any error, but only 1st part is success.
Update method syntax in Laravel is like so :
DB::table('users')
->where('id', 1)
->update(['options->enabled' => true]);
Which means the values we want to update are inside an array, what I've seen is that you separated what you want to update into two arrays
DB::table('Home_Content')->where('id',1)->update(
[$_POST['name'] => $_POST['content']],
[$_POST['title'] => $_POST['titleMsg']]
);
so now you are out of the array
Your code should be like this
DB::table('Home_Content')->where('id',1)->update([
$_POST['name'] => $_POST['content'],
$_POST['title'] => $_POST['titleMsg']
]);
Meaning each pair has a key and a value and separated with comma
If you want to update record in multiple row with id as Array() use following query.
DB::table('users')
->whereIn('id', [1,2,3])
->update(['status' => 1]);

Yii2 Query not quoting correctly

I run into this problem, time and time again. It would be nice to find out how to build queries properly so I can stop resorting to Yii::$app->db->createCommand() as a workaround.
My Yii2 query:
$users = UserSpatial::find()
->select('user_id, harvesine(y(coordinates), x(coordinates), :lat, :lon) as dist, astext(coordinates)')
->where('st_within(coordinates, envelope(linestring(point(:rlon1, :rlat1), point(:rlon2, :rlat2))))')
->orderBy('st_distance(point(:lon, :lat), coordinates)')
->params([
':lon' => $geo->lon,
':lat' => $geo->lat,
':rlon1' => $rlon1,
':rlat1' => $rlat1,
':rlon2' => $rlon2,
':rlat2' => $rlat2
])
->all();
The generated query ends up with backticks in all the wrong places and, oddly enough, not all parameters were backticked (sorry but you'll need to look closely for the misplaced backticks because I didn't know how best to highlight the incorrect placements):
SELECT \`user_id\`, harvesine(y(coordinates), x(coordinates), \`32.7699547\`, \`-116.9911288)\` AS \`dist\`, astext(coordinates)
FROM \`user_spatial\`
WHERE st_within(coordinates, envelope(linestring(point(-117.07730792871, 32.697490931884), point(-116.90494967129, 32.842418468116))))
ORDER BY st_distance(point(-116.9911288, \`32.7699547)\`, \`coordinates)\`
The query should look like the following as I did not wrap double-square-brackets around any of the fields or values:
SELECT \`user_id\`, harvesine(y(coordinates), x(coordinates), 32.7699547, -116.9911288) AS dist, astext(coordinates)
FROM \`user_spatial\`
WHERE st_within(coordinates, envelope(linestring(point(-117.07730792871, 32.697490931884), point(-116.90494967129, 32.842418468116))))
ORDER BY st_distance(point(-116.9911288, 32.7699547), coordinates)
I can live with Yii2 adding some backticks around field names and table names but why on earth is it backticking numerical values? (FYI: the $rlon and $rlat values don't seem to get backticked but I was assuming that was because they are a result of math calculations!?!?).
I've already tried forcing $geo->lon and $geo->lat to float values like so:
'lon' => (float)$geo->lon;
or
'lon' => (float)$geo->lon * 1;
but it didn't help.
Try to use array format for select and orderBy methods, like docs suggest:
Besides column names, you can also select DB expressions. You must use
the array format when selecting a DB expression that contains commas
to avoid incorrect automatic name quoting. For example,
$query->select(["CONCAT(first_name, ' ', last_name) AS full_name",
'email']);
In you case it will be like this:
$users = UserSpatial::find()
->select([
'user_id',
'harvesine(y(coordinates), x(coordinates), :lat, :lon) as dist',
'astext(coordinates)'
])
->where('st_within(coordinates, envelope(linestring(point(:rlon1, :rlat1), point(:rlon2, :rlat2))))')
->orderBy(['st_distance(point(:lon, :lat)', 'coordinates)'])
->params([
':lon' => $geo->lon,
':lat' => $geo->lat,
':rlon1' => $rlon1,
':rlat1' => $rlat1,
':rlon2' => $rlon2,
':rlat2' => $rlat2
])
->all();

How get sum of field in the related table in one query in Yii2

Everyone,
I have 2 tables, that related via third:
Structure looks like below:
Playlist: id, channel_id, name, date
Files: id, name, duration
Playlist-rel-Files: list_id, file_id
I'm using ActiveRecord in my app. When I use such query to get last playlist:
$pl = Playlist::find()
->where(['channel_id' => $model->channel_id])
->orderBy(['date' => SORT_DESC])
->limit(1)
->one();
Then I use such code to get all files, that related with last playlist:
$f = $pl->files;
All is OK, and I get Array of files, but I don't need the list of them, I need only SUM of files duration, to get this SUM, I will need to run through array in foreach, my question is:
how can I modify my first query to get Playlist data AND sum of duration of this playlist's files in one query ? is it possible ?
What you are looking for is a statistical query. You can define a new relation for your count e.g
public function getFileCount() {
return $this->hasMany(Files::className(), ['id' => 'file_id'])
->viaTable('playlist-rel-files', ['playlist_id' => 'id'])
->sum('duration');
}
This is then accessible using the magic property $fileCount and can be added to the query via with for eager loading i.e:
$pl = Playlist::find()
->with('fileCount')
->where(['channel_id' => $model->channel_id])
->orderBy(['date' => SORT_DESC])
->one();

Codeigniter/Mysql: Column count doesn't match value count with insert_batch()?

Alright, so i have a huge list (like 500+) of entries in an array that i need to insert into a MySQL database.
I have a loop that populates an array, like this:
$sms_to_insert[] = array(
'text' => $text,
'contact_id' => $contact_id,
'pending' => $status,
'date' => $date,
'user_id' => $this->userId,
'sent' => "1"
);
And then i send it to the database using the built insert_batch() function:
public function add_sms_for_user($id, $sms) {
//$this->db->delete('sms', array("user_id" => $id)); Irrelevant
$this->db->insert_batch('sms', $sms); // <- This!
}
The error message i get is as follows:
Column count doesn't match value count at row 1.
Now, that doesn't make sense at all. The columns are the same as the keys in the array, and the values are the keys value. So, why is it not working?
Any ideas?
user_id turned out to be null in some situations, that's what caused the error.
EDIT: If you replace insert_batch() with a loop that runs insert() on the array keys you will get more clear error messages.