Laravel 5 + DingoApi paginate - laravel-5

i'm developing an api with Laravel and DingoAPI which returns the message threads of the user with pagination.
$threads = Thread::forUser($currentUserId)->latest('updated_at')->simplePaginate(1);
return API::response()->array($threads);
and i get this kind of response :
{
"per_page": 1,
"current_page": 1,
"next_page_url": "http://my.app/api/messages?page=2",
"prev_page_url": null,
"from": 1,
"to": 1,
"data": [
{
"id": 1,
"subject": null,
"created_at": "2016-03-18 12:33:38",
"updated_at": "2016-03-18 12:33:38",
"deleted_at": null
}
]
}
What can i do to remove some field of the response ? i just need data and next_page_url …
i tried this way :
$array_response = [
'next_page_url' => $threads['next_page_url'],
'data' => $threads['data']
];
but only null values are returned.
I guess Dingo Api is doing some work behind the scene…

Try this
$threads = Thread::forUser($currentUserId)->latest('updated_at')->simplePaginate(1);
$arrayResponse = json_decode(API::response()->array($threads));
$arrayResponseEdited = [
'next_page_url' => $arrayResponse['next_page_url'],
'data' => $arrayResponse['data']
];
return arrayResponseEdited;

Related

Laravel mapWithKeys() returns single value

In laravel I'm getting products array that looks like this
dd($activity->load('products'));
"products": [
{
"id": 1,
"pivot": {
"activity_id": 1,
"product_id": 10,
"quantity": 16
}
},
{
"id": 2,
"pivot": {
"activity_id": 1,
"product_id": 11,
"quantity": 20
}
}
]
I want to get an array of objects and pluck only the pivot.product_id and pivot.quantity from the products array
Expected result:
"selected_products": [
{
"id": 10,
"quantity": 16
},
{
"id": 11,
"quantity": 20
}
]
What I tried
return $activity
->load('products')
->products()
->get()
->mapWithKeys(function ($product, $key) {
return ['id' => $product->pivot->product_id, 'quantity' => $product->pivot->quantity];
});
Result
{
"id": 10,
"quantity": 16
}
There are 2 problems on your code.
First, load('products') is redundant. You cam simple call $activity->products. Or if you need filter on the products , use $activity->products()->where(...)->get()
Second, you should use map for indexed array. mapWithKeys is using for associative array.
return $activity->products
->map(function ($product, $key) {
return ['id' => $product->pivot->product_id, 'quantity' => $product->pivot->quantity];
});

Update Collection value

I am trying to update a column in a Collection but nothing seems to work.
Tried update and $kk->amount = 100 and ['amount'] = 100 but none of that is working.
$newReturnBoxes = collect([
{
"boxable_id": 2,
"box_id": 1,
"boxable_type": "return",
"amount": 3,
"created_at": "2022-03-29T08:29:03.000000Z",
"updated_at": "2022-03-29T08:29:03.000000Z"
},
{
"boxable_id": 3,
"box_id": 1,
"boxable_type": "return",
"amount": 4,
"created_at": "2022-03-29T08:32:02.000000Z",
"updated_at": "2022-03-29T08:32:02.000000Z"
},
{
"boxable_id": 3,
"box_id": 2,
"boxable_type": "return",
"amount": 2,
"created_at": null,
"updated_at": null
}
]);
$returnBoxes->each(function ($trx) use ($newReturnBoxes) {
$amount = $trx->amount;
$boxId = $trx->box_id;
if ($newReturnBoxes->contains('box_id', $boxId)) {
// Already contains key, increment amount
$kk = $newReturnBoxes->firstWhere('box_id', $boxId);
$kk['amount'] = 100; // actually want to increase prev amount with current amount
} else {
$newReturnBoxes->push([
'box_id' => $boxId,
'amount' => $amount
]);
}
});
Maybe the problem is that you are not passing the $newReturnBoxes by reference.
Try to replace this:
use ($newReturnBoxes)
With this:
use (&$newReturnBoxes)
Reference: https://www.php.net/manual/en/language.references.pass.php

i want to change my API response to become array of array object in laravel

