This repository has been archived by the owner on Jul 28, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 32
Adding Resources
Rodolfo Ruiz edited this page Jul 19, 2018
·
9 revisions
For example, we are going to add Categories resource to the project. You will be able to create, read, update and delete records if you follow the steps below:
-
- generate model and migration
php artisan make:model Models/Category --migration
- Category Model Class
<?php
namespace App\Models;
use App\Traits\Eloquent\SearchLikeTrait;
use App\Traits\Models\FillableFields;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use FillableFields, SearchLikeTrait;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'url',
];
/**
* @return mixed
*/
public function getRecordTitle()
{
return $this->name;
}
}
- CreateCategoriesTable Migration Class
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCategoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->text('url');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categories');
}
}
-
- For more information about laravel policies to authorize user actions check the laravel documentation
php artisan make:policy CategoryPolicy --model=Category
- CategoryPolicy Policy Class
<?php
namespace App\Policies;
use App\User;
use App\Models\Category;
use Illuminate\Auth\Access\HandlesAuthorization;
class CategoryPolicy
{
use HandlesAuthorization;
/**
* @param User $user
* @param $ability
* @return bool
*/
public function before(User $user, $ability)
{
if ($user->isAdmin()) {
return true;
}
}
/**
* Determine whether the user can manage records.
*
* @param \App\User $user
* @return mixed
*/
public function manage(User $user)
{
return $user->isAdmin();
}
/**
* Determine whether the user can list models.
*
* @param User $user
* @return mixed
*/
public function viewList(User $user)
{
return $user->isAdmin();
}
/**
* Determine whether the user can view the record.
*
* @param \App\User $user
* @param \App\Models\Category $record
* @return mixed
*/
public function view(User $user, Category $record)
{
return $user->isAdmin();
}
/**
* Determine whether the user can create records.
*
* @param \App\User $user
* @return mixed
*/
public function create(User $user)
{
return $user->isAdmin();
}
/**
* Determine whether the user can update the record.
*
* @param \App\User $user
* @param \App\Models\Category $record
* @return mixed
*/
public function update(User $user, Category $record)
{
return $user->isAdmin();
}
/**
* Determine whether the user can delete the record.
*
* @param \App\User $user
* @param \App\Models\Category $record
* @return mixed
*/
public function delete(User $user, Category $record)
{
return $user->isAdmin();
}
}
- Add the policy to the AuthServiceProvider
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
\App\User::class => \App\Policies\UserPolicy::class,
\App\Models\Category::class => \App\Policies\CategoryPolicy::class,
];
php artisan make:controller Admin/CategoriesController
This boilerplate provides a ResourceController trait that will handle authorization and the typical "CRUD" routes. You just have to focus on the unique settings for every controller like:
- $resourceAlias => resource views path.
- $resourceRoutesAlias => route prefix.
- $resourceModel => what model to use to interact with the database.
- $resourceTitle => title to show when listing, editing and creating.
- resourceStoreValidationData => function to validate values when creating the record
- resourceUpdateValidationData($record) => function to validate values when updating the record.
- getSearchRecords(Request $request, $perPage = 15, $search = null) => function to filter and list records.
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Category;
use App\Traits\Controllers\ResourceController;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class CategoriesController extends Controller
{
use ResourceController;
/**
* @var string
*/
protected $resourceAlias = 'admin.categories';
/**
* @var string
*/
protected $resourceRoutesAlias = 'admin::categories';
/**
* Fully qualified class name
*
* @var string
*/
protected $resourceModel = Category::class;
/**
* @var string
*/
protected $resourceTitle = 'Categories';
/**
* Used to validate store.
*
* @return array
*/
private function resourceStoreValidationData()
{
return [
'rules' => [
'name' => 'required|min:3|max:255'
'url' => 'required|url'
],
'messages' => [],
'attributes' => [],
];
}
/**
* Used to validate update.
*
* @param $record
* @return array
*/
private function resourceUpdateValidationData($record)
{
return $this->resourceStoreValidationData();
}
/**
* @param \Illuminate\Http\Request $request
* @param null $record
* @return array
*/
private function getValuesToSave(Request $request, $record = null)
{
$values = $request->only([
'name', 'url',
]);
return $values;
}
/**
* Retrieve the list of the resource.
*
* @param \Illuminate\Http\Request $request
* @param int $perPage
* @param string|null $search
* @return \Illuminate\Support\Collection
*/
private function getSearchRecords(Request $request, $perPage = 15, $search = null)
{
$query = $this->getResourceModel()::when(! empty($search), function ($query) use ($search) {
$query->like('name', $search);
});
return $query->paginate($perPage);
}
}
/**
* // Matches The "/admin/*" URLs
*/
Route::group(['prefix' => 'admin', 'namespace' => 'Admin', 'as' => 'admin::'], function () {
/**
* Admin Access
*/
Route::group(['middleware' => 'admin'], function () {
/**
* Admin Index
* // Route named "admin::index"
*/
Route::get('/', ['as' => 'index', 'uses' => 'IndexController@index']);
/**
* Manage Users.
* // Routes name "admin.users.*"
*/
Route::resource('users', 'UsersController');
/**
* Manage Categories.
* // Routes name "admin.categories.*"
*/
Route::resource('categories', 'CategoriesController');
});
});
-
- Because the powerfull ResourceController trait is reusing the resources views you only have to create the form and the table sub-views. Optionally you can create a sub-view "_search" when you want to filter by multiple fields. Create "categories" directory under "admin" directory and the following views:
- form.blade.php (you do not have to include form tag, only the fields)
- table.blade.php
- _search.blade.php (optional.)
- Because the powerfull ResourceController trait is reusing the resources views you only have to create the form and the table sub-views. Optionally you can create a sub-view "_search" when you want to filter by multiple fields. Create "categories" directory under "admin" directory and the following views:
/* form.blade.php */
<div class="col-md-12">
<div class="form-group margin-b-5 margin-t-5{{ $errors->has('name') ? ' has-error' : '' }}">
<label for="name">Name *</label>
<input type="text" class="form-control" name="name" placeholder="Name" value="{{ old('name', $record->name) }}" required>
@if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('name') }}</strong>
</span>
@endif
</div>
<!-- /.form-group -->
</div>
<!-- /.col-md-12 -->
<div class="col-md-12">
<div class="form-group margin-b-5 margin-t-5{{ $errors->has('url') ? ' has-error' : '' }}">
<label for="url">URL *</label>
<input type="text" class="form-control" name="url" placeholder="URL" value="{{ old('url', $record->url) }}" required>
@if ($errors->has('url'))
<span class="help-block">
<strong>{{ $errors->first('url') }}</strong>
</span>
@endif
</div>
<!-- /.form-group -->
</div>
<!-- /.col-md-12 -->
/* table.blade.php */
<div class="table-responsive list-records">
<table class="table table-hover table-bordered">
<thead>
<th>#</th>
<th>ID</th>
<th>Name</th>
<th style="width: 100px;">Actions</th>
</thead>
<tbody>
@foreach ($records as $record)
<?php
$tableCounter++;
$editLink = route($resourceRoutesAlias.'.edit', $record->id);
$deleteLink = route($resourceRoutesAlias.'.destroy', $record->id);
$formId = 'formDeleteModel_'.$record->id;
$canUpdate = Auth::user()->can('update', $record);
$canDelete = Auth::user()->can('delete', $record);
?>
<tr>
<td>{{ $tableCounter }}</td>
<td>
@if ($canUpdate)
<a href="{{ $editLink }}">{{ $record->id }}</a>
@else {{ $record->id }} @endif
</td>
<td class="table-text">
<a href="{{ $editLink }}">{{ $record->name }}</a>
</td>
<!-- we will also add show, edit, and delete buttons -->
<td>
<div class="btn-group">
@if ($canUpdate)
<a href="{{ $editLink }}" class="btn btn-info btn-sm"><i class="fa fa-edit"></i></a>
@endif
@if ($canDelete)
<a href="#" class="btn btn-danger btn-sm btnOpenerModalConfirmModelDelete"
data-form-id="{{ $formId }}"><i class="fa fa-trash-o"></i></a>
@endif
</div>
@if ($canDelete)
<!-- Delete Record Form -->
<form id="{{ $formId }}" action="{{ $deleteLink }}" method="POST"
style="display: none;" class="hidden form-inline">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button type="submit" class="btn btn-danger">Delete</button>
</form>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
/* _search.blade.php */
<div class="box-body padding">
<!-- Search -->
<div class="box-tools">
<form id="form-advanced-search" class="form" role="form" method="GET" action="{{ $_listLink }}">
<div class="row">
<div class="col-md-3">
<div class="form-group margin-b-5 margin-t-5">
<label for="search">Name</label>
<input type="text" name="search" class="form-control" value="{{ $search }}" placeholder="Name">
</div>
<!-- /.form-group -->
</div>
<!-- /.col-md-3 -->
</div>
<hr class="margin-b-5 margin-t-5">
<div class="row">
<div class="col-md-3 col-lg-2">
<div class="form-group margin-b-5 margin-t-5">
<label for="per_page">Show</label>
<select name="per_page" class="form-control select2" data-placeholder="Show" data-allow-clear="true" style="width: 100%;">
<option value="" @if ($perPage == '') selected @endif></option>
<option value="15" @if ($perPage == 15) selected @endif>15</option>
<option value="25" @if ($perPage == 25) selected @endif>25</option>
<option value="50" @if ($perPage == 50) selected @endif>50</option>
<option value="100" @if ($perPage == 100) selected @endif>100</option>
</select>
</div>
<!-- /.form-group -->
</div>
<!-- /.col-md-2 -->
<div class="col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4 text-center">
<div class="form-group margin-b-5 margin-t-5" style="margin-top: 20px;">
<button type="submit" class="btn btn-primary margin-r-5 margin-l-5"><i class="fa fa-search"></i> Search</button>
<a href="{{ $_listLink }}" class="btn btn-warning margin-r-5 margin-l-5"><i class="fa fa-eraser"></i> Reset</a>
</div>
</div>
<!-- /.col-md-4 -->
</div>
<hr class="margin-b-5 margin-t-5">
</form>
</div>
<!-- END Search -->
</div>
-
- Now is time to add the new resource to the layouts.partials.backend.sidebar-menu
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu" data-widget="tree">
<li class="header">MAIN NAVIGATION</li>
<li class="{{ \App\Utils::checkRoute(['dashboard::index', 'admin::index']) ? 'active': '' }}">
<a href="{{ route('dashboard::index') }}">
<i class="fa fa-dashboard"></i> <span>Dashboard</span>
</a>
</li>
@if (Auth::user()->can('manage', \App\User::class))
<li class="{{ \App\Utils::checkRoute(['admin::users.index', 'admin::users.create']) ? 'active': '' }}">
<a href="{{ route('admin::users.index') }}">
<i class="fa fa-user-secret"></i> <span>Users</span>
</a>
</li>
@endif
@if (Auth::user()->can('manage', \App\Models\Category::class))
<li class="{{ \App\Utils::checkRoute(['admin::categories.index', 'admin::categories.create']) ? 'active': '' }}">
<a href="{{ route('admin::categories.index') }}">
<i class="fa fa-tags"></i> <span>Categories</span>
</a>
</li>
@endif
</ul>