resources/boost/skills/models/SKILL.md
Eloquent model conventions for mass assignment, casts, relationship naming, activity logging, and mandatory model tests (CRUD + relations).
npx skillsauth add codebar-ag/coding-guidelines modelsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
app/Models/.spatie/laravel-activitylog is installed and configured for activity logging.LogsActivity to these models by default.protected $guarded = []; on all business models; do not use $fillable.casts() method instead of a $casts property.casts(), configure:
decimal:2).LogsActivity trait on all business models that should be audited.getActivitylogOptions() to:
logAll().logOnlyDirty().dontSubmitEmptyLogs().HasMany, BelongsTo, etc.).belongsTo, hasOne, morphOne).hasMany, belongsToMany, morphMany).camelCase based on the related model name (for example, pipelineSteps() for PipelineStep).steps(), runs(), items(), or attachments() when they hide model intent.// --- Relationships ---// --- Status Helpers ---// --- Activity Log ---isDraft(), isPaid()).All relationship renames follow this convention: method name = camelCase(RelatedModelName) with singular/plural matching relation cardinality.
| Model | Old Method | New Method |
| --- | --- | --- |
| Pipeline | steps() | pipelineSteps() |
| Pipeline | runs() | pipelineRuns() |
| PipelineStep | stepRuns() | pipelineStepRuns() |
| PipelineRun | stepRuns() | pipelineStepRuns() |
| PipelineTemplate | steps() | pipelineSteps() |
| Inbox | items() | inboxItems() |
| Inbox | serviceUsers() | inboxServiceUsers() |
| Inbox | importConfigs() | inboxImportConfigs() |
| InboxItem | importConfig() | inboxImportConfig() |
| InboxItem | sections() | inboxItemSections() |
| ProviderType | templates() | providerTypeTemplates() |
| Prompt | attachments() | promptAttachments() |
database/factories/.->draft(), ->paid(), ->archived()) to match model helpers.tests/Unit/Models/ (or the project-standard model-test location).HasMany, BelongsTo, etc.).isDraft() and enum/date casts).namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
class Invoice extends Model
{
use LogsActivity;
protected $guarded = [];
protected function casts(): array
{
return [
'status' => Status::class,
'due_date' => 'date',
'amount' => 'decimal:2',
];
}
// --- Relationships ---
public function invoiceLines(): HasMany
{
return $this->hasMany(InvoiceLine::class);
}
// --- Status Helpers ---
public function isDraft(): bool
{
return $this->status === Status::Draft;
}
public function isPaid(): bool
{
return $this->status === Status::Paid;
}
// --- Activity Log ---
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logAll()
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
}
// tests/Unit/Models/InvoiceTest.php
use App\Enums\Status;
use App\Models\Invoice;
use App\Models\InvoiceLine;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
it('supports invoice CRUD operations', function () {
// Create
$invoice = Invoice::factory()->create([
'status' => Status::Draft,
'amount' => '100.00',
]);
expect($invoice->exists)->toBeTrue();
$this->assertDatabaseHas('invoices', ['id' => $invoice->id, 'amount' => '100.00']);
// Read + cast/helper checks
$fresh = Invoice::query()->findOrFail($invoice->id);
expect($fresh->status)->toBe(Status::Draft)
->and($fresh->isDraft())->toBeTrue();
// Update
$fresh->update(['amount' => '250.00']);
$this->assertDatabaseHas('invoices', ['id' => $fresh->id, 'amount' => '250.00']);
// Delete (supports soft deletes)
$fresh->delete();
$this->assertSoftDeleted('invoices', ['id' => $fresh->id]);
// For hard-deleting models, use instead:
// $this->assertDatabaseMissing('invoices', ['id' => $fresh->id]);
});
it('defines and resolves invoiceLines relation', function () {
$invoice = Invoice::factory()->create();
// Relation shape
expect($invoice->invoiceLines())->toBeInstanceOf(HasMany::class);
// Relation behavior
InvoiceLine::factory()->count(2)->create(['invoice_id' => $invoice->id]);
expect($invoice->invoiceLines)->toHaveCount(2)
->and($invoice->invoiceLines->first())->toBeInstanceOf(InvoiceLine::class);
});
$guarded = [] and does not define $fillable.casts() method, not a $casts property.LogsActivity trait is added where auditing is required.getActivitylogOptions() is configured with logAll(), logOnlyDirty(), and dontSubmitEmptyLogs().camelCase(RelatedModelName) with correct singular/plural form.steps() -> pipelineSteps()).database/factories/.$fillable arrays instead of $guarded = [].$casts as a property instead of a casts() method.LogsActivity trait on business models that should be audited.protected array $fillable = ['name']; and protected array $casts = ['status' => 'string']; instead of $guarded = [] and casts()Enums/SKILL.md — enums are cast in casts()Migrations/SKILL.md — migrations define the model's schemaPestTesting/SKILL.md — preferred style for model testsPHPUnit/SKILL.md — class-based alternative where requiredtesting
Translation and localization conventions for Laravel. Use when adding user-facing strings, creating translation files, or working with lang/ directory.
tools
Reusable behaviour shared across multiple unrelated classes. Traits provide shared Eloquent scopes, accessors, lifecycle hooks, and small stateless helper methods.
development
Tailwind CSS v4 styling conventions. Use when working with CSS, Tailwind utilities, or customizing the theme in Laravel projects.
development
Orchestration classes that coordinate multiple Actions, external APIs, or domain operations into a cohesive workflow. Services own transaction boundaries and third-party API integrations.