Nairametrics Platform Evolution - Roadmap Proposal

Phase C Transformation Guide: WordPress+Acorn → Pure Laravel

📚 Technical Reference: For detailed information about the current Radicle architecture (Sage, Acorn, Blade, Service Providers), see the Architecture Documentation.

Executive Summary

Your transformation through the roadmap phases:

  • Phase A (Now): WordPress + Acorn + Blade templates
  • Phase B (Next): WordPress CMS + Laravel API + Next.js/Expo frontends
  • Phase C (Future): Pure Laravel CMS + Next.js/Expo frontends (from Phase B)

Key Insight: By Phase C, your frontend is ALREADY Next.js + Expo from Phase B. Phase C only replaces the WordPress backend with pure Laravel!


🎯 What Phase C Actually Changes

PHASE B (Before Phase C):
┌─────────────────────────────────────┐
│ WordPress + Acorn (CMS only)        │ ← REPLACED in Phase C
└─────────┬───────────────────────────┘
          │ Laravel API
          ▼
┌─────────────────────────────────────┐
│ Next.js + Expo Monorepo (Frontend)  │ ← STAYS THE SAME!
└─────────────────────────────────────┘

PHASE C (After):
┌─────────────────────────────────────┐
│ Pure Laravel + Filament (CMS)       │ ← NEW!
└─────────┬───────────────────────────┘
          │ Same Laravel API (enhanced)
          ▼
┌─────────────────────────────────────┐
│ Next.js + Expo Monorepo (Frontend)  │ ← NO CHANGES!
└─────────────────────────────────────┘

Phase C Impact: Replace WordPress backend → Frontend stays identical! ✅


📊 Phase-by-Phase Component Evolution

Frontend Components (Phase A → B → C)

Component Phase A Phase B Phase C
Web Frontend Blade templates (WordPress theme) Next.js app Next.js app (no change)
Mobile Apps None Expo iOS/Android Expo iOS/Android (no change)
Styling Tailwind CSS Tailwind CSS Tailwind CSS (no change)
Data Fetching WordPress queries API calls API calls (no change)
Build System Bud.js Phase B Already Did This! Your Next.js + Expo apps are already calling the API:
// apps/web/app/post/[slug]/page.tsx (Phase B - No changes in Phase C)
import { getPost } from '@repo/articles/api';

