Welcome to AleshaCMS

AleshaCMS is a modern, developer-friendly Content Management System built on top of a modern PHP framework. It gives you a powerful admin panel, flexible content types, a visual page builder, and a theming system — all without the bloat.

New here? Start with Installation, then explore the Content Types and Page Builder.

What is AleshaCMS?

AleshaCMS is a full-featured CMS that runs on your own server. It's designed for developers who want control over their codebase, and for content editors who need a clean, intuitive admin interface.

  • Built on a modern PHP framework — familiar conventions, powerful routing, Blade templating.
  • Flexible Content Types — define any content structure (posts, pages, products, events…).
  • Visual Page Builder — drag-and-drop sections with real-time preview.
  • Theme System — swap full themes with color customization per theme.
  • Plugin Architecture — extend functionality without touching core files.
  • SQLite / MySQL ready — works with SQLite out of the box, production-ready on MySQL.

Quick Links

Installation

Get AleshaCMS running locally in minutes.

📄

Content Types

Define your own data structures and fields.

🧩

Page Builder

Build pages visually with drag-and-drop sections.

🎨

Theme Development

Create custom themes with Blade templates.

Requirements

AleshaCMS requires a modern PHP environment. The following dependencies must be installed before proceeding.

Server Requirements

DependencyMinimum VersionRecommended
PHP8.28.3+
Composer2.xLatest
Node.js18.x20+
DatabaseSQLite 3 / MySQL 8 / PostgreSQL 14Any

PHP Extensions

  • pdo + your database driver (pdo_sqlite, pdo_mysql)
  • mbstring
  • openssl
  • fileinfo
  • gd or imagick — for image processing
  • tokenizer, xml, ctype, bcmath

Run php -m to list all installed PHP extensions. the framework's built-in php artisan about also validates your environment.

Installation

AleshaCMS can be set up in just a few minutes using Composer.

1

Download & extract the source

Download the ZIP file provided by your administrator, then extract it and rename the folder:

unzip aleshacms.zip
mv aleshacms my-site
cd my-site

On Windows, right-click the ZIP file and choose Extract All, then rename the extracted folder to my-site.

2

Install PHP dependencies

composer install
3

Copy environment file & generate key

cp .env.example .env
php artisan key:generate
4

Configure database in .env

For SQLite (default — zero config):

DB_CONNECTION=sqlite
# DB_DATABASE defaults to database/database.sqlite

For MySQL:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=aleshacms
DB_USERNAME=root
DB_PASSWORD=secret
5

Run migrations & seed

php artisan migrate --seed

This creates all tables and a default admin user (admin@example.com / password).

6

Install frontend assets

npm install
npm run build
7

Start development server

php artisan serve

Visit http://localhost:8000 for the frontend and http://localhost:8000/admin for the admin panel.

Production — change the default admin password immediately and set APP_ENV=production + APP_DEBUG=false in .env.

Configuration

AleshaCMS uses the framework's standard .env file for environment config, plus a database-driven settings system for runtime configuration.

Environment Variables

KeyDescriptionDefault
APP_NAMEApplication nameAleshaCMS
APP_URLFull URL of your sitehttp://localhost
APP_ENVlocal / productionlocal
APP_DEBUGShow debug errorstrue
DB_CONNECTIONDatabase driversqlite
FILESYSTEM_DISKStorage driver for uploadspublic

Admin Settings

Runtime settings are managed through Admin → Settings and stored in the database via AleshaSetting::get() / AleshaSetting::set().

Setting KeyDescription
site_nameSite name displayed in header and browser tab
site_taglineSubtitle shown in theme header
site_faviconPath to favicon file
meta_descriptionDefault SEO meta description
active_themeSlug of currently active theme
theme_colors.{ThemeName}JSON color overrides per theme

Reading Settings in Code

AleshaCMS registers short class aliases globally — no use import needed anywhere, including Blade templates.

PHP / Blade
// Get a value with default fallback
$siteName = AleshaSetting::get('site_name', 'My Site');

// Store a value
AleshaSetting::set('site_name', 'New Name');

Directory Structure

AleshaCMS follows the framework's standard directory layout with a few additions for themes and plugins.

aleshacms/                   # CMS core (auto-loaded)
├── Http/Controllers/Admin/  # Admin panel controllers
├── Http/Controllers/Frontend/ # Frontend rendering
├── Http/Middleware/          # Custom middleware
├── Models/
│   ├── Post.php              # Unified content model
│   └── CMS/               # Setting, Menu, AuditLog…
└── CMS/
    └── PageBuilder/         # Section renderer

