Introduction

Overview

ValPress is a modern, Laravel-based content management system (CMS) built as a pragmatic alternative to WordPress. It combines Laravel’s developer-first tooling with a familiar CMS experience: plugins, themes, hooks (actions and filters), a clean admin panel, a flexible content model, and an official Marketplace where developers can publish and sell their extensions. ValPress targets teams that want the productivity of a CMS without compromising on code quality, performance, or maintainability — and gives extension authors a platform to build a business around their work. For a detailed comparison of similarities and differences, see ValPress vs WordPress. For comparisons with other Laravel CMS platforms, see Statamic, October CMS, and Winter CMS.

ValPress embraces Laravel conventions: service container, middleware, routing, Blade, Eloquent, queues, and the full ecosystem of packages — while adding a lightweight CMS layer for content, media, theming, and extensibility.

What's new in ValPress 0.6

  • Update Center — staged verify/apply pipeline with readable reports for core, plugin, and theme updates
  • Snapshots & rollback — automatic core file snapshot before apply; restore from the admin UI
  • Repair actions — fix common failures (caches, storage link, plugin migrations) from the update report
  • Extension validation — plugins and themes implement ValidatesUpdate to block unsafe updates
  • Plugin/theme updates in UI — update extensions from the Update Center without SSH
  • Developer toolingphp artisan vp:make-plugin-validation {slug} and a reference example plugin

See Updates & Maintenance and Update Pipeline for details.

Why It Matters

  • Developer experience: Build with Laravel patterns you already know — no custom DSLs or hidden magic.
  • Extensibility: Plugins, themes, and a robust hooks system allow clean customization without editing core files.
  • A market for builders: ValPress was created not only as a CMS, but as the foundation for a new commercial ecosystem. The WordPress plugin and theme market is mature and crowded — established authors with large catalogs dominate discovery, making it difficult for new developers to gain traction and earn from their products. ValPress and its Marketplace offer a growing alternative: a curated platform where quality extensions can find an audience, and where new authors compete on merit rather than years of accumulated marketplace presence.
  • Performance: Cache-friendly architecture and an intentionally small core keep request handling fast.
  • Maintainability: Clear separation of concerns (core vs. plugins/themes), testable code, and Laravel-native tooling.
  • Migration-friendly: Familiar database structure and Eloquent models ease data migrations and imports/exports.

How It Works

At a high level, ValPress bootstraps a Laravel application and layers CMS features on top.

Conceptual architecture (text diagram):

  • HTTP Kernel (Laravel)
    • Global middleware (auth, CSRF, caching)
    • Route handling (web/admin)
  • ValPress Core
    • Post types and taxonomies (Eloquent models, repositories)
    • Options/settings (configurable key-value storage)
    • Media management
    • Admin UI (Blade views + policies/gates)
    • Hook system (actions/filters)
  • Extensions
    • Plugins (business features, integrations)
    • Themes (presentation, Blade templates, assets)

Request lifecycle (simplified):

1) Request enters Laravel’s HTTP kernel and passes middleware. 2) Routes resolve to controllers (core or plugin-provided via registration APIs). 3) Hooks fire (e.g., action events around rendering or admin screens). 4) Controller composes data (Eloquent), renders Blade view (theme/admin), and returns a response. 5) Response may be filtered via output-related hooks.

Usage

Use ValPress in two primary modes:

1) Site building without custom code

  • Install ValPress, choose a theme, enable plugins.
  • Configure content types, menus, and settings from Admin.

2) Application development with custom code (recommended for teams)

  • Create a plugin for domain-specific features and integrations.
  • Register routes, controllers, Blade views, and migrations inside your plugin.
  • Use hooks to extend or alter behavior without forking core.

3) Building and selling extensions

  • Develop plugins or themes for the ValPress ecosystem.
  • Publish to the ValPress Marketplace and reach users who are actively looking for ValPress-compatible products.
  • See Plugins, Themes, and Licensing for publication requirements.

What you need to know first:

  • Laravel basics: routes, controllers, Blade, Eloquent, config, env.
  • ValPress extensions: plugins (logic), themes (presentation), hooks (glue).

Code Examples

The following examples illustrate typical, production-grade usage patterns. They are intentionally explicit and commented for clarity.

Example 1 — Minimal plugin structure with admin menu and content filter:

<?php // public/plugins/acme-contacts/plugin.php
/*
Plugin Name: Acme Contacts
Description: Manage and display contacts; demonstrates routes, admin menu, and a simple filter.
Version: 1.0.0
Author: Acme, Inc.
*/

// 1) Register Blade view namespace for this plugin
view()->addNamespace('acme-contacts', __DIR__ . '/views');

// 2) Add an Admin menu item via a filter (the Admin collects items through `valpress_admin_menu_items`)
add_filter('valpress_admin_menu_items', function (array $items): array {
    if (!is_plugin_active('acme-contacts')) {
        return $items;
    }

    $items['acme-contacts'] = [
        'id' => 'acme-contacts',
        'title' => __('Contacts'),
        'url' => fn () => route('admin.acme_contacts.index'),
        'icon' => 'bi-people',
        'order' => 30,
        'parent' => null,
    ];

    return $items;
});

// 3) Filter post content on the frontend
add_filter('the_content', function (string $content): string {
    // Append a disclosure notice — keep it simple and idempotent
    $notice = '<p class="text-muted small">Content enhanced by Acme Contacts</p>';
    return $content . $notice;
});

