rareloop/lumberjack-core
Lumberjack Core is the foundation of Rareloop's Lumberjack logging package for Laravel. It provides a consistent, handler-driven API for sending application logs to external services such as Loggly and Papertrail, and makes it easy to write custom handlers.
Lumberjack Core is not a Laravel package you drop into an existing Laravel app — it is a Laravel-shaped application layer for a WordPress theme. You get the pieces of Laravel that make theme code pleasant (service container, service providers, config repository, Blade, a router, post models) while WordPress stays the runtime and the source of truth for content, hooks and the database. If your team knows Laravel, the mental model transfers almost 1:1 — minus Eloquent, minus Artisan, minus the HTTP kernel.
Minimal setup:
composer require rareloop/lumberjack-core
Or start from the rareloop/lumberjack theme skeleton, which wires the container and directory layout for you. As of v8.4.0 the illuminate/* constraints allow both 10 and 11, so this is the release to move to if you are planning a Laravel 11–aligned stack (PHP 8.2+). If your host is still on PHP 8.1, stay on Illuminate 10 — 8.4.0 is a constraint widening, not a rewrite.
Where to look first, in order:
bootstrap/ — where the container is created and booted from functions.php.config/app.php — the providers array; this is your registration surface.routes/web.php — route definitions via the Router facade.views/ — Blade templates, one layout with wp_head() / wp_footer().app/ — your post type models, controllers and service providers.First use case — a static page without touching a WP page template:
// routes/web.php
use Rareloop\Lumberjack\Facades\Router;
Router::get('/about', 'AboutController@index');
// app/Http/Controllers/AboutController.php
namespace App\Http\Controllers;
use Rareloop\Lumberjack\Facades\View;
use App\TeamMember;
class AboutController
{
public function index()
{
return View::make('pages.about', [
'team' => TeamMember::all(),
]);
}
}
{{-- views/pages/about.blade.php --}}
@extends('layouts.app')
@section('content')
@foreach ($team as $member)
<h2>{{ $member->post_title }}</h2>
@endforeach
@endsection
Re-save permalinks once after adding the route and you are live.
Route → Controller → Blade, with controllers kept thin. The router exists to take you out of WordPress's template hierarchy for anything that isn't a single post or archive. Put query logic in models or small repositories, keep controllers to "gather, then render", and let Blade do presentation. Anything the router doesn't match falls through to normal WP templates, so you can migrate a site route by route rather than all at once.
Model per post type. Subclass Rareloop\Lumberjack\Post and set protected static $postType; you then get find(), all() and a WP_Query-backed query builder scoped to that type. Register the CPT itself in a service provider's boot() on the init hook, next to the model, so the type and its PHP representation live in one place:
class Event extends Post
{
protected static $postType = 'event';
}
Service providers as the integration layer. This is the single biggest win day to day. Custom post types, taxonomies, shortcodes, admin menus, REST endpoints and add_action / add_filter calls all belong in a provider's register()/boot(), registered in config/app.php. functions.php should be ~5 lines: autoload, bootstrap the app, done. Reviewers can then find every hook in app/Providers instead of grepping a 600-line functions.php.
Config for environment and feature behaviour. Use the config repository (config('app.debug'), config('view.paths')) rather than scattering defined() checks and getenv() calls. Register a provider conditionally in config/app.php for features that shouldn't load in production.
Blade as the only templating layer. One layouts/app.blade.php with wp_head()/wp_footer(), partials via @include, and components for repeated markup. WordPress content goes through wp_kses_post() and {!! !!}; everything else stays in {{ }}. Custom directives (Blade::directive('svg', ...)) are the cheapest way to kill repetitive markup in a theme.
Hook the router into WP, not the other way round. When you need WP conditionals (is_singular(), is_404()) alongside routed URLs, handle it in the controller or a service provider filter — don't try to encode WP's query state into route definitions.
Testing. Controllers and models that receive their dependencies from the container are unit-testable without a WordPress boot; the router is small enough to test with a fake request. For anything touching WP_Query, run against the WP test suite in CI, but keep as much logic as possible in plain PHP classes so most of your suite stays fast.
rareloop/lumberjack-core to ^8.4 and your own illuminate/* constraints together; if you pin Illuminate to ^10 you gain nothing but don't break either.illuminate/*: ^10 on core 8.4 and defer.illuminate/support will collide with the theme's copy in unpredictable ways. composer why illuminate/support is the first thing to run when you see odd container or collection behaviour.$this->foo = ... in controllers will start logging deprecations. Declare properties or use accessors before you widen constraints.flush_rewrite_rules() on theme activation). This trips up every new developer exactly once — put it in the README./about vs /about/ up front and be consistent in links. Overlapping routes resolve by definition order, so keep the most specific first.php artisan make:* or expect a database abstraction; use $wpdb/WP_Query and WP-CLI.functions.php before the container is booted, or calling add_action from a provider registered too late, silently does nothing. Register providers in config/app.php and do work in boot().WP_Query instances inside controllers without wp_reset_postdata() leaks $post into the layout and breaks wp_head(). Always reset after a custom loop.Blade::directive() registrations, view composers for globally-needed data (menus, options), and your own service providers for anything that doesn't fit — the container is the sanctioned escape hatch, so use it instead of patching the theme's vendored copy.How can I help you explore Laravel packages today?