Skip to content

Frontend Integration ​

This guide covers how to integrate your plugin with the frontend of a Thesky9 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
<?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
@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
@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:

  1. Register the shortcode in your service provider
  2. Create a view for the shortcode

Example shortcode registration:

php
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):

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:

  1. Create a widget class in the src/Widgets directory
  2. Register the widget in your service provider

Example widget (src/Widgets/RecentItemsWidget.php):

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:

php
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:

  1. Register the widget area in your service provider
  2. Create a view for the widget area

Example widget area registration:

php
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:

php
{!! dynamic_sidebar('foo_sidebar') !!}

Adding SEO Metadata ​

To add SEO metadata to your plugin's content:

  1. Use the SeoHelper facade in your controllers
  2. Add SEO fields to your forms

Example in a controller:

php
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:

php
->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:

  1. Place your assets in the public/vendor/your-plugin directory
  2. Register and load your assets in your service provider

Example in service provider:

php
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:

php
$this->publishes([
    __DIR__.'/../../resources/assets' => resource_path('vendor/core/plugins/foo'),
    __DIR__.'/../../public' => public_path('vendor/core/plugins/foo'),
], 'public');