Building nollejCraft: A Maintainable CMS Architecture

WordPress powers 40% of the web, but ask developers what it's like to maintain a WordPress site five years into production, and you'll hear stories about plugin conflicts, database bloat, and technical debt that accumulates quickly and becomes increasingly more difficult to maintain.

What if you could build a content management system (CMS) that actually stays maintainable? One that doesn't fight you when you need to extend it, doesn't hide complexity behind "magic," and doesn't require a PhD in WordPress internals to debug?

That's the engineering challenge behind nollejCraft: I had a specific need and wanted to build a content management system that I could continue maintaining years from now.

The Core Architecture

nollejCraft is radically simple:

  • one SQL table
  • three PHP scripts
  • a consistent extension pattern

The _sitePages Table

All content lives in a single table. Pages, blog posts, dashboards, landing pages, everything uses the same schema:

CREATE TABLE _sitePages (
    id INT PRIMARY KEY AUTO_INCREMENT,
    doc_type VARCHAR(50),      -- blog, page, dashboard
    category VARCHAR(50),      -- For navigation grouping
    title VARCHAR(255),
    slug VARCHAR(255),         -- Auto-generated from doc_type/category/title
    content TEXT,              -- Markdown/HTML
    status VARCHAR(20),        -- draft, published, archived
    access_level VARCHAR(20),  -- public, registered, paid
    created_at TIMESTAMP,
    updated_at TIMESTAMP
);

That's it. No wp_posts, wp_postmeta, wp_terms, wp_term_relationships, and the other eleven tables WordPress uses for similar functionality.

Why single table?

  • Direct SQL queries without joins: SELECT * FROM _sitePages WHERE content LIKE '%shortcode%'
  • Transparent data model—you understand the structure in 30 seconds
  • Trivial to backup, migrate, or inspect
  • No hidden relationships to debug at 2am

The Three Scripts

1. index.php - Public Entry Point

Renders published content, parses Markdown via Parsedown, processes shortcodes, generates navigation links from doc_type, category, and title fields.

2. siteManager.php - CRUD Interface

Filter, draft, publish, archive pages. All content types managed in one interface.

3. pageEditForm.php - Content Editor

CodeMirror-powered editor for Markdown/HTML. Handles metadata, content lifecycle, and preview.

Three files. About 800 lines total. That's the entire CMS core.

The Extension Pattern: Shortcode Handlers

The real architectural insight is how nollejCraft extends without bloating: shortcode handlers with consistent signatures.

How It Works

Content can contain shortcodes:

## Member Directory

[[table dataset="members" columns="name,email,company" filter="status='paid'"]]

When index.php renders the page, it:

  1. Finds shortcodes via regex
  2. Extracts tag name (table) and parameters
  3. Calls handle_table($params, $inner)
  4. Replaces shortcode with returned HTML

The Handler Pattern

Every shortcode handler follows the same signature:

function handle_table($params, $inner) {
    // $params = ['dataset'=>'members', 'columns'=>'name,email,company', ...]
    // $inner = content between opening/closing tags (if any)

    // Do whatever you need:
    // - Query database
    // - Call APIs  
    // - Process data
    // - Generate HTML

    return $html;  // String of HTML to inject
}

That's the entire extension API. No hooks. No filters. No action priorities. No namespacing headaches.

Example: Building a Custom Handler

Want a shortcode that displays upcoming events? Write one function:

function handle_events($params, $inner) {
    $limit = $params['limit'] ?? 5;
    $category = $params['category'] ?? 'all';

    $sql = "SELECT * FROM events 
            WHERE date >= CURDATE() 
            AND (category = ? OR ? = 'all')
            ORDER BY date LIMIT ?";

    $events = db_query($sql, [$category, $category, $limit]);

    $html = '<div class="events-list">';
    foreach ($events as $event) {       // generate a card for each event
        $html .= "<div class='event'>";
        $html .= "<h3>{$event['title']}</h3>";
        $html .= "<p>{$event['date']}</p>";
        $html .= "</div>";
    }
    $html .= '</div>';

    return $html;
}

Drop that function in your handlers file. Now this works:

[[events category="workshop" limit="3"]]

Time to implement: 15 minutes.

Compare to WordPress: Register shortcode, learn WordPress API conventions, handle attribute sanitization via WordPress methods, ensure compatibility with page builders, test against common plugins, debug Gutenberg interactions. Time: 2-4 hours if you know what you're doing.

My First Handlers

As I started creating my website with nollejCraft, I realized there are certain patterns that keep coming up, types of data things I need to include on web pages. So my first shortcode handlers were these four:

handle_form($params, $inner) - Generate HTML forms from simple definitions
handle_table($params, $inner) - Display data tables with filtering/sorting
handle_cards($params, $inner) - Card layouts for events, members, content
handle_dazl($params, $inner) - Execute data analytics workflows (integrated with DAZL)

