Skip to content
Back to skills

Acorn Eloquent

ASecurity

Eloquent, model, migration, relationship, hasMany, belongsTo, belongsToMany, Acorn Eloquent, custom table, wp_posts, wp_users, wp_postmeta, factory, query scope, scopePublished, global scope, make:model, make:migration, lando acorn migrate, $fillable, $casts, $timestamps, $primaryKey, HasFactory, db:seed, seeder — Eloquent ORM for custom and WordPress tables via Acorn

  • 13 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 11, 2026
toolsgophpbashsqldebuggingapidatabaseperformance

Works with

  • api

Security analysis

A100/100

Pro scans all 12 files and shows the line behind each finding

Scanned September 11, 2026

npx -y skills add hekivo/superpowers-sage --skill acorn-eloquent --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Acorn Eloquent?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Acorn Eloquent
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/hekivo-acorn-eloquent/badge)](https://www.skillsdirectory.com/skills/hekivo-acorn-eloquent)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: superpowers-sage:acorn-eloquent
description: >
  Eloquent, model, migration, relationship, hasMany, belongsTo, belongsToMany,
  Acorn Eloquent, custom table, wp_posts, wp_users, wp_postmeta, factory,
  query scope, scopePublished, global scope, make:model, make:migration,
  lando acorn migrate, $fillable, $casts, $timestamps, $primaryKey, HasFactory,
  db:seed, seeder — Eloquent ORM for custom and WordPress tables via Acorn
user-invocable: false
---

# Eloquent ORM in WordPress via Acorn

## When to Use Eloquent vs WordPress Functions

| Use case | Use Eloquent | Use WordPress functions |
|---|---|---|
| Custom tables (event logs, testimonials, form entries) | Yes | No |
| Complex joins across custom tables | Yes | No |
| Relationships between custom models | Yes | No |
| Post types, taxonomies, menus | No | Yes (`WP_Query`, `get_posts`) |
| User management, roles, capabilities | No | Yes (`wp_insert_user`, `get_userdata`) |
| Options, transients | No | Yes (`get_option`, `get_transient`) |
| Complex read queries on `wp_posts` | Read-only, carefully | Prefer `WP_Query` first |

**Rule:** WordPress-managed content goes through WordPress functions so hooks fire. Custom application data goes through Eloquent.

## Quick Start — Scripts

```bash
# Create a model (PascalCase name required)
bash skills/acorn-eloquent/scripts/create-model.sh Testimonial

# Create a model with a migration
bash skills/acorn-eloquent/scripts/create-model.sh EventLog --migration

# Run pending migrations
bash skills/acorn-eloquent/scripts/run-migration.sh

# Run with additional flags (passed through to lando acorn migrate)
bash skills/acorn-eloquent/scripts/run-migration.sh --seed
bash skills/acorn-eloquent/scripts/run-migration.sh --step=2
```

Scripts: [`scripts/create-model.sh`](scripts/create-model.sh) · [`scripts/run-migration.sh`](scripts/run-migration.sh)

## Models

Models live in `app/Models/`. Always declare `$table` explicitly with the WordPress prefix.

```php
class Testimonial extends Model
{
    use HasFactory;

    protected $table = 'wp_testimonials';

    protected $fillable = ['author_name', 'company', 'body', 'rating', 'is_featured', 'published_at'];

    protected $casts = [
        'rating'       => 'integer',
        'is_featured'  => 'boolean',
        'published_at' => 'datetime',
        'metadata'     => 'array',
    ];
}
```

See [`references/models.md`](references/models.md) for:
- Full property reference (`$primaryKey`, `$keyType`, `$incrementing`)
- Custom table example
- WP core table mirror example with read-only guard
- Accessors/mutators with `Attribute::make()`

## Assets

Boilerplate templates with `{{PLACEHOLDER}}` tokens:

- **[model-custom-table.php.tpl](assets/model-custom-table.php.tpl)** — Custom table model with `$table`, `$primaryKey`, `$timestamps`, `$fillable`, `$casts`. Replace `{{CLASS_NAME}}`, `{{TABLE_NAME}}`.
- **[model-wp-mirror.php.tpl](assets/model-wp-mirror.php.tpl)** — Read-only WP core table mirror with `$primaryKey = 'ID'`, `$timestamps = false`, write guards. Replace `{{CLASS_NAME}}`, `{{WP_TABLE}}`.

## Migrations

```bash
lando acorn make:migration create_testimonials_table
lando acorn migrate
lando acorn migrate:status
lando acorn migrate:rollback
```

```php
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('wp_testimonials', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id')->nullable();
            $table->string('author_name');
            $table->text('body');
            $table->unsignedTinyInteger('rating')->default(5);
            $table->boolean('is_featured')->default(false);
            $table->timestamp('published_at')->nullable();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('wp_testimonials');
    }
};
```

See [`references/migrations.md`](references/migrations.md) for:
- Column types reference table
- Adding columns to existing tables
- Full migration commands reference
- Naming conventions

## Relationships

```php
class Testimonial extends Model
{
    public function author(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class, 'wp_testimonial_tag', 'testimonial_id', 'tag_id');
    }
}
```

Eager load to avoid N+1:

```php
Testimonial::with('author', 'tags')->get();
```

See [`references/relationships.md`](references/relationships.md) for:
- `wp_posts` → `wp_postmeta` (`hasMany` with `ID` key)
- `wp_terms` → `wp_termmeta`
- Wrapping `wp_users` for read-only relationships
- Polymorphic relationships (event logs)

## Query Scopes

```php
public function scopePublished(Builder $query): void
{
    $query->whereNotNull('published_at')
          ->where('published_at', '<=', now());
}

public function scopeMinRating(Builder $query, int $rating): void
{
    $query->where('rating', '>=', $rating);
}
```

Chain fluently:

```php
Testimonial::published()->featured()->minRating(4)->with('author')->get();
```

See [`references/query-scopes.md`](references/query-scopes.md) for:
- Global scopes with `booted()` registration
- `withoutGlobalScope()` usage
- Conditional scopes with `->when()`
- Raw queries with safe binding

## Factories

```bash
lando acorn make:factory TestimonialFactory
```

```php
class TestimonialFactory extends Factory
{
    protected $model = Testimonial::class;

    public function definition(): array
    {
        return [
            'author_name'  => fake()->name(),
            'body'         => fake()->paragraphs(2, asText: true),
            'rating'       => fake()->numberBetween(1, 5),
            'is_featured'  => fake()->boolean(20),
            'published_at' => fake()->optional(0.8)->dateTimeBetween('-1 year'),
        ];
    }
}
```

```php
Testimonial::factory()->count(10)->create();
Testimonial::factory()->featured()->count(3)->create();
```

Seed:

```bash
lando acorn db:seed --class=TestimonialSeeder
```

See [`references/factories.md`](references/factories.md) for:
- Factory states (`featured()`, `unpublished()`)
- `fake()` helper reference
- Seeder structure and `DatabaseSeeder` registration
- WordPress context in seeders

## Critical Rules

1. **Set `$timestamps = false`** for WP core tables (`wp_posts`, `wp_users`) — they have no `created_at`/`updated_at`.
2. **Use `$table` explicitly** for every model — avoid Eloquent's table name guessing with WP prefix.
3. **Never use Eloquent to create/update WP core table rows in production** — use `wp_insert_post()`, `wp_update_post()`, `update_post_meta()` so hooks fire.
4. **Run migrations via `lando acorn migrate`** — never write raw SQL directly.
5. **Use `$fillable` explicitly** — never use `$guarded = []`; list only known, safe attributes.
6. **Eager load relationships** — use `::with()` or `->load()` to prevent N+1 queries in loops.

## Verification

- Run `lando acorn migrate:status` and confirm all migrations show as "Ran".
- Use `lando acorn tinker` to verify: `App\Models\Testimonial::first()` returns data from the correct table.
- Verify relationships: `App\Models\Testimonial::with('author')->first()` returns related data without errors.

## Failure modes

See [`references/troubleshooting.md`](references/troubleshooting.md) for all failure modes and escalation paths.

## References

Deep content loaded on demand — zero tokens until needed.

- **[models.md](references/models.md)** — Model class, `$table`, `$fillable`, `$casts`, custom primary key, `$timestamps = false` for WP tables, accessors.
- **[migrations.md](references/migrations.md)** — `Schema::create`, column types reference, `up()`/`down()`, running via `lando acorn migrate`.
- **[relationships.md](references/relationships.md)** — `hasMany`, `belongsTo`, `belongsToMany`, WP-specific: Post → PostMeta, Term → TermMeta, polymorphic.
- **[factories.md](references/factories.md)** — `HasFactory`, `definition()`, `fake()` reference, states, seeding with `lando acorn db:seed`.
- **[query-scopes.md](references/query-scopes.md)** — Local scopes, global scopes, `withoutGlobalScope()`, conditional `->when()`, raw queries.
- **[wp-tables.md](references/wp-tables.md)** — Integrating Eloquent models with WordPress core tables (`wp_posts`, `wp_postmeta`, etc.), read-only reporting, canonical patterns.
- **[troubleshooting.md](references/troubleshooting.md)** — Common errors, debugging commands, escalation paths for persistent issues.

## Escalation

- N+1 performance issues: see `superpowers-sage:wp-performance` for eager loading strategies.
- Writing to WP core tables: use WP API functions — `wp_insert_post()`, `update_post_meta()`, `wp_insert_user()`.

Files in this skill

  • SKILL.md8.6 KB
  • assets/model-custom-table.php.tpl981 B
  • assets/model-wp-mirror.php.tpl1.9 KB
  • references/factories.md4.8 KB
  • references/migrations.md4.6 KB
  • references/models.md4.7 KB
  • references/query-scopes.md5.1 KB
  • references/relationships.md4.9 KB
  • references/troubleshooting.md1.3 KB
  • references/wp-tables.md2 KB
  • scripts/create-model.sh640 B
  • scripts/run-migration.sh159 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.