How to customize a collection of objects in Laravel - php

I want to fetch rows from a database, cast them in an array, and then add a custom row so that I can pass the whole thing to a view (Blade file). I've tried using ->push() but I can't seem to get the format of the arrays to match. The below code sample gets rows with the user's id, name, and location.
$profiles = \App\Profiles::orderBy('created_at', 'DESC')->get();
From here, I want to append "distance" to each entry in the collection. I want customized information fetched from the database so that I can pass it seamlessly to a view (Blade file).

You can loop through your array and insert data as below:
$profiles = \App\Profiles::orderBy('created_at', 'DESC')->get();
foreach ($profiles as &$profile) { // loop by reference so it will update current array
$profile->distance = "Your Value";
}
Then you can pass this array to your view. Hope it helps you.

You get a Collection instance, so why don't you use one of its useful methods? In this case Map():
map()
The map method iterates through the collection and passes each value to the given callback. The callback is free to modify the
item and return it, thus forming a new collection of modified items:
$collection = collect([1, 2, 3, 4, 5]);
$multiplied = $collection->map(function ($item, $key) {
return $item * 2;
});
$multiplied->all();
// [2, 4, 6, 8, 10]
So in your case:
$profiles = \App\Profiles::orderBy('created_at', 'DESC')->get();
$profiles->map(function ($profile) {
$profile->distance = 'your value';
return $profile;
});
// Then return it to your view
return view('profiles')->withProfiles('profiles'));
Of course, you can inline all this:
$profiles = \App\Profiles
::orderBy('created_at', 'DESC')
->get()
->map(function($profile) {
$profile->distance = 'your value';
return $profile;
});
// Then return it to your view
return view('profiles')->withProfiles('profiles'));
You said:
I basically want info from the database and my customized info in one
array so that I can pass it easily to a view.
If your desired is to custom/format your data to return it to your view, why noy use Eloquent Resources? Check this section of the documentation.

