example
A worked example
This wires a small blog into a grouped, filtered, authorized admin: authors and their posts, with scopes, a custom action, a role policy, and a dashboard panel.
The models
Two ORM models, an author with many posts:
class Author is Model {
has-many 'posts';
}
class Post is Model {
belongs-to 'author';
self.validate: 'title', { :presence };
}Registering the resources
Both resources go under a Content menu group. Posts get columns, a filter, a
scope, a form, and a custom member action; authors get a nested form for their
posts.
use MVC::Keayl::Admin;
MVC::Keayl::Admin.configure(site-title => 'Blog Admin', mount-path => '/admin');
MVC::Keayl::Admin.register(Post, {
menu(group => 'Content', icon => 'file-text', priority => 1);
column('title', :sortable);
column('author', format => 'link-to-show');
column('published', :sortable);
filter('title', :as<string>);
filter('published', :as<boolean>);
scope('drafts', -> $relation { $relation.where({ published => False }) });
scope('published', -> $relation { $relation.where({ published => True }) }, :default);
field('title');
field('body', :as<text>);
field('published', :as<boolean>);
field('author-id', :as<select>, collection => { Author.all.all.map({ .id => .read-attribute('name') }) });
permit('title', 'body', 'published', 'author-id');
member-action('publish', -> $controller, $record {
$record.update({ published => True });
$controller.redirect-to($controller.path-for('post', $record.id));
}, :confirm('Publish this post?'));
});
MVC::Keayl::Admin.register(Author, {
menu(group => 'Content', icon => 'people', priority => 2);
column('name', :sortable);
field('name');
nested('posts', :multiple, {
field('title');
field('published', :as<boolean>);
});
permit('name');
});Authorizing access
A role policy: editors manage posts but only view authors; admins do everything.
use MVC::Keayl::Admin::Authorization::Role;
MVC::Keayl::Admin.authorize-with(
MVC::Keayl::Admin::Authorization::Role.new(
permissions => {
editor => <index show create update publish>,
admin => <*>,
},
)
);Forbidden actions return a 403, and the controls for them (the new button, row edit and delete, the publish action, batch destroy) are hidden from a role that lacks them.
A dashboard panel and ordering
Order the menu groups and add a dashboard panel listing recent posts:
MVC::Keayl::Admin.menu-group-order('Content');
MVC::Keayl::Admin.dashboard-block(title => 'Recent posts', {
'<ul class="list-unstyled mb-0">'
~ Post.all.order('created_at DESC').limit(5).all.map({ '<li>' ~ .read-attribute('title') ~ '</li>' }).join
~ '</ul>'
});The result
A
Contentgroup in the sidebar with Posts and Authors, Posts first.A sortable, filterable post index with
draftsandpublishedscope tabs, defaulting to published, plus CSV and JSON export of the current view.A post form with a body text area and an author select, and an author form that edits its posts inline.
A
Publishaction on the post show page, gated by the policy.A dashboard listing the resources and a recent-posts panel.