i have a problem for the response, i want to change the response API because i need for my mobile APP, the feature have filter object based on date. So i hope you all can help me to solve the problem
i wanna change the response for my API
before:
{
"tasks": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
},
{
"id": 5,
"user_id": 1,
"title": "ghf",
"date": "2022-02-17 16:05:00",
"deskripsi": "fghf",
"created_at": "2022-02-09T06:05:12.000000Z",
"updated_at": "2022-02-09T06:05:12.000000Z"
},
{
"id": 6,
"user_id": 1,
"title": "fgh",
"date": "2022-02-17 18:05:00",
"deskripsi": "gh",
"created_at": "2022-02-09T06:05:40.000000Z",
"updated_at": "2022-02-09T06:05:40.000000Z"
}
]
}
here is the code for the response API above
return response([
'tasks' => Task::where('user_id', auth()->user()->id)->where('date','>',NOW())->orderBy('date','asc')->get(),
],200);
and i want to change it my response API into this response
{
"tasks": [
{
"date": "2022-02-10",
"task": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
},
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-10 15:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
}
]
},
{
"date": "2022-02-12",
"task": [
{
"id": 7,
"user_id": 1,
"title": "gh",
"date": "2022-02-12 13:05:00",
"deskripsi": "gfh",
"created_at": "2022-02-09T06:05:56.000000Z",
"updated_at": "2022-02-09T06:05:56.000000Z"
}
]
},
]
}
Do groupBy on the resulting Collection from the query (see docs: https://laravel.com/docs/9.x/collections#method-groupby)
For example, you could do:
$tasksGroupedByDate = Task::where(.......)
->get()
->groupBy(fn (Task $task) => $task->date->format('Y-m-d'));
(Note: above uses PHP 7.4 arrow functions. Also, add a date cast on the date column in your Task model to be able to use ->format( directly on the date field)
The above code results to:
{
'2022-01-01' => [
{ Task object },
{ Task object },
{ Task object },
],
'2022-01-02' => [
{ Task object },
{ Task object },
{ Task object },
],
}
(used Task object for brevity, but that will be ['id' => 1, 'title' => 'Task name', .....])
To morph that to the structure you want, you can use map and then values to remove the keys and turn it back to an ordered array:
$tasksGroupedByDate->map(fn ($tasksInGroup, $date) => [
'date' => $date,
'task' => $tasksInGroup
])->values();
If you want to combine everything into one method chain:
return [
'tasks' => Task::where(......)
->get()
->groupBy(fn (Task $task) => $task->date->format('Y-m-d'))
->map(fn ($tasksInGroup, $date) => [
'date' => $date,
'task' => $tasksInGroup
])
->values(),
];
It sounds like you want to create a human friendly date field based on the date column, then group by it.
While solutions do exists to accomplish this at the database level, I believe you'd still need to loop around it again afterwards to get the hierarchy structure you're looking for. I don't think it's too complicated for PHP to loop through it.
My suggestion is as follows:
Before:
return response([
'tasks' => Task::where('user_id', auth()->user()->id)
->where('date','>',NOW())->orderBy('date','asc')->get(),
],200);
After:
$out = [];
$tasks = Task::where('user_id', auth()->user()->id)
->where('date','>',NOW())->orderBy('date','asc')->get();
foreach($tasks as $task) {
$date = strtok((string)$task->date, ' ');
if (empty($out[$date])) {
$out[$date] = (object)['date' => $date, 'task' => []];
}
$out[$date]->task[] = $task;
}
$out = array_values($out);
return response(['tasks' => $out], 200);
Note in the above I'm using the function strtok. This function might look new even to the most senior of php developers.... It's a lot like explode, except it can be used to grab only the first part before the token you're splitting on. While I could have used explode, since the latter part after the token isn't needed, strtok is better suited for the job here.
$task = Task::where('user_id', auth()->user()->id)->where('date','>',NOW())->orderBy('date','asc')->get();
foreach($task as $item){
$date[] = item->date;
$result = Task::where('user_id', auth()->user()->id)->where('date','=', $date)->get();
}
return response([
'tasks' =>
['date' => $date,
'task' => $task]
],200);
maybe something like this

laravel pass array see object

I'm stuck and probably missing something really obvious.. but..
I'm trying to pass an array of roles via jwt to my SPA (not a jwt question - that bit works fine)
I get my list of role names via
$roles = $this->roles->pluck('title')->toArray();
the roles function is
public function roles()
{
return $this->belongsToMany(Role::class);
}
in laravel this works fine and logging $roles out I see ["Admin"]
on the SPA response, however, I see the entire roles object joined to the user object (the user object is supposed to be passed over here) eg:
{
"user": {
"id": 1,
"name": "Admin",
"email": "example#example.com",
"email_verified_at": null,
"user_loggedin_state": null,
"user_login_time": null,
"user_login_hash": "",
"user_log_out_time": null,
"user_phone": "123456",
"user_job": null,
"created_at": null,
"updated_at": null,
"deleted_at": null,
"team_id": 1,
"roles": [
{
"id": 1,
"title": "Admin",
"created_at": null,
"updated_at": null,
"deleted_at": null,
"pivot": {
"user_id": 1,
"role_id": 1
}
}
]
}
}
What I want is
{
"user": {
"id": 1,
"name": "Admin",
"email": "example#example.com",
"email_verified_at": null,
"user_loggedin_state": null,
"user_login_time": null,
"user_login_hash": "",
"user_log_out_time": null,
"user_phone": "123456",
"user_job": null,
"created_at": null,
"updated_at": null,
"deleted_at": null,
"team_id": 1,
"roles": ["Admin"]
}
}
I definitely pass the array to jwt, not the object - to validate this I wrapped the $roles in a function
public function rolesArray()
{
$roles = $this->roles->pluck('title')->toArray();
Log::info($roles);
return $roles;
}
and the jwt fn
public function getJWTCustomClaims() {
return [
'roles' => $this->rolesArray(),
];
}
Use API Resources for getting your own outputs.
Run this command to make a new resource for getting users:
php artisan make:resource UserListResource
In App/Http/Resourcers open UserListResource.php file
Change toArray method to:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'email_verified_at' => $this->email_verified_at,
'user_loggedin_state' => $this->user_loggedin_state,
'user_login_time' => $this->user_login_time,
'user_login_hash' => $this->user_login_hash,
'user_log_out_time' => $this->user_log_out_time,
'user_phone' => $this->user_phone,
'user_job' => $this->user_job,
'create_dates' => [
'created_at_human' => $this->created_at->diffForHumans(),
'created_at' => $this->created_at
],
'update_dates' => [
'updated_at_human' => $this->updated_at->diffForHumans(),
'updated_at' => $this->updated_at
],
'deleted_dates' => [
'deleted_at_human' => $this->deleted_at->diffForHumans(),
'deleted_at' => $this->deleted_at
],
'team_id' => $this->team_id
'roles' => new RolesShowResource($this->roles),
];
}
Then make Roles resources:
php artisan make:resource RolesShowResource
Open the RolesShowResource.php and do these changes:
public function toArray($request)
{
return [
'title' => $this->title,
];
}
So when you want to return objects try using the code below in fetching users:
return UserListResource::collection($users);
ok the bleeding obvious was that all this data is of course in the JWT payload..
I was confused since the moment I added stuff in the custom fields - Laravel sent the User object across in the response as well (which I want to find a way of stopping since its in the clear)