Themes/                      # All themes live here
├── News/
│   ├── theme.json            # Theme manifest
│   └── views/
│       ├── layouts/       # base.blade.php
│       ├── partials/      # header, footer
│       ├── components/
│       │   └── builder/   # hero, text, gallery…
│       └── pages/         # home, single, archive
└── default/

Plugins/                     # Drop-in plugins
├── Disqus/
│   ├── plugin.json
│   └── …
└── …

database/migrations/         # Framework migrations
resources/
├── js/admin/               # Admin Alpine.js components
└── css/                    # Tailwind / theme CSS
routes/web.php               # All routes

Content Types

Content Types define the structure of your content. Think of them as custom post types — each one can have its own fields, slug pattern, and front-end template.

Built-in Types

AleshaCMS ships with two built-in content types:

SlugNameDescription
postPostStandard blog articles
pagePageStatic pages (supports Page Builder)

Creating a Content Type

Go to Admin → Content Types → New. Each content type has:

  • Name — human-readable label (e.g. "Portfolio")
  • Slug — URL-safe identifier (e.g. portfolio)
  • Has Featured Image — toggle thumbnail support
  • Supports Page Builder — enable visual builder for this type
  • Custom Fields — JSON-defined extra fields

Custom Fields

Custom fields are stored as JSON in the custom_fields column on the posts table. Access them in templates via $post->custom_fields['key'].

For boolean flags that need reliable unchecked state saving, create a dedicated database column via migration rather than storing in custom_fields.

Posts & Pages

All content in AleshaCMS — posts, pages, and custom types — is stored in the unified posts table and managed through Admin → Posts.

Post Fields

FieldDescription
titleContent title
slugURL-friendly identifier, auto-generated from title
contentMain body content (rich text)
excerptShort summary for listings
statusdraft / published / scheduled
published_atScheduled or actual publish date
featured_imagePath to cover image
content_typeSlug of its Content Type
page_builderJSON array of page builder sections
builder_fullpageBoolean — disable container padding (edge-to-edge)
custom_fieldsJSON object of extra data

Querying in Theme Templates

PHP
// Latest 6 published posts
$posts = AleshaPost::ofType('post')
    ->published()
    ->latest('published_at')
    ->take(6)
    ->get();

// Single post by slug
$post = AleshaPost::where('slug', $slug)->firstOrFail();

Page Builder

The Page Builder lets editors assemble pages visually using pre-built sections — no code required. Each section maps to a Blade component in the active theme.

Available Sections

TypeDescription
heroFull-width banner with title, subtitle, and CTA button
textRich text content block
galleryImage grid with lightbox support
faqAccordion-style FAQ list
ctaCall-to-action banner with button
sliderAuto-playing image/content slider
embedEmbed iframes (YouTube, Maps, etc.)
newsletterEmail subscription form

Full Width Mode

Each section has a Full Width toggle. When enabled, the section renders edge-to-edge without a centered container. Useful for hero banners and sliders.

The Full Page Builder checkbox at the post level removes the container padding from the entire page, giving the builder full control of the layout.

Drag to Reorder

Sections can be reordered by dragging the handle on the left of each section. Order is saved automatically when the post is saved.

Section Data Structure

JSON
[
  {
    "type": "hero",
    "full_width": true,
    "title": "Welcome to Our Site",
    "subtitle": "The best place to start",
    "button_text": "Get Started",
    "button_url": "/about"
  },
  {
    "type": "text",
    "full_width": false,
    "content": "<p>Some rich text here…</p>"
  }
]

Media Library

The Media Library stores and manages all uploaded files. Images can be browsed from the post editor and page builder sections.

Uploading Files

Go to Admin → Media → Upload. Files are stored in storage/app/public and served via /storage/. Run php artisan storage:link to create the symlink.

Supported Formats

  • Images: jpg, jpeg, png, gif, webp, svg
  • Documents: pdf, docx, xlsx
  • Archives: zip

Accessing Media in Code

PHP
$media = Media::latest()->paginate(20);

// Full URL to file
asset('storage/' . $media->path);

Taxonomies

Taxonomies group content. AleshaCMS ships with Categories and Tags, and supports custom taxonomy types.

Default Taxonomies

TypeURL PatternDescription
category/category/{slug}Hierarchical grouping
tag/tag/{slug}Flat labels

