Description
To better demonstrate the usage of different modules that make up this package we will use a sample application that will cover various needs.
The app will consist of these models: Author
, Post
, Comment
.
Installation and Setup
laravel new project \
&& cd project \
&& composer require larafun/suite
Create a database for this project and update your .env
file accordingly.
Posts
php artisan build:model Post -a
This will create for us the following:
App\Models\Post
database/factories/PostFactory.php
database/migrations/yyyy_mm_dd_hhiiss_create_posts_table.php
App\Http\Controllers\Api\PostController
App\Filters\PostFilter
App\Http\Resources\PostResource
The Post Model
class Post extends Model
{
use ValidatableTrait;
protected $fillable = [
'author_id',
'title',
'body'
];
/**
* Rules
*/
public function savingRules()
{
return [
'author_id' => 'required|numeric',
'title' => 'required|min:2',
'body' => 'required|min:5'
];
}
/**
* Relationships
*/
public function author()
{
return $this->belongsTo(Author::class, 'author_id');
}
public function comments()
{
return $this->hasMany(Comment::class, 'post_id');
}
/**
* Query scopes
*/
public function scopeFilter($query, PostFilter $filter)
{
return $query
->writtenBy($filter->$author_ids)
->containing($filter->search)
->skip($filter->page * $filter->size)
->take($filter->size)
;
}
public function realScopeWrittenBy($query, $author_ids)
{
return $query->whereIn('author_id', $author_ids);
}
public function realScopeContaining($query, $search)
{
return $query->where(function ($query) {
return $query->where('title', 'like', "%$search%")
->orWhere('body', 'like', "%$search%");
});
}
/**
* Presentation
*/
public function getResource()
{
return PostResource::class;
}
}
The Post Controller
class PostController extends Controller
{
/**
* $filter will be automatically constructed with the
* request parameters
*/
public function index(BookFilter $filter)
{
/**
* This will return a json response with pagination information
*/
return Post::filter($filter)->get();
}
public function store(Request $request)
{
/**
* Validation occurs when actually creating the Post
* If any errors are found, a ValidationException will be thrown
*/
return Post::create($request->all());
}
public function show(Post $post)
{
/**
* The resource will be automatically transformed with the rules
* defined in the PostResource
*/
return $post;
}
public function update(Post $post, Request $request)
{
/**
* Validation occurs when updating the resource according
* to the rules specified on the Model
*/
return $post->update($request->all());
}
// ...
}
The Post Resource
class PostResource extends Resource
{
/**
* Relations will stop loading after this depth.
* This way infinite loops or extra large
* responses may be avoided.
* The deepen() method needs to be used on these
* relations for this behavior the take effect
*/
protected $max_depth = 3;
public function boot()
{
/**
* This resource may be used on a Model or on a Collection.
* Using collected() we can wrap the Model in a Collection.
*/
$posts = $this->collected();
/**
* The eager loading takes place only when booting the Resource.
* This will happen when the app is ready to respond and the
* response is ready to be built.
*/
$posts->load('author', 'comments');
}
public function item(Post $post)
{
return [
'id' => $post->id,
'author' => $this->deepen($post->author),
'title' => $post->title,
'body' => $post->body,
'comments' => $this->deepen($post->comments)
];
}
}
The Post Filter
class PostFilter extends Filter
{
public function defaults(): array
{
return [
'search' => null, // searching through title and body
'author_ids' => [], // only posts written by the given authors
'page' => 0,
'size' => 20
];
}
/**
* This method is optional, but when defined, it will apply the rules
* against the constructor arguments
*/
public function rules(): array
{
return [
'size' => 'numeric|max:100', // avoid sending too large responses
];
}
}