Paginate while returning array Api resource object

I need to send multiple object to the api resources. So I am returning multiple Object inside array.Everything works fine but not showing any pagination meta data. like Current_page,next_page url etc.
return [
'across_city' => ServiceProviderCollection::collection($across_city),
'near_by' => ServiceProviderCollection::collection($near_by)
];
My resource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class ServiceProviderCollection extends Resource
{
public function toArray($request)
{
return
[
'name'=>$this->forename,
'rating'=>$this->rating,
'price'=>$this->price,
'distance'=>round($this->distances,1),
];
}
}
My Controller:
here I have two type of users . near_by users and Users across the city. I sent them to the ServiceProviderCollection As an array.
$user_city = $userObj->location->city;
$locationObj = Location::whereNotIn('id', $blockeduser)->where('city',$user_city)->first();
$across_city = $locationObj->users()->simplePaginate(20);
$near_by = User::join('locations as l', 'users.location_id', '=', 'l.id')
->select('users.*', DB::raw('(6371 * acos(cos(radians(' . $coordinates['latitude'] . ')) * cos(radians(`lat`)) * cos(radians(`lng`) - radians(' . $coordinates['longitude'] . ')) + sin(radians(' . $coordinates['latitude'] . ')) * sin(radians(`lat`)))) as distances'))
->having('distances', '<', $max_distance)
->orderBy('distances', 'ASC')
->where('role_id',2)
->whereNotIn('id', $blockeduser)
->simplePaginate(20);
return [
'across_city' => ServiceProviderCollection::collection($across_city),
'near_by' => ServiceProviderCollection::collection($near_by)
];
I want Json Data with pagination.
{"data":{
"across_city": [
{
"name": "??",
"rating": 0,
"price": 0,
"distance": 0,
}
],
"links": {
"first": "",
"last": "",
"prev": null,
"next": ""
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"path": "",
"per_page": 2,
"to": 2,
"total": 6
},
{
"near_by": [
{
"name": "??",
"rating": 0,
"price": 0,
"distance": 0,
}
],
"links": {
"first": "",
"last": "",
"prev": null,
"next": ""
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"path": "",
"per_page": 2,
"to": 2,
"total": 6
}
}
I have no idea, what the purpose of ServiceProviderCollection is. If it creates a regular collection then there is a better way to do so.
To be able to paginate a simple collection you need to add this helper function to your base:
function paginateCollection($items, $perPage = 15, $page = null, $options = [])
{
$page = $page ?: (\Illuminate\Pagination\Paginator::resolveCurrentPage() ?: 1);
$items = $items instanceof \Illuminate\Support\Collection ? $items : \Illuminate\Support\Collection::make($items);
return new \Illuminate\Pagination\LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
}
With this function in place you can make
return [
'across_city' => paginateCollection(collect($across_city), 20),
'near_by' => paginateCollection(collect($near_by), 20)
];
Edit
Do you have generated the corresponding resource collections?
Like so: php artisan make:resource Cities --collection
If so, you can return a paginated resource collection

Resources