Structure

Overview

ValPress is architected to balance the familiar simplicity of a CMS with the robust, modern foundations of the Laravel framework. This section details the directory layout, the core database schema, and the internal request lifecycle that brings plugins and themes to life. Understanding this structure is essential for building scalable extensions and maintaining a healthy installation.

Why It Matters

  • Consistency: Following the established directory layout ensures your code is automatically discovered and registered by the CMS.
  • Data Integrity: Knowing the database schema allows for efficient querying and proper use of taxonomies and metadata.
  • Extensibility: Understanding the request lifecycle—specifically when plugins and themes boot—is critical for using hooks at the correct execution point.
  • Maintainability: A clear separation between core files, user uploads, and extensions prevents "hacking core" and simplifies the update process.

How It Works

Directory Layout

ValPress follows a standard Laravel directory structure with dedicated entry points for extensions.

Core Application

  • app/Core: The engine of ValPress. Contains the PluginManager, ThemeManager, Hook system, and the ValPress facade.
  • app/Models, app/Http/Controllers: Standard Laravel entities for core functionality (e.g., Post models, Admin controllers).
  • app/Providers: Service providers that bootstrap the CMS and its services.

Extension Points

  • public/plugins: Individual directories for each plugin. Each plugin must have a plugin.php entry point or a config.php metadata file.
  • public/themes: Individual directories for each theme. Supports parent-child theme relationships and standard functions.php logic.

Resources and Assets

  • resources/views: Contains core admin views and default frontend templates.
  • public/uploads: The default location for all media assets managed via the Media Library.
  • database/migrations: Core database schema definitions.

Database Schema

ValPress uses a normalized schema designed for high-performance content retrieval.

Table Purpose
posts Stores all content types (posts, pages, and custom post types).
post_types Definitions and settings for each content type.
postmeta Flexible key-value storage for post-specific metadata.
options Global site settings and active plugin/theme lists.
categories / tags Core taxonomies for content classification.
media Metadata for uploaded files and their generated sizes.
translations Multilingual support for core and extension strings.

Request Lifecycle

The request lifecycle in ValPress is an extension of the standard Laravel lifecycle, injected with CMS-specific bootstrapping steps.

  1. Entry Point: The request hits public/index.php.
  2. Framework Boot: Laravel loads service providers defined in config/app.php.
  3. CMS Boot (AppServiceProvider):
    • Plugin Initialization: PluginManager identifies active plugins from the options table and executes their plugin.php files.
    • Resource Registration: Plugins use the ValPress facade to register routes, views, and migrations.
    • Theme Initialization: ThemeManager boots the active theme, loads its functions.php, and sets up the view hierarchy (giving precedence to child themes).
  4. Hook Execution: Core actions like valpress_init and after_setup_theme are fired.
  5. Routing: Laravel matches the request to a registered route (core, plugin, or theme-defined).
  6. Execution & Rendering: The controller executes logic, and the view is rendered using the prioritized template hierarchy.

Usage

Navigating the Core Engine

If you need to understand how the CMS boots extensions, explore app/Core/ValPress.php. This class acts as the central registry for plugin resources:

// Registering a plugin route via the ValPress facade
ValPress::registerRoutes(__DIR__ . '/routes/web.php');

Accessing Database Tables

While you can use standard Eloquent models, ValPress provides helpers for common table operations:

// Get a global site option
$site_name = get_option('site_name');

// Get post metadata
$price = get_post_meta($post_id, 'product_price', true);

Code Examples

Plugin Resource Registration

Inside a plugin's plugin.php, resources should be registered during the boot process to ensure they are available to the framework.

<?php
/*
Plugin Name: Custom Analytics
*/

// Register view namespace so we can use view('analytics::dashboard')
ValPress::registerViews(__DIR__ . '/views', 'analytics');

// Register a migration path for plugin-specific tables
ValPress::registerMigrations(__DIR__ . '/database/migrations');

add_action('valpress_init', function() {
    // Logic to run after all plugins and themes are booted
});

Theme Template Hierarchy

ValPress automatically handles template priority. A child theme can override any parent template simply by matching the filename in its views/ directory.

public/themes/
  ├── parent-theme/
  │   └── views/
  │       └── single.blade.php
  └── child-theme/
      └── views/
          └── single.blade.php  <-- This file will be loaded automatically

Best Practices

  • Never Modify Core: Avoid changing files in app/Core or resources/views/admin. Use hooks (actions and filters) instead.
  • Use the public/ directory for extensions: Keep plugins and themes in their designated folders to ensure the PluginManager and ThemeManager can handle updates and activation.
  • Register Resources via Facades: Use ValPress::registerRoutes() or ValPress::registerViews() instead of manual Laravel registration to ensure proper scoping and cleanup.
  • Prefix Database Keys: When adding entries to the options or postmeta tables, use a unique prefix (e.g., myplugin_settings) to avoid collisions with other extensions.

Common Mistakes

  • Registering routes too late: If you register routes inside a hook that fires after the routing phase, they will never be matched. Always register them directly in plugin.php or functions.php.
  • Hardcoding paths: Always use helpers like plugin_dir_path(__FILE__) or Laravel's public_path() to ensure your code works across different environments.
  • Missing config.php for plugins: While plugin.php headers work, using a config.php is recommended for complex plugins requiring detailed metadata.
  • Direct SQL queries: Avoid writing raw SQL for core tables. Use Eloquent models or ValPress helpers to ensure compatibility with different database drivers (MySQL vs SQLite).

Summary

The ValPress structure is designed for clarity and extensibility. By leveraging standard Laravel patterns and providing dedicated extension points in public/plugins and public/themes, ValPress allows developers to build complex features without compromising the integrity of the core engine. Understanding the AppServiceProvider driven lifecycle is the key to mastering the timing of your customizations.