// 4) Register routes when ValPress initializes
add_action('valpress_init', function () {
    // Use ValPress/Laravel routing to define admin endpoints for the plugin
    \Route::middleware(['web', 'auth', 'can:manage-content'])
        ->prefix('admin/acme-contacts')
        ->name('admin.acme_contacts.')
        ->group(function () {
            \Route::get('/', [\Acme\Contacts\Http\Controllers\ContactsController::class, 'index'])
                ->name('index');
        });
});

Plugin controller and a view:

<?php // public/plugins/acme-contacts/src/Http/Controllers/ContactsController.php

namespace Acme\Contacts\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\View\View;

class ContactsController
{
    // Display a paginated list of contacts stored in plugin tables or core posts
    public function index(Request $request): View
    {
        // In a real plugin, fetch Eloquent models; shown as static array for brevity
        $contacts = collect([
            ['name' => 'Ada Lovelace', 'email' => 'ada@example.com'],
            ['name' => 'Alan Turing',  'email' => 'alan@example.com'],
        ]);

        return view('acme-contacts::admin.index', [
            'contacts' => $contacts,
        ]);
    }
}
{{-- public/plugins/acme-contacts/views/admin/index.blade.php --}}
@extends('admin.layouts.app')

@section('title', __('Contacts'))

@section('content')
    <div class="container py-4">
        <h1 class="h3 mb-3">@lang('Contacts')</h1>

        <div class="card">
            <div class="table-responsive">
                <table class="table table-striped align-middle mb-0">
                    <thead>
                        <tr>
                            <th>@lang('Name')</th>
                            <th>@lang('Email')</th>
                        </tr>
                    </thead>
                    <tbody>
                        @foreach($contacts as $contact)
                            <tr>
                                <td>{{ $contact['name'] }}</td>
                                <td>
                                    <a href="mailto:{{ $contact['email'] }}">{{ $contact['email'] }}</a>
                                </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    </div>
@endsection

Example 2 — Theme snippets (Blade) using template tags and hooks:

{{-- public/themes/acme-blog/views/single.blade.php --}}
@extends('theme::layouts.app')

@section('title', e($post->title))

@section('content')
    <article class="container py-5">
        <header class="mb-4">
            <h1 class="display-5">{{ $post->title }}</h1>
            <p class="text-muted">{{ $post->published_at?->format('M d, Y') }}</p>
        </header>

        {{-- Featured image (template tag provided by core) --}}
        {!! the_featured_image($post, ['class' => 'img-fluid rounded mb-4']) !!}

        {{-- Main content (already passed through `the_content` filters) --}}
        <div class="content">{!! $post->content !!}</div>

        {{-- Post footer hooks for plugins to inject sharing widgets, etc. --}}
        @php(do_action('theme_post_footer', $post))
    </article>
@endsection

Advanced Usage

  • Custom post types (CPTs): Register programmatically in a plugin or via Admin UI to model domain entities (e.g., Events, Products). CPTs integrate with taxonomies, permalinks, and admin listing screens.
  • Admin customization: Use filters such as valpress_admin_menu_items to contribute structured menus and submenus. Use actions to enqueue assets on specific screens, or to hook into saving/publishing flows.
  • Routing: Keep plugin routes namespaced and behind relevant middleware (auth, authorization). Prefer controller classes to closures for testability.
  • Caching: Leverage Laravel caches for expensive queries; clear affected caches in relevant action hooks.
  • Testing: Place plugin tests under public/plugins/{slug}/tests and run via php artisan test using SQLite for speed.

Best Practices

  • Never modify core files; implement features in plugins or themes.
  • Keep plugins focused: one responsibility per plugin to ease maintenance and updates.
  • Use hooks for integration points; avoid hard-coding references to other plugins.
  • Follow PSR-12 and Laravel naming conventions; prefer dependency injection.
  • Localize all user-facing strings with __() and @lang for i18n.
  • Validate and authorize all admin actions; use policies/gates where appropriate.
  • Ship database migrations with plugins that own tables; write idempotent migrations.
  • Keep Blade templates slim; push data preparation to controllers/view models.

Common Mistakes

  • Editing files under app/Core or vendor directories — customization should live in plugins/themes.
  • Registering routes without middleware, exposing admin endpoints publicly.
  • Echoing raw HTML without sanitization; always escape or trust only sanitized content.
  • Bypassing hooks and tightly coupling plugins; prefer decoupled communication through actions/filters.
  • Mixing presentation and business logic in Blade views.

Summary

ValPress brings a Laravel-native development model to a CMS context. Build features as plugins, present them with themes, and connect everything through a simple hooks system. You retain the full power of Laravel — routing, middleware, Blade, Eloquent — while gaining a focused CMS layer that stays out of your way. For developers looking beyond client work, the Marketplace opens a path to sell extensions in an ecosystem that is still taking shape — a deliberate alternative to competing in saturated legacy marketplaces. This introduction set expectations and provided working examples you can adapt as you build.

Compatibility and versioning note: This document targets ValPress 1.x. When upgrading to 2.x, review the release notes for breaking changes, particularly around hook names, admin UI contracts, and any modified configuration keys. Prefer forward-compatible patterns (namespaced route names, DI in controllers, idempotent migrations) to reduce friction when upgrading.