Managing Taxonomies

Manage via Admin → Taxonomies. Select a type from the tab bar, then create/edit/delete terms. Each term has a name, slug, and optional description.

Assigning to Posts

In the post editor, select categories and tags from the sidebar panel. A post can belong to multiple terms of any taxonomy type.

Themes

Themes control the visual appearance of your site. AleshaCMS supports multiple themes — activate one from the admin and customize its colors without touching code.

Switching Themes

Go to Admin → Themes. Each installed theme shows a preview card. Click Activate to switch instantly.

Color Customization

Themes that define a colors block in theme.json will show a Colors button on their card. Click it to open the color picker panel, choose custom colors, and save. Changes take effect immediately — no rebuild needed.

Colors are stored in the database under the key theme_colors.{ThemeName} and injected as CSS custom properties in the theme's <head>.

Bundled Themes

ThemeDescriptionCSS Variables
NewsNewspaper-style theme with ticker bar, red nav--news-red, --news-red-dark
defaultClean minimal theme with indigo accent--theme-primary, --theme-primary-dark

Menus

Menus are assigned to named locations. Each theme declares which locations it supports, and editors populate them through the admin panel.

Menu Locations

Location SlugUsed By
primaryMain navigation bar (desktop + mobile)
footerFooter link columns

Menu Items

Each item has a Label, URL, Target (_self / _blank), and optional children for dropdown support.

Loading a Menu in a Template

PHP (Blade)
$menu = AleshaMenu::where('location', 'primary')
    ->with('items.children')
    ->first();

@foreach($menu->items as $item)
    <a href="{{ $item->url }}">{{ $item->label }}</a>
@endforeach

Sliders

Sliders are named collections of slides (image + title + subtitle + link) that can be embedded anywhere in your theme templates.

Creating a Slider

Go to Admin → Sliders → New. Give it a name (used as the identifier in templates), then add slides with images from the Media Library.

Rendering a Slider

PHP (Blade)
$slider = AleshaSlider::where('name', 'homepage')
    ->with('slides')
    ->first();

Users & Roles

AleshaCMS uses a simple role system. All users with the admin role can access the admin panel.

Roles

RoleAccess
adminFull admin panel access
editorContent management only (posts, media)
userFrontend only

Managing Users

Go to Admin → Users to create, edit, and delete users. Password changes take effect immediately.

Default credentials after fresh install are admin@example.com / password. Change this immediately on any non-local environment.

Settings

Site-wide settings are grouped and managed via Admin → Settings. Settings are stored in the database and can be read anywhere via AleshaSetting::get().

Setting Groups

GroupSettings
GeneralSite name, tagline, favicon, meta description
SEODefault meta title pattern, robots, sitemap
SocialFacebook, Twitter, Instagram, YouTube URLs
AdvancedCustom CSS / JS injection, analytics code

Plugins

Plugins extend AleshaCMS without modifying core files. Drop a plugin folder into Plugins/ and enable it from Admin → Plugins.

Bundled Plugins

PluginDescription
DisqusAdds Disqus comment threads to post pages

Plugin State

Each plugin has an is_active flag. Toggle via the admin panel. The CMS checks AleshaPlugin::where('slug', '...')->where('is_active', true)->exists() before loading plugin features.

Audit Logs

AleshaCMS records every significant admin action to the audit_logs table. Logs include the action, user, affected model, IP address, and timestamp.

Viewing Logs

Go to Admin → Logs. Use the search bar to filter by action, username, or IP address. Logs are paginated (20 per page).

Log Fields

FieldDescription
actione.g. post.created, theme.activated
userAdmin user who performed the action
model_type / model_idAffected Eloquent model
ip_addressRequest IP
user_agentBrowser/client string

Theme Structure

A theme is a folder inside Themes/. Its name is the theme slug. The minimum required files are a theme.json manifest and a base layout.

Minimum Theme Structure

Themes/MyTheme/
├── theme.json
└── views/
    ├── layouts/
    │   └── base.blade.php       # Master layout
    ├── partials/
    │   ├── header.blade.php
    │   └── footer.blade.php
    ├── pages/
    │   ├── home.blade.php       # Homepage
    │   ├── single.blade.php     # Single post/page
    │   ├── archive.blade.php    # Post listing
    │   └── taxonomy.blade.php   # Category/tag archive
    └── components/
        └── builder/           # One file per section type
            ├── hero.blade.php
            ├── text.blade.php
            └── …

