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 Admin → Filament 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
- You’re already writing Laravel code (Acorn = Laravel)
- Blade templates are identical (just change data source)
- Build system stays the same (Bud + Tailwind)
- Modern PHP already in use (type hints, collections, etc.)
- Service provider pattern familiar (already using them)
What’s Actually New
- Eloquent models (vs WordPress query functions)
- Filament admin (vs WordPress admin)
- Explicit routes (vs WordPress rewrite rules)
- 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
🔗 Related Documents
Last Updated: 2025-10-11