wpstarter/framework
WpStarter Framework brings Laravel-style development to WordPress: service container, routing, Eloquent ORM, Blade views, and migrations. Build plugins and themes with modern PHP architecture while staying fully compatible with WordPress.
Requirements (v2.0.0)
Installation
composer require wpstarter/framework:^2.0
Publish the configuration (if needed):
php artisan vendor:publish --provider="WpStarter\Framework\ServiceProvider"
# or, if the package registers a tag:
php artisan vendor:publish --tag=wpstarter-config
Bootstrapping WordPress
Register the WordPress bootstrap in app/Providers/AppServiceProvider.php. This is unchanged in v2.0.0 — the package's facade API surface is the same; only the Laravel baseline moved.
use WpStarter\Framework\Facades\WordPress;
public function boot(): void
{
WordPress::boot();
}
First Use Case: Accessing WordPress Data
use WpStarter\Framework\Facades\WordPress;
public function showPost(int $id)
{
$post = WordPress::post()->find($id);
return response()->json($post);
}
Key Facades
WordPress: Core facade for bootstrapping and utilities.WordPress::post(): Interact with posts.WordPress::user(): Manage users.WordPress::option(): Handle options.Where to look first in a v2 project
bootstrap/app.php — Laravel 12 registers middleware, aliases, and route groups here (there is no app/Http/Kernel.php).app/Providers/AppServiceProvider.php — WordPress::boot() lives here.config/wpstarter.php (published) — services, paths, and integration toggles.Unchanged from v1; typed constructor/property promotion is a good fit on PHP 8.2+:
use WpStarter\Framework\Facades\WordPress;
class PostController extends Controller
{
public function index()
{
$posts = WordPress::post()->all(['posts_per_page' => 10]);
return view('posts.index', compact('posts'));
}
}
Inject the WordPress service into your own service and resolve it from the container:
class PostService
{
public function getPublishedPosts()
{
return WordPress::post()->all(['post_status' => 'publish']);
}
}
Bind it in AppServiceProvider::register() and inject PostService into controllers.
The middleware class itself is the same, but Laravel 12 has no HTTP Kernel — register it in bootstrap/app.php:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'wp.auth' => \App\Http\Middleware\WordPressAuthMiddleware::class,
'wp.bootstrap' => \App\Http\Middleware\BootstrapWordPress::class,
]);
})
class WordPressAuthMiddleware
{
public function handle($request, Closure $next)
{
if (! WordPress::user()->isLoggedIn()) {
abort(403);
}
return $next($request);
}
}
use WpStarter\Framework\Facades\WordPress;
WordPress::on('wp_loaded', function () {
// Custom logic after WordPress loads
});
Register these inside a service provider's boot() — after WordPress::boot() has run.
Blade::directive('wpPost', function ($expression) {
return "<?php echo WpStarter\Framework\Facades\WordPress::post()->find({$expression})->title; ?>";
});
Usage:
<h1>@wpPost(1)</h1>
Route::get('/posts', function () {
return PostResource::collection(WordPress::post()->all());
});
For Laravel 12's slim skeleton, install laravel/sanctum or use php artisan install:api before relying on routes/api.php.
Keeps non-WordPress endpoints (health checks, webhooks) free of WordPress boot cost:
Route::middleware(['wp.bootstrap'])->group(function () {
Route::get('/posts', [PostController::class, 'index']);
});
Upgrading from v1.x is a breaking change
laravel/framework ^12 will fail resolution.php artisan vendor:publish --provider="WpStarter\Framework\ServiceProvider" --force and diff config/wpstarter.php; don't blindly overwrite tuned config.Bootstrap Timing
WordPress::post() in a service provider's register() will fail.WordPress::boot() in boot(), not register(), and register WordPress::on(...) hooks after it.Middleware Lives in bootstrap/app.php Now
app/Http/Kernel.php. Copy-pasted v1 middleware registration (protected $middlewareGroups, $routeMiddleware) will silently do nothing.Caching Conflicts
php artisan cache:clear
wp cache flush # If using WP-CLI
Laravel 12 defaults to the database cache driver, so a stale WordPress-side object cache and Laravel's cache can disagree — clear both after content or plugin changes.
Query Conflicts
$wpdb) with Eloquent queries in the same request. Use transactions or separate them clearly.Plugin/Theme Overrides
Multisite Limitations
wp-config.php for WP_ALLOW_MULTISITE and test manually.Carbon 3 Date Handling
Carbon::parse($post->post_date) before diffing or formatting, and verify any ->diffForHumans() output your views rely on.Enable WordPress Debugging
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true); // Logs to /wp-content/debug.log
Laravel-WordPress Logs
\Log::info('WordPress post ID:', ['id' => $post->ID]);
Verify the Baseline
php artisan about
Confirms the resolved Laravel version and PHP version — the fastest way to check a v2 install actually landed on Laravel 12.
Check Hooks/Filters
Use WordPress::hasAction('hook_name') to verify a hook exists before adding listeners.
Custom WordPress Facades
class CustomWordPress extends \WpStarter\Framework\Facades\WordPress
{
public static function customMethod()
{
return self::post()->find(1)->custom_field;
}
}
Override WordPress Classes
$this->app->bind('wp_query', function () {
return new CustomWPQuery();
});
Add Custom WPStarter Services
'services' => [
'custom' => \WpStarter\Framework\Services\CustomService::class,
],
WordPress::service('custom')->doSomething();
Modify Query Parameters
WordPress::extend('post', function ($query) {
$query->addParam('custom_param', 'value');
return $query;
});
Lazy-Load WordPress Defer bootstrapping to a middleware group rather than on every request:
Route::middleware(['wp.bootstrap'])->group(function () {
// Routes requiring WordPress
});
Batch Queries
Use WordPress::post()->batch() for large datasets to reduce memory usage.
Cache WordPress Data
$posts = Cache::remember('wp_posts', now()->addHour(), function () {
return WordPress::post()->all();
});
On Laravel 12 the default cache store is database; for high-traffic sites point this at Redis via CACHE_STORE=redis.
Warm Up After Deploys
Laravel 12's php artisan optimize caches config/routes/views — run it in your deploy script, and invalidate the wp_posts-style keys whenever WordPress content is published (hook save_post).
How can I help you explore Laravel packages today?