I'm trying to use Laravel's resources & Collection to build a small API.
I would like to recover all the posts of the categories
My relation on my model :
/*
|--------------------------------------------------------------------------
| RELATIONS
|--------------------------------------------------------------------------
*/
public function posts()
{
return $this->belongsToMany(Post::class, 'post_category');
}
Category controller :
public function index()
{
$categories = Category::all();
return (new CategoryCollection(CategoryResource::collection($categories)));
}
My categoryResource :
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug
];
}
My CategoryCollection
public function toArray($request)
{
return [
'data' => $this->collection,
'posts' => PostResource::collection($this->whenLoaded('posts')),
];
}
I try to recover the posts of a category first. When I make the following command I get an error: Method ... relationLoaded does not exist
'posts' => PostResource::collection($this->whenLoaded('posts'))
What did I not understand?
I also created two PostCollection and PostResource files (basic, I did not modify them)
public function toArray($request)
{
return parent::toArray($request);
}
public function index()
{
$categories = Category::all();
return (CategoryResource::collection($categories));
}
this is might help , try this
and if you want to use posts resource
you need make posts resources
and inside the CategoryResource
'posts'=>PostResource::collection($this->posts)
cannot use Resource in Collection.
Use this
public function index()
{
$categories = Category::all();
return (new CategoryCollection($categories));
}
Related
I'm using Laravel 5.8 and I wanted to retrieve some data from an Api route:
Route::prefix('v1')->namespace('Api\v1')->group(function(){
Route::get('/articles','ArticleController#index');;
});
Then I made a Resource Collection and a Controller as well:
ArticleCollection.php:
public function toArray($request)
{
return parent::toArray($request);
}
ArticleController:
public function index()
{
$articles = Article::all();
return new ArticleCollection($articles);
}
Now it properly shows data:
{"data":[{"art_id":3,"art_cat_id":15,"art_author_id":23973,"art_title":"\u0627\u062d\u06cc\u0627\u06cc...
But when I change the Resource Collection to this:
public function toArray($request)
{
return [
'title' => $this->art_title,
'desc' => $this->art_description,
];
}
And trying to find an specific article in the Controller:
public function index()
{
$articles = Article::find(1);
return new ArticleCollection($articles);
}
I will get this error somehow:
FatalThrowableError
Call to a member function toBase() on null
So what's going wrong here? How can I solve this issue?
Change this:
public function index()
{
$articles = Article::find(1);
return new ArticleCollection($articles);
}
to this:
public function index()
{
$articles = Article::where('id',1)->get();
return new ArticleCollection($articles);
}
This is because the find returns just one record and you are converting that to a collection, by using get() you can return a collection as you intend to here.
I have a resource Controller with this index method like this:
public function index()
{
$args = [];
$args = array_merge($args, $this->data_creator(35, 12, 'book'));
$args = array_merge($args, $this->data_creator(37, 12, 'kit'));
$args = array_merge($args, $this->data_creator(38, 12, 'game'));
$args['menu_links'] = [
'books' => route('shopping-products.category', Category::find(25)->slug),
'videos' => route('shopping-products.category', Category::find(24)->slug),
'kits' => route('shopping-products.category', Category::find(23)->slug),
'games' => route('shopping-products.category', Category::find(22)->slug),
];
return view('frontend.shop.products.index', $args);
}
But it returns this error:
Trying to get property 'slug' of non-object
And when I dd(Category::find(25), Category::find(24), Category::find(23), Category::find(22)); I get NULL results.
Meaning that it can not find data with specified ids.
However there are 25 records stored at the categories table:
So what is going wrong here? How can I fix this issue?
I would really appreciate any idea or suggestion from you guys...
Thanks in advance.
Here is Category.php Model:
class Category extends Model
{
use Sluggable, SoftDeletes;
protected $table = 'categories';
protected $primaryKey = 'cat_id';
protected $guarded = [];
/**
* Return the sluggable configuration array for this model.
*
* #return array
*/
public function sluggable()
{
return [
'slug' => [
'source' => 'cat_name'
]
];
}
public function path()
{
return "/products/categories/$this->slug";
}
public function children()
{
return $this->hasMany(Category::class, 'cat_parent_id', 'cat_id');
}
public function parents()
{
return $this->hasMany(Category::class, 'cat_id', 'cat_parent_id');
}
public function products()
{
return $this->belongsToMany(Product::class, 'category_products', 'ctp_cat_id', 'ctp_prd_id');
}
public function news()
{
return $this->belongsToMany(News::class, 'category_news', 'ctn_cat_id', 'ctn_nws_id');
}
public function galleries()
{
return $this->belongsToMany(Gallery::class, 'category_galleries', 'ctg_cat_id', 'ctg_gly_id');
}
public function uploaded()
{
return $this->hasMany(UploadedFile::class, 'upf_object_id', 'cat_id')->where('upf_object_type_id', '=', '107');
}
public function articles()
{
return $this->belongsToMany(Article::class, 'article_category', 'act_cat_id', 'act_art_id');
}
public function olympiadExam()
{
return $this->belongsToMany(OlympiadExam::class, 'olympiads_exams_categories', 'oec_ole_id', 'oec_cat_id');
}
public function olympiadExamQuestion()
{
return $this->belongsToMany(OlympiadExamQuestion::class, 'olympiads_exams_questions_categories', 'oes_cat_id', 'oes_oeq_id')->orderBy('oeq_number', 'asc');
}
public function attr_attributes()
{
return $this->hasMany(CategoryAttribute::class, 'category_id', 'cat_id');
} //
public function attr_product()
{
return $this->hasMany(Product::class, 'prd_cat_att_id', 'cat_id');
} //
public function couponRelation()
{
return $this->hasMany(couponRelation::class, 'object_id', 'cat_id')->where('object_type', 'product_category');
}
public function magazines()
{
return $this->belongsToMany(Magazine::class, 'category_magazine', 'category_id', 'magazine_id');
}
}
And when I do: dd(Category::where('cat_id', 25), Category::where('cat_id', 24), Category::where('cat_id', 23), Category::where('cat_id', 22)); I get this as result:
The problem is because you are using SoftDeletes so soft deleted models will automatically be excluded from query results. In your case, look like Category with id 22, 23, 24, 25 are soft deleted. To get it, you need to use withTrashed() as mentioned in the doc. For example:
Category::withTrashed()->find(22)->slug
per an answer above: if you are using soft deletes you need to add
Category::withTrashed()
However, you can wrap the command in an optional() helper function.
optional(Category::find(22))->slug
// if you are using soft delete
optional( Category::withTrashed()->find(22) )->slug
this will return null if 22 does not exist instead of throwing an exception error.
Laravel 5.8
PHP 7.4
I want to load the relationships conditionally like
http://127.0.0.1:8000/api/posts
and
http://127.0.0.1:8000/api/posts/1 are my end points now, I want to load comments like
http://127.0.0.1:8000/api/posts/?include=comments and
http://127.0.0.1:8000/api/posts/1/?include=comments
If the query parameter is there, only then it should load comments with posts or it should load only posts/post
I am doing this by referring a blog post
now, RequestQueryFilter
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
class RequestQueryFilter
{
public function attach($resource, Request $request = null)
{
$request = $request ?? request();
return tap($resource, function($resource) use($request) {
$this->getRequestIncludes($request)->each(function($include) use($resource) {
$resource->load($include);
});
});
}
protected function getRequestIncludes(Request $request)
{
// return collect(data_get($request->input(), 'include', [])); //single relationship
return collect(array_map('trim', explode(',', data_get($request->input(), 'include', [])))); //multiple relationships
}
}
and in helper
<?php
if ( ! function_exists('filter') ) {
function filter($attach)
{
return app('filter')->attach($attach);
}
}
?>
in PostController
public funciton show(Request $request, Post $post) {
return new PostResource(filter($post));
}
but when I am trying to retrieve
http://127.0.0.1:8000/api/posts/1/?include=comments getting no comments, with no error in log
A work around will be PostResource
public function toArray($request)
{
// return parent::toArray($request);
$data = [
'id' => $this->id,
'name' => $this->title,
'body' => $this->content,
];
$filter = $request->query->get('include', '');
if($filter){
$data[$filter] = $this->resource->$filter;
}
return $data;
}
I want to load the relationships conditionally like
Lazy Eager Loading using the load() call
The Lazy Eager Loading accomplishes the same end results as with() in Laravel, however, not automatically. For example:
?include=comments
// Get all posts.
$posts = Post::without('comments')->all();
if (request('include') == 'comments')) {
$posts->load('comments');
}
return PostResource::collection($posts);
Alternativelly, you could require the include query string to be an array:
?include[]=comments&include[]=tags
// Validate the names against a set of allowed names beforehand, so there's no error.
$posts = Post::without(request('includes'))->all();
foreach (request('includes') as $include) {
$posts->load($include);
}
return PostResource::collection($posts);
The call without() is only required in case you defined your model to automatically eager load the relationships you want to conditionally load.
With all data filtered in Controller, just make sure to display only loaded relations in your PostResource
public function toArray($request) {
$data = [...];
foreach ($this->relations as $name => $relation)
{
$data[$name] = $relation;
}
return $data;
}
I would create a custom resource for the posts with
php artisan make_resource
command.
E.g. PostResource.
The toArray function of the resource must return the data.
PostResource.php
public function toArray($request){
$data =['title' => $this->resource->title,
'body' => $this->resource->body,
'images' => new ImageCollection($this->whenLoaded('images')),
];
$filter = $request->query->get('filter', '');
if($filter){
$data['comments'] => new CommentCollection($this->resource->comments);
}
return $data;
}
Also, for collections, you need to create a ResourceCollection.
PostResourceCollection.php
class PostResourceCollection extends ResourceCollection
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request
* #return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}
In your controller:
PostsController.php
//show one post
public function show(Post $post, Request $request)
{
/**this response is for API or vue.js if you need to generate view, pass the resource to the view */
return $this->response->json( new PostResource($post));
}
//list of posts
public function index(Request $request)
{
$posts = Post::all();
/**this response is for API or vue.js if you need to generate view, pass the resource to the view */
return $this->response->json( new PostResourceCollection($posts));
}
Partial Solution
It will need a small change in resource class
public function toArray($request)
{
// return parent::toArray($request);
$data = [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'comments' => new CommentCollection($this->whenLoaded('comments')),
'images' => new ImageCollection($this->whenLoaded('images')),
];
return $data;
}
and it will load comments and images if loaded and that depends on the include query parameter, if that is not included, it will not load the relationship.
However,
In post collection
return [
'data' => $this->collection->transform(function($post){
return [
'id' => $post->id,
'title' => $post->title,
'body' => $post->body,
'comments' => new CommentCollection($post->whenLoaded('comments')),
'images' => new ImageCollection($post->whenLoaded('images')),
];
}),
];
will results in
"Call to undefined method App\Models\Customer::whenLoaded()",, if anyone suggests a complete solution, it will be a great help, if I will able to do, it I will update here.
I am working on REST API under Yii2, however I have an issue with what the model should return per different actions.
For example in listing actions I need to for example to return 4 attributes and for the details action I need to return 10 attributes from the same model.
What is the best or standard way in Yii2 to implement this.
Example:
/articles
return [id, title, image, date]
/articles/7
return [id, title, image, date, author, likes, last_review]
Thank you
for /article/7 :
public function actionView($id)
{
return User::findOne($id);
}
or
public function actionView($id)
{
return User::find()->select([id, title, image, date, author, likes,last_review])->one();
}
for /articles :
public function actionIndex()
{
return User::find()->select([id, title, image, date])->all();
}
Just write methods in your model.
class MyModel extends ActiveRecord
{
private static $fieldset_1 = [
'id', 'title', 'image', 'date'
];
private static $fieldset_2 = [
'id', 'title', 'image', 'date', 'author', 'likes', 'last_review'
];
public static function get(int $id)
{
if($id > 0) {
return static::find()
->select(self::$fieldset_1)
->where(['id' => $id])
->asArray()
->one();
}
}
public static function getList()
{
return static::find()
->select(self::$fieldset_2)
->asArray()
->all();
}
}
In Controller
class MyController extends Controller{
public function actionListing(){
return $this->asJson(MyModel::getList());
}
public function actionDetails($id){
return $this->asJson(MyModel::get((int)$id));
}
}
I have this function in the Controller:
/**
*
* Edit Registration
*
*/
public function edit(Registration $id)
{
$logs = Log::where('registration_id', $id->id)->users()->get();
dd($logs);
return view('registrations_edit', ['registration' => $id, 'log' => $logs]);
}
The documentation says I can call
Log::where('registration_id', $id->id)->users()->get();
when I define users() in the Model.
public function users(){
return $this->belongsTo('App\User', 'id', 'user_id');
}
but when I call users() in the Controller i always get
Call to undefined method Illuminate\Database\Query\Builder::users()
What am I doing wrong?
Kind regards
I think you can try this :
public function edit(Registration $id)
{
$logs = Log::with('users')->where('registration_id', $id->id)->get();
dd($logs);
return view('registrations_edit', ['registration' => $id, 'log' => $logs]);
}
Hope this work for you
If your relations write in right way, try this:
$logs = Log::where('registration_id', $id->id)->users->get();