Each handler is 50-150 lines of focused PHP. There's no framework overhead, no crazy abstraction layers, just functions that take parameters, do something, and return HTML.

Why This Matters for Maintenance

Debuggable

Something not rendering?

SELECT * FROM _sitePages WHERE id = 123;

See the exact content stored. You can get right to the page source without worrying about things like post meta to check, or revision history cluttering things. You just go straight to the data.

Shortcode not working? Open the projectLib.php file and go straight to handle_whatever() directly. No jumping through hooks to figure out execution order.

Testable

Want to test a handler? Call it:

$html = handle_table(['dataset'=>'test', 'columns'=>'a,b,c'], '');
assert(strpos($html, '<table>') !== false);

You don't need to boot WordPress, mock global state, or fight the testing infrastructure.

Extensible Without Complexity

Client needs custom functionality? Write a handler function. 30-60 minutes for most features.

No plugin development. No marketplace submission. No compatibility testing across WordPress versions.

Your business logic, your code, your timeline.

Navigation and URL Structure

Each row in the SQL table (page on the site) has a unique slug.

Site navigation is automatically generated from content structure:

  • Top-level menu from doc_type values
  • Dropdowns from category groupings
  • Slugs from doc_type/category/title

Add a page with doc_type="resources" and category="guides", get a menu item automatically. No manual menu management.

URLs follow the pattern: /resources/guides/getting-started

Clean, predictable, SEO-friendly.

Access Control

Three visibility levels built into the core:

public - Anyone can view
registered - Requires user account
paid - Requires paid subscription

Set access_level on any page. Middleware checks authentication before rendering.

For more complex permissions, extend the middleware. No plugin ecosystem to navigate.

Integration Points

nollejCraft doesn't try to be a complete business platform. It integrates with systems that manage business data.

The optional nollejVault integration demonstrates the pattern: handlers can query any database table or API endpoint. Return data in a standard format, and existing handlers (table, cards, charts) render it automatically.

This separation means nollejCraft stays focused on content presentation while business logic lives where it belongs—in your business systems.

Performance Characteristics

Single table queries are fast. Markdown parsing via Parsedown is efficient. Handler functions execute directly—no framework overhead.

For a typical site (hundreds of pages, moderate traffic), nollejCraft runs comfortably on shared hosting. No caching plugins needed for reasonable performance.

For higher traffic, add standard PHP optimization (opcache, edge caching). The simple architecture doesn't fight you.

When to Use nollejCraft

This architecture makes sense when:

You need custom functionality - Writing handlers is faster than configuring plugins
You value transparency - Direct SQL access and simple code beats "magic"
You're comfortable with PHP - Extending requires basic PHP skills
You want long-term maintainability - Simpler systems age better
Your content connects to business data - Handler pattern handles this naturally

It's not for everyone. If you need a massive plugin ecosystem or a drag-and-drop page builder, stick with established platforms.

But if you've spent years maintaining WordPress sites and wondered if there's a better way, there absolutely is.

The Design Philosophy

nollejCraft prioritizes:

Simplicity over features - Solve 80% of use cases exceptionally well
Transparency over abstraction - Understand what's happening by reading code
Extensibility over completeness - Make adding features straightforward
Maintainability over cleverness - Future you will thank present you

These guiding principles are actually architectural constraints on purpose. They prevent the system from becoming what it set out to replace.

Looking Forward

The core architecture is stable.

Extensions happen through handlers, not core modifications. Adding a new shortcode handler does not break anything that already works. Each handler only executes when a shortcode is placed in a document and the html is rendered. If the new shortcode you're testing is on a page flagged as DRAFT, you're the only one that can see the page. And therefore, the only person that will know if the shortcode does what it's supposed to do.

Future additions might include:

  • Additional built-in handlers (charts, calendars)
  • Template system improvements
  • Enhanced developer tooling
  • API for headless CMS usage

But the foundation is here now. A single table, three scripts, handler pattern.

Final Thoughts

Building yet another CMS wasn't the goal. Maintaining my website and articles in the most straight-forward way possible, that was the goal.

By rejecting the "everything for everyone" approach and focusing on a clean architecture with consistent extension patterns, nollejCraft demonstrates that simpler systems can be more capable where it counts: solving real problems without accumulating technical debt.

The test isn't whether it does everything WordPress does. Can I maintain my website without spending hours coding? That's the question. And I know the database structure in thorough, and the 3 core programs that manage the data and serve up the site are solid.

Insight: The real test is whether a website can be maintained confidently five years from now without touching much code.

For developers tired of fighting their tools, that makes all the difference.


nollejCraft is open to implementation partners and custom deployments. The architecture supports rapid customization for client-specific needs while maintaining the core simplicity that makes long-term maintenance feasible.