Laravel 7.0 pagination not working in method chain - php

Here i am using method chaining on Laravel Eloquent model (User) with pagination,but after calling method each pagination stop working.
Is this behaviour expected or i am missing something. there noting mentioned about this on official docs.
Works fine
User::paginate(10)
->appends(request()->all());
Output
{
"data": [
{
"id": 1,
"email": "user#email.com",
},
{
"id": 2,
"email": "two#email.com",
},
],
"current_page": 1,
"first_page_url": "//localhost/users?page=1",
"last_page": 5,
"last_page_url": "//localhost/users?page=5",
"next_page_url": "//localhost/users?page=2",
"path": "//localhost/users",
"per_page": 10,
"prev_page_url": null,
"total": 50
}
But problem arrives when i call each() method on it
Not working
User::paginate(10)
->appends(request()->all())
->each(function ($user) {
$user['someAttribute'] = 'value';
return $user;
})
Output (pagination not working)
plain simple result only query records. (Omitted pagination info)
[
{
"id": 1,
"email": "user#email.com",
},
{
"id": 2,
"email": "two#email.com",
},
]

I don't think you can retrieve the pagination properties after altering the $items. You'd have to convert the altered data into the LengthAwarePaginator object manually.
$users = User::paginate(15);
$alteredUsers = $users->getCollection()
->each(function($user) {
$user['someAttribute'] = 'value';
return $user;
});
$newPaginatedUsers = new \Illuminate\Pagination\LengthAwarePaginator(
$alteredUsers,
$users->total(),
$users->perPage(),
$users->currentPage(),
[
'path' => \Request::URL(), // optional
]
)->appends(request()->all());
You can look at the source here and have a better idea of how to build the object.

Create an accessor:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function getSomeAttributeAttribute()
{
return 'value';
}
}
$users = User::paginate(10)->appends(request()->all());
https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor
https://laravel.com/docs/8.x/eloquent-serialization#appending-values-to-json

Related

Appended one accessor but resource has more than one, how is this possible? - Laravel

I am trying to use an accessor on a model to return the status whether a relationship exists.
My User model:
class User {
protected $appends = ['has_profile'];
public function profile()
{
return $this->hasOne(Profile::class)
}
public function getHasProfileAttribute()
{
$exists = $this->profile;
if($exists){
return 1;
}
else{
return 0;
}
}
}
The problem is when the User model is loaded via User::find(1)->get();, the profile property is also loaded into JSON resource whereas, I only want the has_profile attribute in my JSON return. How should I query the relationship existence without loading it, or should I unload the relationship?
What I Get
"data": {
"id": 270,
"name": "John Doe",
"mobile_number": "01234567890",
"created_at": "2021-08-19T06:55:33.000000Z",
"updated_at": "2021-08-19T06:55:33.000000Z",
"deleted_at": null,
"has_profile": 1,
"profile": {
"id": 1,
"details": "Details"
}
}
What I want
"data": {
"id": 270,
"name": "John Doe"
"mobile_number": "01234567890",
"created_at": "2021-08-19T06:55:33.000000Z",
"updated_at": "2021-08-19T06:55:33.000000Z",
"deleted_at": null,
"has_profile": 1
}
Updated Solution
The problem was $this->profile which led to the profile relation being attached. When used as $this->profile()->get(); or $this->profile()->first(); it works as expected.
You can use unset to remove the attribute profile.
public function getHasProfileAttribute()
{
$exists = $this->profile;
unset($this->profile);
if($exists){
return 1;
}
else{
return 0;
}
}
You can use the except() method from the documentation
User::find(1)->get()->except('profile');
Maybe you have to change order i can't test right now but it's the idea

How to remove pivot keyword from json using laravel?

I am trying to fetch data from games table which has pivot table user_games. Below code if works fine for me
$UserGames = User::with(['games' => function ($query){
$query->withPivot('highscore','level');
}])->find(request()->user()->id);
I am getting following json response
{
"data": [
{
"id": 2,
"name": "culpa",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27",
"pivot": {
"user_id": 2,
"game_id": 2,
"highscore": 702,
"level": 3
}
}
]
}
But I wanted to remove pivot keyword from above json and pull pivot detail into root as like below my desire response
{
"data": [
{
"id": 2,
"name": "culpa",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27",
"user_id": 2,
"highscore": 702,
"level": 3
}
]
}
Can someone kindly guide me how to fix the issue. I would appreciate. Thank you so much
You can utilise hidden and appends on the pivot model to re-structure the returned data.
class PivotModel extends model
{
protected $hidden = ['pivot'];
protected $appends = ['user_id'];
public function getUserIdAttribute()
{
return $this->pivot->user_id;
}
}
Reference for hidden
Reference for appends
You can convert the json into an array than reconvert it to json.
$UserGames = User::with(['games' => function ($query){
$query->withPivot('highscore','level');
}])->find(request()->user()->id);
$UserGames = json_decode($UserGames, true);
$pivot = $UserGames['data'][0]['pivot'];
unset($UserGames['data'][0]['pivot']);
$UserGames = json_encode(array_merge($UserGames[0], $pivot));
You can override the User model's jsonSerialize method which is called in the toJson method, this is the initial method body:
public function jsonSerialize()
{
return $this->toArray();
}
And you can do something like this:
public function jsonSerialize()
{
$attrs = $this->toArray();
if (isset($attrs['pivot'])) {
$attrs = array_merge($attrs, $attrs['pivot']);
unset($attrs['pivot']);
}
return $attrs;
}

Laravel sort object by Key