$profiles is an Eloquent collection, not an array.
$profiles = Profiles::latest()->get();
foreach ($profiles as $profile) {
$profile->distance = 'your value';
}
return view('profile', compact('profiles');

Related

how to make an eloquent result into an array Laravel

I want to combine two data search results into one array, I use array_merge but there is an array_merge() error:
Argument # 1 is not an array
How to turn $vendor's eloquent results into an array and combine it with $plucked?
$vendor = Vendor::find($id);
$vendor_detail = VendorDetail::where('vendor_id',$id)->get();
$plucked = $vendor_detail->pluck('vendor_profile_value','vendor_profile_name');
$coba = array_merge($vendor,$plucked);
$plucked already an array
I think the problem here is that $vendor is not yet an array
You could do it like this:
$vendor = Vendor::find($id);
$vendor_details = VendorDetail
::select('vendor_profile_value', 'vendor_profile_name')
->where('vendor_id', $id)
->get()
->toArray();
$coba = array_merge($vendor,$vendor_details);
The get() method execute the query returning a Collection instance, in which you can call the toArray() method.
Side note
As far as I can see, you could make use of relationships and eager loading.
If you have a one-to-many relationship defined like this in your Vendor model:
public function details()
{
return $this->hasMany(VendorDetails::class);
}
Then, you could eager load the relationship like this:
$vendor = Vendor::with('details')->find($id);
// ^^^^^^^^^^^^^^
You could even just load the wanted fields:
$vendor = Vendor::with('details:vendor_profile_value,vendor_profile_name')
->find($id);
Then, your object will have a new attribute called "details" containing the related objects (or a collection of the limited selected fields).
You can convert the $vendor to an Array like below.
$vendor = Vendor::find($id)->toArray();

Get key from collection item

How do I get the key from a collection item?
$posts= Post::all();
Example (doesn't work):
$key = $posts->where('id', $id)->getKey();
The all() will return a collection without keys. If you're talking about integer keys like 0, 1, 2 etc, use the search() method:
$key = $posts->search(function($i) use($id) {
return $i->id === $id;
});
Try $post_ids = Post::pluck('id');
That grabs just the id column from all the Post records and returns them as a collection.
If you want just a plain array, add toArray():
$post_ids = Post::pluck('id')->toArray();

Laravel - Carry array through map

Let's say I have a model collection that I'm mapping through like this:
$alreadyImported = [];
$players = Players::whereNotIn('id', $alreadyImported)
->get()
->random(25)
->pluck('id');
$groups = $players->map(function ($item, $key) use ($alreadyImported) {
array_merge($alreadyImported, $item->id);
$group = [
'username' => $item['username'],
];
return $group;
});
// $groups is a pivot table with group and players
Why does my $globalList always start at []? How can I carry the already-merged $globalList to the next map iteration?
The player IDs does not matter. It's for show. I am looking to pass the array through the map iterations.
Just use pluck() to get IDs from the collection:
$ids = $players->pluck('id');
Or, if you just need IDs:
$ids = Players::where('banned', false)->pluck('id');
If you're going to add any other data, you don't need to merge it to some array or a collection because map() will create a new collection.
Finally, you don't need to use collect() because get() will return collection.

Laravel 5.3 pagination on sortBy collection

Why the paginate method doesn't work on this example ?
$shopIds = Follower::whereUserId($user->id)->orderBy('created_at','desc')->get()->pluck('shop_id')->toArray();
$shops = Shop::whereIn('id',$shopIds)->with('deals')->with('deals.likes')->paginate($this->perPage)->sortBy(function($likes) {
return $likes->count();
});
dd($shops);
Thank's for help ;)
The paginate is just working fine but the sortBy method is creating the problem for you because when you use sortBy it returns a new collection.
So finally your $shops is an instance of Illuminate\Support\Collection not of Illuminate\Pagination\LengthAwarePaginator.
You can try it as:
$paginated_shops = Shop::whereIn('id',$shopIds)
->with('deals')
->with('deals.likes')
->paginate($this->perPage);
$shops = $paginated_shops->sortBy(function($likes) {
return $likes->count();
});
$shops = new LengthAwarePaginator($shops, $paginated_shops->total(), $paginated_shops->perPage());
Remember to add use statement at the top of the class as:
use Illuminate\Pagination\LengthAwarePaginator;
Thank you guys!
I fix it like that
$shopIds = Follower::whereUserId($user->id)->orderBy('created_at','desc')->get()->pluck('shop_id')->toArray();
$shopIds = Shop::whereIn('id',$shopIds)->with('deals')->with('deals.likes')->get()->sortBy(function($likes) {
return $likes->count();
})->pluck('id')->toArray();
$orderedIds = implode(',',$shopIds);
$shops = Shop::whereIn('id', $shopIds)->whereNotIn('user_id', [$user->id])->orderByRaw(\DB::raw("FIELD(id, ".$orderedIds." )"))->paginate($this->perPage);
dd($shops);
and now I have an instance of Illuminate\Pagination\LengthAwarePaginator
Thank's
Like #Amit mentionned, $shops is a collection, thus not a LengthAwarePaginator. You are going to have to build the paginator and slice it yourself... not too complicated...
For example, let's assume you are hitting your route, calling your controller... then returning results to a view... Now that route is going to get requested with the pagination info in the request, you job is to get the result $shops into an array, slice the array into the right result for the page and return that to your view... I have not run this code... so take this as an example... but it will look something like:
public function yourController{
// This will return a collection... we will convert it to array later
$shops = Shop::whereIn('id',$shopIds)->with('deals')->with('deals.likes')->paginate($this->perPage)->sortBy(function($likes) {
return $likes->count();
});
$shopsArray = $shops->toArray();
// Here we deal with the slicing... you need to slice the page you are
// at the paging info is in the request...
// this basically gets the request's page variable... or defaults to 1
$page = Paginator::resolveCurrentPage('page') ?: 1;
// Assume 15 items per page... so start index to slice our array
$startIndex = ($page - 1) * 15;
// Length aware paginator needs a total count of items... to paginate properly
$total = count($shopsArray);
// Eliminate the non relevant items by slicing the array to page content...
$results = array_slice($shopsArray, $startIndex, 15);
$result = new LengthAwarePaginator($results, $total, 15, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => 'page',
]);
return view('yourView', compact('result'));
}

Laravel 5.2 - filtering on a custom attribute and then paginating

So I know how to paginate using paginate() and I know how to filter based on an Accessor (a where() on the collection). However, paginate takes in a query builder and where() on a collection returns a collection.
So if I want to get a bunch of items / filter by a custom attribute and then paginate the result set....how do i do that??
Accessor:
public function getRequiredToReportAttribute()
{
// return boolean based off of complicated business logic
}
index method:
public function index()
{
//what im doing (redacted)
$employers = (new App\Employers')->paginate($this->perPage);
// what I would like to be doing
$employers = (new App\Employers)->where('required_to_report', '=', true)->paginate($this->perPage);
return $this->sendResponse($employers);
}
In the case that you want to work with accesors, you could by iterating the collection after you get your query, something like this:
$result = Model::get()->filter(function($item) {
return $item->require_to_report === true;
});
Here you have all records of your model and then you could create a manual paginator:
$paginator = new Illuminate\Pagination\Paginator($result, 10);
you have with this approach a weakness when you have too many records, the performance could be affected.
Based off of Jose Rojas answer and this post I built a LengthAwarePaginator for a collection filtering on an attribute accessor. Here's an example of how to do it:
$collection = Model::all();
//Filter your collection
$filteredItems = $collection->filter(function($col) {
return $col->require_to_report === true;
});
// Setup necessary information for LengthAwarePaginator
$currentPage = LengthAwarePaginator::resolveCurrentPage();
$pageLimit = 20;
// slice the current page items
$currentItems = $filteredItems->slice(pageLimit * ($currentPage - 1), pageLimit)->values();
// you may not need the $path here but might be helpful..
$path = "/api/v1/employers";
// Build the new paginator
$paginator = new LengthAwarePaginator($currentItems, count($filteredItems), $pageLimit, $currentPage, ['path' => $path]);
return $paginator;

Categories