export default async function PostPage({ params }) {
  const post = await getPost(params.slug);
  
  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

Phase C Impact: None! Next.js stays the same.


2. Laravel API - Minor Enhancements 🔧

Phase B (Current in Phase C):

// API reads from WordPress database
public function show(string $slug)
{
    $post = DB::connection('wordpress')
        ->table('wp_posts')
        ->where('post_name', $slug)
        ->first();
        
    return response()->json($post);
}

Phase C (Enhanced):

// API reads from Laravel database
public function show(string $slug)
{
    $post = Post::published()
        ->with(['author', 'category', 'tags'])
        ->whereSlug($slug)
        ->firstOrFail();
        
    return new PostResource($post);
}

Migration Effort: Swap WordPress queries for Eloquent models


3. Admin Interface - Complete Replacement 🆕

Phase B: WordPress admin Phase C: Filament admin (see examples/PostResource.php)

// Phase C: Filament replaces WordPress post editor
class PostResource extends Resource
{
    protected static ?string $model = Post::class;
    
    public static function form(Form $form): Form
    {
        return $form->schema([
            Forms\Components\TextInput::make('title'),
            Forms\Components\RichEditor::make('content'),
            Forms\Components\Select::make('status'),
            // Modern, fast, customizable interface
        ]);
    }
}

Migration Effort: Build Filament resources for all content types


4. API Routes - Enhanced in Phase C 🔧

Phase B: routes/api.php (reads WordPress)

Route::get('/posts', function() {
    // Query WordPress database
    return DB::connection('wordpress')->table('wp_posts')->get();
});

Phase C: routes/api.php (reads Laravel)

Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/{slug}', [PostController::class, 'show']);
// API routes stay the same, just swap data source

Frontend Impact: NONE! API contract stays identical.


5. Eloquent Models - New in Phase C 🆕

Phase C: app/Http/Controllers/PostController.php

namespace App\Http\Controllers;

use App\Models\Post;

class PostController extends Controller
{
    public function show(string $slug)
    {
        $post = Post::published()
            ->with(['author', 'category', 'tags'])
            ->whereSlug($slug)
            ->firstOrFail();
            
        return view('single', compact('post'));
    }
}

6. Models - New in Phase C 🆕

See examples/PostModel.php for complete model implementation.

Key Pattern: WordPress functions → Eloquent methods

WordPress Laravel Eloquent
get_posts() Post::query()->get()
get_the_title() $post->title
get_permalink() $post->permalink (accessor)
has_post_thumbnail() $post->featured_image !== null
the_category() $post->category->name

7. Admin Interface - Completely New 🆕

WordPress AdminFilament Resources

See examples/PostResource.php for complete Filament implementation.

Editor Experience Comparison:

Feature WordPress Filament
Post editing Classic/Block editor Rich editor component
Media upload Media library Drag & drop uploader
Categories Checkboxes Searchable dropdown
Publishing Publish metabox Status select + datetime
Bulk actions Limited Extensive & customizable
Speed Slow on large sites Fast, modern interface

8. Database Schema - Transformed 🔄

WordPress (wp_posts):

ID, post_title, post_content, post_excerpt, post_status, 
post_type, post_date, post_modified...

Laravel Migration (posts):

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->string('slug')->unique();
    $table->text('content');
    $table->text('excerpt')->nullable();
    $table->enum('status', ['draft', 'published', 'scheduled']);
    $table->string('type')->default('post');
    $table->string('featured_image')->nullable();
    $table->foreignId('author_id')->constrained('users');
    $table->foreignId('category_id')->nullable()->constrained();
    $table->timestamp('published_at')->nullable();
    $table->json('meta')->nullable();
    $table->timestamps();
    $table->softDeletes();
});

🔄 Data Migration Process

Step 1: Export from WordPress

// Migration script: database/migrations/ImportFromWordPress.php
public function up()
{
    // Connect to old WordPress database
    DB::connection('wordpress')->table('wp_posts')
        ->where('post_status', 'publish')
        ->where('post_type', 'post')
        ->chunk(100, function($posts) {
            foreach ($posts as $wpPost) {
                Post::create([
                    'title' => $wpPost->post_title,
                    'slug' => $wpPost->post_name,
                    'content' => $wpPost->post_content,
                    'excerpt' => $wpPost->post_excerpt,
                    'status' => 'published',
                    'published_at' => $wpPost->post_date,
                    'author_id' => $this->mapUser($wpPost->post_author),
                    'meta' => $this->extractMeta($wpPost->ID),
                ]);
            }
        });
}

Step 2: Validate Migration

# Run validation script
php artisan migrate:validate

# Output:
# ✓ Posts: 5,423 migrated (100%)
# ✓ Users: 156 migrated (100%)
# ✓ Media: 12,845 files migrated (100%)
# ✓ Comments: 8,932 migrated (100%)
# ✓ Categories: 45 migrated (100%)
# ✓ Tags: 892 migrated (100%)

🎨 Frontend Assets - No Changes! ✅

Your current build setup stays identical:

// bud.config.ts - STAYS THE SAME
export default async (bud: Bud) => {
  bud
    .entry(`app`, [`@scripts/app`, `@styles/app`])
    .entry(`editor`, [`@scripts/editor`, `@styles/editor`])
    // ... same configuration
}
// tailwind.config.ts - STAYS THE SAME
export default {
  content: [
    "./resources/**/*.{php,vue,js,ts,jsx,tsx}",
    "./app/**/*.php",
  ],
  theme: {
    extend: {
      // ... same configuration
    }
  }
}

Zero changes needed!


📁 Final Directory Structure Comparison

Current (Phase A/B)

