Naming Conventions Guide
Last Updated: 2026-01-22
Status: Active
Audience: Developers
This guide documents the naming conventions used throughout the Client Portal codebase to ensure consistency and maintainability.
Table of Contents
- PHP Classes
- Methods and Functions
- Variables
- Database
- Files and Directories
- Routes
- Configuration
- Views
- Tests
PHP Classes
Controllers
| Pattern | Convention | Example |
|---|
| Resource controllers | PascalCase + Controller | ClientController, InvoiceController |
| Nested resources | Parent + Child | ClientProjectController |
| Admin controllers | Admin prefix in namespace | Admin\ClientController |
// Good
namespace App\Http\Controllers;
class ClientController extends Controller {}
namespace App\Http\Controllers\Admin;
class DashboardController extends Controller {}
// Bad
class clientController {} // Wrong case
class ClientsController {} // Don't pluralize
Models
| Pattern | Convention | Example |
|---|
| Model names | PascalCase singular | Client, Invoice, ProjectFile |
| Pivot models | Both names combined | ClientUser |
// Good
class Client extends Model {}
class ProjectFile extends Model {}
class InvoiceItem extends Model {}
// Bad
class Clients extends Model {} // Don't pluralize
class project_file extends Model {} // Wrong case
Services
| Pattern | Convention | Example |
|---|
| Service classes | PascalCase + Service | InvoiceService, FileUploadService |
| Action classes | PascalCase + Action verb | CreateInvoiceAction, SendNotificationAction |
// Good
class InvoiceService {}
class CreateInvoiceAction {}
// Bad
class InvoiceServiceClass {} // Redundant suffix
class invoiceService {} // Wrong case
Service Naming Standards (Detailed)
This section provides detailed naming standards for the Services directory.
Service Classes
All service classes should use the *Service.php suffix:
| Type | Convention | Example |
|---|
| Domain services | {Domain}{Purpose}Service.php | ClientAnalyticsService.php |
| Analysis services | {Domain}AnalysisService.php | TrendAnalysisService.php |
| Processing services | {Domain}ProcessingService.php | DataProcessingService.php |
// Good
class TrendAnalysisService {} // Analysis functionality
class FileUploadService {} // Upload handling
class NotificationDeliveryService {} // Delivery logic
// Bad
class TrendAnalyzer {} // Missing Service suffix
class FileUploader {} // Missing Service suffix
Builder Services (with Dependencies)
Builders that have dependencies (use DI) should have *BuilderService.php suffix:
// Good - has DI dependencies
class ChartBuilderService {
public function __construct(private ChartFactory $factory) {}
}
class ReportQueryBuilderService {
public function __construct(private DataSourceRegistry $registry) {}
}
Pure Builders (No Dependencies)
Pure utility builders without dependencies can use *Builder.php without the Service suffix:
// Good - pure utility, no DI
class CacheKeyBuilder {
public static function build(string ...$parts): string {}
}
Registry Classes
Registry classes use *Registry.php suffix (no Service):
// Good
class ReportRegistry {}
class WidgetRegistry {}
class ExportFormatRegistry {}
DTOs (Data Transfer Objects)
DTOs should be placed in App\DataTransferObjects\ namespace:
| Pattern | Location | Example |
|---|
| DTOs | App\DataTransferObjects\ | ParsedQuery.php, ExportResultDto.php |
| Suffixes | *Data.php or *Dto.php | SearchResultData.php |
// Good - in App\DataTransferObjects
namespace App\DataTransferObjects;
readonly class ParsedQuery {
public function __construct(
public string $textSearch,
public array $filters = [],
) {}
}
Value Objects
Value objects with logic should be placed in App\Support\{Domain}\:
| Pattern | Location | Example |
|---|
| Value objects | App\Support\{Domain}\ | App\Support\Search\SearchContext.php |
// Good - in App\Support\Search
namespace App\Support\Search;
final class SearchContext {
public function __construct(
public readonly ?User $user = null,
public readonly int $limit = 10,
) {}
}
Directory Structure for Services
app/Services/
├── Analytics/
│ ├── TrendAnalysisService.php # Analysis service
│ ├── KpiService.php # Domain service
│ └── DrillDown/
│ └── DrillDownQueryBuilder.php # Pure builder
├── Reports/
│ ├── ReportService.php # Domain service
│ ├── ChartBuilderService.php # Builder with DI
│ ├── ReportRegistry.php # Registry
│ └── Builder/
│ └── CustomReportBuilder.php # Implementation
└── Cache/
└── CacheKeyBuilder.php # Pure utility
app/DataTransferObjects/
├── ParsedQuery.php # Search DTO
├── SearchResults.php # Results DTO
└── LeadScore.php # Scoring DTO
app/Support/
└── Search/
└── SearchContext.php # Value object
Other Classes
| Type | Convention | Example |
|---|
| Form Requests | PascalCase + Request | StoreClientRequest, UpdateInvoiceRequest |
| Resources | PascalCase + Resource | ClientResource, InvoiceResource |
| Policies | PascalCase + Policy | ClientPolicy, ProjectPolicy |
| Events | PascalCase (past tense) | InvoiceCreated, FileUploaded |
| Listeners | PascalCase (action) | SendInvoiceNotification |
| Jobs | PascalCase (action) | ProcessPayment, GeneratePdfInvoice |
| Mail | PascalCase + Mail/Notification | InvoiceMail, WelcomeNotification |
| Middleware | PascalCase | EnsureUserIsAdmin, CheckClientAccess |
| Exceptions | PascalCase + Exception | InvoiceNotFoundException, InvalidStatusException |
Methods and Functions
General Methods
| Type | Convention | Example |
|---|
| Regular methods | camelCase | getActiveProjects(), calculateTotal() |
| Boolean methods | is/has/can/should prefix | isActive(), hasPermission(), canEdit() |
| Accessor methods | get + Attribute | getFullNameAttribute() |
| Mutator methods | set + Attribute | setPasswordAttribute() |
// Good
public function getActiveClients() {}
public function isOverdue(): bool {}
public function hasUnpaidInvoices(): bool {}
public function canAccessProject(Project $project): bool {}
// Bad
public function GetActiveClients() {} // Wrong case
public function active(): bool {} // Missing 'is' prefix for boolean
Controller Methods
Follow Laravel resource conventions:
| Method | Purpose | Example |
|---|
index() | List resources | Display all clients |
create() | Show create form | Display new client form |
store() | Save new resource | Save new client |
show() | Display single resource | Display client details |
edit() | Show edit form | Display edit client form |
update() | Update resource | Update client |
destroy() | Delete resource | Delete client |
Variables
Local Variables
| Type | Convention | Example |
|---|
| Regular variables | camelCase | $clientName, $totalAmount |
| Boolean variables | is/has/should prefix | $isActive, $hasErrors |
| Collections | Plural camelCase | $clients, $invoiceItems |
| Single items | Singular camelCase | $client, $invoice |
// Good
$activeClients = Client::where('is_active', true)->get();
$isOverdue = $invoice->due_date < now();
$totalAmount = $items->sum('amount');
// Bad
$active_clients = []; // Use camelCase
$overdue = true; // Add 'is' prefix for booleans
$client_list = []; // Use plural without 'list'
Constants
| Type | Convention | Example |
|---|
| Class constants | SCREAMING_SNAKE_CASE | MAX_FILE_SIZE, DEFAULT_ROLE |
| Enum cases | PascalCase | Draft, Active, Completed |
// Good
class Invoice
{
public const MAX_LINE_ITEMS = 100;
public const DEFAULT_DUE_DAYS = 30;
}
enum InvoiceStatus: string
{
case Draft = 'draft';
case Sent = 'sent';
case Paid = 'paid';
}
// Bad
class Invoice
{
public const maxLineItems = 100; // Wrong case
public const Max_Items = 100; // Wrong case
}
Database
Tables
| Pattern | Convention | Example |
|---|
| Table names | snake_case plural | clients, project_files |
| Pivot tables | Singular alphabetical | client_user, project_tag |
| Polymorphic | *able suffix | commentable, taggable |
// Good - Migration
Schema::create('project_files', function (Blueprint $table) {});
Schema::create('client_user', function (Blueprint $table) {}); // Pivot
// Bad
Schema::create('ProjectFiles', function (Blueprint $table) {}); // Wrong case
Schema::create('project_file', function (Blueprint $table) {}); // Should be plural
Columns
| Pattern | Convention | Example |
|---|
| Regular columns | snake_case | company_name, due_date |
| Foreign keys | singular_table_id | client_id, project_id |
| Boolean columns | is/has prefix | is_active, has_paid |
| Timestamps | snake_case | created_at, paid_at |
| Polymorphic | *able_type, *able_id | taggable_type, taggable_id |
// Good
$table->string('company_name');
$table->foreignId('client_id')->constrained();
$table->boolean('is_active')->default(true);
$table->timestamp('paid_at')->nullable();
// Bad
$table->string('companyName'); // Use snake_case
$table->foreignId('clientID'); // Use snake_case
$table->boolean('active'); // Add 'is_' prefix
Indexes
| Pattern | Convention | Example |
|---|
| Index names | table_columns_index | invoices_client_id_index |
| Unique indexes | table_columns_unique | users_email_unique |
Files and Directories
PHP Files
| Type | Convention | Example |
|---|
| Classes | PascalCase.php | ClientController.php, Invoice.php |
| Traits | PascalCase.php | HasFiles.php, Notifiable.php |
| Interfaces | PascalCase.php | Invoiceable.php, Exportable.php |
Directory Structure
app/
├── Actions/ # Single-purpose action classes
├── Enums/ # PHP enums
├── Events/ # Event classes
├── Exceptions/ # Custom exceptions
├── Http/
│ ├── Controllers/
│ │ ├── Admin/ # Admin controllers
│ │ └── Portal/ # Client portal controllers
│ ├── Middleware/
│ └── Requests/ # Form request classes
├── Listeners/ # Event listeners
├── Mail/ # Mailable classes
├── Models/ # Eloquent models
├── Notifications/ # Notification classes
├── Policies/ # Authorization policies
├── Providers/ # Service providers
└── Services/ # Service classes
Migrations
| Pattern | Example |
|---|
| Create table | 2024_01_01_000001_create_clients_table.php |
| Add column | 2024_01_02_000001_add_status_to_invoices_table.php |
| Modify column | 2024_01_03_000001_modify_amount_in_invoices_table.php |
Routes
Route Names
| Pattern | Convention | Example |
|---|
| Resource routes | plural.action | clients.index, clients.show |
| Nested resources | parent.child.action | clients.projects.index |
| Admin routes | admin.resource.action | admin.clients.index |
| Portal routes | portal.resource.action | portal.dashboard |
// Good
Route::resource('clients', ClientController::class);
// Generates: clients.index, clients.create, clients.store, etc.
Route::prefix('admin')->name('admin.')->group(function () {
Route::resource('clients', Admin\ClientController::class);
});
// Generates: admin.clients.index, admin.clients.show, etc.
// Bad
Route::get('/clients', [ClientController::class, 'index'])->name('getClients');
Route::get('/clients', [ClientController::class, 'index'])->name('client-list');
Route URIs
| Pattern | Convention | Example |
|---|
| Resources | kebab-case plural | /clients, /project-files |
| Parameters | camelCase | /clients/{client}, /invoices/{invoice} |
| Actions | kebab-case | /invoices/{invoice}/send-reminder |
// Good
Route::get('/project-files', [ProjectFileController::class, 'index']);
Route::post('/invoices/{invoice}/mark-paid', [InvoiceController::class, 'markPaid']);
// Bad
Route::get('/projectFiles', ...); // Use kebab-case
Route::get('/project_files', ...); // Use kebab-case
Configuration
Config Keys
| Pattern | Convention | Example |
|---|
| Config files | snake_case.php | client_portal.php |
| Config keys | snake_case | max_file_size, default_role |
| Nested keys | dot notation | client_portal.files.max_size |
// config/client_portal.php
return [
'max_file_size' => 10 * 1024 * 1024, // 10MB
'default_role' => 'client',
'invoices' => [
'due_days' => 30,
'reminder_days' => [7, 3, 1],
],
];
// Usage
config('client_portal.max_file_size');
config('client_portal.invoices.due_days');
Environment Variables
| Pattern | Convention | Example |
|---|
| Variable names | SCREAMING_SNAKE_CASE | APP_NAME, DB_CONNECTION |
| Custom variables | PREFIX_NAME | CLIENT_PORTAL_MAX_FILE_SIZE |
# Good
APP_NAME="Client Portal"
CLIENT_PORTAL_MAX_FILE_SIZE=10485760
MAIL_FROM_ADDRESS="noreply@example.com"
# Bad
appName="Client Portal" # Wrong case
maxFileSize=10485760 # Wrong case, needs prefix
Views
Blade Files
| Pattern | Convention | Example |
|---|
| View files | kebab-case.blade.php | client-dashboard.blade.php |
| Directories | kebab-case | admin/, portal/, components/ |
| Components | kebab-case.blade.php | invoice-card.blade.php |
| Layouts | kebab-case.blade.php | app.blade.php, guest.blade.php |
resources/views/
├── admin/
│ ├── dashboard.blade.php
│ └── clients/
│ ├── index.blade.php
│ └── show.blade.php
├── portal/
│ └── dashboard.blade.php
├── components/
│ ├── invoice-card.blade.php
│ └── file-upload.blade.php
└── layouts/
├── app.blade.php
└── guest.blade.php
Component Classes
// Good - Component class
namespace App\View\Components;
class InvoiceCard extends Component {}
// Usage in Blade
<x-invoice-card :invoice="$invoice" />
Tests
Test Files
| Type | Convention | Example |
|---|
| Feature tests | PascalCaseTest.php | ClientManagementTest.php |
| Unit tests | PascalCaseTest.php | InvoiceServiceTest.php |
| Test methods | test_snake_case | test_user_can_create_client() |
// Good
class ClientManagementTest extends TestCase
{
public function test_admin_can_create_client(): void
{
// ...
}
public function test_client_cannot_access_admin_dashboard(): void
{
// ...
}
}
// Alternative with attributes (Pest-style in PHPUnit)
#[Test]
public function admin_can_create_client(): void
{
// ...
}
Test Directory Structure
tests/
├── Feature/
│ ├── Admin/
│ │ └── ClientManagementTest.php
│ ├── Auth/
│ │ ├── LoginTest.php
│ │ └── RegistrationTest.php
│ └── Portal/
│ └── DashboardTest.php
└── Unit/
├── Models/
│ └── InvoiceTest.php
└── Services/
└── InvoiceServiceTest.php
Quick Reference
Do's
- Use PascalCase for classes, traits, interfaces, enums
- Use camelCase for methods, variables, parameters
- Use snake_case for database tables, columns, config keys
- Use kebab-case for URLs, view files, CSS classes
- Use SCREAMING_SNAKE_CASE for constants and env variables
- Be consistent within a file and across the codebase
Don'ts
- Avoid abbreviations that aren't universally understood
- Avoid mixing naming conventions within the same category
- Avoid single-letter variable names (except in loops)
- Avoid Hungarian notation (
$strName, $intCount)