Creating Your First Panel
Before you can create a panel, you must understand the anatomy of one.
A CRUD style panel is normally made up of 5 components.
A Model
To save things, you'll need an Eloquent model.
You can set this up however you like, a really basic one could be created with something like...
php artisan make:model Page -m
The only requirement for models (if you want to use the update/store helpers) is that you define all your editable fields within the fillable
property, as we use Page::getFillable()
for mapping.
class Page extends Model
{
protected $fillable = [
'name',
'slug',
'body',
'tags',
'header_image',
];
}
A Controller
Maelstrom allows you to use any sort of controller, however when creating a CRUD style panel it's easiest to use a resourceful one.
You can create this where ever and however you like, we normally just use the artisan command, something like below will be okay!
php artisan make:controller Admin\\PageController -r -m Page
You will then need to flesh out your controller, using our panel helpers.
To start with you need to instantiate an instance of a Panel and bind it to a Model.
use App\Page;
class PageController extends Controller
{
protected $panel;
public function __construct()
{
$this->panel = maelstrom(Page::class);
}
//... Other methods omitted for demo purposes.
}
Our global helper just uses the IoC container to spin up a new Panel class, If you don't like global helpers then you can do something more like....
use App\Page;
use Maelstrom\Panel;
use Psr\Container\ContainerInterface;
class PageController extends Controller
{
protected $panel;
public function __construct(ContainerInterface $container)
{
$this->panel = $container->makeWith(Panel::class, [
'model' => Page::class,
]);
}
//... Other methods omitted for demo purposes.
}
Now you have your instance of a Maelstrom Panel, you can fill out the other methods.
The Index/Table Page of Entries
We just need to return our view (which doesn't't yet exist) with an $entries
variable.
$entries
you can build yourself, however our helper will apply all filters, transformations, searches, pagination etc.
public function index()
{
// Vanilla Laravel
return view('admin.pages-index', [
'entries' => $this->panel->getEntries(),
])
// Using Helpers
return $this->panel->index('admin.pages-index');
}
The Form to Create New Entries
We must provide a view which will be used for the create form.
You will need to provide an $action
for the form, usually the store endpoint and a $method
, this is so the form knows which HTTP Verb to use.
public function create()
{
// Vanilla Laravel
return view('admin.pages-form', [
'action' => route('pages.store'),
'method' => 'POST',
])
// Using Helpers
return $this->panel->create('admin.pages-form');
}
Storing of Newly Created Entries
We provide a ->store()
method which automatically handles various things such as Uploads, Relationships, Data Mapping, Flash Messages etc.
However if you need a custom save
method then you're free to do this, just remember to provide a $flash
message (['type' => 'success', 'message' => 'saved'])
and return to the edit form.
public function store()
{
// Vanilla Laravel
//... Well, we don't really know what you'd want to do here, that's up to you.
return redirect()->route('pages.edit', $page);
// Using Helpers
$this->panel->store('Success message here!');
return $this->panel->redirect('edit');
}
Validation and permissions can be handled using the native Laravel features. e.g. You can inject a Request class which checks policies to verify if the action is allowed.
Displaying an Entry
We think this is a pointless page for a backend, however if you need a page, then you can make one however you like. We just redirect it to the edit form.
public function show(Page $page)
{
return redirect()->route('pages.edit', $page);
}
Editing an Entry
To edit an entry, you must first provide the Panel with the entry that it will be using.
You can do this various ways e.g. Page::findOrFail($id)
, as long as you provide the loaded Eloquent model to the method.
We just use the route/model binding system that Laravel provide.
public function edit(Page $page)
{
$this->panel->setEntry($page);
// Vanilla Laravel
return view('admin.pages-form', [
'action' => route('pages.update'),
'method' => 'PUT',
]);
// Using Helpers
return $this->panel->edit('admin.pages-form');
}
Updating an Entry
Updating an entry is almost identical to storing one, again you can inject your own custom Validation and Policies if you need.
You can see an example below injecting a custom Request which houses the validation.
public function update(PageRequest $request, Page $page)
{
$this->panel->setEntry($page);
// Vanilla Laravel
//... Well, we don't really know what you'd want to do here, that's up to you.
return redirect()->route('pages.edit', $page);
// Using Helpers
$this->panel->update('Success message here!');
return $this->panel->redirect('edit');
}
Deleting an Entry
I hope you're starting to spot a pattern here! But deleting is as normal!
(We'll also show you a trick with SoftDeletes
) in a minute!
public function destroy(Page $page)
{
// Vanilla Laravel
$page->delete();
// Using Helpers
$this->panel->setEntry($page);
$this->panel->destroy('Success message!');
return $this->panel->redirect('index');
}
If your model uses SoftDeletes
then you'll be able to use the destroy()
method to toggle it's state. e.g.
If you pass through a model through destroy()
various times, if it uses the SoftDelete
trait it will check if it's already trashed, if it is, then it will restore it.
We can check the state of the model and provide a suitable success message and redirect location.
public function destroy(Page $page)
{
$this->panel->setEntry($page);
$message = $page->trashed() ? 'Page restored' : 'Page deleted';
$this->panel->destroy($message);
return $this->panel->redirect($page->exists() ? 'edit' : 'index');
}
A Route
You can manage your routes however you like, including applying middleware or guards to them e.g.
Route::prefix('/admin')->middleware('auth')->group(function () {
Route::resource('pages', 'Admin\PageController');
});
An Index Template
Our Blade components all use the normal Laravel directives, such as @include
@component
etc to give the most flexibility.
The index template is what displays the listing of all your entries, we provide slots and sections to add your UI elements.
The templates are pretty empty to start with, however we can quickly build up an interface. We will extend the index
layout.
We will add a "create" button and display the table of entries by passing in $columns
to display. You should be able to pass in the options that the Ant Design table component supports for it's headers https://ant.design/components/table/.
We provide an additional property for type
and this takes the name of one of our column types (see column type documentation for more).
@extends('maelstrom::layouts.index')
@section('buttons')
@include('maelstrom::buttons.button', [
'url' => route('pages.create'),
'label' => 'Create Page'
])
@endsection
@section('content')
@include('maelstrom::components.table', [
'columns' => [
[
'label' => 'Name',
'name' => 'page',
'sortable' => true,
'type' => 'EditLinkColumn',
'searchable' => true,
],
]
])
@endsection
Although you can pass in an array of columns to the table component, if you need more advance configuration, you should consider registering it via your controller.
Here we generate some filters for a column based off existing categories
public function index()
{
return $this->panel->index('admin.pages-index')
->with('columns', [
[
'label' => 'Name',
'name' => 'colour',
'sortable' => true,
'type' => 'EditLinkColumn',
'searchable' => true,
],
[
'label' => 'Category',
'name' => 'category.name',
'filterMultiple' => false,
'filters' => Category::all()->map(function ($category) {
return [
'text' => $category->name,
'value' => $category->id
];
})
],
]);
}
A Form Template
Now we have a index page, we can create a form page.
There are many many options for creating forms which you can read about in our other documentation, but here we have a simple example.
@extends('maelstrom::layouts.form')
@section('content')
@component('maelstrom::components.form', [
'action' => $action,
'method' => $method,
])
@include('maelstrom::inputs.text', [
'name' => 'name',
'label' => 'Post Name',
'required' => true,
])
@include('maelstrom::inputs.text', [
'name' => 'slug',
'label' => 'Post Slug',
'required' => true,
'html_type' => 'url',
])
@include('maelstrom::inputs.wysiwyg', [
'name' => 'body',
'label' => 'Post Body',
'required' => true,
])
@include('maelstrom::inputs.tags', [
'name' => 'tags',
'label' => 'Tags',
])
@include('maelstrom::components.media_manager', [
'name' => 'header_image',
'label' => 'Featured Image',
])
@endcomponent
@endsection
Et Voilà
If all went to plan, you should now be able to access your routes and manage some entities!
You may have realised a lack of navigation, in the next section we'll show you how to setup the sidebar.
← Installing Sidebar →