Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .ai/factories.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
You are an expert Laravel assistant, following best development practices and PhpStan Level 10. Given the name of an Eloquent model and the structure of its database table, you generate a complete factory class that adheres to the following best practices:

- Declare `declare(strict_types=1);`
- Declare the property `protected $model = YourModel::class;`
- If the model have an Enum use `fake()->randomElement(EnumClass::class)`
- In the `definition()` method, map each column of the table to an appropriate fake value using the `fake()` helper from Faker PHP:
- Text fields → `fake()->word()`, `fake()->name()`, or `fake()->sentence()`, as appropriate.
- Unique fields → `fake()->unique()`
- Date fields → `now()` or `fake()->dateTime()`
- Passwords → `static::$password ??= Hash::make('password')`
- Tokens → `Str::random()`
- If applicable, include `state()` methods like `unverified()` or any other custom state with the signature `public function stateName(): static`
- Use PHPDoc to document the extension of `Factory<YourModel>`
- Do not define `created_at` and `updated_at` if they're present in the table—Laravel handles them automatically
- If there is a relationship, use the factory class directly instead of the model, e.g., `UserFactory::new()` instead of `User::factory()`

Additionally, automatically analyze the structure of the `your_table` (columns, types, nullability, unique indexes) and generate the appropriate array lines inside `definition()` for each field.

---

**Example Factory:**

```php
<?php

declare(strict_types=1);

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use App\Backoffice\Users\Domain\Models\User;

/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<User>
*/
class UserFactory extends Factory
{
protected $model = User::class;

/**
* The current password being used by the factory.
*/
protected static ?string $password;

/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
'employee_id' => EmployeeFactory::new(),
'status' => fake()->randomElement(EnumClass::class)
];
}

/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
```

Input:

1. Model Name
2. Table with columns and types

Expected Output:
A complete Factory class following the example above. Now generate the corresponding factory for the provided model and table.
242 changes: 242 additions & 0 deletions .ai/laravel-php-ai-guidelines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# Laravel & PHP Guidelines for AI Code Assistants

This file contains Laravel and PHP coding standards optimized for AI code assistants like Claude Code, GitHub Copilot, and Cursor. These guidelines are derived from Spatie's comprehensive Laravel & PHP standards.

## Core Laravel Principle

**Follow Laravel conventions first.** If Laravel has a documented way to do something, use it. Only deviate when you have a clear justification.

## PHP Standards

- Follow PSR-1, PSR-2, and PSR-12
- Use camelCase for non-public-facing strings
- Use short nullable notation: `?string` not `string|null`
- Always specify `void` return types when methods return nothing
- Enforce strict types and array shapes via PHPStan.
- Use PHP v8.4 features

## Class Structure
- Use typed properties, not docblocks:
- Constructor property promotion when all properties can be promoted:
- One trait per line:

## Type Declarations & Docblocks
- Use typed properties over docblocks
- Specify return types including `void`
- Use short nullable syntax: `?Type` not `Type|null`
- Document iterables with generics:
```php
/** @return Collection<int, User> */
public function getUsers(): Collection
```

### Docblock Rules
- Don't use docblocks for fully type-hinted methods (unless description needed)
- **Always import classnames in docblocks** - never use fully qualified names:
```php
use \Spatie\Url\Url;
/** @return Url */
```
- Use one-line docblocks when possible: `/** @var string */`
- Most common type should be first in multi-type docblocks:
```php
/** @var Collection|SomeWeirdVendor\Collection */
```
- If one parameter needs docblock, add docblocks for all parameters
- For iterables, always specify key and value types:
```php
/**
* @param array<int, MyObject> $myArray
* @param int $typedArgument
*/
function someFunction(array $myArray, int $typedArgument) {}
```
- Use array shape notation for fixed keys, put each key on it's own line:
```php
/** @return array{
first: SomeClass,
second: SomeClass
} */
```

## Control Flow
- **Happy path last**: Handle error conditions first, success case last
- **Avoid else**: Use early returns instead of nested conditions
- **Separate conditions**: Prefer multiple if statements over compound conditions
- **Always use curly brackets** even for single statements
- **Ternary operators**: Each part on own line unless very short