radicle/
├── app/
│   ├── Providers/        ← Laravel service providers
│   └── View/Composers/   ← Laravel view composers
├── bedrock/              ← WordPress core
├── config/               ← Laravel-style config
├── database/
│   ├── migrations/       ← Laravel migrations
│   └── seeders/          ← Laravel seeders
├── public/
│   └── content/          ← WordPress content directory
├── resources/
│   ├── scripts/          ← JS/Alpine
│   ├── styles/           ← CSS/Tailwind
│   └── views/            ← Blade templates
├── bud.config.ts         ← Bud build config
└── tailwind.config.ts    ← Tailwind config

Phase C (Pure Laravel)

radicle-laravel/
├── app/
│   ├── Filament/         ← NEW: Admin resources
│   │   └── Resources/
│   ├── Http/
│   │   └── Controllers/  ← NEW: Web controllers
│   ├── Models/           ← NEW: Eloquent models
│   ├── Providers/        ← KEPT: Enhanced
│   └── View/Composers/   ← KEPT: Updated
├── config/               ← KEPT: Expanded
├── database/
│   ├── migrations/       ← KEPT: + content schema
│   └── seeders/          ← KEPT: + content seeds
├── public/
│   ├── dist/             ← Same build output
│   └── storage/          ← Laravel storage
├── resources/
│   ├── scripts/          ← KEPT: Same JS
│   ├── styles/           ← KEPT: Same CSS
│   └── views/            ← KEPT: Updated data
├── routes/               ← NEW: Laravel routes
│   ├── web.php
│   └── api.php
├── storage/              ← Laravel file storage
├── bud.config.ts         ← KEPT: Identical
└── tailwind.config.ts    ← KEPT: Identical

🚀 Migration Timeline

Month 1-2: Setup & Planning

  • ✓ Create new Laravel app
  • ✓ Copy reusable components
  • ✓ Design database schema
  • ✓ Plan migration strategy

Month 3-5: Build Laravel CMS

  • ✓ Create Eloquent models
  • ✓ Build Filament resources
  • ✓ Set up authentication
  • ✓ Create controllers & routes

Month 5-7: Content Migration

  • ✓ Write migration scripts
  • ✓ Test data transfer
  • ✓ Validate content integrity
  • ✓ Set up redirects

Month 7-8: Frontend Integration

  • ✓ Update Blade templates
  • ✓ Update view composers
  • ✓ Test all pages
  • ✓ Fix broken links

Month 8-9: Training & Launch

  • ✓ Train editorial team
  • ✓ Parallel run (both systems)
  • ✓ Gradual cutover
  • ✓ Decommission WordPress

💡 Key Insights

What Makes This Transition Easier

  1. You’re already writing Laravel code (Acorn = Laravel)
  2. Blade templates are identical (just change data source)
  3. Build system stays the same (Bud + Tailwind)
  4. Modern PHP already in use (type hints, collections, etc.)
  5. Service provider pattern familiar (already using them)

What’s Actually New

  1. Eloquent models (vs WordPress query functions)
  2. Filament admin (vs WordPress admin)
  3. Explicit routes (vs WordPress rewrite rules)
  4. Migration scripts (one-time data transfer)

📊 Effort Estimation

Component Reusability Migration Effort
Blade templates 95% 🟢 Low (find/replace)
View composers 80% 🟢 Low (update data source)
Service providers 70% 🟡 Medium (remove WP hooks)
CSS/Tailwind 100% ✅ None
JavaScript/Alpine 100% ✅ None
Build config 100% ✅ None
Models 0% 🔴 High (build from scratch)
Admin interface 0% 🔴 High (Filament resources)
Controllers 0% 🟡 Medium (straightforward)
Data migration 0% 🔴 High (critical process)

Overall: 50% of code transfers directly, 50% needs rebuilding


🎯 Success Factors

Acorn gives you Laravel experience now
Blade templates transfer with minimal changes
Build system stays identical
Team already thinks in Laravel patterns
Gradual migration reduces risk
Filament provides better UX than WordPress admin



Last Updated: 2025-10-11