This repo is a Laravel 12 backend + React 19 frontend using Inertia. Key boundaries:
- Backend:
app/Http/Controllers/Admin/*andapp/Http/Controllers/Site/*. Admin-only routes live inroutes/admin.php(auth + can:admin). Public/site routes inroutes/web.php. - Frontend: Inertia pages in
resources/js/pages/admin/*andresources/js/pages/site/*. Vite entrypoints:resources/js/entries/admin.tsxandsite.tsx.
What you must follow (short rules for an AI code agent):
- Keep Admin vs Site separation: add new admin features under
app/Http/Controllers/Adminandresources/js/pages/admin. - Use FormRequest classes for validation in
app/Http/Requests/Admin/*orSite/*. Put authorization logic inauthorize(). - Follow the ImageService pattern: controllers pass storage path and dimensions to
App\Services\ImageService::processImageWithDimensions(...). The service returns the stored filename (DB stores filename only). Example use:storagePath: 'users', width:200, height:200, prefix:'avatar'inUserController. - Caching: avoid
Cache::flush(). Use targeted keys or tags. There is a centralApp\Services\CacheServiceused for users list caching (users_list_page_{page}_per_{perPage}) and tag-aware invalidation. Use thecache_service()helper function for convenient access to CacheService methods. - Tests: use Pest / RefreshDatabase trait. For admin tests use
actingAs($admin)where$admin = User::factory()->create(['role' => 'admin']). - Static checks: run Pint (PHP formatting), PHPStan (level 5), and ESLint/TypeScript checks before PRs. Commands:
./vendor/bin/pint
./vendor/bin/phpstan analyze --memory-limit=2G
npx eslint . --fix
./vendor/bin/pestExamples & file pointers (copyable patterns):
- User list caching (controller):
app/Http/Controllers/Admin/UserController.phpusesCacheService::rememberUsersList($page,$perPage,300, fn() => User::select(...)->paginate($perPage))andCacheService::clearUsersList()after creates/updates/deletes. - Cache helper function: Use
cache_service()for convenient access. Example:cache_service()->rememberUsersList($page, $perPage, 300, $callback)orcache_service()->clearUsersList(). - Image processing (service):
app/Services/ImageService.php— validate (multiple checks), convert to WebP, resize (cover), return filename only; deletion expects disk path likeusers/filename.webp. - Model accessor:
App\Models\User::getImageUrlAttribute()returnsasset('storage/' . $this->attributes['image'])with a fallbackasset('user.svg').
When making changes, be explicit in commits/PR descriptions: list files changed, migrations (Y/N), tests added/updated, and local run steps. Keep changes small and unit-tested.
If you need clarification about a design decision (e.g. whether to use cache tags in production), ask before making broad changes.
This application includes global helper functions for common operations:
Function: cache_service()
Purpose: Provides convenient access to the centralized CacheService instance.
Location: app/helpers.php (autoloaded via composer.json)
Usage Examples:
// In a controller or service
$users = cache_service()->rememberUsersList($page, $perPage, 300, function() {
return User::select(['id', 'name', 'email'])->paginate($perPage);
});
// Clear cache after mutations
cache_service()->clearUsersList();
// Check cache driver capabilities
if (cache_service()->supportsTags()) {
// Use tag-based caching
}Benefits:
- No need to inject CacheService in every controller
- Cleaner, more Laravel-idiomatic code
- Same functionality as directly using CacheService
When to Use:
- For one-off cache operations in controllers/services
- When dependency injection feels too heavy
- In routes/closures where DI is less convenient
When to Use Dependency Injection Instead:
- When class needs CacheService in multiple methods
- For better testability with mocking
- When following strict SOLID principles
-
Variable Type Casting
- Cast regex matches and option values explicitly
- Use
(int),(string),(float),(bool)casts when needed - Example:
$year = (int) $matches[1]; $count = (int) $this->option('limit'); $price = (float) $data['amount'];
-
Array Type Hints
- Use
array<string, mixed>,array<int, Model>for complex arrays - Use
list<T>for indexed arrays - Example:
public function fetchUsers(): array<int, User> { } public function getConfig(): array<string, mixed> { }
- Use
-
Nullable Types
- Use
?TypeorType|nullfor nullable values - Example:
public function find(int $id): ?User { } public function getData(): string|null { }
- Use
-
String Interpolation
- Don't use complex expressions in encapsed strings
- Assign to variables first, then use in strings
- Example (❌ WRONG):
echo "Count: {count($array)}";
- Example (✅ CORRECT):
$count = count($array); echo "Count: {$count}";
-
Static Method Calls
- Verify method exists before calling on facades/models
- Use proper Laravel facades and methods
- Example (✅ CORRECT):
Log::info('Message'); // ✅ Exists DB::table('users')->get(); // ✅ Exists
-
Model & Class References
- Always define models with proper namespace imports
- Don't reference non-existent classes
- Example:
use App\Models\User; // Then use User::find($id)
Always run PHPStan before committing:
./vendor/bin/phpstan analyze --memory-limit=2GExpected output for clean code:
[OK] No errors
If errors appear:
- Fix type declarations first
- Add proper casts for variables
- Check for undefined classes/methods
- Verify array type hints
- Run tests to ensure functionality:
./vendor/bin/pest
| Issue | Cause | Fix |
|---|---|---|
| "Parameter expects int, string given" | Option not cast | Use (int) $this->option('name') |
| "Call to undefined method" | Non-existent facade method | Check Laravel docs for correct method |
| "Cannot cast array to string" | Array in encapsed string | Extract to variable first |
| "Call to undefined class" | Model not imported or doesn't exist | Add use App\Models\Model; |
| "Unknown class" | Model/class not found | Create model or verify namespace |
| "Encapsed string part is non-string" | Non-scalar in string | Assign to variable, then use |
ESLint Setup: TypeScript + React + Prettier integration
- Config File:
eslint.config.js - Command:
npx eslint . --fix - Auto-fix: Most issues fixed automatically with
--fixflag
When writing TypeScript/React code, ensure:
-
No Explicit
anyType- Never use
any- be specific with types - Use
unknownwhen type is truly unknown and cast explicitly - Use union types for multiple possibilities
- Example (❌ WRONG):
const data = response as any; const items = props.items as any;
- Example (✅ CORRECT):
interface UserData { id: number; name: string; } const data = response as unknown as UserData; const items = props.items as Item[];
- Never use
-
Type-safe Props Casting
- When casting Inertia props, use
unknownas intermediary - Define proper interfaces for props
- Example:
interface PageProps { logs: Pagination; archives: Archive[]; statistics: Statistics; } const { logs, archives, statistics } = usePage().props as unknown as PageProps;
- When casting Inertia props, use
-
No Unused Variables
- Remove variables that are declared but never used
- Comment out code instead of leaving unused variables
- Example (❌ WRONG):
const result = await response.json(); window.location.reload();
- Example (✅ CORRECT):
await response.json(); window.location.reload();
-
Record Type Hints
- Use
Record<string, unknown>instead ofRecord<string, any> - Use specific types in Record values
- Example:
context?: Record<string, unknown>; level_distribution: Record<string, number>;
- Use
-
Array Type Parameters
- Use specific types in array destructuring
- Avoid
anyin map callbacks - Example (❌ WRONG):
.map(([level, count]: any) => { ... })
- Example (✅ CORRECT):
.map(([level, count]: [string, number]) => { ... })
-
Import Organization
- Import UI components from
@/components/ui/* - Import hooks from
@/hooks/* - Import layouts from
@/layouts/* - Group imports: external, then internal, then UI
- Example:
import React, { useMemo, useState } from 'react'; import { Head, usePage } from '@inertiajs/react'; import { Badge } from '@/components/ui/badge'; import AppLayout from '@/layouts/app-layout'; import { useDebounce } from '@/hooks/use-debounce';
- Import UI components from
-
Component Props Types
- Define interfaces for all component props
- Use specific types, never
any - Example:
interface MyComponentProps { items: Item[]; onSelect: (id: number) => void; isLoading?: boolean; } export default function MyComponent({ items, onSelect, isLoading = false }: MyComponentProps)
-
Event Handlers
- Properly type event handlers
- Don't use
anyfor events - Example:
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)} onClick={() => handleClick()} onSelect={(item: Item) => processItem(item)}
Always run ESLint before committing:
npx eslint . --fixExpected output for clean code: No errors or warnings should appear
If errors appear:
- Check for explicit
anytypes - replace with proper types - Check for unused variables - remove or use them
- Check Record types - use
unknowninstead ofany - Check array destructuring - add type parameters
- Run type check:
npm run type-checkortsc --noEmit
| Issue | Cause | Fix |
|---|---|---|
| "Unexpected any. Specify a different type" | Using any type explicitly |
Replace with specific type or unknown |
| "is assigned a value but never used" | Unused variable declared | Remove variable or use it |
| "is defined but never used" | Unused import/function | Remove unused import or function |
| "Missing return type" | Function lacks return type annotation | Add return type: : ReturnType |
| "Unsafe assignment to any" | Casting to any without specificity |
Use intermediate unknown cast |
| "Generic type parameter not specified" | Missing type parameter | Add <Type> to generic call |
PHP Code Formatter for Laravel
- Config File: Laravel Pint uses default Laravel preset (can be customized in pint.json)
- Command:
./vendor/bin/pint(formats in place) - Command (Test):
./vendor/bin/pint --test(check without fixing) - Command (Parallel):
./vendor/bin/pint --parallel(faster on multi-core systems) - Preset: Laravel standard PSR-12 style
Pint automatically handles most formatting, but ensure:
-
Code Style Compliance
- 4-space indentation (not tabs)
- Line length consideration (no hard limit, but readability matters)
- PSR-12 compliance for PHP code
- Consistent spacing around operators
-
Import Organization
- Pint automatically sorts and organizes imports
- Group related imports together
- Use namespaces properly
-
Class & Method Formatting
- One blank line between methods
- Proper brace placement (opening brace on same line)
- Consistent spacing in control structures
- Example (✅ CORRECT):
public function getUserData(int $userId): array { $user = User::find($userId); return $user->getData(); }
-
Return Type Declarations
- Always include return types (enforced by PHPStan)
- Use union types for multiple return types
- Use nullable types with
?Type - Example:
public function find(int $id): ?User { } public function getData(): string|int|null { }
-
Constructor Property Promotion
- Use PHP 8 constructor property promotion
- Makes code more concise and readable
- Example (✅ CORRECT):
public function __construct( private readonly UserRepository $repo, private readonly CacheService $cache, ) {}
-
String Formatting
- Use single quotes for simple strings
- Use double quotes for strings with interpolation
- Avoid unnecessary string concatenation
- Example:
$simple = 'This is simple'; $interpolated = "User: {$user->name}";
Always run Pint before committing:
./vendor/bin/pintCheck without fixing (for CI/CD verification):
./vendor/bin/pint --testUse parallel mode for faster formatting:
./vendor/bin/pint --parallelRecommended workflow:
# Format code with Pint
./vendor/bin/pint
# Verify with PHPStan
./vendor/bin/phpstan analyze --memory-limit=2G
# Run tests
./vendor/bin/pest
# Then commitTool Chain Priority:
- Pint - PHP formatting (must run first)
- PHPStan - Type checking and static analysis
- ESLint - JavaScript/TypeScript formatting and linting
- Pest - Test suite verification
Complete Code Quality Check:
# Format PHP code
./vendor/bin/pint
# Check PHP types
./vendor/bin/phpstan analyze --memory-limit=2G
# Format TypeScript/React
npx eslint . --fix
# Final Check
composer pre-commit
# Run tests
./vendor/bin/pest --no-coverageExpected Output:
✅ Pint: PASS (code formatted)
✅ PHPStan: [OK] No errors
✅ ESLint: (no output = success)
✅ Pest: Tests passed
| Issue | Cause | Fix |
|---|---|---|
| Code doesn't look formatted | Pint not run | Run ./vendor/bin/pint |
| Tests fail after Pint | Unintended changes | Review with --test flag first |
| Inconsistent spacing | Missed Pint run | Add Pint to pre-commit hook |
| Line too long (>120 chars) | Readability concern | Break into multiple lines manually |
| Formatting conflicts with IDE | IDE auto-format on save | Disable IDE auto-format, use Pint |
✅ Consistency: All PHP code follows same standard
✅ Readability: Uniform formatting across codebase
✅ Collaboration: Team follows same rules
✅ CI/CD: Can fail builds if code not formatted
✅ Git diffs: Less noise from formatting changes
All project documentation should be organized in the /docs folder with clear category-based structure.
Root Documentation Path: /docs
docs/
├── log-audit/ # Logging and audit related docs
│ ├── README_SECURITY_LOGS.md # Security logs overview
│ ├── SECURITY_LOGS_MONTHLY_ARCHIVE.md
│ ├── CHANGES_SUMMARY.md
│ ├── IMPLEMENTATION_COMPLETE.md
│ ├── LOGS_COMPARISON_QUICK.md
│ ├── LOGS_VISUAL_COMPARISON.md
│ └── SECURITY_LOGS_ANALYSIS.md
│
├── scurity-audit/ # Security audit related docs
│ ├── SECURITY_README.md # Security overview
│ ├── SECURITY_INDEX.md # Security index/table of contents
│ ├── SECURITY_SUMMARY.md
│ ├── SECURITY_ANALYSIS.md
│ ├── SECURITY_AUDIT_2025.md
│ ├── SECURITY_CHECKLIST.md
│ ├── SECURITY_IMPROVEMENTS.md
│ └── SECURITY_FIXES_IMMEDIATE.md
│
├── api/ # API Documentation (create if needed)
│ └── endpoints.md
│
├── architecture/ # Architecture & Design Docs (create if needed)
│ └── overview.md
│
├── guide/ # User/Developer Guides (create if needed)
│ └── getting-started.md
│
└── troubleshooting/ # Troubleshooting & FAQ (create if needed)
└── common-issues.md
- Purpose: Documentation for security logs, audit trails, and logging system
- Content Types:
- Security log implementation guides
- Monthly archival processes
- Log analysis and comparison docs
- Implementation summaries
- Examples:
SECURITY_LOGS_MONTHLY_ARCHIVE.md- How monthly archival worksIMPLEMENTATION_COMPLETE.md- Implementation overviewCHANGES_SUMMARY.md- Change tracking
- Purpose: Security analysis, audits, and security-related documentation
- Content Types:
- Security audit reports
- Security analysis documents
- Security checklists
- Vulnerability fixes and improvements
- Security index/navigation
- Examples:
SECURITY_AUDIT_2025.md- Annual security auditSECURITY_CHECKLIST.md- Security compliance checklistSECURITY_INDEX.md- Index of all security docs
- Purpose: API endpoints, request/response examples, authentication
- Suggested Files:
endpoints.md- All API endpointsauthentication.md- Auth methodserrors.md- Error handling
- Purpose: System architecture, design patterns, database schema
- Suggested Files:
overview.md- System overviewdatabase-schema.md- Database designdesign-patterns.md- Architecture patterns
- Purpose: Step-by-step guides, tutorials, onboarding
- Suggested Files:
getting-started.md- Quick start guidedevelopment-setup.md- Dev environment setupdeployment.md- Deployment guide
- Purpose: Common issues, solutions, and frequently asked questions
- Suggested Files:
common-issues.md- Problem solutionsfaq.md- Frequently asked questions
When Creating New Documentation:
-
Choose Appropriate Category
- Place documentation in existing category if it fits
- Create new category folder if needed (e.g.,
docs/performance/) - Category names should be lowercase, hyphen-separated
-
File Naming Convention
- Use UPPERCASE_WITH_UNDERSCORES.md for document names
- Start with context: SECURITY_, API_, GUIDE_*, etc.
- Include README.md or INDEX.md for category overview
-
Documentation Structure
# Document Title **Last Updated**: YYYY-MM-DD **Category**: Folder name **Status**: Draft/Review/Final ## Overview Brief description of what this doc covers ## Table of Contents - Section 1 - Section 2 ## Content Sections ### Section 1 Content here ### Section 2 Content here ## Related Documents - [Related Doc](./path/to/related-doc.md) - [External Link](https://example.com)
-
README/INDEX Pattern
- Each category should have a README.md or INDEX.md
- Acts as navigation for the category
- Lists all documents in the category with brief descriptions
-
Cross-Referencing
- Link to related documents using relative paths
- Keep documentation interconnected
- Update links when moving or renaming files
View all documentation:
find docs -name "*.md" -type f | sortList documentation by category:
ls -la docs/Create new documentation category:
mkdir -p docs/new-category/
touch docs/new-category/README.md- Location: All documentation MUST be in
/docsfolder - Organization: Documentation MUST be organized by category
- Naming: File names MUST be UPPERCASE_WITH_UNDERSCORES.md
- Categories: Create category folders for logical grouping
- Index: Each category SHOULD have README.md or INDEX.md
- Links: Use relative paths for cross-references
- Updates: Keep documentation in sync with code changes
- Status: Mark document status (Draft/Review/Final) in frontmatter
Copilot MUST:
- ✅ Place docs in appropriate
/docs/category/folder - ✅ Use UPPERCASE_WITH_UNDERSCORES.md naming
- ✅ Include proper frontmatter with status and date
- ✅ Create category folder if it doesn't exist
- ✅ Create/update README.md or INDEX.md in category
- ✅ Add cross-references to related docs
- ✅ Use relative path links for references
- ✅ Follow markdown best practices /home/indatech/Documents/PROJECT/fullstack-laravel-react-starter/.github/copilot-instructions.md