Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Wp Eloquent Laravel Package

as247/wp-eloquent

Wp-Eloquent brings Laravel's Eloquent ORM to WordPress, letting you define models and query posts, users, terms, and custom tables with fluent syntax and relationships. It reduces SQL boilerplate and makes WordPress data access feel like modern PHP.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require as247/wp-eloquent

No illuminate/database dependency is required — it ships vendored. PHP 7.0+ and a WordPress bootstrap with an available $wpdb are the only hard requirements.

Bootstrap once

Eloquent must be initialized after WordPress has loaded $wpdb (i.e. on plugins_loaded, init, or later — never inside wp-config.php):

use WPEloquent\Core;

Core::init(); // wires the capsule to $wpdb, applies $wpdb->prefix

Wrap the call in a "have we run yet?" guard if you're in a plugin that might be included twice.

First use case: read published posts

use WPEloquent\Model\Post;

$recent = Post::query()
    ->where('post_type', 'post')
    ->where('post_status', 'publish')
    ->orderByDesc('post_date')
    ->limit(5)
    ->with(['author', 'meta'])
    ->get();

foreach ($recent as $p) {
    echo $p->post_title;
    echo $p->author->display_name;
    echo $p->meta->firstWhere('meta_key', '_thumbnail_id')->meta_value ?? '';
}

Where to look first

  • src/WPEloquent/Model/ — Post, Term, User, Comment, Attachment, plus their *Meta companions. These are your ready-made Eloquent entry points into core WP tables.
  • src/WPEloquent/Core.php — bootstrap, connection, and query-log handles.
  • The bundled Illuminate/Database/ — the version you actually get. Note it: many modern Eloquent features aren't there.
  • README examples — the canonical patterns for meta, terms, author.

Implementation Patterns

1. Custom post types via inheritance

namespace App\Models;

use WPEloquent\Model\Post;

class Event extends Post
{
    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope('post_type', function ($q) {
            $q->where('post_type', 'event');
        });
    }
}

Event::where('post_status', 'publish')->get() now silently scopes to event.

2. Filter by post meta

Post::whereHas('meta', function ($q) {
    $q->where('meta_key', 'featured')
      ->where('meta_value', '1');
})->get();

For multi-key meta sets, chain whereHas for each meta_key — it's cleaner than a raw join and still composes with scopes.

3. Filter by taxonomy term

Post::whereHas('terms', function ($q) {
    $q->where('slug', 'news')
      ->where('taxonomy', 'category');
})->get();

4. Eager loading is non-optional for lists

Post::with(['author', 'meta', 'terms'])
    ->where('post_status', 'publish')
    ->paginate(20);

WordPress's post/term tables are indexed for wp_query, not for Eloquent's lazy loading. Without with() you will trigger one query per term/meta row.

5. Write path: read with Eloquent, write through WP APIs

The strongest pattern in practice is hybrid:

  • Reads, filtering, reporting, joins across wp_posts + wp_postmeta + wp_term_relationships — Eloquent.
  • Writes — wp_insert_post(), wp_update_post(), wp_set_object_terms(), update_post_meta() — because those fire save_post / transition_post_status, populate guid and the _gmt columns correctly, handle revisions and autosaves, and invalidate the object cache.

If you must write via Eloquent, be prepared to populate post_date_gmt, post_modified_gmt, and guid yourself, and to manually do_action('save_post', ...).

6. Custom plugin tables

class Order extends \Illuminate\Database\Eloquent\Model
{
    protected $table      = 'shop_orders';
    public $timestamps    = false;
    // Add `protected $connection = 'wp';` only if Core::init() did not
    // register the capsule connection as default.
}

Create the table with dbDelta() — Eloquent's schema builder isn't wired to the WP connection.

7. Transactions for multi-step writes

Grab the capsule's connection and wrap:

Core::getConnection()->transaction(function () {
    // multiple inserts/updates
});

8. REST / admin integration

Boot once, reuse. Models serialize to JSON with ->toArray(); add protected $hidden for anything you don't want leaked (meta serialized payloads, user hashes).

Gotchas and Tips

  • Bootstrap order is the #1 failure mode. "Call to a member function prepare() on null" almost always means Core::init() ran before $wpdb existed. Hook it on plugins_loaded or later.
  • Multisite prefix trap. Core::init() uses $wpdb->prefix (current blog). A "get all sites' posts" query should use $wpdb->base_prefix or a second, explicitly-configured connection — otherwise you'll silently read only the current site.
  • The base Post model is unfiltered. It will happily return revisions, nav menu items, attachments, and drafts. Every query needs post_type and (usually) post_status clauses, or a global scope. This is the most common source of "why did my query return 47 rows?".
  • Timestamps mismatch. WP schemas have post_date / post_modified, not created_at / updated_at. If you see "unknown column created_at", set public $timestamps = false; on the model (or map them explicitly).
  • No hooks, no cache invalidation on save(). Eloquent writes bypass save_post, transition_post_status, and wp_cache_* invalidation. Any plugin listening for those events or relying on cached WP_Post objects will see stale or missing data. Prefer WP write APIs.
  • Object cache bypass on reads. Eloquent queries go straight to MySQL. On a page where get_posts() already warmed wp_cache_posts, mixing read paths duplicates work. In performance-sensitive loops, use one or the other.
  • Meta values are raw strings. Booleans, JSON, serialized arrays — Eloquent gives you the LONGTEXT value untouched. Cast via accessors with maybe_unserialize(), json_decode(), or (bool).
  • Vendored Illuminate is pinned. Features introduced in newer Laravel releases — booted(), withAggregate, whereRelation, HasOneThrough, typed relation return values — may not be present. Test before you adopt a newer pattern. Also confirm whether the vendored namespace is Illuminate\Database\... (collides with any other Composer package requiring illuminate/database) or a prefixed one. Don't run this alongside tareq1988/wp-eloquent.
  • term_taxonomy_id vs term_id. wp_term_relationships links to term_taxonomy_id, not term_id. The provided terms relation handles the extra hop through wp_term_taxonomy; if you write raw joins, don't forget it. The classic symptom: your "in category 5" filter returns nothing.
  • Pagination uses ?page=. Eloquent's paginate() doesn't read get_query_var('paged'). In a theme context, either resolve the current page yourself or use ->forPage($page, $perPage).
  • Debugging. Reach the capsule connection (e.g. Core::getConnection()->enableQueryLog()) and dump getQueryLog(), or ->toSql() + getBindings() on a builder. DB::listen may or may not be wired depending on the bundled Illuminate version.
  • Extending base models: call parent. The meta, terms, author, comments relations on WPEloquent\Model\Post encode the correct foreign keys and the term-taxonomy hop. Override them only via parent::meta(), never from scratch.
  • Releases are sparse (last tag Nov 2023). Budget for a pinned fork if you need a bugfix; the code is small and self-contained enough that patching a Relation or a Model in src/ is usually the shortest path.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style