View Namespacing

AleshaCMS registers each theme's views under the namespace theme-{ThemeSlug}. Use this in Blade includes:

Blade
@include('theme-MyTheme::partials.header')
@extends('theme-MyTheme::layouts.base')

theme.json

Every theme must have a theme.json file at its root. This file declares the theme's metadata, color definitions, and supported features.

Full Example

JSON
{
  "name": "My Theme",
  "slug": "MyTheme",
  "version": "1.0.0",
  "author": "Your Name",
  "description": "A clean, modern theme for AleshaCMS.",
  "colors": [
    {
      "key": "primary",
      "label": "Primary Color",
      "default": "#4f46e5",
      "css_var": "--theme-primary"
    },
    {
      "key": "primary_dark",
      "label": "Primary Dark (Hover)",
      "default": "#4338ca",
      "css_var": "--theme-primary-dark"
    }
  ]
}

Fields

FieldRequiredDescription
nameYesHuman-readable theme name
slugYesMust match the folder name exactly
versionNoSemantic version string
authorNoTheme author name
descriptionNoShort description shown in admin
colorsNoArray of color definitions for the color picker

Blade Templates

AleshaCMS theme templates are standard framework Blade files. Each file has a specific role — the frontend controller resolves which template to render based on the requested URL. Below is a complete breakdown of every template file and the variables available in each.

Template Resolution Map

URL PatternTemplate File
/pages/home.blade.php
/{slug} (page/post)pages/single.blade.php
/post (archive listing)pages/archive.blade.php
/category/{slug}, /tag/{slug}pages/taxonomy.blade.php

layouts/base.blade.php

Master layout inherited by all page templates via @extends. Defines the HTML shell, loads assets, includes header/footer partials, and exposes named slots via @yield.

SlotDescription
@yield('title')Browser tab title
@yield('description')Meta description tag
@yield('head')Extra CSS/meta injected inside <head>
@yield('content')Main page body
@yield('scripts')Extra JS before </body>
layouts/base.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="UTF-8">
    <title>@yield('title', AleshaSetting::get('site_name'))</title>
    <meta name="description" content="@yield('description')">
    @vite(['resources/css/theme.css'])
    @yield('head')
</head>
<body>
    @include('theme-MyTheme::partials.header')
    <main>@yield('content')</main>
    @include('theme-MyTheme::partials.footer')
    @yield('scripts')
</body>
</html>

partials/header.blade.php

Rendered on every page via @include in the base layout. Responsible for the site logo, navigation bar, and any top-bar elements (breaking news ticker, date bar, etc.).

Variables are fetched directly inside the partial using @php:

partials/header.blade.php
@php
    $siteName   = AleshaSetting::get('site_name', config('app.name'));
    $tagline    = AleshaSetting::get('site_tagline', '');
    $menu       = AleshaMenu::where('location', 'primary')
                    ->with('items.children')->first();
    $currentUrl = '/' . request()->path();
@endphp

<header>
    <a href="/">{{ $siteName }}</a>

    @if($menu && $menu->items->count())
        @foreach($menu->items as $item)
            <a href="{{ $item->url }}"
               class="{{ $currentUrl === $item->url ? 'active' : '' }}">
                {{ $item->label }}
            </a>
            @if($item->children->count())
                @foreach($item->children as $child)
                    <a href="{{ $child->url }}">{{ $child->label }}</a>
                @endforeach
            @endif
        @endforeach
    @endif
</header>

partials/footer.blade.php

Included at the bottom of every page. Typically contains footer nav links, social links, and copyright. Fetches its own data via @php.

partials/footer.blade.php
@php
    $siteName   = AleshaSetting::get('site_name');
    $footerMenu = AleshaMenu::where('location', 'footer')
                    ->with('items')->first();
    $social = [
        'facebook'  => AleshaSetting::get('social_facebook'),
        'twitter'   => AleshaSetting::get('social_twitter'),
        'instagram' => AleshaSetting::get('social_instagram'),
    ];
@endphp

<footer>
    <p>© {{ date('Y') }} {{ $siteName }}</p>

    @if($footerMenu)
        @foreach($footerMenu->items as $item)
            <a href="{{ $item->url }}">{{ $item->label }}</a>
        @endforeach
    @endif

    @foreach(array_filter($social) as $platform => $url)
        <a href="{{ $url }}" target="_blank">{{ ucfirst($platform) }}</a>
    @endforeach
