I was trying to build an API with Laravel but I failed to categories data from a table.
The API JSON should look like this:
[
{
id: 1,
name: 'John Doe',
birth_place: 'Birmingham',
mathematics: [
{
"id": 2,
"title": "Beginner Level in Mathematics",
},
{
"id": 4,
"title": "Intermediate Level in Mathematics",
},
],
physics: [
{
"id": 1,
"title": "Beginner Level in Physics",
},
{
"id": 3,
"title": "Intermediate Level in Physics",
},
]
},
{id: 2,
name: 'John Best',
birth_place: 'London',
chemics: [
{
"id": 5,
"title": "Beginner Level in Chemics",
},
],
}
]
But currently my data looks like this:
[
{
id: 1,
name: 'John Doe',
birth_place: 'Birmingham',
{
"id": 1,
"title": "Beginner Level in Physics",
"topic": physics
},
{
"id": 2,
"title": "Beginner Level in Mathematics",
"topic": mathematics
},
{
"id": 3,
"title": "Intermediate Level in Physics",
"topic": physics
},
{
"id": 4,
"title": "Intermediate Level in Mathematics",
"topic": mathematics
},
},
{id: 2,
name: 'John Best',
birth_place: 'London',
{
"id": 5,
"title": "Beginner Level in Chemics",
"chemics": mathematics
},
}
]
I was reading about ResourceCollection in Laravel but couldn't figure out how to transform the json output.
All Eloquent relations are setup and are working fine. It is just the problem how to transform the json into 'categories' which come from a table column called "topic".
In my api.php -routes I define only one Controller and this controller is returning the User resource collection:
use App\Http\Controllers\UserController;
Route::get('/users', [UserController::class, 'index']);
Route::get('/users/{id}', [UserController::class, 'show']);
UserController
namespace App\Http\Controllers;
use App\Models\User;
use App\Http\Resources\UserResource;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function index()
{
$users = User::with('exams')->get();
return UserResource::collection($users);
}
public function show($id)
{
$user = User::with('exams')->findOrFail($id);
return new UserResource($user);
}
}
UserResource
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'birth_place' => $this->birth_place,
'topic' => ExamResource::collection($this->whenLoaded('exams'))
];
}
}
ExamResource
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ExamResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'topic' => $this->topic,
];
}
}
ATTENTION: THIS IS JUST AN EXAMPLE AND IS NOT APPLICAPBLE FOR CODE MENTIONED ABOVE) I know that there must be something like this with a map function:
return [
'data' => $this->collection->map(function ($user) {
return [
'personal_info' => [
'id' => $user->id,
'name' => $user->name,
'age' => $user->age,
],
'contact_info' => [
'email' => $user->email,
'phone' => $user->phone,
],
'address_info' => [
'address' => $user->address,
'city' => $user->city,
'state' => $user->state,
'postal_code' => $user->postal_code,
],
];
}),
'meta' => [
'total' => $this->collection->count(),
// Add more metadata if needed
],
];
To summarize everything.
I define the routes which calls the controller to take action.
Then in my controller I call the model and give my user contoller this data. This is the place where I define my main json array? I call my examcontroller here as part of the json array
As a seperate resource file where I define my array for this resource. This will be used in usercontroller.
I would like to understand where I have to define the map or count or whatever (in normal resource file or resourceCollection file? If I need both files how I can call the resourcecollection file in the resource file?
For example, in future I would also like to have aggregates for the exams like:
[
{
id: 1,
name: 'John Doe',
birth_place: 'Birmingham',
aggregates: [
mathematics: 2,
physics: 2
],
mathematics: [
{
"id": 2,
"title": "Beginner Level in Mathematics",
},
{
"id": 4,
"title": "Intermediate Level in Mathematics",
},
],
physics: [
{
"id": 1,
"title": "Beginner Level in Physics",
},
{
"id": 3,
"title": "Intermediate Level in Physics",
},
]
},
{id: 2,
name: 'John Best',
birth_place: 'London',
aggregates: [
chemics: 1,
]
chemics: [
{
"id": 5,
"title": "Beginner Level in Chemics",
},
],
}
]
I hope someone can help me or how I can get more examples to understand API resource better. I was searching the internet for long time and really couldn't figure it out.
And sorry for any non-technical wording, I am not familiar with Laravel because I think I am still a beginner in this area.
The problem is in your UserResource. As kris gjika mentioned in the comments, your ExamResource is not structuring the data the way you want it to be output.
Your desired output has the user’s id, name, birth_place and the a key for each topic with a set of entries. These are the same as the related exams but grouped by the topic value then with the topic value removed from the data.
So you need your UserResource to include a key and value for every topic. One approach would be to group exams by topic and then spread the resulting collection into the return array. This would give you the required key for each topic
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'birth_place' => $this->birth_place,
…$this->whenLoaded('exams’)->groupBy(‘topic)
];
}
}
You'll be left with the topic key/value in each result still but you can add another map to remove those. Something like:
…$this->whenLoaded('exams’)->groupBy(‘topic)
->map(function($topic) {
return $topic->map(function($result) {
return [
'id' => $result['id'],
'title' => $result['title'],
];
});
});