laravel-qol maintained by bywyd
Laravel QoL
Laravel Quality of Life - A collection of helpful traits, utilities, and tools to enhance your Laravel development experience.
Features
Media Management
- HasImages Trait - Easy image management with ordering and tagging
- HasFiles Trait - Generic file upload and management
- HasVideos Trait - Video upload with metadata support
- PhotoImage Model - Complete image model with helper methods
- File Model - Flexible file handling with type detection
- Video Model - Video model with duration, resolution, and thumbnails
Model Enhancements
- HasHistory Trait - Automatic model change tracking
- HasRoles Trait - Complete role and permission system for users
- HasIntegrations Trait - Manage user integrations with OAuth, API keys, and credentials
- HasSettings Trait - Universal settings system (app-wide, per-user, per-model)
- HasUuid Trait - Automatic UUID generation for models
- HasSlug Trait - Automatic slug generation from any field
- HasStatus Trait - Status management with active/inactive scopes
- Sortable Trait - Easy ordering/sorting functionality
- Cacheable Trait - Built-in model-level caching
- Searchable Trait - Simple and full-text search capabilities
Authorization
- Role Model - Hierarchical role system with levels
- Permission Model - Granular permission control
- Middleware - Route protection with role, permission, or both
- Blade Directives - Template-level authorization checks
- Gates Integration - Automatic Laravel Gate registration
Installation
Install via Composer:
composer require bywyd/laravel-qol
Configuration
Publish the configuration file (optional):
php artisan vendor:publish --tag=laravel-qol-config
Publish the migrations:
php artisan vendor:publish --tag=laravel-qol-migrations
php artisan migrate
Usage
HasHistory Trait
Track all changes made to your models automatically:
use Bywyd\LaravelQol\Traits\HasHistory;
class Post extends Model
{
use HasHistory;
// Optional: Exclude specific attributes from history
protected $historyExcludedAttributes = ['views', 'updated_at'];
// Optional: Only log specific events
protected $historyEvents = ['created', 'updated'];
// Optional: Keep histories when model is deleted
protected $deleteHistoriesOnDelete = false;
}
// Usage
$post = Post::find(1);
$post->histories; // Get all history records
$post->latestHistory; // Get the latest history
// Manual history logging
$post->logHistory(HistoryLogTypes::CUSTOM, 'Custom action performed');
// Temporarily disable history logging
$post->withoutHistory(function($post) {
$post->update(['title' => 'No history logged']);
});
HasImages Trait
Manage images for your models with ease:
use Bywyd\LaravelQol\Traits\HasImages;
class Product extends Model
{
use HasImages;
}
// Usage
$product = Product::find(1);
// Upload an image
$image = $product->uploadImage($request->file('image'), 0, 'gallery');
// Get all images
$product->images;
// Get images by tag
$product->imagesByTag('gallery');
// Get primary image
$product->primaryImage();
// Reorder images
$product->reorderImages([3, 1, 2]); // Array of image IDs
// Delete an image
$product->deleteImage($image);
// Delete all images
$product->deleteAllImages();
HasFiles Trait
Upload and manage any type of file:
use Bywyd\LaravelQol\Traits\HasFiles;
class Document extends Model
{
use HasFiles;
}
// Usage
$document = Document::find(1);
// Upload a file
$file = $document->uploadFile($request->file('attachment'), 0, 'contract', [
'department' => 'Legal'
]);
// Get all files
$document->files;
// Get files by tag
$document->filesByTag('contract');
// Get document files (PDFs, DOCs, etc.)
$document->documents();
// Download a file
return $file->download();
// Delete a file
$document->deleteFile($file);
HasVideos Trait
Manage video uploads with metadata:
use Bywyd\LaravelQol\Traits\HasVideos;
class Course extends Model
{
use HasVideos;
}
// Usage
$course = Course::find(1);
// Upload a video
$video = $course->uploadVideo($request->file('video'), 0, 'lesson-1');
// Access video properties
$video->url; // Public URL
$video->human_size; // "50.5 MB"
$video->human_duration; // "5:23"
$video->aspect_ratio; // "16:9"
// Quality checks
$video->isHD(); // 720p or higher
$video->isFullHD(); // 1080p or higher
$video->is4K(); // 2160p or higher
// Get HD videos
$course->hdVideos();
// Delete a video
$course->deleteVideo($video);
HasUuid Trait
Automatically generate UUIDs for your models:
use Bywyd\LaravelQol\Traits\HasUuid;
class User extends Model
{
use HasUuid;
// Optional: Customize UUID column
protected $uuidColumn = 'uuid';
}
// Usage
$user = User::create(['name' => 'John']);
$user->uuid; // "550e8400-e29b-41d4-a716-446655440000"
// Find by UUID
$user = User::findByUuid('550e8400-e29b-41d4-a716-446655440000');
$user = User::findByUuidOrFail($uuid);
HasSlug Trait
Automatic slug generation from any field:
use Bywyd\LaravelQol\Traits\HasSlug;
class Article extends Model
{
use HasSlug;
// Optional: Customize slug source
protected $slugSource = 'title';
// Optional: Customize slug column
protected $slugColumn = 'slug';
// Optional: Prevent regeneration on update
protected $regenerateSlugOnUpdate = false;
}
// Usage
$article = Article::create(['title' => 'Hello World']);
$article->slug; // "hello-world"
// Find by slug
$article = Article::findBySlug('hello-world');
$article = Article::findBySlugOrFail('hello-world');
HasStatus Trait
Manage model status with convenient methods:
use Bywyd\LaravelQol\Traits\HasStatus;
class Task extends Model
{
use HasStatus;
// Optional: Customize status column
protected $statusColumn = 'status';
// Optional: Customize status values
protected $activeStatusValue = 1;
protected $inactiveStatusValue = 0;
}
// Usage
$task = Task::find(1);
// Status checks
$task->isActive();
$task->isInactive();
// Status changes
$task->activate();
$task->deactivate();
$task->toggleStatus();
// Query scopes
Task::active()->get();
Task::inactive()->get();
Task::status(1)->get();
Sortable Trait
Add ordering functionality to your models:
use Bywyd\LaravelQol\Traits\Sortable;
class MenuItem extends Model
{
use Sortable;
// Optional: Customize sort column
protected $sortColumn = 'order';
}
// Usage
$item = MenuItem::find(1);
// Move operations
$item->moveUp();
$item->moveDown();
$item->moveTo(5);
$item->swapWith($otherItem);
// Query scope
MenuItem::ordered()->get(); // Ordered by sort column
MenuItem::ordered('desc')->get();
Cacheable Trait
Built-in model caching:
use Bywyd\LaravelQol\Traits\Cacheable;
class Settings extends Model
{
use Cacheable;
// Optional: Customize cache prefix
protected $cachePrefix = 'settings';
// Optional: Customize TTL (seconds)
protected $cacheTtl = 3600;
}
// Usage
$settings = Settings::find(1);
// Cache data
$value = $settings->remember('config', function() {
return expensive_operation();
});
// Cache forever
$value = $settings->rememberForever('permanent', function() {
return static_data();
});
// Clear cache
$settings->clearCache();
Searchable Trait
Add search functionality:
use Bywyd\LaravelQol\Traits\Searchable;
class Product extends Model
{
use Searchable;
// Define searchable columns
protected $searchable = ['name', 'description', 'category.name'];
}
// Usage
// Simple search
Product::search('laptop')->get();
// Custom columns
Product::search('laptop', ['name', 'sku'])->get();
// Full-text search (MySQL)
Product::fullTextSearch('gaming laptop')->get();
HasRoles Trait
Complete role and permission system for User models:
use Bywyd\LaravelQol\Traits\HasRoles;
class User extends Authenticatable
{
use HasRoles;
}
// Assign roles
$user->assignRole('admin');
$user->assignRole(['editor', 'moderator']);
// Remove roles
$user->removeRole('editor');
// Sync roles (removes all existing roles and assigns new ones)
$user->syncRoles(['admin', 'super-admin']);
// Check roles
$user->hasRole('admin'); // true
$user->hasAnyRole(['admin', 'editor']); // true if user has any
$user->hasAllRoles(['admin', 'editor']); // true if user has all
// Give direct permissions
$user->givePermission('edit-posts');
$user->givePermission(['edit-posts', 'delete-posts']);
// Revoke permissions
$user->revokePermission('delete-posts');
// Sync permissions
$user->syncPermissions(['edit-posts', 'view-posts']);
// Check permissions
$user->hasPermission('edit-posts'); // true
$user->hasAnyPermission(['edit-posts', 'delete-posts']); // true if has any
$user->hasAllPermissions(['edit-posts', 'view-posts']); // true if has all
// Get all permissions (direct + from roles)
$user->getAllPermissions();
// Check super admin
$user->isSuperAdmin(); // true if has super-admin role or * permission
// Query scopes
User::role('admin')->get();
User::role(['admin', 'editor'])->get();
User::permission('edit-posts')->get();
User::permission(['edit-posts', 'delete-posts'])->get();
Role Model
Manage roles with hierarchical levels:
use Bywyd\LaravelQol\Models\Role;
// Create a role
$role = Role::create([
'name' => 'Administrator',
'slug' => 'admin',
'description' => 'Full access to the system',
'level' => 100, // Higher = more privileges
'is_default' => false,
]);
// Assign permissions to role
$role->givePermission('edit-posts');
$role->givePermission(['delete-posts', 'manage-users']);
// Revoke permissions
$role->revokePermission('delete-posts');
// Sync permissions
$role->syncPermissions(['edit-posts', 'view-posts']);
// Check if role has permission
$role->hasPermission('edit-posts'); // true
// Check if super admin
$role->isSuperAdmin(); // true if has * permission
// Get users with this role
$role->users;
// Query scopes
Role::default()->first(); // Get default role
Role::byLevel()->get(); // Order by level
Role::byLevel('desc')->get();
Permission Model
Create and manage permissions:
use Bywyd\LaravelQol\Models\Permission;
// Create a permission
$permission = Permission::create([
'name' => 'Edit Posts',
'slug' => 'edit-posts',
'description' => 'Can create and edit posts',
'group' => 'posts', // Group related permissions
]);
// Get permissions by group
Permission::byGroup('posts')->get();
// Get all permissions grouped
$grouped = Permission::getAllGrouped();
// Returns: ['posts' => [...], 'users' => [...]]
// Wildcard permission (grants all permissions)
Permission::create([
'name' => 'All Permissions',
'slug' => '*',
'description' => 'Super admin permission',
]);
Route Protection with Middleware
Protect routes using middleware:
// In your routes file
Route::middleware(['role:admin'])->group(function () {
Route::get('/admin/dashboard', [AdminController::class, 'index']);
});
// Multiple roles (OR condition)
Route::middleware(['role:admin|editor'])->group(function () {
Route::get('/posts/create', [PostController::class, 'create']);
});
// Permission middleware
Route::middleware(['permission:edit-posts'])->group(function () {
Route::put('/posts/{post}', [PostController::class, 'update']);
});
// Multiple permissions (OR condition)
Route::middleware(['permission:edit-posts|delete-posts'])->group(function () {
Route::get('/posts/manage', [PostController::class, 'manage']);
});
// Role OR Permission (if user has either)
Route::middleware(['role_or_permission:admin|edit-posts'])->group(function () {
Route::post('/posts', [PostController::class, 'store']);
});
Blade Directives
Use in your Blade templates:
{{-- Check single role --}}
@role('admin')
<a href="/admin">Admin Panel</a>
@endrole
{{-- Alternative syntax --}}
@hasrole('admin')
<p>You are an admin</p>
@endhasrole
{{-- Check any role --}}
@hasanyrole(['admin', 'editor'])
<button>Edit Content</button>
@endhasanyrole
{{-- Check all roles --}}
@hasallroles(['admin', 'super-admin'])
<button>Critical Action</button>
@endhasallroles
{{-- Check permission --}}
@permission('edit-posts')
<a href="/posts/create">Create Post</a>
@endpermission
{{-- Alternative syntax --}}
@haspermission('delete-posts')
<button class="btn-danger">Delete</button>
@endhaspermission
{{-- Check any permission --}}
@hasanypermission(['edit-posts', 'delete-posts'])
<div>Post Management</div>
@endhasanypermission
{{-- Check all permissions --}}
@hasallpermissions(['edit-posts', 'publish-posts'])
<button>Publish</button>
@endhasallpermissions
{{-- Using @else --}}
@role('admin')
<p>Admin content</p>
@else
<p>Regular user content</p>
@endrole
Laravel Gates
Permissions are automatically registered as Gates:
// In your controller or anywhere
if (Gate::allows('edit-posts')) {
// User can edit posts
}
if (Gate::denies('delete-posts')) {
// User cannot delete posts
}
// Using authorize
$this->authorize('edit-posts');
// In routes
Route::get('/posts/{post}/edit', [PostController::class, 'edit'])
->can('edit-posts');
Policy Integration
Use with Laravel Policies:
// In your Policy
public function update(User $user, Post $post)
{
return $user->hasPermission('edit-posts') || $user->id === $post->user_id;
}
public function delete(User $user, Post $post)
{
return $user->hasPermission('delete-posts') ||
$user->hasRole('admin');
}
Creating a Complete Authorization System
// 1. Create permissions
$permissions = [
['name' => 'View Posts', 'slug' => 'view-posts', 'group' => 'posts'],
['name' => 'Create Posts', 'slug' => 'create-posts', 'group' => 'posts'],
['name' => 'Edit Posts', 'slug' => 'edit-posts', 'group' => 'posts'],
['name' => 'Delete Posts', 'slug' => 'delete-posts', 'group' => 'posts'],
['name' => 'Manage Users', 'slug' => 'manage-users', 'group' => 'users'],
];
foreach ($permissions as $permission) {
Permission::create($permission);
}
// 2. Create roles
$superAdmin = Role::create([
'name' => 'Super Admin',
'slug' => 'super-admin',
'level' => 100,
]);
$superAdmin->givePermission('*'); // All permissions
$admin = Role::create([
'name' => 'Admin',
'slug' => 'admin',
'level' => 50,
]);
$admin->givePermission(['view-posts', 'create-posts', 'edit-posts', 'manage-users']);
$editor = Role::create([
'name' => 'Editor',
'slug' => 'editor',
'level' => 25,
]);
$editor->givePermission(['view-posts', 'create-posts', 'edit-posts']);
$user = Role::create([
'name' => 'User',
'slug' => 'user',
'level' => 1,
'is_default' => true,
]);
$user->givePermission('view-posts');
// 3. Assign to users
$user = User::find(1);
$user->assignRole('super-admin');
HasIntegrations Trait
Manage user integrations with third-party services:
use Bywyd\LaravelQol\Traits\HasIntegrations;
class User extends Authenticatable
{
use HasIntegrations;
}
// Create OAuth integration (e.g., Google, GitHub, Facebook)
$user->createOAuthIntegration('google', [
'provider_id' => '123456789',
'provider_name' => 'Google',
'access_token' => 'ya29.a0AfH6SMC...',
'refresh_token' => '1//0gOZp...',
'expires_in' => 3600, // seconds
'metadata' => [
'email' => 'user@gmail.com',
'name' => 'John Doe',
'avatar' => 'https://...',
],
]);
// Create API Key integration (e.g., Stripe, AWS, SendGrid)
$user->createApiKeyIntegration('stripe', [
'provider_name' => 'Stripe',
'api_key' => 'sk_test_51H...',
'api_secret' => 'whsec_...',
'metadata' => [
'account_id' => 'acct_123',
'mode' => 'test',
],
]);
// Create custom integration
$user->createIntegration('webhook', 'webhook', [
'provider_name' => 'My Webhook Service',
'credentials' => [
'url' => 'https://api.example.com/webhook',
'secret' => 'webhook_secret_123',
],
]);
// Check if user has integration
if ($user->hasIntegration('google')) {
// User has Google integration
}
// Check if integration is active
if ($user->hasActiveIntegration('stripe')) {
// Stripe integration is active
}
// Get integration
$integration = $user->getIntegration('google');
// Access decrypted credentials
$accessToken = $user->getIntegrationAccessToken('google');
$apiKey = $user->getIntegrationApiKey('stripe');
$apiSecret = $user->getIntegrationApiSecret('stripe');
// Check token validity
$integration = $user->getIntegration('google');
if ($integration->hasValidToken()) {
// Token exists and not expired
}
// Activate/Deactivate integration
$user->activateIntegration('google');
$user->deactivateIntegration('stripe');
// Remove integration
$user->removeIntegration('github');
// Update metadata
$user->updateIntegrationMetadata('google', [
'last_sync' => now(),
'sync_count' => 5,
]);
// Mark as used (updates last_used_at)
$user->markIntegrationAsUsed('stripe');
// Get all integrations
$user->integrations; // All integrations
$user->activeIntegrations; // Only active
// Filter by type
$user->oauthIntegrations(); // OAuth only
$user->apiKeyIntegrations(); // API keys only
$user->validIntegrations(); // Valid tokens only
// Query scopes
UserIntegration::provider('google')->get();
UserIntegration::type('oauth')->get();
UserIntegration::active()->get();
UserIntegration::validToken()->get();
UserIntegration Model
Direct model usage:
use Bywyd\LaravelQol\Models\UserIntegration;
$integration = UserIntegration::find(1);
// Encrypted credential methods
$integration->setAccessToken('new_token');
$integration->setRefreshToken('new_refresh');
$integration->setApiKey('sk_test_123');
$integration->setApiSecret('secret_456');
$integration->save();
// Decrypted access
$token = $integration->getDecryptedAccessToken();
$refresh = $integration->getDecryptedRefreshToken();
$apiKey = $integration->getDecryptedApiKey();
$apiSecret = $integration->getDecryptedApiSecret();
// Status checks
$integration->isTokenExpired(); // Check if token expired
$integration->hasValidToken(); // Token exists and not expired
// Actions
$integration->activate();
$integration->deactivate();
$integration->markAsUsed();
// Relationships
$integration->user; // Get the user
Common Integration Examples
// Google OAuth
$user->createOAuthIntegration('google', [
'provider_id' => $googleUser->id,
'provider_name' => 'Google',
'access_token' => $googleUser->token,
'refresh_token' => $googleUser->refreshToken,
'expires_in' => $googleUser->expiresIn,
'metadata' => [
'email' => $googleUser->email,
'name' => $googleUser->name,
],
]);
// GitHub OAuth
$user->createOAuthIntegration('github', [
'provider_id' => $githubUser->id,
'provider_name' => 'GitHub',
'access_token' => $githubUser->token,
'metadata' => [
'username' => $githubUser->nickname,
'repos_url' => $githubUser->user['repos_url'],
],
]);
// Stripe
$user->createApiKeyIntegration('stripe', [
'provider_name' => 'Stripe',
'api_key' => config('services.stripe.secret'),
'metadata' => [
'customer_id' => $stripeCustomer->id,
'mode' => 'live',
],
]);
// AWS
$user->createApiKeyIntegration('aws', [
'provider_name' => 'AWS',
'api_key' => $credentials['access_key_id'],
'api_secret' => $credentials['secret_access_key'],
'credentials' => [
'region' => 'us-east-1',
'bucket' => 'my-bucket',
],
]);
// SendGrid
$user->createApiKeyIntegration('sendgrid', [
'provider_name' => 'SendGrid',
'api_key' => $sendgridApiKey,
'metadata' => [
'from_email' => 'noreply@example.com',
'from_name' => 'My App',
],
]);
// Slack Webhook
$user->createIntegration('slack', 'webhook', [
'provider_name' => 'Slack',
'credentials' => [
'webhook_url' => 'https://hooks.slack.com/services/...',
'channel' => '#general',
],
]);
Security Features
All sensitive data is automatically encrypted:
- Access tokens
- Refresh tokens
- API keys
- API secrets
- Custom credentials
The trait uses Laravel's built-in encryption, ensuring data is secure at rest.
Middleware
The package includes 10 production-ready middleware:
SetLocale Middleware
Automatically sets application locale based on multiple sources (priority order):
// In your routes or middleware group
Route::middleware('locale')->group(function () {
// Your routes
});
// Supports:
// 1. Query parameter: ?locale=es
// 2. Session: session('locale')
// 3. User preference: $user->getPreferredLocale()
// 4. Cookie: locale=fr
// 5. Accept-Language header
RestrictAccess Middleware
Maintenance mode with granular access control:
// Enable in .env
ACCESS_RESTRICTION_ENABLED=true
ALLOWED_IPS="192.168.1.1,10.0.0.0/24,172.16.*.*"
BYPASS_TOKEN=secret-token-123
// Apply to routes
Route::middleware('restrict.access')->group(function () {
// Protected routes
});
// Access with bypass token
// ?bypass_token=secret-token-123
// Header: X-Bypass-Token: secret-token-123
ForceJsonResponse Middleware
Force JSON responses for API applications:
Route::middleware('force.json')->group(function () {
// All responses will be JSON
});
LogRequestResponse Middleware
Log all HTTP requests and responses:
Route::middleware('log.request')->group(function () {
// Requests/responses logged
});
// Configure in config/laravel-qol.php
'logging' => [
'log_requests' => true,
'log_responses' => true,
'log_request_body' => false,
'log_response_body' => false,
'sensitive_keys' => ['password', 'token'],
],
SecurityHeaders Middleware
Add security headers automatically:
Route::middleware('security.headers')->group(function () {
// Security headers added
});
// Adds: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection,
// HSTS, CSP, Referrer-Policy, Permissions-Policy
RateLimitByUser Middleware
Rate limiting per user or IP:
// 60 requests per minute
Route::middleware('rate.limit.user:60,1')->group(function () {
//
});
// 100 requests per 5 minutes
Route::middleware('rate.limit.user:100,5')->group(function () {
//
});
ConvertEmptyStringsToNull Middleware
Convert empty strings to null in requests:
Route::middleware('convert.empty.strings')->group(function () {
// '' becomes null
});
TrimStrings Middleware
Automatically trim string inputs:
Route::middleware('trim.strings')->group(function () {
// All strings trimmed (except passwords)
});
ApiVersioning Middleware
API versioning support:
// Require specific version
Route::middleware('api.version:v1')->group(function () {
//
});
// Accept any supported version
Route::middleware('api.version')->group(function () {
$version = request()->attributes->get('api_version');
});
// Version sources:
// - Header: Accept: application/vnd.api.v1+json
// - Header: X-API-Version: v1
// - Query: ?version=v1
// - URL: /api/v1/users
CorsMiddleware
Advanced CORS handling:
Route::middleware('cors')->group(function () {
//
});
// Configure in config/laravel-qol.php
'cors' => [
'allowed_origins' => ['*'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE'],
'allowed_headers' => ['Content-Type', 'Authorization'],
'allow_credentials' => false,
],
Universal Settings System
Flexible settings system supporting app-wide, per-user, and per-model settings.
App-Wide Settings
Use the Settings facade for application-level settings:
use Bywyd\LaravelQol\Facades\Settings;
// Set settings
Settings::set('site_name', 'My Application');
Settings::set('items_per_page', 25);
Settings::set('maintenance_mode', false);
Settings::set('features', ['api', 'webhooks', 'exports']);
// Organize by groups
Settings::set('smtp_host', 'smtp.gmail.com', 'email');
Settings::set('smtp_port', 587, 'email');
Settings::set('theme_color', '#FF5733', 'appearance');
// Get settings
$siteName = Settings::get('site_name');
$perPage = Settings::get('items_per_page', 10); // with default
// Get all settings in a group
$emailSettings = Settings::getGroup('email');
// ['smtp_host' => 'smtp.gmail.com', 'smtp_port' => 587]
// Check if exists
if (Settings::has('api_key')) {
//
}
// Remove setting
Settings::remove('old_setting');
// Set multiple at once
Settings::setMultiple([
'key1' => 'value1',
'key2' => 'value2',
], 'group_name');
// Increment/Decrement numeric values
Settings::increment('page_views');
Settings::decrement('credits', 5);
// Toggle boolean values
Settings::toggle('feature_enabled');
// Store with metadata
Settings::set('api_key', 'secret', 'api', true, [
'description' => 'Third-party API key',
'editable' => false,
]);
Per-User Settings
Add the HasSettings trait to your User model:
use Bywyd\LaravelQol\Traits\HasSettings;
class User extends Authenticatable
{
use HasSettings;
}
// Set user preferences
$user->setSetting('theme', 'dark');
$user->setSetting('language', 'es');
$user->setSetting('notifications_enabled', true);
$user->setSetting('email_frequency', 'daily', 'notifications');
// Get user settings
$theme = $user->getSetting('theme', 'light'); // with default
$language = $user->getSetting('language');
// Organize by groups
$user->setSetting('push_enabled', true, 'notifications');
$user->setSetting('email_enabled', false, 'notifications');
$notificationSettings = $user->getSettingsGroup('notifications');
// Get all user settings
$allSettings = $user->getAllSettings();
// ['general.theme' => 'dark', 'general.language' => 'es', ...]
// Public settings (visible to others)
$user->setSetting('profile_visibility', 'public', 'privacy', true);
$publicSettings = $user->getAllSettings(true); // only public
// Batch operations
$user->setSettings([
'theme' => 'dark',
'font_size' => 'medium',
'compact_mode' => true,
], 'appearance');
// Clear settings
$user->clearSettings(); // all
$user->clearSettings('notifications'); // specific group
// Numeric operations
$user->incrementSetting('posts_count');
$user->decrementSetting('credits', 10);
// Boolean operations
$user->toggleSetting('notifications_enabled');
Per-Model Settings
Add the HasSettings trait to any model:
use Bywyd\LaravelQol\Traits\HasSettings;
class Post extends Model
{
use HasSettings;
}
$post = Post::find(1);
// Model-specific settings
$post->setSetting('featured', true);
$post->setSetting('visibility', 'public');
$post->setSetting('allow_comments', true);
$post->setSetting('views_count', 0, 'analytics');
// Get settings
$featured = $post->getSetting('featured', false);
$visibility = $post->getSetting('visibility');
// Analytics example
$post->incrementSetting('views_count', 1, 'analytics');
$post->setSetting('last_viewed_at', now(), 'analytics');
$analytics = $post->getSettingsGroup('analytics');
// ['views_count' => 150, 'last_viewed_at' => '2024-01-01 12:00:00']
Settings Features
Type Support:
- String
- Integer
- Float
- Boolean
- Array
- JSON
Automatic Caching:
- Settings are cached automatically
- Cache cleared on update/delete
- Configurable TTL
Organization:
- Group settings logically
- Isolate by model instance
- Public vs private settings
Metadata:
- Store additional info about settings
- Descriptions, editability flags, etc.
Real-World Examples
// E-commerce: Product settings
$product->setSetting('on_sale', true);
$product->setSetting('discount_percentage', 20);
$product->setSetting('stock_alert_threshold', 5, 'inventory');
// Blog: Post settings
$post->setSetting('featured', true);
$post->setSetting('allow_comments', false);
$post->setSetting('publish_at', '2024-12-25 00:00:00', 'scheduling');
// SaaS: Organization settings
$organization->setSetting('max_users', 50, 'limits');
$organization->setSetting('api_enabled', true, 'features');
$organization->setSetting('webhook_url', 'https://...', 'integrations');
// Multi-tenant: Tenant customization
$tenant->setSetting('primary_color', '#FF5733', 'branding');
$tenant->setSetting('logo_url', 'https://...', 'branding');
$tenant->setSetting('custom_domain', 'tenant.example.com', 'domain');
// User preferences dashboard
$preferences = $user->getSettingsGroup('preferences');
foreach ($preferences as $key => $value) {
echo "{$key}: {$value}";
}
CommonScopes Trait
Add powerful query scopes to your models:
use Bywyd\LaravelQol\Traits\CommonScopes;
class Product extends Model
{
use CommonScopes;
}
// Usage examples:
Product::active()->get();
Product::recent(7)->get(); // Last 7 days
Product::thisMonth()->get();
Product::popular('views_count', 100)->get();
Product::published()->get();
Product::whereLike('search term', ['name', 'description'])->get();
Product::smartPaginate(20); // Auto-handles per_page from request
Available scopes: active(), inactive(), recent(), older(), today(), thisWeek(), thisMonth(), thisYear(), betweenDates(), latest(), oldest(), whereIds(), whereNotIds(), whereLike(), whereEmpty(), whereNotEmpty(), random(), popular(), featured(), published(), draft(), smartPaginate()
ApiResponse Trait
Standardized API responses for controllers:
use Bywyd\LaravelQol\Traits\ApiResponse;
class UserController extends Controller
{
use ApiResponse;
public function index()
{
$users = User::paginate();
return $this->paginated($users, 'Users retrieved successfully');
}
public function store(Request $request)
{
$user = User::create($request->validated());
return $this->created($user, 'User created successfully');
}
public function show(User $user)
{
return $this->success($user);
}
public function update(Request $request, User $user)
{
$user->update($request->validated());
return $this->updated($user);
}
public function destroy(User $user)
{
$user->delete();
return $this->deleted();
}
}
Available methods: success(), error(), created(), updated(), deleted(), notFound(), unauthorized(), forbidden(), validationError(), serverError(), paginated(), noContent()
Request Macros
Enhanced request handling:
// Check if any of the keys exist
if (request()->hasAny(['email', 'username'])) {
// ...
}
// Check if all keys exist
if (request()->hasAll(['name', 'email', 'password'])) {
// ...
}
// Get boolean value (handles 'true', '1', 'yes', 'on')
$active = request()->boolean('is_active', false);
// Get array of IDs from comma-separated or array
$ids = request()->ids('user_ids'); // "1,2,3" or [1,2,3] => [1,2,3]
// Get sanitized search term
$search = request()->search('q');
// Get real IP (considering proxies, Cloudflare, etc.)
$ip = request()->realIp();
// Check if mobile device
if (request()->isMobile()) {
// ...
}
// Get sort parameters
$sort = request()->sort('created_at', 'desc');
// Returns: ['column' => 'created_at', 'direction' => 'desc']
// Get filters (removes empty values)
$filters = request()->filters(['status', 'category', 'price_min']);
Collection Macros
Extended collection functionality:
// Recursively convert to array
$data = collect($nested)->recursive()->toArray();
// Group by multiple keys
$grouped = $users->groupByMultiple(['country', 'city']);
// Export to CSV
$csv = $users->toCsv(['ID', 'Name', 'Email']);
// Check for duplicates
if ($items->hasDuplicates('email')) {
// ...
}
// Transpose (rows to columns)
$transposed = collect([[1, 2], [3, 4]])->transpose();
// Result: [[1, 3], [2, 4]]
// Get statistics
$stats = $numbers->stats();
// Returns: ['count', 'sum', 'avg', 'min', 'max', 'median']
// Filter null/empty values
$filtered = $collection->filterNull()->filterEmpty();
// Manual pagination
$paginated = $collection->paginate(15);
Validation Rules
Custom validation rules:
use Bywyd\LaravelQol\Rules\PhoneNumber;
use Bywyd\LaravelQol\Rules\StrongPassword;
use Bywyd\LaravelQol\Rules\Username;
// Phone number validation
'phone' => ['required', new PhoneNumber()],
// Strong password validation
'password' => [
'required',
new StrongPassword(
minLength: 8,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecialChars: true
)
],
// Username validation
'username' => [
'required',
new Username(
minLength: 3,
maxLength: 20,
allowDash: true,
allowUnderscore: true,
allowDot: false
)
],
Helper Functions
Global utility functions:
// Limit words in string
$excerpt = str_limit_words($text, 10);
// Simple money formatting
$price = money_format_simple(1234.56, '$', 2); // "$1,234.56"
// Calculate percentage
$percent = percentage(25, 100); // 25.00
// Filter array recursively
$filtered = array_filter_recursive($array);
// Sanitize filename
$safe = sanitize_filename('My File (1).pdf'); // "My_File_1.pdf"
// Generate random string
$token = generate_random_string(32); // Alphanumeric
$password = generate_random_string(16, false); // With special chars
// Convert bytes to human readable
$size = bytes_to_human(1048576); // "1.00 MB"
// Convert human readable to bytes
$bytes = human_to_bytes('10MB'); // 10485760
// Check if string is valid JSON
if (is_json($string)) {
// ...
}
// Safe Carbon parsing
$date = carbon_parse_safe($input, now());
// Active route helper (for navigation)
<li class="{{ active_route(['users.*', 'profile']) }}">Users</li>
// Get client browser
$browser = get_client_browser(); // "Chrome", "Firefox", etc.
// Truncate middle of string
$truncated = truncate_middle('very-long-filename.txt', 20); // "very-long...ame.txt"
Database Utilities
QueryLogger
Log and analyze database queries:
use Bywyd\LaravelQol\Utilities\QueryLogger;
// Enable query logging
QueryLogger::enable();
// Your code here...
User::all();
Post::with('comments')->get();
// Get all queries
$queries = QueryLogger::getQueries();
// Get total execution time
$time = QueryLogger::getTotalTime(); // in milliseconds
// Get query count
$count = QueryLogger::getCount();
// Get slowest queries
$slow = QueryLogger::getSlowestQueries(10);
// Log to file
QueryLogger::logToFile('database');
// Dump for debugging
QueryLogger::dump();
// Clear logged queries
QueryLogger::clear();
// Disable logging
QueryLogger::disable();
ModelUtility
Useful model introspection methods:
use Bywyd\LaravelQol\Utilities\ModelUtility;
// Get table columns
$columns = ModelUtility::getTableColumns(User::class);
// Get fillable columns
$fillable = ModelUtility::getFillableColumns($user);
// Get hidden columns
$hidden = ModelUtility::getHiddenColumns($user);
// Get dirty (changed but not saved) attributes
$dirty = ModelUtility::getDirtyAttributes($user);
// Get changed attributes after save
$changes = ModelUtility::getChangedAttributes($user);
// Check if attribute exists
if (ModelUtility::hasAttribute($user, 'email')) {
// ...
}
// Clone model
$clone = ModelUtility::cloneModel($user, ['id', 'created_at']);
// Get loaded relations
$relations = ModelUtility::getLoadedRelations($user);
// Check if relation is loaded
if (ModelUtility::isRelationLoaded($user, 'posts')) {
// ...
}
// Diff two models
$diff = ModelUtility::diff($originalUser, $modifiedUser);
// Returns: ['email' => ['old' => 'old@example.com', 'new' => 'new@example.com']]
Usage Examples
Example 1: API Controller with All Features
use App\Models\Product;
use Bywyd\LaravelQol\Traits\ApiResponse;
use Bywyd\LaravelQol\Rules\StrongPassword;
use Illuminate\Http\Request;
class ProductController extends Controller
{
use ApiResponse;
public function index(Request $request)
{
$query = Product::query();
// Use CommonScopes
if ($request->boolean('active_only')) {
$query->active();
}
if ($search = $request->search('q')) {
$query->whereLike($search, ['name', 'description']);
}
// Use Request macros for sorting
$sort = $request->sort('created_at', 'desc');
$query->orderBy($sort['column'], $sort['direction']);
// Smart pagination
$products = $query->smartPaginate();
return $this->paginated($products, 'Products retrieved successfully');
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'price' => 'required|numeric|min:0',
'is_active' => 'boolean',
]);
$product = Product::create($validated);
return $this->created($product, 'Product created successfully');
}
}
Example 2: Using Utilities for Performance Monitoring
use Bywyd\LaravelQol\Utilities\QueryLogger;
// In a middleware or service provider
if (app()->environment('local')) {
QueryLogger::enable();
app()->terminating(function () {
if (QueryLogger::getCount() > 50) {
logger()->warning('High query count detected', [
'total_queries' => QueryLogger::getCount(),
'total_time' => QueryLogger::getTotalTime(),
'slowest' => QueryLogger::getSlowestQueries(5),
]);
}
});
}
Example 3: Model with All Traits
use Illuminate\Database\Eloquent\Model;
use Bywyd\LaravelQol\Traits\{
HasHistory,
HasRoles,
HasSettings,
HasUuid,
HasSlug,
HasStatus,
Sortable,
Cacheable,
Searchable,
CommonScopes
};
class Article extends Model
{
use HasHistory,
HasRoles,
HasSettings,
HasUuid,
HasSlug,
HasStatus,
Sortable,
Cacheable,
Searchable,
CommonScopes;
protected $fillable = ['title', 'content', 'status'];
protected $slugSource = 'title';
protected $searchable = ['title', 'content'];
// Now you have access to all features:
// - History tracking
// - Role-based permissions
// - Per-model settings
// - UUID primary key
// - Auto-generated slugs
// - Status management
// - Sortable ordering
// - Model caching
// - Full-text search
// - Query scopes
}
// Usage
$article = Article::create(['title' => 'My Article', 'content' => '...']);
$article->logHistory(HistoryLogTypes::CUSTOM, 'Published');
$article->setSetting('views_count', 0);
$articles = Article::active()->published()->thisMonth()->get();
Available Traits
Media Traits
- HasImages - Image management with ordering, tagging, and URLs
- HasFiles - Generic file management with type detection
- HasVideos - Video management with metadata and thumbnails
Model Enhancement Traits
- HasHistory - Automatic change tracking with old/new values
- HasRoles - Complete role & permission system with middleware and Blade directives
- HasIntegrations - OAuth, API keys, and third-party service credentials
- HasUuid - Auto-generate UUIDs on model creation
- HasSlug - Auto-generate unique slugs from any field
- HasStatus - Active/inactive status management
- Sortable - Ordering and reordering functionality
- Cacheable - Model-level caching with auto-invalidation
- Searchable - Simple and full-text search
Authorization System
- Role Model - Hierarchical roles with level-based access
- Permission Model - Granular permission control with groups
- Route Middleware -
role,permission,role_or_permission - Blade Directives -
@role,@permission,@hasanyrole, etc. - Laravel Gates - Auto-registered from permissions
- Super Admin - Wildcard permission support
Requirements
- PHP 8.1 or higher
- Laravel 10.0 or higher
License
The MIT License (MIT). Please see License File for more information.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.