Laravel Collection - Remove Object containing empty array - laravel

I have an Collection of objects - some of which contain empty arrays.
object(Illuminate\Support\Collection)#34225 (1) {
["items":protected]=>
array(0) {
}
}
object(Illuminate\Support\Collection)#34226 (1) {
["items":protected]=>
array(0) {
}
}
object(Illuminate\Support\Collection)#34227 (1) {
["items":protected]=>
array(0) {
}
}
object(Illuminate\Support\Collection)#34228 (1) {
["items":protected]=>
array(0) {
}
}
object(Illuminate\Database\Eloquent\Collection)#23760 (1) {
["items":protected]=>
array(2) {
[0]=>
object(App\Models\File)#23766 (27) {
["primaryKey":protected]=>
string(6) "FileID"
["table":protected]=>
I could use someones help in filtering the Collection of objects so that the objects containing empty arrays are gone/removed.
So that all that remains are the objects with non empty arrays
object(Illuminate\Database\Eloquent\Collection)#23760 (1) {
["items":protected]=>
array(2) {
[0]=>
object(App\Models\File)#23766 (27) {
["primaryKey":protected]=>
string(6) "FileID"
I have populated the collection using
$things = $foos->get($thing->ID, collect());
Any help would be greatly appreciated

You may convert it to an array using toArray() method
$things = $foos->get($thing->ID, collect())->toArray();
foreach($things as $thing) {
if(empty($thing['items'])) {
unset($thing);
}
}
$things = array_values($things);
Or
Using filter()
The filter method filters the collection using the given callback, keeping only those items that pass a given truth test:
$things = $foos->get($thing->ID, collect());
$filtered = $things->filter(function ($value, $key) {
return !empty($value->items) ;
});
$result = $filtered->all();

Collection Unexpectedly :) has method filter.
$collection = collect([
[],
['1'],
[],
]);
$collection->filter(); // will return collection with only [ [1] ]

Related

Iterate through laravel result object

My Code is like below.
$prayers = Prayer_time::where('mosque_id', $request->mosque_id)
->where('month', $month)
->where('date', $date)
->first();
foreach ($prayers as $key => $prayer) {
if ($prayer != null) {
$payer_times[$key] = $prayer;
}
}
return response()->json(['prayer_times' => $payer_times], 200);
I am getting below output.
{
"prayer_times": {
"incrementing": true,
"exists": true,
"timestamps": true
}
}
How can I iterate through result?
Assuming that first one is an API Controller and you want to iterate over the prayer_times in a frontend
res.data.prayers_time.each({
DO WHATEVER
})

Laravel: Cannot reindex collection's array in eager loading after using unset()

I have following code:
// User.php
public function groups() {
return $this->belongsToMany(
Group::class,
'group_user',
'user_id',
'group_id',
'id'
);
}
// Group.php
public function users() {
return $this->belongsToMany(
User::class,
'group_class',
'group_id',
'user_id',
'id'
);
}
And in routes/web.php
Route::get('/test', function () {
$me = App\User::first();
$group = App\Group::with('users')->first();
foreach ($group->users as $user_index => $user) {
// Show all users (a.k.a members) of this group, except myself
if ($user->id == $me->id) {
unset($group->users[$user_index]);
}
}
return $group;
}):
Result:
{
"id": 1,
"name": "ABC Group",
"users": { // This should be array right?
"1": { // This should be start with 0
"id": 2,
"name": "...",
"email": "...",
},
"2": { // This should be 1
"id": 3,
"name": "...",
"email": "...",
}
}
}
What I have tried:
#1 Put values() in the end of foreach loop, like:
foreach ($group->users as $user_index => $user) {
// Show all users (a.k.a members) of this group, except myself
if ($user->id == $me->id) {
unset($group->users[$user_index]);
}
$group->users->values(); // Not working
}
#2 Put values() after the foreach loop, like:
Route::get('/test', function () {
$me = App\User::first();
$group = App\Group::with('users')->first();
foreach ($group->users as $user_index => $user) {
// Show all users (a.k.a members) of this group, except myself
if ($user->id == $me->id) {
unset($group->users[$user_index]);
}
}
$group->users->values(); // Still not working
return $group;
}):
Expected result:
{
"id": 1,
"name": "ABC Group",
"users": [ // Array
{ // index 0
"id": 2,
"name": "...",
"email": "...",
},
{ // index 1
"id": 3,
"name": "...",
"email": "...",
}
]
}
Q: How to reindex collection array in eager loading after using unset()?
Thanks in advance
You've got a few things to unpack here that might help you.
First, your query returns a Laravel collection of users attached to the single model Group. Laravel has a bit of magic in the background that allows for array notation as well, but probably easiest to think about this as a collection for your purposes. In some cases, you can translate this to an array using Laravel's toArray() method, something like:
$userArray = $group->users->toArray();
For dropping an index, or in this case a user from the Group's users, take a look at the forget() method, which works on the collection object.
However, I think you may wish to come at this from the reverse... Pull the unwanted index(es) in a single query, rather than having to loop through the collection after the fact. Something like this may be of value to you:
$me = App\User::first();
$group = App\Group::with(['users' => function($query) use($me){
$query->where('users.id', '!=', $me->id);
}])->first();
This query will remove the unwanted user from the collection right out of the database, eliminating the need for additional code, which is what I think you were after.
HTH.

Laravel Eloquent : Getting object instead of array

Trying to get a hotel in detail for hotel management API, in which for some hotels, getting
$hotel->rooms
as object and for some as array. The eloquent query in Hotel model as below.
public function detail($hotelid) {
return $this->with(['rooms','roomType'])->find($hotelid);
}
public function rooms() {
return $this->hasMany(HotelRooms::class, 'hotels_id')->where('status','active');
}
HotelRoom Model
public function roomType(){
return $this->hasOne(RoomType::class,'id','room_type_id')->where('status','active');
}
Controller
public function __construct(){
$this->model = new Hotel();
}
public function hotelDetail(Request $request){
$data = $this->model->detail($request->input('hotel_id'));
foreach($data->rooms as $key=>$room){
if(!$room->roomType){
unset($data->rooms[$key]);
continue;
}
}
return response()->json([
'status' => true,
'status_message' => 'successful',
'data' => $data,
]);
}
response
{
"id":"id",
"name":"name",
"rooms":{
"1":{},
"2":{}
}
}
{
"id":"id",
"name":"name",
"rooms":[
{},
{},
]
}
When you use unset on array, your array indexes remain for each item. Same as for collection->filter() or for array_filter() that is actually used in collection->filter(). That is why you need to rebuild the indexes:
$data->rooms = array_values($data->rooms->toArray());
to reindex the array.
Or use the foreach, but push values to new array:
$filteredRooms = [];
foreach($data->rooms as $key=>$room){
if(!$room->roomType){
continue;
}
$filteredRooms[] = $room;
}
$data->rooms = $filteredRooms;
Or instead of foreach loop, use filter for collection in combination with values() :
$filteredRooms = $data->rooms->filter(function ($room, $key) {
return (!$room->roomType)? false : true;
})->values();
After filtering array you have to re-index your array.
foreach($data->rooms as $key=>$room){
if(!$room->roomType){
unset($data->rooms[$key]);
continue;
}
}
$data->rooms = $data->rooms->values();

Laravel API : how to get only some fields and sort the response

I would like to know if I do the things correctly.
Let's say I have a table "countries". To get only some fields of this table, in a certain order, I have this url :
/countries?fields=id,country_name&desc=country_name
And the result is clear:
[
{
"id": "SP",
"country_name": "Spain"
},
{
"id": "IT",
"country_name": "Italy"
},
{
"id": "FR",
"country_name": "France"
},
{
"id": "CN",
"country_name": "China"
} ]
To do that I have this route :
Route::get('/countries', 'CountryController#index');
And the method index is :
public function index(Request $request)
{
$query = Country::query();
if ($request->has('fields')){
$fields = explode(',', $request->input('fields') );
foreach ($fields as $field) {
$query->addSelect($field);
}
}
if ($request->has('sort')){
$query->orderBy($request->input('sort'));
}
if ($request->has('desc')){
$query->orderBy($request->input('desc'), 'desc');
}
$countries = $query->get();
return response()->json($countries, 200);
}
It works fine.
My question is am I doing the things correctly ? Is there any other methods ?
To prevent unknown column exception try this:
import Schema class use Illuminate\Support\Facades\Schema;
add this:
$table = "countries";
if ($request->has('fields')){
$fields = explode(',', $request->input('fields') );
foreach ($fields as $field) {
if(!Schema::hasColumn($table,$field)){
return response("Unknown field", 422);
}
$query->addSelect($field);
}
}
What you are doing to me has no vulnerability in it and is very optimized too.
You can try using this:
$countries = $query->pluck('id', 'country_name');
Please check and let me know.

Laravel and redis scan

I am trying to use redis scan with laravel. I can make a single request which returns 10 keys but I wish to loop until all the keys have been returned. I am unsure how to do this with laravel. Currently I have
$test = Redis::scan(0, 'match', '*keypattern*');
I don't know if there is a 'laravel' way of doing this.
EDIT:
I used composer to import predis/predis and got it working with
use Predis\Collection\Iterator;
use Predis;
...
$client = new Predis\Client([
'scheme' => 'tcp',
'host' => 'localhost',
'port' => 6379,
]);
foreach (new Iterator\Keyspace($client, '*keypattern*') as $key) {
$arr[] = $key;
}
but I would like to know the laravel way
EDIT:
var_dump of the single Redis::scan
array(2) {
[0]=>
string(4) "23"
[1]=>
array(10) {
[0]=>
string(19) "key17"
[1]=>
string(19) "key72"
[2]=>
string(76) "key11"
[3]=>
string(19) "key73"
[4]=>
string(19) "key63"
[5]=>
string(19) "key87"
[6]=>
string(19) "key70"
[7]=>
string(19) "key65"
[8]=>
string(19) "key82"
[9]=>
string(19) "key43"
}
}
Thanks #martinczerwi
Here is a non-recursional version:
function scanAllForMatch($pattern)
{
$cursor = 0;
do {
list($cursor, $keys) = Redis::scan($cursor, 'match', $pattern);
foreach ($keys as $key) {
yield $key;
}
} while ($cursor);
}
As the Redis facade passes commands directly to Predis (or Redis itself you might say), this goes hand in hand with the Redis docs (http://redis.io/commands/scan). You can use the cursor (first array entry) for subsequent calls to iterate until the cursor is zero.
I've put together a recursive approach, to scan all entries:
function scanAllForMatch ($pattern, $cursor=null, $allResults=array()) {
// Zero means full iteration
if ($cursor==="0") {
return $allResults;
}
// No $cursor means init
if ($cursor===null) {
$cursor = "0";
}
// The call
$result = Redis::scan($cursor, 'match', $pattern);
// Append results to array
$allResults = array_merge($allResults, $result[1]);
// Recursive call until cursor is 0
return scanAllForMatch($pattern, $result[0], $allResults);
}
Note that you might need to add $this before the recursion, if you use this in a class (would be return $this->scanAllForMatch(...))
You would call it like that:
// Don't pass a cursor yourself!
$allResults = scanAllForMatch('*keypattern*');

Resources