This package was created to easily manage controller crudable actions and adjust responses and requests without problems. Package also provide great way to perform search on eloquent models.
You can install package via composer. Add repository to your composer.json
"repositories": [
{
"type": "vcs",
"url": "https://github.com/mindz-team/laravel-crudable"
}
],
And run
composer require mindz-team/laravel-crudable
Publish config file
php artisan vendor:publish --provider="Mindz\LaravelCrudable\LaravelCrudableServiceProvider" --tag="config"
Extend desired controller class with CrudableController
. Class will extend your controller with methods index
,show
,store
,update
,delete
.
Tu use it just create a route in desired location:
Route::resource('users', UserController::class);
You can limit the controller methods using standard route limiters
Route::resource('users', UserController::class)->only(['index','show']);
Route::resource('users', UserController::class)->except(['index','show']);
By default crudable controller tries to discover model automatically using controller name. In example, if controller is
named UserController
an User
model will be looked for in configurable namespace
Default model name space is App\Models
but you can change it in config/crudable.php
There is a way to avoid all that magic. If you just want to indicate a model of your desire you may use
method getModel
protected function getModel() {
return App\Models\User::class;
}
There is one obvious rule. returned class must extend Illuminate\Database\Eloquent.
Depends of returned type there are two possible options supported by this package. An object or collection
Crudable methods like show
,store
,update
returns an object.
Default behaviour is to discover model name based resource in namespace stored in config. Default: App\Http\Resources
Example: If model name used in Controller is User
then correct resource name is App\Http\Resources\UserResource
If no resource was found crudable package uses Illuminate\Http\Resources\Json\JsonResource
to create response.
Of course there is another way to indicate directly a resource file regardless of its name using
method getObjectResource
protected function getObjectResource() {
return App\Http\Resources\SomeOtherResource::class;
}
Another obvious rule it that returned class must extends Illuminate\Http\Resources\Json\JsonResource
Collection is returned when using index
crudable method.
Primary a package tries to discover appropriate class of a collection resource based on config namespace and name of model used in controller.
Example: If an model is User
then class App\Http\Resources\UserCollectionResource
will be looked for.
There is a common practice that in some cases collection and object resources using same resource. Therefore if collection resource is missing but an object collection is present it will be used.
If no object resource or collection resource was found the default behaviour is to return response in collection
using Illuminate\Http\Resources\Json\JsonResource
.
Of course there is another way to indicate directly a collection resource file regardless of its name using
method getCollectionResource
protected function getCollectionResource() {
return App\Http\Resources\SomeOtherCollectionResource::class;
}
And another obvious rule it that returned class must extends Illuminate\Http\Resources\Json\JsonResource
Selectable resources provides a fluent way to manipulate response objects, it is something like a graphql way.
To use selectable resource you should provide a query string with resource
key like GET /products?resource=customResourceFilter
.
Or you can store it inside a different folder, then you should provide resource path using dot notation like GET /products?resource=productsFiltersCatalog.customResourceFilter
.
To create selectable resource you should create it normally within App\Http\Resources
(if you didn't change
resources namespace in crudable.php
config file, if you change namespace then you should store it accordingly).
As a name of class you should always provide Resource
as a postfix, for example ShortenedProductResource
.
All index
method responses are by default paginated. Default number of items per page is 10
. This number can be
adjustable by method pagination
protected function pagination() {
return 20;
}
To disable pagination for controller pagination
method should return null
,false
or 0
. To disable pagination for
single request a query parameter pagination
with false
need to be passed.
To maintain pagination query string params you can use withQueryString
method
protected function withQueryString() {
return true;
}
To collection response there is a possibility to add meta
information with method meta
protected function meta() {
return [
"active_users"=> 10,
"users_registered_in_last_month"=> 50,
];
}
The package also gives ability to attach a FormRequest
to every crudable request to handle stuff like validation and
accessability. There are tho ways to use FormRequest
while performing request.
Package is able to discover FormRequest
class based on model name and namespace set in config/crudable.php
file.
There is a simple dependency. For example: if crudable model name is User
then FormRequest
classes are by default in
those locations.
Method | Model | Class |
---|---|---|
index |
User |
App\Http\Requests\Users\IndexUserRequest |
show |
User |
App\Http\Requests\Users\ShowUserRequest |
store |
User |
App\Http\Requests\Users\StoreUserRequest |
update |
User |
App\Http\Requests\Users\UpdateUserRequest |
destroy |
User |
App\Http\Requests\Users\DestroyUserRequest |
All fo those classes can also be indicated directly via method.
Example:
protected function getIndexFromRequestClass() {
return App\Http\Requests\SomeOtherRequest::class;
}
Dependencies regarding method name shapes like this.
Method | Method |
---|---|
index |
getIndexFromRequestClass |
show |
getShowFromRequestClass |
store |
getStoreFromRequestClass |
update |
getUpdateFromRequestClass |
destroy |
getDestroyFromRequestClass |
Of course must extends Illuminate\Foundation\Http\FormRequest
Another core feature of this package is ability to easily search and sort index
method responses via eloquent query
builder. This can be achieved in two ways.
To use this method of search and sort your model used in crudable controller have to implement SearchableWithScope
interface. It will force your model to implement scopeSearch
method where you can build your query as you like.
class User extends Model implements SearchableWithScope {
public function scopeSearch(Builder $query): Builder
$query->whereNull('deleted_at);
if(request()->has('active')){
$query->where('active',request()->input('active'));
}
return $query;
}
}
The scopeSearch
method must return $query
instance.
Another way of search and sort is to use laravel pipelines pattern. It allows to use more abstract and decoupled approach.
To use this method there is a necessity to implement Searchable
interface. It will force your model to
implement search
which returns an array of filter classes to build a query.
class User extends Model implements Searchable {
public function searchFilters(): array
return [
'App\Filters\Active:class',
'App\Filters\NotDeleted:class',
'App\Filters\Email:class',
'App\Filters\CreateBySort:class',
];
}
}
Every filter build small part of query.
For example App\Filters\Email:class
should be implemented like this
namespace App\Filters;
use Closure;
class Email
{
public function handle($request, Closure $next)
{
if (!request()->has('email')) {
return $next($request);
}
return $next($request)->where('email', 'like', '%' . request()->input('email') . '%');
}
}
By default Filters
classes must use Clousure
and implement handle
method like in example above.
Big advantage of using filters is fact that they are reusable.
To easily create a filter class use artisan command
php artisan make:filter Active
There is a possibility to use default sort which allows to sort object with no additional implementation but it is limited only to base model fields. Any sort, regarding fields returned by relation must be separately implemented either by scope or pipelines.
Example. If User
model in crudable controller has an email
field it will be sorted simple by providing sort_by
and sort_direction
parameters to request
GET http://examplehost/api/users?sort_by=email&sort_direction=asc
Parameter sort_direction
can have asc
or desc
value and sort_by
is the name of base model field.
Any more complex sorting like sort by the count of users items must be implemented via scope or pipeline filter.
Please see CHANGELOG for more information what has changed recently.
If you discover any security related issues, please email r.szymanski@mindz.it instead of using the issue tracker.
Author: Roman Szymański r.szymanski@mindz.it
The MIT License (MIT). Please see License File for more information