How to access a nested field in json with Laravel? - php

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);

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

Laravel 7.0 pagination not working in method chain

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

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.

Add extra key in laravel eloquent result

I have a laravel query builder result that looks like this
{
"data": [
{
"id": "",
"awardID": 2,
"title": "Dummy title",
"status": "active",
"raceStart":"",
"raceEnd:":""
}
]
}
What i want to output is something like this
{
"data": [
{
"id": "",
"awardID": 2,
"title": "Dummy title",
"status": "active",
"period": {
"raceStart":"",
"raceEnd:":""
}
}
]
}
This would have been much easier if the period was a table with a 1 to 1 relationship with parent table but this is not the case here.
How can this be achieved?
Check if this will work. I haven't tried though but according to documentation we can add accessor and mutators. But it will change every response you are doing with the model.
Using Eloquent
// Your Model
class Race extends Model
{
{...}
protected $appends = ['period'];
// accessor
public function getPeriodAttribute($value)
{
$this->attributes['period'] = (object)[];
$this->attributes['period']['raceStart'] = $this->attributes['raceStart'];
$this->attributes['period']['raceEnd'] = $this->attributes['raceEnd'];
unset($this->attributes['raceStart']); = $value;
unset($this->attributes['raceEnd']);
return $this->attributes['period'];
}
}
Now when you will access $race->period will give the raceStart and raceEnd value.
Ref: https://laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators
else another option is after query, do a map
{...}
->map(function($data) {
$data->period = (object)[];
$data->period['raceStart'] = $data->raceStart;
$data->period['raceEnd'] = $data->raceEnd;
unset($data->raceStart);
unset($data->raceEnd);
return $data;
});
Ref: https://laravel.com/docs/5.4/eloquent-collections#introduction
Using QueryBuilder
$races = DB::table('races')->get();
$races = array_map(function ($data) {
$data->period = (object)[
"raceStart" => $data->raceStart,
"raceEnd" => $data->raceEnd
];
unset($data->raceStart);
unset($data->raceEnd);
return $data;
}, $races->data);

Categories