Frontend Integration
This guide covers how to integrate your plugin with the frontend of a Botble CMS website.
Public Controllers
To display your plugin's content on the frontend, you'll need to create public controllers. These controllers handle requests from the frontend and return views.
Example public controller (src/Http/Controllers/PublicController.php
):
<?php
namespace Botble\Foo\Http\Controllers;
use Botble\Base\Facades\BaseHelper;
use Botble\Foo\Models\Item;
use Botble\SeoHelper\Facades\SeoHelper;
use Botble\Theme\Facades\Theme;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class PublicController extends Controller
{
public function index(Request $request)
{
SeoHelper::setTitle(trans('plugins/foo::foo.name'));
$items = Item::query()
->where('status', 'published')
->orderBy('created_at', 'DESC')
->paginate(10, ['*'], 'page', $request->integer('page', 1));
Theme::breadcrumb()
->add(trans('plugins/foo::foo.name'), route('public.foo.index'));
return Theme::scope('foo.index', compact('items'))->render();
}
public function detail($slug)
{
$item = Item::query()
->where([
'slug' => $slug,
'status' => 'published',
])
->first();
if (!$item) {
abort(404);
}
SeoHelper::setTitle($item->name)
->setDescription($item->description);
Theme::breadcrumb()
->add(trans('plugins/foo::foo.name'), route('public.foo.index'))
->add($item->name, route('public.foo.detail', $item->slug));
return Theme::scope('foo.detail', compact('item'))->render();
}
}
Theme Views
To display your plugin's content in the theme, you'll need to create theme views. These views are located in the theme's directory, not in your plugin.
Example theme view structure:
platform/themes/your-theme/views/foo/
├── index.blade.php
└── detail.blade.php
Example index view (platform/themes/your-theme/views/foo/index.blade.php
):
@php
Theme::set('page', 'foo-items');
@endphp
{!! Theme::partial('page-header', ['title' => __('plugins/foo::foo.name')]) !!}
<div class="container">
<div class="row">
<div class="col-md-9">
@if ($items->count() > 0)
<div class="row">
@foreach ($items as $item)
<div class="col-md-4 mb-4">
<div class="card h-100">
<a href="{{ route('public.foo.detail', $item->slug) }}">
<img src="{{ RvMedia::getImageUrl($item->image, 'medium', false, RvMedia::getDefaultImage()) }}" class="card-img-top" alt="{{ $item->name }}">
</a>
<div class="card-body">
<h5 class="card-title">
<a href="{{ route('public.foo.detail', $item->slug) }}">{{ $item->name }}</a>
</h5>
<p class="card-text">{{ Str::limit($item->description, 100) }}</p>
</div>
<div class="card-footer">
<small class="text-muted">{{ $item->created_at->diffForHumans() }}</small>
</div>
</div>
</div>
@endforeach
</div>
{!! $items->links() !!}
@else
<div class="alert alert-info">
{{ __('No items found') }}
</div>
@endif
</div>
<div class="col-md-3">
{!! Theme::partial('sidebar') !!}
</div>
</div>
</div>
Example detail view (platform/themes/your-theme/views/foo/detail.blade.php
):
@php
Theme::set('page', 'foo-detail');
@endphp
{!! Theme::partial('page-header', ['title' => $item->name]) !!}
<div class="container">
<div class="row">
<div class="col-md-9">
<div class="card mb-4">
@if ($item->image)
<img src="{{ RvMedia::getImageUrl($item->image, 'large', false, RvMedia::getDefaultImage()) }}" class="card-img-top" alt="{{ $item->name }}">
@endif
<div class="card-body">
<h1 class="card-title">{{ $item->name }}</h1>
<p class="card-text">{{ $item->description }}</p>
<div class="card-text">
{!! BaseHelper::clean($item->content) !!}
</div>
</div>
<div class="card-footer">
<small class="text-muted">{{ __('Posted') }}: {{ $item->created_at->diffForHumans() }}</small>
</div>
</div>
</div>
<div class="col-md-3">
{!! Theme::partial('sidebar') !!}
</div>
</div>
</div>
Adding Custom Shortcodes
Shortcodes allow you to add dynamic content to pages and posts. To add a custom shortcode:
- Register the shortcode in your service provider
- Create a view for the shortcode
Example shortcode registration:
Shortcode::register('foo-items', trans('plugins/foo::shortcodes.foo_items.name'), trans('plugins/foo::shortcodes.foo_items.description'), function ($shortcode) {
$limit = $shortcode->limit ?: 6;
$category = $shortcode->category;
$items = Item::query()
->where('status', BaseStatusEnum::PUBLISHED)
->with('slugable')
->orderBy('created_at', 'DESC')
->limit($limit)
->withCount('comments')
->get();
return view('plugins/foo::shortcodes.items', compact('items', 'shortcode'))->render();
}, [
'name' => [
'title' => trans('plugins/foo::shortcodes.foo_items.name'),
'type' => 'text',
'tab' => trans('core/base::forms.content'),
],
'limit' => [
'title' => trans('core/base::forms.limit'),
'type' => 'number',
'default_value' => 6,
'tab' => trans('core/base::forms.content'),
],
'category' => [
'title' => trans('plugins/foo::shortcodes.foo_items.category'),
'type' => 'customSelect',
'source' => route('foo.categories.list'),
'tab' => trans('core/base::forms.content'),
],
]);
Example shortcode view (resources/views/shortcodes/items.blade.php
):
<div class="foo-items">
<h2>{{ $shortcode->name ?: trans('plugins/foo::foo.items') }}</h2>
<div class="row">
@foreach ($items as $item)
<div class="col-md-4 mb-4">
<div class="card h-100">
<a href="{{ route('public.foo.detail', $item->slug) }}">
<img src="{{ RvMedia::getImageUrl($item->image, 'medium', false, RvMedia::getDefaultImage()) }}" class="card-img-top" alt="{{ $item->name }}">
</a>
<div class="card-body">
<h5 class="card-title">
<a href="{{ route('public.foo.detail', $item->slug) }}">{{ $item->name }}</a>
</h5>
<p class="card-text">{{ Str::limit($item->description, 100) }}</p>
</div>
</div>
</div>
@endforeach
</div>
</div>
Adding Custom Widgets
Widgets can be added to the dashboard or other areas of the admin panel. To add a widget:
- Create a widget class in the
src/Widgets
directory - Register the widget in your service provider
Example widget (src/Widgets/RecentItemsWidget.php
):
<?php
namespace Botble\Foo\Widgets;
use Botble\Base\Widgets\Card;
use Botble\Foo\Models\Item;
class RecentItemsWidget extends Card
{
public function __construct()
{
parent::__construct();
$this->setTitle(trans('plugins/foo::foo.recent_items'));
$this->setIcon('ti ti-box');
$this->setColor('#f3c200');
$this->setRoute(route('foo.index'));
$this->setPermission('foo.index');
}
public function getStats(): int
{
return Item::query()->where('status', 'published')->count();
}
}
Register in your service provider:
public function boot(): void
{
// ...
add_filter(DASHBOARD_FILTER_ADMIN_LIST, function ($widgets) {
return (new RecentItemsWidget())
->setColumn('col-md-3 col-sm-6')
->init($widgets);
}, 9, 1);
}
Adding Custom Widget Areas
To add a custom widget area:
- Register the widget area in your service provider
- Create a view for the widget area
Example widget area registration:
if (is_plugin_active('widget')) {
app(WidgetInterface::class)->registerSidebar([
'id' => 'foo_sidebar',
'name' => trans('plugins/foo::foo.widgets.sidebar_name'),
'description' => trans('plugins/foo::foo.widgets.sidebar_description'),
]);
}
To display the widget area in your theme:
{!! dynamic_sidebar('foo_sidebar') !!}
Adding SEO Metadata
To add SEO metadata to your plugin's content:
- Use the SeoHelper facade in your controllers
- Add SEO fields to your forms
Example in a controller:
public function show($slug)
{
$item = Item::query()
->where(['slug' => $slug, 'status' => 'published'])
->first();
if (!$item) {
abort(404);
}
SeoHelper::setTitle($item->name)
->setDescription($item->description)
->setImage(RvMedia::getImageUrl($item->image))
->setUrl($item->url);
return view('plugins/foo::themes.show', compact('item'));
}
Adding SEO fields to a form:
->add('seo_meta', 'seoMeta', [
'label' => trans('packages/seo-helper::seo-helper.meta_box_header'),
]);
Adding Custom CSS and JavaScript
To add custom CSS and JavaScript:
- Place your assets in the
public/vendor/your-plugin
directory - Register and load your assets in your service provider
Example in service provider:
public function boot(): void
{
// ...
$this->app->booted(function () {
add_action(BASE_ACTION_ENQUEUE_SCRIPTS, [$this, 'registerAssets'], 99);
});
}
public function registerAssets(): void
{
Assets::addStylesDirectly([
'vendor/core/plugins/foo/css/foo.css',
])
->addScriptsDirectly([
'vendor/core/plugins/foo/js/foo.js',
]);
}
To publish your assets during plugin installation:
$this->publishes([
__DIR__.'/../../resources/assets' => resource_path('vendor/core/plugins/foo'),
__DIR__.'/../../public' => public_path('vendor/core/plugins/foo'),
], 'public');