</footer>

pages/home.blade.php

Rendered at /. Receives variables injected by FrontendController::resolve(). Typically displays a slider, featured posts, and category sections.

VariableTypeDescription
$featuredPostsCollection<Post>Latest published posts for homepage grid
$sliderSlider|nullActive homepage slider with slides
$categoriesCollection<Taxonomy>All category terms
pages/home.blade.php
@extends('theme-MyTheme::layouts.base')

@section('title', AleshaSetting::get('site_name'))

@section('content')

    @if($slider && $slider->slides->count())
        @foreach($slider->slides as $slide)
            <div style="background-image:url('{{ asset('storage/' . $slide->image) }}')">
                <h2>{{ $slide->title }}</h2>
                <p>{{ $slide->subtitle }}</p>
                @if($slide->link)
                    <a href="{{ $slide->link }}">{{ $slide->button_text }}</a>
                @endif
            </div>
        @endforeach
    @endif

    @foreach($featuredPosts as $post)
        <article>
            @if($post->featured_image)
                <img src="{{ asset('storage/' . $post->featured_image) }}"
                     alt="{{ $post->title }}">
            @endif
            <h3><a href="/{{ $post->content_type }}/{{ $post->slug }}">
                {{ $post->title }}
            </a></h3>
            <time>{{ $post->published_at?->format('d M Y') }}</time>
            <p>{{ Str::limit(strip_tags($post->excerpt ?: $post->content), 120) }}</p>
        </article>
    @endforeach

@endsection

pages/single.blade.php

Rendered for any individual post or page URL (/{slug} or /{type}/{slug}). Page builder sections are pre-rendered by the controller and passed as $pageBuilderHtml — just output it with {!! !!}.

VariableTypeDescription
$postPostThe resolved post/page model
$pageBuilderHtmlstringPre-rendered page builder HTML (empty string if not using builder)
$builderFullpageboolTrue = edge-to-edge layout, no container padding
$relatedPostsCollection<Post>Posts from same category (optional)
pages/single.blade.php
@extends('theme-MyTheme::layouts.base')

@section('title', $post->title)
@section('description', Str::limit(strip_tags($post->excerpt ?: $post->content), 160))

@section('content')

@if($pageBuilderHtml)
    {{-- Page Builder mode: output pre-rendered HTML from controller --}}
    {!! $pageBuilderHtml !!}
@else
    {{-- Standard content mode --}}
    <article>
        <h1>{{ $post->title }}</h1>
        <div class="article-meta">
            {{ $post->published_at?->format('d F Y') }}
            @if($post->author)
                · {{ $post->author->name }}
            @endif
        </div>
        @if($post->featured_image)
            <img src="{{ asset('storage/' . $post->featured_image) }}"
                 alt="{{ $post->title }}">
        @endif
        <div class="article-body">
            {!! $post->content !!}
        </div>
        @if($post->custom_fields['source_url'] ?? null)
            <p>Source: <a href="{{ $post->custom_fields['source_url'] }}">
                {{ $post->custom_fields['source_url'] }}
            </a></p>
        @endif
    </article>
@endif

@endsection

pages/archive.blade.php

Rendered at /post (and any custom content type archive). Shows a paginated list of published posts, supports search filtering via ?search= query string.

VariableTypeDescription
$postsLengthAwarePaginatorPaginated post collection
$typestringContent type slug (e.g. post)
$searchstring|nullCurrent search query from ?search=
pages/archive.blade.php
@extends('theme-MyTheme::layouts.base')
@section('title', 'Latest Posts')

@section('content')

    <form action="/post" method="GET">
        <input type="text" name="search" value="{{ $search }}"
               placeholder="Search...">
    </form>

    @forelse($posts as $post)
        <article>
            <h2><a href="/{{ $post->content_type }}/{{ $post->slug }}">
                {{ $post->title }}
            </a></h2>
            <time>{{ $post->published_at?->format('d M Y') }}</time>
        </article>
    @empty
        <p>No posts found.</p>
    @endforelse

    {{ $posts->withQueryString()->links() }}

@endsection

pages/taxonomy.blade.php

Rendered for category (/category/{slug}) and tag (/tag/{slug}) archives. Shows all posts belonging to that taxonomy term.

VariableTypeDescription
$taxonomyTaxonomyCurrent term model (name, slug, description)
$postsLengthAwarePaginatorPaginated posts in this term
$typestringcategory or tag
pages/taxonomy.blade.php
@extends('theme-MyTheme::layouts.base')
@section('title', $taxonomy->name)

