No meta value from collection object - php

class SongsCollection extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => SongResource::collection($this->collection),
'meta' => ['song_count' => $this->collection->count()],
];
}
}
in the controller
$data = new SongsCollection(Song::all());
dd($data);
`
it only display the image below, but without the meta array contain the song_count?
How to get the meta->song_count value ?

Laravel doc says:
Every resource class defines a toArray method which returns the array of attributes that should be converted to JSON when the resource is returned as a response from a route or controller method.
So, dd($data) will just dump the resource object which has the toArray method. If you want to get the meta field, you must call $data->toJson() method. If you just return $data in your api endpoint, laravel itself will call toJson method internally as stated in the above doc.

In the blade template, the laravel gives a song collection, which can not be retrive the meta from this collection, Below is how I get the meta data:
$datas = json_encode($datas,true);
//dd($data);
$datas = json_decode($datas);
// dd($datas);
foreach($datas->meta as $link){
echo $link;
}
Hope this helps.

Related

laravel : Error Call to a member function all() on array

This is used to export excel
My controllers:
public function reportExport(Request $request)
{
return Excel::download(new ReportExport($request), 'LAPORAN ABSENSI.xlsx');
}
Export folder : ReportExport
return [
"att_cross" => $att_cross,
"belum_absen" => $belum_absen,
"absen" => $absen,
];
but, when I return one of the data is successful, for example :
return $absen;
I want to display the contents of the variable $absent, $belum_absen, $att_cross but I get an error message "Call to a member function all() on array "
how can I display the data for these three parameters?
Read the docs for Excel package, you need to map() the output.

how to do authorization on a nested route in laravel

I have a nested route like this:
Route::get('/products/{product}/auctions/create', [AuctionController::class, 'create']);
All it does is it sets an auction on a given product. A Product - hasMany - Auctions.
AuctionPolicy to be invoked to authorize actions on an Auction instance:
// App\Providers\AuthServiceProvider
protected $policies = [
Auction::class => AuctionPolicy::class
]
AutionPolicy:
public function create(User $user, Product $product) // <-- where do I get this $product from?
{
if( $product->user_id === $user->id ) {
return $product->canCreateNewAuction();
}
return false;
}
AuctionController:
public function create(Request $request, Product $product)
{
$this->authorize('create', Auction::class);
}
It throws an Exception: AuctionPolicy::create expects two params, only one passed.
How do I pass $product to AuctionPolicy::create method.
Laravel allows to pass additional data to policies.
Supply Additional Context
In the ->authorize method, instead of passing Auction class string, we can also pass an array with additional data.
In this example, we want to be able to pass $product to the invoked Auction Policy:
$this->authorize('create', [Auction::class, $product]);
The first item of this array specifies which Policy (Auction::class => AuctionPolicy::class) to invoke and rest are additional data.
It doesn't have to be a class string, it could also be an instance.
$this->authorize('show', [$auction, $product]);

Using callable params to modify a query

I am using craftable to generate an admin panel for my app.
I have an Organisation model that belongs to an Organisation Type model.
In the index listing, I want to be able to display the Organisation Type name rather than the _id. To do this, I have to modify this query, to eager load the relationship using the 'with' method.
The method signature for the listing is:
public static function processRequestAndGet($request, $columns = array(), $searchIn = null, $modifyQuery = null, $locale = null)
and the index method is:
$data = AdminListing::create(Organisation::class)->processRequestAndGet(
// pass the request with params
$request,
// set columns to query
['id', 'organisation_type_id', 'name', 'active'],
// set columns to searchIn
['id', 'name']
);
if ($request->ajax()) {
return ['data' => $data];
}
return view('admin.organisation.index', ['data' => $data]);
Craftable, provides a modifyQuery method to, but i'm not sure how to use it:
public function index(IndexMovie $request)
{
$data = AdminListing::create(Movie::class)
->modifyQuery(function($query) use ($request){
if ($request->has('author_id')) {
$query->where('author_id', $request->author_id);
}
})
->get();
Can someone help me use the callback to modify the query so that I can include the related table data?
Okay so I've came across the exact same problem and I'd like to share my way of doing it.
craftable uses a processRequestAndGet function that takes as a 4th parametre the intended callable query. So when you need to use that parametre rather than trying to access the modifyQuery function directly.
$data_r = AdminListing::create(Organisation::class)
->processRequestAndGet(
// pass the request with params
$request,
// set columns to query
['id', 'organisation_type_id', 'name', 'active'],
// set columns to searchIn
['id', 'name'],
function($query) use ($request){
if ($request->has('author_id')) {
$query->where('author_id', $request->author_id);
}
}
);
You were almost right, just pass the intended callback inside your processRequestAndGet and voilà.

Laravel presenters for JSON responses

I’ve recently discovered that presenters (like this one) implement the decorator pattern and are a great way to add fields and logic to existing Laravel models. Take the following example for my question below:
// Tack on a new readable timestamp field.
public function timeago()
{
return $this->object->created_at->whenForHumans();
}
// Wrap an existing field with some formatting logic
public function created_at()
{
return $this->object->created_at->format('Y-m-d');
}
I can then use these presenter fields in my view:
{{ $object->timeago }}
{{ $object->created_at }}
How would you implement the decorator pattern for an API that returns JSON responses rather than Blade views? In all the Laravel/JSON articles I have read, objects are immediately returned without undergoing any transformation / presenter logic. e.g.:
// converting a model to JSON
return User::find($id)->toJson();
// returning a model directly will be converted to JSON
return User::all();
// return associated models
return User::find($id)->load('comments')->get();
How can I implement presenter fields in my JSON response?
$object->timeago
$object->created_at
As you mentioned, User::all returns JSON, so do something like:
Some function to get data and return a decorated response:
public function index()
{
$news = News::all();
return $this->respond([
'data' => $this->newsTransformer->transformCollection($news->toArray())
]
);
}
The above function will call Transformer::transformCollection:
<?php namespace Blah\Transformers;
abstract class Transformer {
public function transformCollection(array $items)
{
return array_map([$this, 'transform'], $items);
}
public abstract function transform($item);
}
which in turn will call NewsTransformer::transform():
public function transform($news)
{
return [
'title' => $news['title'],
'body' => $news['body'],
'active' => (boolean) $news['some_bool'],
'timeago' => // Human readable
'created_at' => // Y-m-d
];
}
The end result being JSON with the format you require, in this case:
{
data: {
title: "Some title",
body: "Some body...",
active: true,
timeago: "On Saturday, 1st of March",
created_at: "2014-03-01"
}
}
By the way, Laracasts has an excellent series on building APIs -- hope that helps!
For clarity, the respond function in the first code snippet just wraps the data with a status code, and any headers, something like:
return Response::json($data, 200);

Using accessors and mutators in Laravel json responses

So I'm making an API that produces a json response instead of doing View::make('foo', compact('bar')).
With blade templating
My controller simply returns the Eloquent model for Users:
public function index()
{
return User::all();
}
protected function getFooAttribute()
{
return 'bar';
}
And my view will be able to use it, along with the foo attribute (which isn't a field in the user's table).
#foreach($users as $user)
<p>{{$user->name}} {{$user->foo}}</p>
#endforeach
With Angular JS + json response
However, now that I'm not using blade but rather grabbing the json and displaying it with Angular JS I'm not able to do this:
<p ng-repeat="user in users">{{user.name}} {{user.foo}}</p>
Is there a way to cleanly pack the json response such that I have:
[{"name": "John", "foo": "bar"}, ...]
Warning: I've never built an API before and I've only started programming/web dev last December. This is my attempt:
public function index()
{
$response = User::all();
foreach($response as $r)
{
$r->foo = $r->foo;
}
return $response;
}
Yeah there is, example:
return Response::json([User:all()], 200);
Usually you want more than that though..
return Response::json([
'error' => false,
'data' => User:all()
], 200);
200 is the HTTP Status Code.
To include the attributes you need to specify these attributes to automatically append onto the response in your model.
protected $appends = array('foo');
public function getFooAttribute()
{
return 'bar';
}

Categories