200 lines
7.9 KiB
Markdown
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*
|