7.9 KiB
7.9 KiB
Coding Conventions
Analysis Date: 2026-02-13
Naming Patterns
Files:
- Classes: PascalCase (e.g.,
FinanceDocument.php,MembershipFeeCalculator.php) - Controllers: PascalCase with
Controllersuffix (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
Servicesuffix (e.g.,MembershipFeeCalculator.php,SettingsService.php) - Requests: PascalCase with action +
Requestsuffix (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()inapp/helpers.php) - Test methods: camelCase, prefixed with
test_or use/** @test */doc comment (seetests/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
Collectiontype hints - Enums: Not used; status values stored as class constants (e.g., in
FinanceDocument.phplines 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:
- PHP core/standard library classes (
use Illuminate\*,use App\*) - Third-party library classes (
use Spatie\*,use Barryvdh\*) - Application namespaces (
use App\Models\*,use App\Services\*,use App\Traits\*) - Facades and static imports last (
use Illuminate\Support\Facades\*)
Example from MemberPaymentController.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.phplines 75-81) - Catch generic
\Exceptionwhen 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
SeedsRolesAndPermissionstrait)
Example from BankReconciliationController.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
AuditLoggerstatic class for business logic audit trail (seeapp/Support/AuditLogger.php) - Call pattern:
AuditLogger::log($action, $auditable, $metadata) - Example usage in
MemberPaymentController.phpand other controllers - All member/finance/permission changes should be logged via AuditLogger
Comments
When to Comment:
- Trait usage instructions (see
HasApprovalWorkflow.phplines 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.phplines 17-23) - Parameter types documented with
@param Type $variable - Return types documented with
@return Type - Example from
calculate()method:
/**
* 計算會費金額
*
* @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
?Typeor default values
Return Values:
- Always include return type hint (e.g.,
: array,: bool,: void) - Use arrays for multiple values needing structure (see
MembershipFeeCalculator.phpline 24) - Use model instances for Eloquent operations
- Return
nullexplicitly when appropriate (use?Typehint)
Module Design
Exports:
- Laravel models are auto-discovered via namespace
- Controllers are auto-discovered via
App\Http\Controllersnamespace - Requests are auto-discovered via
App\Http\Requestsnamespace - Traits are explicitly
used 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:
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