I have the following which I would like to order alphabetically by the Key i.e first for each array group would be "bname", followed by "created_at".
{
"leads": [
{
"lead_id": 1,
"zoho_lead": null,
"bname": "ABC Limited",
"tname": "ABC",
"source_id": 11,
"industry_id": 1,
"user_id": 1,
"created_at": "2017-09-06 15:54:21",
"updated_at": "2017-09-06 15:54:21",
"user": "Sean McCabe",
"source": "Unknown",
"industry": "None"
},
{
"lead_id": 2,
"zoho_lead": 51186111981,
"bname": "Business Name Limited",
"tname": "Trading Name",
"source_id": 11,
"industry_id": 1,
"user_id": 1,
"created_at": "2017-06-01 12:34:56",
"updated_at": null,
"user": "John Doe",
"source": "Unknown",
"industry": "None"
}
]
}
I'm trying to use ksort like so in the foreach loop:
class LeadController extends Controller
{
use Helpers;
public function index(Lead $leads)
{
$leads = $leads->all();
foreach($leads as $key => $lead){
$lead->user = User::where('id', $lead->user_id)->first()->name;
$lead->source = Source::where('id', $lead->source_id)->first()->name;
$lead->industry = Industry::where('id', $lead->industry_id)->first()->name;
$lead->ksort();
}
return $leads;
}
But I get the following error:
Call to undefined method Illuminate\\Database\\Query\\Builder::ksort()
How do I use this function, or is there a Laravel way of doing this, or a better way altogether?
Thanks.
Managed to get it to return with the Keys in alphabetical order, so below is the solution in-case someone else should require it:
public function index(Lead $leads)
{
$leadOut = Array();
$leads = $leads->all();
foreach($leads as $key => $lead){
$lead->user = User::where('id', $lead->user_id)->first()->name;
$lead->source = Source::where('id', $lead->source_id)->first()->name;
$lead->industry = Industry::where('id', $lead->industry_id)->first()->name;
//Convert to Array
$leadOrder = $lead->toArray();
//Sort as desired
ksort($leadOrder);
//Add to array
$leadOut[] = $leadOrder;
}
return $leadOut;
}
There is likely a cleaner way to do this, but it works for my instance, and perhaps additional answers may be posted that are better.
You could do something like:
return Lead::with('user', 'source', 'industry')->get()->map(function ($lead) {
$item = $lead->toArray();
$item['user'] = $lead->user->name;
$item['source'] = $lead->source->name;
$item['industry'] = $lead->industry->name;
ksort($item);
return $item;
});
This should be much more efficient as it will eager load the relationships rather than make 3 extra queries for each iteration.

Laravel Eloquent - how to join a table

I working on an API for my app.
I'm trying to pull items from the database and return them in JSON object,
my items table looks like this:
Items
-id
-name
-description
-price
-currency_id
-company_id
this is how I'm getting the items:
$rows = Company::where('guid',$guid)
->first()
->items()
->orderBy('name', $sort_order);
I want to replace the currency_id with a currency object that contains all the columns of currency table
so my result will be like this:
[
{
'id':'1',
'name':'name',
'description': 'example',
'price':'100',
'currency':{
'id':'1',
'name':'usd',
'symbol': '$'
}
}
]
update:
This is my currencies table:
id
name
symbol
code
Edit 2: The user's problem was more complex than this since there was pagination and search integration with the query. Helped with https://pastebin.com/ppRH3eyx
Edit : I've tested the code. So here.
In Company model
public function items()
{
return $this->hasMany(Item::class);
}
In Item model
public function currency()
{
return $this->belongsTo(Currency::class);
}
Controller logic
$items = Company::with(['items' => function($query) use ($sort_order) {
$query->with('currency')->orderBy('name', $sort_order);
}])
->where('guid', $guid)
->first()
->items;
Result with test data
[
{
"id": 2,
"name": "Toy",
"description": "Random text 2",
"price": 150,
"company_id": 1,
"currency_id": 1,
"currency": {
"id": 1,
"name": "usd",
"symbol": "$",
"code": "USD"
}
},
{
"id": 1,
"name": "Phone",
"description": "Random text",
"price": 100,
"company_id": 1,
"currency_id": 1,
"currency": {
"id": 1,
"name": "usd",
"symbol": "$",
"code": "USD"
}
}
]
Try this.
$rows = Company::with('items.currency')
->where('guid', $guid)
->first()
->items()
->orderBy('name', $sort_order);
Try below
Make one relationship in Item Model
public function currencies() {
return $this->hasMany('App\Currency');
}
then do below in your controller
$row=Items::All()->with('currencies');

How to access a nested field in json with Laravel?

Hello I need to access to a nested field within a json file. I am using Laravel 5 and I have this in controller:
public function getLanguages($id){
$user=User::find($id);
if (!$user)
{
return response()->json(['errors'=>array(['code'=>404,'message'=>'No se encuentra un usuario con ese código.'])],404);
}
return Response::make(json_encode($user->languages), 200)->header('Content-Type', 'application/json');
}
This is the output:
[
{
"id": 1,
"name": "Español",
"pivot": {
"id_user": 1,
"id_language": 1
}
},
{
"id": 2,
"name": "Inglés",
"pivot": {
"id_user": 1,
"id_language": 2
}
},
{
"id": 3,
"name": "Alemán",
"pivot": {
"id_user": 1,
"id_language": 3
}
}
]
I want to access to the name of languages that a user has, how can I do that? This is a many to many relationship between User and Language, is better access to the languages trough the User model? or access from the pivot table?
Thanks.
Try This
$user = User::find($id)->languages->toArray();
$languages = array_column($user, 'name');
return Response::make(json_encode($languages), 200)->header('Content-Type', 'application/json');
Since $user->languages is an array containing the languages, you could use array_map, e.g.
function extract_name($el) {
return $el['name'];
}
$names = array_map('extract_name', $user->languages);

Categories