Files
sides/.planning/codebase/CONVENTIONS.md

189 lines
7.9 KiB
Markdown

# Coding Conventions
**Analysis Date:** 2026-05-20
## Naming Conventions
**Files:**
- Controllers: PascalCase (e.g., `SirenController.php`, `AdminController.php`, `MapController.php`)
- **Exception:** `cctvController.php` uses lowercase — inconsistent with project convention
- Models: PascalCase singular (e.g., `User.php`)
- Exports: PascalCase (e.g., `WaterLevelExport.php`, `HourlyRainfallExport.php`)
- Notifications: PascalCase (e.g., `ResetPasswordNotification.php`)
- Services: PascalCase (e.g., `FcmService.php`)
- Middleware: PascalCase (e.g., `AdminMiddleware.php`, `LocalizationMiddleware.php`)
- Form Requests: PascalCase (e.g., `ProfileUpdateRequest.php`, `LoginRequest.php`)
- Migrations: `YYYY_MM_DD_HHMMSS_description_snake_case.php`
- Blade views: `snake_case` directories + `kebab-case` or `snake_case` files (e.g., `layout/notification/rainfall.blade.php`)
- JavaScript: `camelCase.js` (e.g., `homemap.js`, `graph.js`, `rfhistory.js`)
**Classes:**
- PascalCase for all classes, following PSR-4 autoloading
- Controllers suffixed with `Controller` (e.g., `RainfallController`)
- Exports suffixed with `Export` (e.g., `HourlyRainfallExport`)
**Methods:**
- camelCase (e.g., `stationDisplay()`, `rainfallSum()`, `exportHistoricalWl()`)
- **Exception:** `SirenHistory()` uses PascalCase — inconsistent
- Public methods in controllers follow action naming: `index()`, `store()`, `update()`, `destroy()`, `edit()`
**Variables:**
- camelCase for local variables (e.g., `$stationFilter`, `$rainfallData`, `$displayDate`)
- camelCase for class properties (e.g., `$stationid`, `$startDate` in Export classes)
- `$stationid` used as one word (not `$stationId`) — project-specific convention
**Database Tables:**
- snake_case lowercase singular (e.g., `station`, `rainfall`, `waterlevel`, `siren`, `notification`, `users`)
- `users` is the only plural table name
**Columns:**
- snake_case lowercase (e.g., `stationid`, `active_time`, `access_level`, `login_attempts`, `is_blocked`, `cctv_link`)
- No consistent convention: some use compound words without underscore (e.g., `stationid`, `waterlevel`)
## Code Style
**Formatting:**
- `.editorconfig` present at both project root and `src/`
- Indent: 4 spaces (no tabs, except Makefiles)
- Charset: UTF-8
- End of line: LF
- Trailing whitespace trimmed (except `.md` files)
- YAML files: 2-space indent
- No ESLint or Prettier configured for JavaScript
**Linting:**
- `laravel/pint` installed as dev dependency (PHP code style fixer)
- No evidence of a `pint.json` configuration file — using defaults
- No frontend linting tools configured
**Bracket Style:**
- Opening braces on same line for classes and methods
- Control structures: opening brace on same line (K&R style)
- Multi-line function calls and arrays: trailing comma not consistently used
**Import/Use Statement Organization:**
- Laravel framework imports first
- Then third-party packages (e.g., `Carbon`, `Maatwebsite\Excel`, `DomPDF`)
- Then application imports (e.g., `App\Models\User`, `App\Services\FcmService`)
- Blank line between groups is not consistently applied
- Example from `WaterLevelController.php`:
```php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
// Add this for Export data to excel
use App\Exports\WaterLevelExport;
use Maatwebsite\Excel\Facades\Excel;
```
**Docblock Usage:**
- PHPDoc blocks present on model properties (`$fillable`, `$hidden`, `casts()`)
- Docblocks on factory methods and notification methods (Breeze scaffolded)
- Custom controllers use inline `//` comments extensively rather than docblocks
- Comment style in controllers follows a pattern:
```php
// Function Retrieve Current Water Level Data
public function index(Request $request)
```
- Each SQL query is annotated with block comments:
```php
// TABLE : STATION JOIN TABLE WATERLEVEL
// COLUMN : name,datetime,waterlevel
// INPUT : $stationCondition from resources/views/...
// OUTPUT : resources/views/layout/waterlevel.blade.php
```
## File Organization
**How files are grouped:**
- Controllers grouped by domain in flat structure under `app/Http/Controllers/`
- API controllers in `app/Http/Controllers/Api/` subdirectory
- Auth controllers in `app/Http/Controllers/Auth/` subdirectory (Breeze scaffolded)
- Exports in `app/Exports/`
- Services in `app/Services/`
- Notifications in `app/Notifications/`
- Views follow a `layout.{domain}` naming pattern (e.g., `layout.rainfall`, `layout.admin.stationmgmt`)
**One class per file:**
- Adhered to throughout — one class per file
**Directory naming:**
- All lowercase directories (e.g., `Controllers/`, `Models/`, `Services/`, `Exports/`)
- Views use nested lowercase directories (e.g., `layout/notification/history/`)
## Git Conventions
**Branch naming:**
- Only 2 commits in the repository — no discernible branch naming convention
**Commit message patterns:**
- Observed messages:
- `first commit`
- `fix: configuration docker-compose.yml`
- The second commit suggests a possible `type: description` convention (fix:, feat:, etc.)
## Laravel-Specific Patterns
**Eloquent Model Patterns:**
- Only `User.php` exists as an Eloquent model (`app/Models/User.php`)
- `HasFactory` and `Notifiable` traits used on User
- `$fillable` array for mass assignment (not `$guarded`)
- `casts()` method (Laravel 12 style) instead of `$casts` property
- Custom notification override: `sendPasswordResetNotification()`
**Controller Patterns:**
- **Not resource controllers** — methods are custom-named (e.g., `stationDisplay()`, `userDisplay()`, `storeStation()`)
- Heavy use of raw SQL via `DB::select()` and `DB::table()` instead of Eloquent ORM
- `collect(DB::select(...))` pattern to wrap raw SQL results into collections
- Controller validation done inline with `$request->validate()` — not consistently using Form Requests
- Redirect pattern: `redirect()->back()->with('success', __('toast.key'))`
- View compact pattern: `return view('blade.path', compact('var1', 'var2'))`
**Form Request Usage:**
- `ProfileUpdateRequest` for profile updates (`app/Http/Requests/ProfileUpdateRequest.php`)
- `LoginRequest` for authentication (`app/Http/Requests/Auth/LoginRequest.php`)
- Admin controller does inline `$request->validate()` instead of Form Requests
- **Inconsistent:** Form Requests exist but most validation is inline
**Resource/API Resource Usage:**
- Not used — API responses use `response()->json($data)` directly
- No API Resource classes defined
**Policy Usage:**
- Not used — authorization handled via custom `AdminMiddleware` checking `access_level`
- `app/Http/Middleware/AdminMiddleware.php` checks `Auth::user()->access_level !== 1`
**Event/Listener Patterns:**
- Not used — no events or listeners defined
- Notification push (FCM) is called directly from API controller
**Route Patterns:**
- Named routes used consistently: `->name('rainfall')`, `->name('stationmanagement.store')`
- Route grouping by middleware: `Route::middleware('auth')`, `Route::middleware(['admin'])`
- Admin routes use POST for updates (not PUT/PATCH) — non-RESTful
- API routes in `routes/api.php` with no auth middleware
**Localization:**
- Bilingual: English (`en`) and Bahasa Malaysia (`bm`) in `resources/lang/`
- `LocalizationMiddleware` sets locale via session
- `__('key')` helper used for translations in views and controllers
**Middleware:**
- Custom middleware registered in `bootstrap/app.php`
- `AdminMiddleware` for admin-only routes
- `LocalizationMiddleware` for language switching
**Export Pattern (Maatwebsite Excel):**
- Export classes implement `FromCollection`, `WithHeadings`, `ShouldAutoSize`
- Constructor injection for filter parameters
- Raw SQL queries in `collection()` method
- `headings()` method returns array of column names
**PDF Export Pattern (DomPDF):**
- `Pdf::loadView('blade.path', compact('data'))->setPaper('a4','potrait')`
- Note: `'potrait'` is a typo — should be `'portrait'` (present in multiple files)
---
*Convention analysis: 2026-05-20*