```php
// Happy path last
if (! $user) {
return null;
}

if (! $user->isActive()) {
return null;
}

// Process active user...

// Short ternary
$name = $isFoo ? 'foo' : 'bar';

// Multi-line ternary
$result = $object instanceof Model ?
$object->name :
'A default value';

// Ternary instead of else
$condition
? $this->doSomething()
: $this->doSomethingElse();
```

## Laravel Conventions

### Routes
- URLs: kebab-case (`/open-source`)
- Route names: camelCase (`->name('openSource')`)
- Parameters: camelCase (`{userId}`)
- Use single action controllers only: `'/uri', ListSessionsController::class`

### Controllers
- Singular resource name and single action controller (`StorePostController`)
- Only __invoke method
- Without inheritance
- If you need validation, always use a FormRequest
- Business Logic via DI with an action

### Configuration
- Files: kebab-case (`pdf-generator.php`)
- Keys: snake_case (`chrome_path`)
- Add service configs to `config/services.php`, don't create new files
- Use `config()` helper, avoid `env()` outside config files

### Artisan Commands
- Names: kebab-case (`delete-old-records`)
- Always provide feedback (`$this->comment('All ok!')`)
- Show progress for loops, summary at end
- Put output BEFORE processing item (easier debugging):
```php
$items->each(function(Item $item) {
$this->info("Processing item id `{$item->id}`...");
$this->processItem($item);
});

$this->comment("Processed {$items->count()} items.");
```

## Strings & Formatting

- **String interpolation** over concatenation:

## Enums

- Use PascalCase for enum values:

## Comments

- **Avoid comments** - write expressive code instead
- When needed, use proper formatting:
```php
// Single line with space after //

/*
* Multi-line blocks start with single *
*/
```
- Refactor comments into descriptive function names

## Whitespace

- Add blank lines between statements for readability
- Exception: sequences of equivalent single-line operations
- No extra empty lines between `{}` brackets
- Let code "breathe" - avoid cramped formatting

## Validation

- Use array notation for multiple rules (easier for custom rule classes):
```php
public function rules() {
return [
'email' => ['required', 'email'],
];
}
```
- Custom validation rules use snake_case:
```php
Validator::extend('organisation_type', function ($attribute, $value) {
return OrganisationType::isValid($value);
});
```

## Blade Templates

- Indent with 4 spaces
- No spaces after control structures:
```blade
@if($condition)
Something
@endif
```

## Authorization

- Policies use camelCase: `Gate::define('editPost', ...)`
- Use CRUD words, but `view` instead of `show`

## Translations

- Use `__()` function over `@lang`:

## API Routing

- Use plural resource names: `/errors`
- Use kebab-case: `/error-occurrences`
- Limit deep nesting for simplicity:
```
/error-occurrences/1
/errors/1/occurrences
```

## Testing

- Keep test classes in same file when possible
- Use descriptive test method names
- Follow the arrange-act-assert pattern

## Quick Reference

### Naming Conventions
- **Classes**: PascalCase (`StoreUserController`, `OrderStatus`)
- **Methods/Variables**: camelCase (`getUserName`, `$firstName`)
- **Routes**: kebab-case (`/open-source`, `/user-profile`)
- **Config files**: kebab-case (`pdf-generator.php`)
- **Config keys**: snake_case (`chrome_path`)
- **Artisan commands**: kebab-case (`php artisan delete-old-records`)

### File Structure
- Controllers: verb + singular resource name + `Controller` (`StoreUserController`)
- Views: camelCase (`openSource.blade.php`)
- Jobs: action-based (`CreateUser`, `SendEmailNotification`)
- Events: tense-based (`UserRegistering`, `UserRegistered`)
- Listeners: action + `Listener` suffix (`SendInvitationMailListener`)
- Commands: action + `Command` suffix (`PublishScheduledPostsCommand`)
- Mailables: purpose + `Mail` suffix (`AccountActivatedMail`)
- Resources/Transformers: plural + `Resource`/`Transformer` (`UsersResource`)
- Enums: descriptive name, no prefix (`OrderStatus`, `BookingType`)

### Migrations
- do not write down methods in migrations, only up methods

### Code Quality Reminders

#### PHP
- Use typed properties over docblocks
- Prefer early returns over nested if/else
- Use constructor property promotion when all properties can be promoted
- Avoid `else` statements when possible
- Use string interpolation over concatenation
- Always use curly braces for control structures
Loading