A 7B code model fine-tuned on 235 Laravel 13.x instruction-to-code pairs with Laravel Boost guidelines baked into training. Covers 18+ Laravel class types including API Resources with relationship loading, Form Request precision hooks, and Pest feature tests. Trained on Apple M2 Pro 16GB — no cloud GPU needed.
Correct test logic; style defaults to PHPUnit class unless explicitly prompted
E7
Pest Test
⚠️ pass*
Pagination test passes in real Laravel
E8
Pest Test
⚠️ pass*
Auth/ownership tests pass in real Laravel
*Pest tests pass functionally in a real Laravel 13.2.0 + SQLite project (14/14 assertions pass), but the model defaults to PHPUnit class-based syntax unless the prompt explicitly requests Pest function syntax.
Real-world validation
All eval prompts were tested in a real Laravel 13.2.0 + SQLite project:
php artisan test → 14/14 passed (28 assertions)
Tests cover: POST create (auth, 401, 422 validation), GET paginated (meta.total), DELETE own/other (204, 403).
Root causes found in Sprint 3
Gap
Root cause
v5 fix
API Resources: bare $this->posts
Model saw shell from pretraining but no whenLoaded examples
12 examples with whenLoaded, $this->when, mergeWhen, whenNotNull, whenCounted
Form Requests: no messages()
Model generated rules() only — basic shell from pretraining
10 examples with messages(), attributes(), prepareForValidation(), after()
Pest tests: PHPUnit class style
Strong pretraining prior to class-based tests; uses() not anchored
15 Pest examples + explicit BOOST_CONTEXT block; prompt must specify "Pest function syntax"
Evaluation: API Resource (E1)
Prompt:"Create UserResource with optional posts collection (whenLoaded) and subscription (whenLoaded). Use whenNotNull for email_verified_at."
Correct: whenLoaded used (not $this->posts), whenNotNull used (not ternary), returns array.
Evaluation: Form Request (E4)
Prompt:"StoreInvoiceRequest with messages(), attributes(), prepareForValidation() that rounds amount."
php
1<?php23namespaceApp\Http\Requests;45useIlluminate\Foundation\Http\FormRequest;67classStoreInvoiceRequestextendsFormRequest8{9publicfunctionauthorize():bool10{11returntrue;12}1314publicfunctionrules():array15{16return[17'client_id'=>['required','exists:clients,id'],18'amount'=>['required','numeric','min:0.01'],19'due_date'=>['required','date','after:today'],20'notes'=>['nullable','string','max:1000'],21];22}2324publicfunctionmessages():array25{26return[27'client_id.required'=>'The client is required.',28'amount.required'=>'The amount is required.',29'amount.numeric'=>'The amount must be a number.',30'due_date.required'=>'The due date is required.',31];32}3334publicfunctionattributes():array35{36return[37'client_id'=>'Client',38'due_date'=>'Due Date',39];40}4142publicfunctionprepareForValidation():void43{44$this->merge([45'amount'=>round($this->amount,2),46]);47}48}
Prompt:"Pest test for POST /api/posts: auth creates (201 + assertDatabaseHas), unauth returns 401, missing title returns 422 with validation errors."
php
1<?php23useApp\Models\Post;4useApp\Models\User;5useIlluminate\Foundation\Testing\RefreshDatabase;67uses(RefreshDatabase::class);89test('authenticated user creates a post with title and body',function(){10$user=User::factory()->create();1112$this->actingAs($user)13->postJson('/api/posts',['title'=>'Test Post','body'=>'Body text.'])14->assertCreated();1516$this->assertDatabaseHas('posts',['title'=>'Test Post','user_id'=>$user->id]);17});1819test('unauthenticated user gets 401',function(){20$this->postJson('/api/posts',['title'=>'Test Post','body'=>'Body text.'])21->assertUnauthorized();22});2324test('authenticated user sends missing title gets 422',function(){25$user=User::factory()->create();2627$this->actingAs($user)28->postJson('/api/posts',['body'=>'Body text.'])29->assertUnprocessable()30->assertJsonValidationErrors(['title']);31});
Tested in real Laravel 13.2.0 — 3/3 assertions pass.
Usage
With adapter (recommended)
python
1from mlx_lm import load, generate
23SYSTEM ="""You are a senior Laravel developer. Write clean, production-ready Laravel 13.x code.
4Output only the PHP file contents — no markdown, no explanation, no ```php fences.
56### API Resources
7// Always extend JsonResource. toArray() returns array, not JsonResponse.
8// Relationships: $this->whenLoaded('relation', fn() => RelatedResource::make($this->relation))
9// Collections: $this->whenLoaded('items', fn() => ItemResource::collection($this->items))
10// Conditional field: $this->when($condition, $value)
11// Conditional block: $this->mergeWhen($condition, ['field' => $value])
12// Nullable field: $this->whenNotNull($this->deleted_at)
13// ResourceCollection: override with() to add pagination metadata
14// Never call toArray() manually — Laravel calls it automatically
1516### Form Request precision
17// messages(): return ['field.rule' => 'Custom message'] — overrides default error text
18// attributes(): return ['field' => 'Human Name'] — used in :attribute placeholder
19// prepareForValidation(): call $this->merge([...]) to normalize input before rules run
20// after(): return [fn(Validator $v) => $v->errors()->addIf($condition, 'field', 'msg')]
21// passedValidation(): runs after all rules pass — use for side effects, not validation
22// NEVER use $this->validate() — that is a Controller method
2324### Pest Feature Tests
25// File: tests/Feature/SomeTest.php
26// uses(RefreshDatabase::class); at top of file
27// HTTP: $this->getJson('/api/route'), postJson(), putJson(), patchJson(), deleteJson()
28// Status: ->assertOk() (200), ->assertCreated() (201), ->assertNoContent() (204)
29// ->assertUnprocessable() (422), ->assertUnauthorized() (401), ->assertForbidden() (403)
30// JSON: ->assertJson(['key' => 'value']), ->assertJsonStructure(['data' => ['id', 'name']])
31// ->assertJsonCount(3, 'data'), ->assertJsonPath('data.0.name', 'Alice')
32// DB: assertDatabaseHas('table', ['col' => 'val']), assertDatabaseMissing(), assertDatabaseCount()
33// Auth: $this->actingAs(User::factory()->create())
34// Fakes: Queue::fake(), Event::fake(), Mail::fake() then ::assertPushed/Dispatched/Sent
35// IMPORTANT: Pest uses function syntax (uses/test/it) NOT class-based syntax
3637### Artisan Commands
38// Always extend Illuminate\\Console\\Command
39// handle() returns Command::SUCCESS or Command::FAILURE (never raw int)
40// count(): use count($array) — Command has NO $this->count property
41// Accumulators: ALL keys in same array use (?? 0) + $value pattern
42// Initialize ALL closure variables BEFORE the closure definition
43// Facades\\Progress does NOT exist"""4445model, tok = load(46"mlx-community/Qwen2.5-Coder-7B-Instruct-4bit",47 adapter_path="fchis/Laravel-13x-Qwen2.5-Coder-7B-Instruct-LoRA"48)4950messages =[51{"role":"system","content": SYSTEM},52{"role":"user","content":"Create a UserResource with whenLoaded for posts (PostResource collection) and subscription (SubscriptionResource). Use whenNotNull for email_verified_at."}53]54text = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)55output = generate(model, tok, prompt=text, max_tokens=1200)5657if'<|im_end|>'in output:58 output = output[:output.index('<|im_end|>')]59print(output.strip())
CLI pipeline
bash
1pip install mlx-lm
2git clone https://github.com/florinel-chis/laravel-ai-gen
3cd your-laravel-project
4python3 laravel-gen.py --model 7b "UserResource with posts collection and subscription via whenLoaded"
Model Details
Detail
Value
Base model
Qwen2.5-Coder-7B-Instruct (4-bit via MLX)
Fine-tuning
LoRA, 8 layers, rank=8, scale=20, lr=1e-5
Training data
235 instruction-to-code pairs with Boost guidelines
Resumed from
v4 iter 150 (val_loss 0.055)
Best checkpoint
Iter 150 (val_loss 0.032)
Training hardware
Apple M2 Pro, 16GB unified memory
Training time
~19 minutes (150 iters, max_seq_length 1500)
Peak memory
10.820 GB
Val loss
0.032 (iter 150)
Training History
Version
Examples
Val loss
Key change
v1 (1B)
90
—
Proof of concept
v2 (7B)
162
0.178
First 7B, repetition loops
v3 (7B)
187
0.076
+25 artisan examples, fixed repetition
v4 (7B)
202
0.055
+15 precision examples, 3 bug patterns eliminated
v5 (7B)
235
0.032
+33 examples: API Resources, Form Requests, Pest Tests