Files
usher-manage-stack/.planning/codebase/CONVENTIONS.md
2026-02-13 10:34:18 +08:00

200 lines
7.9 KiB
Markdown

# Coding Conventions
**Analysis Date:** 2026-02-13
## Naming Patterns
**Files:**
- Classes: PascalCase (e.g., `FinanceDocument.php`, `MembershipFeeCalculator.php`)
- Controllers: PascalCase with `Controller` suffix (e.g., `MemberPaymentController.php`)
- Traits: PascalCase with descriptive names (e.g., `HasApprovalWorkflow.php`, `HasAccountingEntries.php`)
- Models: PascalCase singular (e.g., `Member.php`, `FinanceDocument.php`)
- Services: PascalCase with `Service` suffix (e.g., `MembershipFeeCalculator.php`, `SettingsService.php`)
- Requests: PascalCase with action + `Request` suffix (e.g., `StoreMemberRequest.php`, `UpdateIssueRequest.php`)
- Database: Migration files use timestamp prefix with snake_case (Laravel convention)
**Functions:**
- Methods: camelCase (e.g., `createMember()`, `getBaseAmount()`, `isSelfApproval()`)
- Helper functions: camelCase, wrapped in function_exists check (e.g., `settings()` in `app/helpers.php`)
- Test methods: camelCase, prefixed with `test_` or use `/** @test */` doc comment (see `tests/Unit/MembershipPaymentTest.php`)
- Model query scopes: camelCase (Laravel convention)
**Variables:**
- Local variables: camelCase (e.g., `$baseAmount`, `$feeDetails`, `$discountRate`)
- Model attributes: snake_case in database, accessed as camelCase properties (Laravel convention)
- Constants: UPPER_SNAKE_CASE (e.g., `STATUS_PENDING`, `AMOUNT_TIER_SMALL`, `IDENTITY_PATIENT`)
- Protected/private properties: camelCase with prefix (e.g., `$settings`, `$feeCalculator`)
**Types:**
- Model classes: Use `User`, `Member`, `FinanceDocument` (singular)
- Collections: Arrays or `Collection` type hints
- Enums: Not used; status values stored as class constants (e.g., in `FinanceDocument.php` lines 15-59)
- Nullable types: Use `?Type` (e.g., `?User $user`)
- Return type hints: Always specified (e.g., `public function calculate(...): array`)
## Code Style
**Formatting:**
- Tool: Laravel Pint (PHP code style formatter)
- Run command: `./vendor/bin/pint`
- Indentation: 4 spaces (configured in `.editorconfig`)
- Line endings: LF (configured in `.editorconfig`)
- Final newline: Required (configured in `.editorconfig`)
- Trailing whitespace: Trimmed (configured in `.editorconfig`)
**Language versions:**
- PHP: 8.1+ (required in `composer.json`)
- Laravel: 10.10+ (required in `composer.json`)
**Linting:**
- Tool: PHPStan (static analysis)
- Run command: `./vendor/bin/phpstan analyse`
- No ESLint/Prettier for frontend (basic Tailwind + Alpine.js)
## Import Organization
**Order:**
1. PHP core/standard library classes (`use Illuminate\*`, `use App\*`)
2. Third-party library classes (`use Spatie\*`, `use Barryvdh\*`)
3. Application namespaces (`use App\Models\*`, `use App\Services\*`, `use App\Traits\*`)
4. Facades and static imports last (`use Illuminate\Support\Facades\*`)
**Example from `MemberPaymentController.php`:**
```php
use App\Mail\PaymentSubmittedMail;
use App\Models\Member;
use App\Models\MembershipPayment;
use App\Models\User;
use App\Services\MembershipFeeCalculator;
use App\Support\AuditLogger;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\Rule;
```
**Path Aliases:**
- No custom aliases configured; use standard PSR-4 autoloading
- Namespace hierarchy: `App\Models\`, `App\Services\`, `App\Http\Controllers\`, `App\Http\Requests\`, `App\Traits\`, `App\Support\`
## Error Handling
**Patterns:**
- Use try-catch blocks for complex operations with side effects (see `BankReconciliationController.php` lines 75-81)
- Catch generic `\Exception` when handling database/file operations
- Use DB transactions for multi-step operations requiring rollback
- Authorization: Use `$this->authorize('permission-name')` in controllers (Gate authorization)
- Validation: Use Form Request classes (`StoreMemberRequest`, `UpdateMemberRequest`) in controller methods
- Permission checks: Use Spatie Laravel Permission with roles and permissions (see `SeedsRolesAndPermissions` trait)
**Example from `BankReconciliationController.php`:**
```php
DB::beginTransaction();
try {
// Handle bank statement file upload
$statementPath = null;
if ($request->hasFile('bank_statement_file')) {
$statementPath = $request->file('bank_statement_file')->store('bank-statements', 'local');
}
// ... process data
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()->with('error', 'Error message');
}
```
## Logging
**Framework:** None configured; uses Laravel's default logging or direct logging via `Storage` facades.
**Patterns:**
- Use `AuditLogger` static class for business logic audit trail (see `app/Support/AuditLogger.php`)
- Call pattern: `AuditLogger::log($action, $auditable, $metadata)`
- Example usage in `MemberPaymentController.php` and other controllers
- All member/finance/permission changes should be logged via AuditLogger
## Comments
**When to Comment:**
- Trait usage instructions (see `HasApprovalWorkflow.php` lines 7-14)
- Complex business logic requiring explanation (e.g., multi-tier approval, fee calculations)
- Prevent: Obvious comments describing what code does (let code be self-documenting)
- Include: Comments explaining WHY, not WHAT
**JSDoc/TSDoc:**
- Use PHPDoc for public methods (see `MembershipFeeCalculator.php` lines 17-23)
- Parameter types documented with `@param Type $variable`
- Return types documented with `@return Type`
- Example from `calculate()` method:
```php
/**
* 計算會費金額
*
* @param Member $member 會員
* @param string $feeType 會費類型 (entrance_fee | annual_fee)
* @return array{base_amount: float, discount_amount: float, final_amount: float, disability_discount: bool, fee_type: string}
*/
```
**Chinese Comments:**
- UI-facing text and business logic comments may use Traditional Chinese
- Code comments default to English for consistency
- No specific rule enforced; follow existing pattern in related files
## Function Design
**Size:** Keep under 30-40 lines per method
- Controllers: Each action method typically 20-30 lines
- Services: Business logic methods 15-25 lines
- Traits: Utility methods 5-15 lines
**Parameters:**
- Maximum 5 parameters per method
- Use dependency injection for services (constructor or method parameter)
- Use Form Request classes for validation parameters (not raw `Request`)
- Optional parameters use `?Type` or default values
**Return Values:**
- Always include return type hint (e.g., `: array`, `: bool`, `: void`)
- Use arrays for multiple values needing structure (see `MembershipFeeCalculator.php` line 24)
- Use model instances for Eloquent operations
- Return `null` explicitly when appropriate (use `?Type` hint)
## Module Design
**Exports:**
- Laravel models are auto-discovered via namespace
- Controllers are auto-discovered via `App\Http\Controllers` namespace
- Requests are auto-discovered via `App\Http\Requests` namespace
- Traits are explicitly `use`d in consuming classes
**Barrel Files:**
- Not used; each file has single responsibility
- Use explicit imports rather than wildcard imports
**Status Constants Pattern:**
- All statuses defined as class constants, never magic strings or enums
- Grouped by category (approval status, disbursement status, recording status)
- Examples from `FinanceDocument.php`:
```php
public const STATUS_PENDING = 'pending';
public const STATUS_APPROVED_SECRETARY = 'approved_secretary';
public const DISBURSEMENT_PENDING = 'pending';
public const DISBURSEMENT_REQUESTER_CONFIRMED = 'requester_confirmed';
```
**Service Injection Pattern:**
- Constructor injection for services (see `MemberPaymentController.__construct()`)
- Store in protected property for method access
- Use type hints for IDE support
**Trait Usage Pattern:**
- Mixed concern traits (e.g., `HasApprovalWorkflow`, `HasAccountingEntries`) used in models
- Test helper traits (e.g., `SeedsRolesAndPermissions`, `CreatesFinanceData`) used in test classes
- Traits documented at top with usage instructions
---
*Convention analysis: 2026-02-13*