Generated: 2025-10-16 Framework: Laravel 10.x PHP Version: 8.2+ Environment: Local Development
| Software | Version | Purpose |
|---|---|---|
| PHP | 8.2+ | Backend runtime |
| Composer | 2.x | PHP dependency management |
| MySQL | 5.7+ or 8.0+ | Primary database |
| Redis | Latest | Cache & queue backend |
| Node.js | 16+ | Frontend asset compilation |
| npm | 8+ | JavaScript dependency management |
| Software | Purpose |
|---|---|
| Meilisearch | Full-text search engine |
| Docker | Containerized development environment |
| Postman | API testing |
| MySQL Workbench | Database management GUI |
Ensure the following PHP extensions are installed:
php -m | grep -E '(bcmath|ctype|fileinfo|json|mbstring|openssl|pdo|tokenizer|xml|gd|curl|zip)'
Required extensions:
- bcmath - Arbitrary precision mathematics
- ctype - Character type checking
- fileinfo - File information
- json - JSON support
- mbstring - Multi-byte string handling
- openssl - Cryptographic functions
- pdo - Database abstraction
- pdo_mysql - MySQL driver for PDO
- tokenizer - Token parsing
- xml - XML processing
- gd or imagick - Image manipulation
- curl - HTTP client
- zip - ZIP archive handling
git clone <repository-url> digital2
cd digital2
composer install
Note: If you encounter memory issues:
bash
COMPOSER_MEMORY_LIMIT=-1 composer install
npm install
cp .env.example .env
php artisan key:generate
This sets the APP_KEY in your .env file, which is used for encryption.
Edit .env file with your local configuration (see Environment Configuration section).
php artisan migrate
With seeders (optional):
bash
php artisan migrate --seed
php artisan storage:link
This creates a symbolic link from public/storage to storage/app/public.
php artisan serve
API will be available at: http://localhost:8000
APP_NAME=Digital
APP_ENV=local
APP_KEY= # Auto-generated by key:generate
APP_DEBUG=true # Enable detailed error messages
APP_URL=http://localhost:8000 # Base URL for the application
Production Settings:
- Set APP_ENV=production
- Set APP_DEBUG=false
- Use HTTPS in APP_URL
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=digital_db # Your database name
DB_USERNAME=root # Your database user
DB_PASSWORD= # Your database password
For Docker/Local MySQL:
env
DB_HOST=mysql
DB_PORT=3306
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
Cache & Queue with Redis:
env
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
Without Redis (development):
env
CACHE_DRIVER=file
QUEUE_CONNECTION=database
SESSION_DRIVER=file
For cloud file storage:
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=your_bucket_name
Local Development (skip S3):
env
FILESYSTEM_DRIVER=local
Development (Mailtrap):
env
MAIL_MAILER=smtp
MAIL_HOST=sandbox.smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_mailtrap_username
MAIL_PASSWORD=your_mailtrap_password
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="digital@tbf.ro"
MAIL_FROM_NAME="${APP_NAME}"
Production (AWS SES):
env
MAIL_MAILER=ses
MAIL_HOST=email-smtp.eu-central-1.amazonaws.com
MAIL_PORT=587
MAIL_USERNAME=your_ses_username
MAIL_PASSWORD=your_ses_password
SES_CONFIGURATION_SET=my-first-configuration-set
For full-text search:
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=your_master_key
Install Meilisearch: ```bash
brew install meilisearch
curl -L https://install.meilisearch.com | sh
docker run -d -p 7700:7700 getmeili/meilisearch:latest ```
Import existing data to Meilisearch:
bash
php artisan scout:import "App\Models\User"
php artisan scout:import "App\Models\Deal"
php artisan scout:import "App\Models\Task"
For AI features:
OPENAI_API_KEY=sk-...
OPENAI_ORGANIZATION=org-...
For real-time notifications:
WS_HOST=tcp://127.0.0.1
WS_PORT_NOTIFICATION=9080
WS_PORT_PROJECT=9090
WS_PORT_MEETING=9070
WS_SECRET=your_random_secret_key
Start WebSocket Server:
bash
php artisan websockets:serve
For push notifications:
FIREBASE_SERVER_KEY=your_firebase_server_key
For payment processing:
STRIPE_API_KEY=sk_test_...
STRIPE_API_PUBLISHEABLE_KEY=pk_test_...
Set the frontend application URL:
FE_URL="http://localhost:3000" # Local VueJS dev server
MySQL Command Line:
sql
CREATE DATABASE digital_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
MySQL Workbench:
1. Right-click “Schemas” → “Create Schema”
2. Name: digital_db
3. Charset: utf8mb4
4. Collation: utf8mb4_unicode_ci
Fresh Migration (drops all tables):
bash
php artisan migrate:fresh
Fresh with Seeders:
bash
php artisan migrate:fresh --seed
Rollback Last Migration:
bash
php artisan migrate:rollback
Rollback All Migrations:
bash
php artisan migrate:reset
Check Migration Status:
bash
php artisan migrate:status
Run All Seeders:
bash
php artisan db:seed
Run Specific Seeder:
bash
php artisan db:seed --class=UserSeeder
Export Database:
bash
mysqldump -u root -p digital_db > backup_$(date +%Y%m%d).sql
Import Database:
bash
mysql -u root -p digital_db < backup_20251016.sql
List All Commands:
bash
php artisan list
Get Help for a Command:
bash
php artisan help migrate
Start Server:
bash
php artisan serve
Custom Host/Port:
bash
php artisan serve --host=0.0.0.0 --port=8080
Run Queue Worker:
bash
php artisan queue:work
Run Queue Worker (auto-reload on code changes):
bash
php artisan queue:listen
Process Single Job:
bash
php artisan queue:work --once
Clear Failed Jobs:
bash
php artisan queue:flush
Retry Failed Jobs:
bash
php artisan queue:retry all
Monitor Queue (Laravel Horizon - if installed):
bash
php artisan horizon
Clear All Caches:
bash
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
Cache Configuration:
bash
php artisan config:cache
Cache Routes:
bash
php artisan route:cache
Optimize Application:
bash
php artisan optimize
Clear Optimization:
bash
php artisan optimize:clear
Run Scheduler Manually:
bash
php artisan schedule:run
List Scheduled Tasks:
bash
php artisan schedule:list
Production Cron Entry:
cron
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1
Development Build:
bash
npm run dev
Watch for Changes:
bash
npm run watch
Watch with Polling:
bash
npm run watch-poll
Hot Module Replacement:
bash
npm run hot
Production Build:
bash
npm run production
Create Model:
bash
php artisan make:model ModelName
Create Model with Migration:
bash
php artisan make:model ModelName -m
Create Model with Migration, Factory, and Seeder:
bash
php artisan make:model ModelName -mfs
Create Migration:
bash
php artisan make:migration create_table_name_table
Create Controller:
bash
php artisan make:controller Api/ControllerName
Create API Resource Controller:
bash
php artisan make:controller Api/ControllerName --api
Create Form Request:
bash
php artisan make:request RequestName
Run All Tests:
bash
php artisan test
Run Specific Test File:
bash
php artisan test tests/Feature/UserTest.php
Run Tests with Coverage:
bash
php artisan test --coverage
Pull Latest Changes
bash
git pull origin main
composer install
npm install
php artisan migrate
Start Services ```bash
php artisan serve
php artisan queue:work
npm run watch
php artisan websockets:serve ```
Develop Features
git checkout -b feature/new-featuregit commit -m "Add new feature"Before Pushing ```bash
php artisan test
php artisan cache:clear
./vendor/bin/phpcs
git push origin feature/new-feature ```
Example: Create “Report” endpoint
Create Model & Migration
bash
php artisan make:model Report -m
Define Migration (database/migrations/xxxx_create_reports_table.php)
php
Schema::create('reports', function (Blueprint $table) {
$table->id();
$table->foreignId('instance_id')->constrained();
$table->string('title');
$table->text('description');
$table->foreignId('user_id')->constrained();
$table->timestamps();
$table->softDeletes();
});
Run Migration
bash
php artisan migrate
Define Model (app/Models/Report.php)
```php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Auth;
class Report extends Model { use SoftDeletes;
protected $fillable = ['instance_id', 'title', 'description', 'user_id'];
/**
* Boot method to auto-set instance_id
*/
public static function boot()
{
parent::boot();
self::creating(function(Report $report){
$authUser = Auth::user();
if ($authUser && !$report->instance_id) {
$report->instance_id = $authUser->instance_id;
}
});
}
/**
* Relationship: Report belongs to User
*/
public function user()
{
return $this->belongsTo(User::class);
}
} ```
Create Controller
bash
php artisan make:controller Api/ReportController --api
Define Controller (app/Http/Controllers/Api/ReportController.php)
```php
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller; use App\Models\Report; use Illuminate\Http\Request;
class ReportController extends Controller { /**
* Display a listing of the resource.
*/
public function index()
{
$reports = Report::where('instance_id', auth()->user()->instance_id)
->with('user')
->paginate(15);
return response()->json([
'success' => true,
'data' => $reports
]);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'description' => 'required|string',
]);
$report = Report::create($validated);
return response()->json([
'success' => true,
'data' => $report,
'message' => 'Report created successfully'
], 201);
}
/**
* Display the specified resource.
*/
public function show($id)
{
$report = Report::where('instance_id', auth()->user()->instance_id)
->findOrFail($id);
return response()->json([
'success' => true,
'data' => $report
]);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, $id)
{
$report = Report::where('instance_id', auth()->user()->instance_id)
->findOrFail($id);
$validated = $request->validate([
'title' => 'sometimes|required|string|max:255',
'description' => 'sometimes|required|string',
]);
$report->update($validated);
return response()->json([
'success' => true,
'data' => $report,
'message' => 'Report updated successfully'
]);
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
$report = Report::where('instance_id', auth()->user()->instance_id)
->findOrFail($id);
$report->delete();
return response()->json([
'success' => true,
'message' => 'Report deleted successfully'
]);
}
} ```
Add Routes (routes/api.php)
php
Route::middleware(['auth:sanctum'])->group(function () {
Route::apiResource('reports', ReportController::class);
});
Test Endpoint ```bash
curl -H “Authorization: Bearer {token}” http://localhost:8000/api/reports
curl -X POST -H “Authorization: Bearer {token}” \ -H “Content-Type: application/json” \ -d ‘{“title”:“Test Report”,“description”:“Description”}’ \ http://localhost:8000/api/reports ```
🎯 Purpose: These standards ensure consistency, maintainability, and best practices across the Digital codebase.
📌 Mandatory: All developers must follow these guidelines when contributing to the project.
✓ Use Model Binding when ID is passed via URL
// ✅ CORRECT - Use Model Binding
public function show(User $user)
{
return new UserResource($user);
}
// ❌ INCORRECT - Manual ID lookup
public function show($id)
{
$user = User::findOrFail($id);
return new UserResource($user);
}
Benefits: - Automatic 404 handling - Cleaner code - Type safety
For models with instance_id field, the index() method must:
- Accept Instance $instance parameter
- Fetch data through the instance relationship
// ✅ CORRECT - Instance-scoped query
public function index(Instance $instance)
{
$users = $instance->users()
->with('department', 'role')
->paginate(15);
return UserResourceCollection::make($users);
}
// ❌ INCORRECT - Direct query without instance binding
public function index()
{
$users = User::where('instance_id', auth()->user()->instance_id)
->paginate(15);
return UserResourceCollection::make($users);
}
If endpoint returns database data (more than just an ID), use Resource or ResourceCollection
// ✅ CORRECT - Using Resource
public function show(User $user)
{
return new UserResource($user);
}
public function index(Instance $instance)
{
$users = $instance->users()->paginate(15);
return UserResourceCollection::make($users);
}
// ❌ INCORRECT - Returning raw model data
public function show(User $user)
{
return response()->json(['data' => $user]);
}
See: API Contracts - Resources
Dropdown/select data must be returned by:
1. InstanceController::filters() method (preferred)
2. Or a dedicated filters() method in the specific controller
// ✅ CORRECT - Filters method
public function filters(Instance $instance)
{
return response()->json([
'success' => true,
'data' => [
'departments' => $instance->departments()
->select('id', 'name')
->get(),
'roles' => $instance->roles()
->select('id', 'name')
->get(),
'statuses' => UserStatusEnum::options(),
]
]);
}
Usage in frontend:
javascript
// Fetch filters before showing form
const filters = await axios.get('/api/instances/{id}/users/filters');
// Use filters.data.departments for dropdown
Requests with pagination or view_more must include total_results field
// ✅ CORRECT - Include total_results
public function index(Instance $instance, Request $request)
{
$perPage = $request->input('per_page', 15);
$users = $instance->users()->paginate($perPage);
return response()->json([
'success' => true,
'data' => UserResource::collection($users->items()),
'total_results' => $users->total(), // ← Required
]);
}
If more than 2-3 validation rules exist, create a FormRequest class
// ✅ CORRECT - FormRequest for complex validation
php artisan make:request UserStoreRequest
// app/Http/Requests/UserStoreRequest.php
class UserStoreRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'first_name' => 'required|string|max:255',
'last_name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'department_id' => 'required|exists:departments,id',
'role_id' => 'required|exists:roles,id',
];
}
}
// Controller
public function store(UserStoreRequest $request)
{
$user = User::create($request->validated());
return new UserResource($user);
}
Naming convention: {Model}{Action}Request
- UserStoreRequest
- ProductUpdateRequest
- DealDeleteRequest
Always use $request->validated() to get validated data only
// ✅ CORRECT - Use validated data
public function store(UserStoreRequest $request)
{
$validated = $request->validated();
$user = User::create($validated);
return new UserResource($user);
}
// ❌ INCORRECT - Using all() exposes security risk
public function store(Request $request)
{
$user = User::create($request->all()); // ← Mass assignment vulnerability!
return new UserResource($user);
}
Why? $request->all() includes ALL request data, including potentially malicious fields like is_admin, instance_id, etc.
Add custom messages only if default Laravel messages are insufficient
// ✅ CORRECT - Use defaults when possible
public function rules()
{
return [
'email' => 'required|email', // Default message is fine
];
}
// ✅ CORRECT - Custom message when needed
public function rules()
{
return [
'vat_number' => 'required|regex:/^[A-Z]{2}[0-9]{8,10}$/',
];
}
public function messages()
{
return [
'vat_number.regex' => 'VAT number must follow EU format (e.g., RO12345678)',
];
}
Every model must implement SoftDeletes trait
// ✅ CORRECT - All models use SoftDeletes
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model
{
use SoftDeletes; // ← Required
protected $fillable = ['first_name', 'last_name', 'email'];
}
Migration:
php
Schema::create('users', function (Blueprint $table) {
$table->id();
// ... other fields
$table->timestamps();
$table->softDeletes(); // ← Adds deleted_at column
});
All models must auto-set instance_id in the boot() method
// ✅ CORRECT - Auto-set instance_id
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
class Task extends Model
{
protected $fillable = ['title', 'description', 'instance_id'];
/**
* Boot method to auto-set instance_id on creation
*
* @return void
*/
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$authUser = Auth::user();
if ($authUser && !$model->instance_id) {
$model->instance_id = $authUser->instance_id;
}
});
}
}
Why? This ensures multi-tenancy is enforced automatically.
All Eloquent relationships must use snake_case naming
// ✅ CORRECT - snake_case relationships
public function partner_address()
{
return $this->hasOne(PartnerAddress::class);
}
public function product_type()
{
return $this->belongsTo(ProductType::class);
}
public function user_files()
{
return $this->hasMany(UserFile::class);
}
// ❌ INCORRECT - camelCase
public function partnerAddress() // ← Wrong
{
return $this->hasOne(PartnerAddress::class);
}
Usage:
php
$user->partner_address; // ✅ Correct
$user->partnerAddress; // ❌ Wrong
All values stored in database (nomenclatures, enums, statuses) must be in English
// ✅ CORRECT - English values in DB
DB::table('vacation_types')->insert([
'name' => 'Annual Leave', // ← English
'code' => 'ANNUAL',
]);
// ❌ INCORRECT - Romanian in database
DB::table('vacation_types')->insert([
'name' => 'Concediu de odihnă', // ← Wrong
]);
See: Rule 6: Translations
Collections/lists must always use ResourceCollection
// ✅ CORRECT - ResourceCollection for lists
public function index(Instance $instance)
{
$users = $instance->users()->paginate(15);
return UserResourceCollection::make($users);
}
// Or with collection helper
public function index(Instance $instance)
{
$users = $instance->users()->get();
return UserResource::collection($users);
}
// ❌ INCORRECT - Returning raw array
public function index()
{
$users = User::all();
return response()->json(['data' => $users]); // ← Missing transformation
}
Single objects must use Resource classes
// ✅ CORRECT - Resource for single object
public function show(User $user)
{
return new UserResource($user);
}
public function store(UserStoreRequest $request)
{
$user = User::create($request->validated());
return new UserResource($user);
}
Example Resource:
php
// app/Http/Resources/UserResource.php
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'full_name' => $this->first_name . ' ' . $this->last_name,
'email' => $this->email,
'department' => new DepartmentResource($this->whenLoaded('department')),
'role' => new RoleResource($this->whenLoaded('role')),
'created_at' => $this->created_at
];
}
}
Do not add unnecessary log statements (error, warning, info) in production code
// ❌ INCORRECT - Unnecessary logging
public function index(Instance $instance)
{
Log::info('Fetching users'); // ← Remove
$users = $instance->users()->get();
Log::info('Users fetched: ' . $users->count()); // ← Remove
return UserResource::collection($users);
}
// ✅ CORRECT - Only log exceptions or critical events
public function store(UserStoreRequest $request)
{
try {
$user = User::create($request->validated());
return new UserResource($user);
} catch (\Exception $e) {
Log::error('Failed to create user', [
'error' => $e->getMessage(),
'request' => $request->validated(),
]);
throw $e;
}
}
When to log: - ✅ Exceptions and errors - ✅ Critical business events (payment processed, etc.) - ✅ Security events (unauthorized access) - ❌ Routine operations (fetching data, saving records)
Authorization should be done at middleware level, not manually in methods
// ✅ CORRECT - Middleware authorization
Route::middleware(['auth:sanctum', 'check.right:ADMIN_HR'])
->group(function () {
Route::apiResource('users', UserController::class);
});
// ❌ INCORRECT - Manual checks in every method
public function index()
{
if (!auth()->user()->hasRight(RightEnum::ADMIN_HR)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
// ... rest of code
}
List of rights that can be managed through middleware is defined in:
- app/Policies/OrganigramModulePolicy.php
Example middleware usage:
php
// routes/api.php
Route::middleware(['auth:sanctum', 'check.right:ADMIN_CRM'])
->prefix('deals')
->group(function () {
Route::get('/', [DealController::class, 'index']);
Route::post('/', [DealController::class, 'store']);
});
Available rights (examples):
- ADMIN_OBJECTIVE,
- MANAGER_OBJECTIVE,
- EMPLOYEE_OBJECTIVE,
- ADMIN_PROCEDURE,
- AUDITOR_PROCEDURE,
- EMPLOYEE_PROCEDURE,
- ADMIN_ORGANIGRAM,
- EMPLOYEE_ORGANIGRAM,
- ADMIN_CRM,
- EMPLOYEE_CRM,
See: app/Enums/RightEnum.php for complete list
All user-facing text must have translations in all supported languages
Supported languages: - 🇬🇧 English (en) - 🇷🇴 Romanian (ro) - 🇩🇪 German (de) - 🇪🇸 Spanish (es) - 🇮🇹 Italian (it)
// ✅ CORRECT - Translation keys
return response()->json([
'message' => __('messages.user_created_successfully'),
]);
// resources/lang/en/messages.php
return [
'user_created_successfully' => 'User created successfully',
];
// resources/lang/ro/messages.php
return [
'user_created_successfully' => 'Utilizator creat cu succes',
];
Messages and nomenclature stored in database must be in English
// ✅ CORRECT - English in database, translations separate
DB::table('vacation_types')->insert([
'name' => 'Sick Leave', // ← English in DB
'code' => 'SICK',
]);
// Frontend displays translated version:
__('vacation_types.sick_leave') // Returns "Concediu medical" in Romanian
// ❌ INCORRECT - Non-English in database
DB::table('vacation_types')->insert([
'name' => 'Concediu medical', // ← Wrong
]);
Validation errors and exceptions must have translations
// ✅ CORRECT - Translated error messages
public function messages()
{
return [
'email.required' => __('validation.email_required'),
'email.unique' => __('validation.email_already_exists'),
];
}
// ❌ INCORRECT - Hardcoded English messages
public function messages()
{
return [
'email.required' => 'Email is required', // ← Not translated
];
}
Before submitting a pull request, verify:
[ ] Controllers:
Instance $instance parameterfilters() methodtotal_results[ ] Requests:
$request->validated() instead of $request->all()[ ] Models:
boot() method auto-sets instance_id[ ] Resources:
[ ] Authorization:
[ ] Translations:
| Category | Rule | Example |
|---|---|---|
| Controllers | Model Binding | show(User $user) not show($id) |
| Controllers | Instance Binding | index(Instance $instance) |
| Controllers | Use Resources | return new UserResource($user) |
| Requests | Use validated() | $request->validated() not $request->all() |
| Requests | FormRequest | Create for 3+ validation rules |
| Models | SoftDeletes | use SoftDeletes; in all models |
| Models | Auto instance_id | Set in boot() method |
| Models | Relationships | partner_address() not partnerAddress() |
| Resources | Collections | Use ResourceCollection::make() |
| Resources | Single Objects | Use new UserResource() |
| Authorization | Middleware | check.right:ADMIN_HR in routes |
| Translations | Database | English only in database |
| Translations | UI Text | Translate all 5 languages |
📚 Related Documentation: - Architecture - Multi-Tenancy - Architecture - Authorization - API Contracts - Response Formats - Data Models - Model Patterns
All Tests:
bash
php artisan test
Specific Test Suite:
bash
php artisan test --testsuite=Feature
php artisan test --testsuite=Unit
Specific Test File:
bash
php artisan test tests/Feature/UserTest.php
With Coverage:
bash
php artisan test --coverage
Create Test:
bash
php artisan make:test UserTest
php artisan make:test UserTest --unit
Example Feature Test (tests/Feature/UserTest.php):
```php
<?php
namespace Tests\Feature;
use App\Models\User; use App\Models\Instance; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase;
class UserTest extends TestCase { use RefreshDatabase;
/**
* Test user can be created.
*/
public function test_user_can_be_created()
{
$instance = Instance::factory()->create();
$user = User::factory()->create([
'instance_id' => $instance->id,
'first_name' => 'John',
'last_name' => 'Doe',
'email' => 'john@example.com',
]);
$this->assertDatabaseHas('users', [
'email' => 'john@example.com',
]);
}
/**
* Test API endpoint returns users.
*/
public function test_api_returns_users_list()
{
$instance = Instance::factory()->create();
$user = User::factory()->create(['instance_id' => $instance->id]);
$response = $this->actingAs($user, 'sanctum')
->getJson('/api/users');
$response->assertStatus(200)
->assertJsonStructure([
'success',
'data' => [
'*' => ['id', 'first_name', 'last_name', 'email']
]
]);
}
} ```
Install Telescope:
bash
composer require laravel/telescope
php artisan telescope:install
php artisan migrate
Access Dashboard:
http://localhost:8000/telescope
Install Debug Bar:
bash
composer require barryvdh/laravel-debugbar --dev
Disable in Production:
env
DEBUGBAR_ENABLED=false
Log All Queries: ```php // In AppServiceProvider boot() use Illuminate\Support\Facades\DB;
DB::listen(function($query) { \Log::info( $query->sql, $query->bindings, $query->time ); }); ```
Using dd() in Controllers:
php
public function index()
{
$data = User::all();
dd($data); // Dump and die
}
Using ray() (Recommended):
bash
composer require spatie/laravel-ray
ray($user); // Outputs to Ray app
ray()->showQueries(); // Log all queries
Create Migration:
bash
php artisan make:migration add_new_right_to_rights_table
Define Migration:
php
public function up()
{
DB::table('rights')->insert([
'name' => 'ADMIN_REPORTS',
'description' => 'Manage reports module',
'module' => 'reports',
]);
}
Add to RightEnum:
php
// app/Enums/RightEnum.php
class RightEnum
{
const ADMIN_REPORTS = 'ADMIN_REPORTS';
// ... other rights
}
Check Permission in Controller:
php
if (!auth()->user()->hasRight(RightEnum::ADMIN_REPORTS)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
Example: Add custom fields to Task model
Add Trait to Model: ```php use App\Traits\HasCustomFields;
class Task extends Model { use HasCustomFields; } ```
Create Custom Field Definition (via API or seeder):
php
CustomField::create([
'instance_id' => 1,
'name' => 'priority_score',
'field_type' => 'number',
'entity_type' => 'task',
'is_required' => false,
]);
Set/Get Custom Field Values:
php
$task->setCustomFieldValue('priority_score', 95);
$score = $task->getCustomFieldValue('priority_score');
Add Searchable Trait: ```php use Laravel\Scout\Searchable;
class Task extends Model { use Searchable;
/**
* Get the indexable data array for the model.
*/
public function toSearchableArray()
{
return [
'model_id' => $this->id,
'model' => class_basename(static::class),
'name' => $this->title,
'description' => $this->description,
'access_ids' => [$this->user_id], // Permission filtering
];
}
/**
* Override search index name
*/
public function searchableAs()
{
return 'global_index';
}
} ```
Import Existing Data:
bash
php artisan scout:import "App\Models\Task"
Issue: “No application encryption key has been specified”
Solution:
bash
php artisan key:generate
Issue: “Class not found” errors
Solution:
bash
composer dump-autoload
php artisan clear-compiled
php artisan config:clear
Issue: “SQLSTATE[HY000] [2002] Connection refused”
Solution:
- Check MySQL is running: mysql -u root -p
- Verify .env database credentials
- Check DB_HOST (use 127.0.0.1 instead of localhost)
Issue: “419 Page Expired” on API requests
Solution:
- CSRF protection is disabled for API routes
- Ensure using Authorization: Bearer {token} header
- Verify token is valid: php artisan sanctum:purge-expired
Issue: Queue jobs not processing
Solution: ```bash
php artisan queue:restart
php artisan queue:work –verbose
php artisan queue:failed ```
Issue: Storage link broken after deployment
Solution:
bash
php artisan storage:link
Issue: Permission denied on storage directories
Solution:
bash
chmod -R 775 storage bootstrap/cache
chown -R www-data:www-data storage bootstrap/cache
Issue: Meilisearch connection refused
Solution: ```bash
brew services start meilisearch
docker start meilisearch
curl http://localhost:7700/health ```
Document Generated: 2025-10-16 Last Updated: 2025-10-28 (Added Development Standards & Best Practices) Maintained By: Development Team