@section('content')

    <header>
        <span>{{ ucfirst($type) }}</span>
        <h1>{{ $taxonomy->name }}</h1>
        @if($taxonomy->description)
            <p>{{ $taxonomy->description }}</p>
        @endif
        <small>{{ $posts->total() }} article(s)</small>
    </header>

    @foreach($posts as $post)
        <article>
            <h2><a href="/{{ $post->content_type }}/{{ $post->slug }}">
                {{ $post->title }}
            </a></h2>
        </article>
    @endforeach

    {{ $posts->links() }}

@endsection

Using {!! !!} vs {{ }} — always use {{ }} (escaped) for user-entered text like titles and names. Only use {!! !!} (unescaped) for trusted HTML content like $post->content which comes from the admin editor.

Color System

AleshaCMS injects saved color values as CSS custom properties into every page. Use var(--your-var) throughout your theme CSS — users can change them from the admin without touching code.

How It Works

  1. Define colors in theme.json with key, default, and css_var.
  2. In your base layout, read saved values and emit a :root block:
Blade
@php
  $colors = AleshaSetting::get('theme_colors.MyTheme', []);
@endphp
<style>
  :root {
    --theme-primary: {{ $colors['primary'] ?? '#4f46e5' }};
    --theme-primary-dark: {{ $colors['primary_dark'] ?? '#4338ca' }};
    /* Derived tints using CSS color-mix() */
    --theme-light: color-mix(in srgb, var(--theme-primary) 15%, #fff);
    --theme-bg: color-mix(in srgb, var(--theme-primary) 8%, #fff);
  }
</style>

Use color-mix(in srgb, var(--theme-primary) 35%, #fff) to auto-derive lighter tints — no need to ask users to pick every shade manually.

Never Hardcode Colors

All color values in your theme's CSS and Blade files must reference CSS variables. Never hardcode hex values like #4f46e5 — users won't be able to change them from the admin panel.

Builder Components

Each page builder section type maps to a Blade file in views/components/builder/. AleshaCMS passes section data as variables — your component just renders it.

Received Variables

Every builder component receives these variables automatically:

VariableDescription
$full_widthBoolean — remove container padding for edge-to-edge layout
Section-specific fieldse.g. $title, $content, $images, etc.

Full Width Pattern

Blade
@php $fw = $full_width ?? false; @endphp

<section style="padding: 60px 0;">
  <div style="{{ $fw ? '' : 'max-width:1100px; margin:0 auto; padding:0 20px;' }}">
    {{-- your section content --}}
  </div>
</section>

Gallery Lightbox

Gallery components should implement a lightbox using the window._glbReg registry pattern so multiple galleries on the same page work independently:

JS (inline)
window._glbReg = window._glbReg || {};
window._glbReg['{{ $gid }}'] = @json($imgs);

if (!window.galleryOpen) {
  window.galleryOpen = function(id, i) { /* show modal */ };
  window.galleryClose = function(id) { /* hide modal */ };
  window.galleryNav = function(id, dir) { /* prev/next */ };
}

Plugin Structure

Plugins are self-contained folders inside Plugins/. They are loaded by the CMS when active and can register routes, views, middleware, and service providers.

Minimum Plugin Structure

Plugins/MyPlugin/
├── plugin.json       # Plugin manifest
├── Plugin.php        # Main plugin class (optional)
└── views/           # Blade views (optional)

plugin.json

JSON
{
  "name": "My Plugin",
  "slug": "MyPlugin",
  "version": "1.0.0",
  "author": "Your Name",
  "description": "What this plugin does."
}

Checking Plugin State in Templates

PHP
$active = AleshaPlugin::where('slug', 'MyPlugin')
    ->where('is_active', true)
    ->exists();

if ($active) {
    // Render plugin output
}

Plugin Settings

Plugins can store their own settings using the Setting model with namespaced keys.

Convention

Prefix all plugin setting keys with the plugin slug to avoid collisions:

PHP
// Save
AleshaSetting::set('myplugin_api_key', $request->api_key);

// Read
$key = AleshaSetting::get('myplugin_api_key', '');

Admin UI for Plugin Settings

Register a settings group under Admin → Settings by adding your keys to the settings controller's group map, or provide your own admin route + view within the plugin.

The Disqus plugin uses disqus_shortname as its setting key — a good reference implementation to follow.