twenycode / laravel-blueprint
Core components and utilities for Laravel applications
Installs: 66
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/twenycode/laravel-blueprint
Requires
- php: ^8.2
- jenssegers/agent: ^2.6
- rap2hpoutre/laravel-log-viewer: ^2.4
- rappasoft/laravel-authentication-log: ^5.0
- realrashid/sweet-alert: ^7.3
- spatie/laravel-activitylog: ^4.10
- spatie/laravel-permission: ^6.21
- twenycode/blade-morph: ^1.4
- vinkla/hashids: ^13.0
Requires (Dev)
- barryvdh/laravel-debugbar: ^3.15
- beyondcode/laravel-query-detector: ^2.1
- mockery/mockery: ^1.6
- phpunit/phpunit: ^11.0
- dev-main
- v2.x-dev
- v2.1.0
- v2.0.12
- v2.0.11
- v2.0.10
- v2.0.9
- v2.0.8
- v2.0.7
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v1.x-dev
- v1.5.2
- v1.5.1
- v1.5.0
- v1.4.3
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.0
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.0
- v1.0.16
- v1.0.15
- v1.0.14
- v1.0.13
- v1.0.12
- v1.0.11
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
This package is auto-updated.
Last update: 2025-11-05 23:40:55 UTC
README
A comprehensive architecture and utilities package for Laravel applications that provides a standardized structure, core components, and common patterns to accelerate development.
Table of Contents
- Introduction
- Features
- Requirements
- Installation
- Configuration
- Architecture Overview
- ID Management Approaches
- Quick Start Guide
- Core Components
- Helper Functions
- Caching System
- SweetAlert Integration
- Blade Morph Integration
- Testing
- Migration Guide
- Best Practices
- API Documentation
- Troubleshooting
- Contributing
- License
- Support
Introduction
Laravel Blueprint provides a solid foundation for building Laravel applications with a clean architecture, standardized patterns, and reusable components. It implements the repository pattern, service layer, and includes numerous utilities and base classes to streamline your development process.
Why Laravel Blueprint?
- Standardized Architecture: Follow proven patterns out of the box
- Time-Saving: Pre-built components for common tasks
- Flexible: Choose between hashed IDs or UUIDs
- Performance: Built-in intelligent caching system
- Maintainable: Clear separation of concerns
- Well-Tested: Comprehensive error handling and logging
Features
🏗️ Architecture Components
- Repository Pattern - Comprehensive data access layer with built-in caching support
- Service Layer - Business logic abstraction with database transaction management
- Resource Controllers - Base controllers with CRUD operations, error handling, and flash messaging
- Form Request Validation - Permission-based request validation with custom rules
🔧 Model & Database
- Enhanced Models - Feature-rich base models with common attributes and methods
- Dual ID Support - Choose between hashed integer IDs or native Laravel UUIDs
- Soft Delete Support - Ready-to-use methods for handling soft deletes
- Activity Logging - Integrated Spatie Activity Log for audit trails
- Migration Helpers - Standardized migration patterns for both ID types
🚀 Performance & Caching
- Intelligent Caching - Automatic cache management with model observers
- User-Specific Caching - Context-aware caching for multi-tenant applications
- Cache Invalidation - Automatic cache clearing on model changes
- Tag-Based Cache - Efficient cache management with Redis/Memcached
🎨 UI & UX
- Flash Messaging - Integrated SweetAlert for beautiful notifications
- Error Handling - Standardized error handling across all components
- API Support - JSON response handling for API controllers
- Bootstrap Pagination - Pre-configured pagination styling
- Blade Morph - Smooth UI transitions and morphing effects
🛠️ Utilities & Helpers
- Helper Functions - Extensive utility functions for dates, text, and numbers
- Custom Validation Rules - Composite key uniqueness validation
- Text Processing - Advanced text manipulation and formatting functions
- File Size Formatting - Human-readable file size conversion
Requirements
- PHP: 8.2 or higher
- Laravel: 10.0, 11.0, or 12.0
- Cache Driver: Redis or Memcached (for tag-based cache invalidation)
- Dependencies:
- vinkla/hashids: ^13.0
- realrashid/sweet-alert: ^7.3
- twenycode/blade-morph: ^1.3
- spatie/laravel-activitylog: ^4.10
- spatie/laravel-permission: ^6.21
- rap2hpoutre/laravel-log-viewer: ^2.4
- rappasoft/laravel-authentication-log: ^4.0
Installation
Step 1: Install the Package
Install the package via Composer:
composer require twenycode/laravel-blueprint
Step 2: Publish Configuration
Publish the configuration files:
php artisan vendor:publish --provider="TwenyCode\LaravelBlueprint\TwenyLaravelBlueprintServiceProvider" --tag="tcb-config"
This publishes:
config/tweny-blueprint.php- Main configuration settings
Step 3: Configure Cache Driver
For optimal performance, ensure you're using Redis or Memcached:
CACHE_DRIVER=redis
Step 4: Install Dependencies (Optional)
If you want to use all features, install the optional dependencies:
# For activity logging php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations" php artisan migrate # For permissions php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" php artisan migrate # For SweetAlert php artisan vendor:publish --provider="RealRashid\SweetAlert\SweetAlertServiceProvider" # For Blade Morph (smooth UI transitions) php artisan vendor:publish --provider="TwenyCode\BladeMorph\BladeMorphServiceProvider" # For Log Viewer # Access logs at /log-viewer route # For Authentication Logging php artisan vendor:publish --provider="Rappasoft\LaravelAuthenticationLog\LaravelAuthenticationLogServiceProvider" php artisan migrate
Configuration
Basic Configuration
Edit config/tweny-blueprint.php:
return [ // Cache Configuration 'cache' => [ 'enabled' => env('BLUEPRINT_CACHE_ENABLED', true), 'ttl' => env('BLUEPRINT_CACHE_TTL', 3600), // 1 hour 'driver' => env('CACHE_DRIVER', 'redis'), ], // Model Observers 'observers' => [ 'enabled' => env('BLUEPRINT_OBSERVERS_ENABLED', true), 'models' => [ App\Models\User::class, App\Models\Post::class, // Add your models here ], ], // Authorization 'authorization' => [ 'enabled' => env('BLUEPRINT_AUTHORIZATION_ENABLED', true), 'super_admin_role' => env('BLUEPRINT_SUPER_ADMIN_ROLE', 'superAdmin'), ], // Pagination 'pagination' => [ 'per_page' => env('BLUEPRINT_PER_PAGE', 15), 'max_per_page' => 100, ], // HashIDs Configuration 'hashids' => [ 'default' => 'main', 'connections' => [ 'main' => [ 'salt' => env('HASHIDS_SALT', env('APP_KEY')), 'length' => 6, 'alphabet' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', ], ], ], ];
Environment Variables
Add to your .env file:
# Cache Configuration BLUEPRINT_CACHE_ENABLED=true BLUEPRINT_CACHE_TTL=3600 CACHE_DRIVER=redis # Observers BLUEPRINT_OBSERVERS_ENABLED=true # Authorization BLUEPRINT_AUTHORIZATION_ENABLED=true BLUEPRINT_SUPER_ADMIN_ROLE=superAdmin # Pagination BLUEPRINT_PER_PAGE=15 # HashIDs (for integer ID obfuscation) HASHIDS_SALT="${APP_KEY}"
Architecture Overview
Laravel Blueprint follows a clean architecture pattern with clear separation of concerns:
┌─────────────────────────────────────────┐
│ Controllers │
│ (Handle HTTP & Route Logic) │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Services │
│ (Business Logic & Transactions) │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Repositories │
│ (Data Access & Caching) │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Models │
│ (Eloquent ORM & Relationships) │
└─────────────────────────────────────────┘
Layer Responsibilities
Controllers:
- Handle HTTP requests
- Validate input (via Form Requests)
- Call service methods
- Return views or JSON responses
Services:
- Implement business logic
- Manage database transactions
- Coordinate multiple repositories
- Handle complex operations
Repositories:
- Abstract data access
- Manage caching
- Query optimization
- CRUD operations
Models:
- Define database structure
- Manage relationships
- Provide accessors/mutators
- Implement model events
ID Management Approaches
Laravel Blueprint supports two approaches for handling model IDs. Choose the one that best fits your application's needs:
🔢 Option 1: Hashed Integer IDs (Traditional)
Perfect for obfuscating sequential database IDs while maintaining integer performance.
Advantages:
- Smaller URL parameters
- Better database performance with integer indexes
- Easier debugging (can decode IDs)
- Lower storage requirements
Disadvantages:
- Sequential IDs can be guessed (even when hashed)
- Requires encoding/decoding overhead
- Limited security benefits
When to use:
- Working with existing integer-based systems
- Performance is critical for large datasets
- Sequential ID exposure is a minor concern
- Need smaller URL parameters
Example:
Original ID: 123
Hashed ID: xR9kL2
URL: /users/xR9kL2
🔑 Option 2: Native Laravel UUIDs (Recommended)
Ideal for distributed systems, enhanced security, and when you need globally unique identifiers.
Advantages:
- Globally unique (no collisions)
- Non-sequential (harder to guess)
- Better security for public APIs
- Works well in distributed systems
- Can be generated offline
Disadvantages:
- Larger URLs (36 characters)
- Slightly slower than integer indexes
- More storage space required
- Harder to read/debug
When to use:
- Building new applications
- Enhanced security is paramount
- Working with distributed systems or microservices
- Need offline ID generation capability
- Building public APIs where ID exposure matters
- Multi-tenant applications
Example:
UUID: 550e8400-e29b-41d4-a716-446655440000
URL: /users/550e8400-e29b-41d4-a716-446655440000
Comparison Table
| Feature | Hashed Integer IDs | Native UUIDs |
|---|---|---|
| URL Length | Short (6-8 chars) | Long (36 chars) |
| Performance | Faster | Slightly slower |
| Security | Moderate | High |
| Guessable | Somewhat | No |
| Global Uniqueness | No | Yes |
| Storage | 4-8 bytes | 16 bytes |
| Readability | Good | Poor |
| Distribution | Centralized | Distributed |
Quick Start Guide
Using Hashed Integer IDs
1. Create a Model
<?php namespace App\Models; use TwenyCode\LaravelBlueprint\Models\BaseModel; use TwenyCode\LaravelBlueprint\Traits\HashingIdsTrait; class Product extends BaseModel { use HashingIdsTrait; protected $fillable = [ 'name', 'description', 'price', 'stock', 'is_active', ]; protected $casts = [ 'price' => 'decimal:2', 'stock' => 'integer', ]; // Relationships public function category() { return $this->belongsTo(Category::class); } public function reviews() { return $this->hasMany(Review::class); } }
2. Create a Migration
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::create('products', function (Blueprint $table) { $table->id(); $table->foreignId('category_id')->constrained()->onDelete('cascade'); $table->string('name'); $table->text('description')->nullable(); $table->decimal('price', 10, 2); $table->integer('stock')->default(0); $table->boolean('is_active')->default(true); $table->integer('sort_order')->default(0); $table->timestamps(); $table->softDeletes(); $table->index(['is_active', 'category_id']); $table->index('created_at'); }); } public function down() { Schema::dropIfExists('products'); } };
3. Create a Repository
<?php namespace App\Repositories; use App\Models\Product; use TwenyCode\LaravelBlueprint\Repositories\BaseRepository; class ProductRepository extends BaseRepository { public function __construct(Product $model) { parent::__construct($model); $this->relationships = ['category', 'reviews']; } /** * Find products by category */ public function findByCategory(int $categoryId) { return $this->handleError(function () use ($categoryId) { return $this->model ->with($this->relationships) ->where('category_id', $categoryId) ->where('is_active', true) ->get(); }, 'find products by category'); } /** * Find low stock products */ public function findLowStock(int $threshold = 10) { return $this->handleError(function () use ($threshold) { return $this->model ->where('stock', '<=', $threshold) ->where('is_active', true) ->orderBy('stock', 'asc') ->get(); }, 'find low stock products'); } /** * Search products */ public function searchProducts(string $query) { return $this->handleError(function () use ($query) { return $this->model ->with($this->relationships) ->where('is_active', true) ->where(function ($q) use ($query) { $q->where('name', 'LIKE', "%{$query}%") ->orWhere('description', 'LIKE', "%{$query}%"); }) ->get(); }, 'search products'); } }
4. Create a Service
<?php namespace App\Services; use App\Repositories\ProductRepository; use TwenyCode\LaravelBlueprint\Services\BaseService; use Illuminate\Support\Facades\Log; class ProductService extends BaseService { public function __construct(ProductRepository $repository) { parent::__construct($repository); } /** * Create a new product with validation */ public function createProduct(array $data) { return $this->transaction(function () use ($data) { // Set default values $data['is_active'] = $data['is_active'] ?? true; $data['stock'] = $data['stock'] ?? 0; // Create the product $product = $this->repository->create($data); // Log activity Log::info("Product created: {$product->name}", ['id' => $product->id]); return $product; }); } /** * Update product stock */ public function updateStock($id, int $quantity, string $operation = 'add') { return $this->transaction(function () use ($id, $quantity, $operation) { $product = $this->repository->find($id); $newStock = $operation === 'add' ? $product->stock + $quantity : $product->stock - $quantity; // Prevent negative stock if ($newStock < 0) { throw new \Exception('Insufficient stock'); } $product->update(['stock' => $newStock]); Log::info("Stock updated for product: {$product->name}", [ 'operation' => $operation, 'quantity' => $quantity, 'new_stock' => $newStock ]); return $product; }); } /** * Get products by category */ public function getProductsByCategory(int $categoryId) { return $this->repository->findByCategory($categoryId); } /** * Get low stock products */ public function getLowStockProducts(int $threshold = 10) { return $this->repository->findLowStock($threshold); } }
5. Create a Form Request
<?php namespace App\Http\Requests; use TwenyCode\LaravelBlueprint\Http\Requests\AuthorizedFormRequest; class ProductStoreRequest extends AuthorizedFormRequest { public function authorize() { return $this->checkPermission('create-product'); } public function rules() { return [ 'category_id' => 'required|exists:categories,id', 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'price' => 'required|numeric|min:0', 'stock' => 'nullable|integer|min:0', 'is_active' => 'nullable|boolean', ]; } public function messages() { return [ 'category_id.required' => 'Please select a category', 'category_id.exists' => 'Selected category is invalid', 'name.required' => 'Product name is required', 'price.required' => 'Price is required', 'price.min' => 'Price cannot be negative', ]; } }
6. Create a Controller
<?php namespace App\Http\Controllers; use App\Http\Requests\ProductStoreRequest; use App\Http\Requests\ProductUpdateRequest; use App\Services\ProductService; use TwenyCode\LaravelBlueprint\Http\Controllers\BaseResourceController; class ProductController extends BaseResourceController { public function __construct(ProductService $service) { $this->layer = $service; $this->controllerName = 'Product'; $this->baseViewName = 'products'; $this->baseRouteName = 'products'; $this->resourceVariable = 'product'; $this->hasRelationShips = true; } /** * Store a newly created product */ public function store(ProductStoreRequest $request) { return $this->handleError(function () use ($request) { $this->layer->createProduct($request->validated()); return $this->successRoute( $this->baseRouteName . '.index', 'Product created successfully' ); }, 'create product', $request->input()); } /** * Update the specified product */ public function update(ProductUpdateRequest $request, $id) { return $this->handleError(function () use ($id, $request) { $this->layer->update($id, $request->validated()); return $this->successRoute( $this->baseRouteName . '.index', 'Product updated successfully' ); }, 'update product', $request->input()); } /** * Get products by category */ public function byCategory($categoryId) { return $this->handleError(function () use ($categoryId) { $products = $this->layer->getProductsByCategory($categoryId); return view($this->baseViewName . '.index', [ 'products' => $products ]); }, 'get products by category'); } }
Using Native UUIDs
The process is similar, but with a few key differences:
1. Create a UUID Model
<?php namespace App\Models; use TwenyCode\LaravelBlueprint\Models\BaseUuidModel; class Order extends BaseUuidModel { protected $fillable = [ 'user_id', 'total_amount', 'status', 'is_active', ]; protected $casts = [ 'total_amount' => 'decimal:2', ]; // Relationships public function user() { return $this->belongsTo(User::class); } public function items() { return $this->hasMany(OrderItem::class); } }
2. Create UUID Migration
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::create('orders', function (Blueprint $table) { $table->uuid('id')->primary(); $table->uuid('user_id'); $table->decimal('total_amount', 10, 2); $table->string('status')->default('pending'); $table->boolean('is_active')->default(true); $table->timestamps(); $table->softDeletes(); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->index(['status', 'is_active']); }); } public function down() { Schema::dropIfExists('orders'); } };
3. Create UUID Repository
<?php namespace App\Repositories; use App\Models\Order; use TwenyCode\LaravelBlueprint\Repositories\BaseUuidRepository; class OrderRepository extends BaseUuidRepository { public function __construct(Order $model) { parent::__construct($model); $this->relationships = ['user', 'items']; } /** * Find orders by user */ public function findByUser(string $userId) { return $this->handleError(function () use ($userId) { return $this->model ->with($this->relationships) ->where('user_id', $userId) ->latest() ->get(); }, 'find orders by user'); } }
Core Components
Models
Laravel Blueprint provides two base model classes with common functionality:
BaseModel (For Hashed Integer IDs)
use TwenyCode\LaravelBlueprint\Models\BaseModel; class YourModel extends BaseModel { // Your model implementation }
Built-in Features:
- Activity logging (via Spatie Activity Log)
- Date casting for common date fields
- Boolean casting for
is_activeandis_boolean - Query scopes:
active(),inactive(),ordered() - Status management:
activate(),deactivate(),toggleActive()
Example Usage:
// Using scopes $activeProducts = Product::active()->ordered()->get(); // Status management $product = Product::find(1); $product->activate(); $product->deactivate(); $product->toggleActive();
BaseUuidModel (For UUIDs)
use TwenyCode\LaravelBlueprint\Models\BaseUuidModel; class YourModel extends BaseUuidModel { // Your model implementation }
Inherits all BaseModel features plus:
- Automatic UUID generation
- UUID validation
- Non-incrementing primary key
- String-based primary key type
Repositories
Repositories handle all data access logic and provide a consistent interface for querying models.
BaseRepository
use TwenyCode\LaravelBlueprint\Repositories\BaseRepository; class YourRepository extends BaseRepository { protected array $relationships = ['relation1', 'relation2']; public function __construct(YourModel $model) { parent::__construct($model); } }
Available Methods:
// Basic retrieval $repository->all(); // Get all records $repository->allWithRelations(); // Get all with relationships $repository->active(); // Get active records only $repository->activeWithRelations(); // Get active with relationships $repository->inactive(); // Get inactive records $repository->inactiveWithRelations(); // Get inactive with relationships // Finding records $repository->find($id); // Find by ID $repository->findMany([1, 2, 3]); // Find multiple IDs $repository->search('term', ['name']); // Search in columns // Pagination $repository->paginate(15); // Paginate results // Creating/Updating $repository->create($data); // Create new record $repository->createMany($records); // Bulk create $repository->update($id, $data); // Update record // Deleting $repository->delete($id); // Soft delete $repository->deleteBy('column', 'value'); // Delete by criteria // Soft deletes $repository->trashed(); // Get trashed records $repository->findTrashed($id); // Find trashed by ID $repository->restore($id); // Restore trashed $repository->forceDelete($id); // Permanently delete // Status management $repository->toggleStatus($model); // Toggle is_active // Utilities $repository->pluckActive('name', 'id'); // Pluck as key-value $repository->orderBy('name', 'asc'); // Order results $repository->query(); // Get query builder
Custom Repository Example:
<?php namespace App\Repositories; use App\Models\Post; use TwenyCode\LaravelBlueprint\Repositories\BaseRepository; use Carbon\Carbon; class PostRepository extends BaseRepository { public function __construct(Post $model) { parent::__construct($model); $this->relationships = ['author', 'category', 'tags']; } /** * Find published posts */ public function findPublished() { return $this->handleError(function () { return $this->model ->with($this->relationships) ->where('status', 'published') ->where('published_at', '<=', Carbon::now()) ->latest('published_at') ->get(); }, 'find published posts'); } /** * Find posts by author */ public function findByAuthor(int $authorId) { return $this->handleError(function () use ($authorId) { return $this->model ->with($this->relationships) ->where('author_id', $authorId) ->latest() ->get(); }, 'find posts by author'); } /** * Find trending posts */ public function findTrending(int $days = 7, int $limit = 10) { return $this->handleError(function () use ($days, $limit) { return $this->model ->with($this->relationships) ->where('status', 'published') ->where('published_at', '>=', Carbon::now()->subDays($days)) ->orderBy('views_count', 'desc') ->limit($limit) ->get(); }, 'find trending posts'); } }
BaseUuidRepository
use TwenyCode\LaravelBlueprint\Repositories\BaseUuidRepository; class YourRepository extends BaseUuidRepository { // Same as BaseRepository, but no ID encoding/decoding }
Services
Services contain your business logic and orchestrate operations across multiple repositories.
BaseService
use TwenyCode\LaravelBlueprint\Services\BaseService; class YourService extends BaseService { public function __construct(YourRepository $repository) { parent::__construct($repository); } public function customBusinessLogic(array $data) { return $this->transaction(function () use ($data) { // Your business logic here // Multiple repository calls // Complex validations // External API calls return $this->repository->create($data); }); } }
Available Methods:
// All repository methods are available through the service $service->all(); $service->find($id); $service->create($data); $service->update($id, $data); $service->delete($id); // Plus additional service-specific methods $service->activate($id); $service->deactivate($id); $service->toggleStatus($id);
Complex Service Example:
<?php namespace App\Services; use App\Repositories\OrderRepository; use App\Repositories\ProductRepository; use App\Repositories\UserRepository; use TwenyCode\LaravelBlueprint\Services\BaseService; use Illuminate\Support\Facades\Mail; use App\Mail\OrderConfirmation; class OrderService extends BaseService { protected ProductRepository $productRepository; protected UserRepository $userRepository; public function __construct( OrderRepository $repository, ProductRepository $productRepository, UserRepository $userRepository ) { parent::__construct($repository); $this->productRepository = $productRepository; $this->userRepository = $userRepository; } /** * Create a new order with validation and stock management */ public function createOrder(array $data) { return $this->transaction(function () use ($data) { // Validate stock availability foreach ($data['items'] as $item) { $product = $this->productRepository->find($item['product_id']); if ($product->stock < $item['quantity']) { throw new \Exception("Insufficient stock for {$product->name}"); } } // Calculate total $total = 0; foreach ($data['items'] as $item) { $product = $this->productRepository->find($item['product_id']); $total += $product->price * $item['quantity']; } // Create order $order = $this->repository->create([ 'user_id' => $data['user_id'], 'total_amount' => $total, 'status' => 'pending', ]); // Create order items and update stock foreach ($data['items'] as $item) { $order->items()->create([ 'product_id' => $item['product_id'], 'quantity' => $item['quantity'], 'price' => $this->productRepository->find($item['product_id'])->price, ]); // Reduce stock $this->productRepository->update($item['product_id'], [ 'stock' => $product->stock - $item['quantity'] ]); } // Send confirmation email $user = $this->userRepository->find($data['user_id']); Mail::to($user->email)->send(new OrderConfirmation($order)); return $order; }); } /** * Cancel an order and restore stock */ public function cancelOrder($orderId) { return $this->transaction(function () use ($orderId) { $order = $this->repository->find($orderId); if ($order->status === 'completed') { throw new \Exception('Cannot cancel completed orders'); } // Restore stock foreach ($order->items as $item) { $product = $this->productRepository->find($item->product_id); $this->productRepository->update($item->product_id, [ 'stock' => $product->stock + $item->quantity ]); } // Update order status $order->update(['status' => 'cancelled']); return $order; }); } }
Controllers
BaseResourceController (Web)
For traditional web applications with views:
use TwenyCode\LaravelBlueprint\Http\Controllers\BaseResourceController; class YourController extends BaseResourceController { public function __construct(YourService $service) { $this->layer = $service; $this->controllerName = 'YourModel'; $this->baseViewName = 'your-views'; $this->baseRouteName = 'your.routes'; $this->resourceVariable = 'yourVariable'; $this->hasRelationShips = true; } }
Available Routes:
Route::resource('products', ProductController::class); Route::get('products/trash', [ProductController::class, 'trashed'])->name('products.trash'); Route::post('products/{id}/restore', [ProductController::class, 'restore'])->name('products.restore'); Route::delete('products/{id}/force', [ProductController::class, 'forceDelete'])->name('products.forceDelete'); Route::post('products/{id}/toggle', [ProductController::class, 'toggleActive'])->name('products.toggle');
BaseApiResourceController (API)
For RESTful API applications:
use TwenyCode\LaravelBlueprint\Http\Controllers\BaseApiResourceController; class YourApiController extends BaseApiResourceController { public function __construct(YourService $service) { $this->layer = $service; $this->controllerName = 'YourModel'; $this->baseRouteName = 'api.your.routes'; $this->resourceVariable = 'yourVariable'; $this->hasRelationShips = true; } }
API Routes:
Route::apiResource('products', ProductApiController::class); Route::get('products/trash', [ProductApiController::class, 'trashed']); Route::post('products/{id}/restore', [ProductApiController::class, 'restore']); Route::delete('products/{id}/force', [ProductApiController::class, 'forceDelete']); Route::post('products/{id}/toggle', [ProductApiController::class, 'toggleActive']);
Form Requests
use TwenyCode\LaravelBlueprint\Http\Requests\AuthorizedFormRequest; class YourRequest extends AuthorizedFormRequest { public function authorize() { return $this->checkPermission('create-your-model'); } public function rules() { return [ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:your_table,email', ]; } public function messages() { return [ 'name.required' => 'The name field is required', 'email.unique' => 'This email is already taken', ]; } }
Helper Functions
Laravel Blueprint includes a comprehensive set of helper functions for common tasks.
Date Helpers
use TwenyCode\LaravelBlueprint\Helpers\DateHelper; // Format date ranges $range = formatDateDuration('2025-01-15', '2025-02-20'); // Output: "15 Jan - 20 Feb, 2025" // Calculate days between dates $days = DateHelper::numberOfDays('2025-01-01', '2025-01-15'); // Output: 14 // Calculate age in days $age = DateHelper::calculateAge('1990-01-01'); // Output: 12784 (as of 2025) // Human-readable time ago $timeAgo = DateHelper::formatTimeAgo('2025-01-01 12:00:00'); // Output: "26 days ago" // Date difference $diff = DateHelper::dateDifference('2025-01-01', '2025-03-01'); // Output: "2 months" // Remaining days $remaining = DateHelper::calculateRemainingDays('2025-12-31'); // Output: 338
Number Helpers
use TwenyCode\LaravelBlueprint\Helpers\NumberHelper; // Format file sizes $size = formatFileSize(1024 * 1024); // Output: "1.00 MB" $size = formatFileSize(1536 * 1024); // Output: "1.50 MB" // Format currency with decimals $formatted = formatCurrencyDecimal(1234.56); // Output: "1,234.56" // Format currency without decimals $formatted = formatCurrency(1234.56); // Output: "1,235" // Format money with symbol $formatted = formatMoney(1234.56); // Output: "$ 1,234.56" // Calculate percentage $result = NumberHelper::calculatePercentNumber(15, 200); // Output: 30 (15% of 200)
Text Helpers
use TwenyCode\LaravelBlueprint\Helpers\TextHelper; // String manipulation $text = TextHelper::removeUnderscore('hello_world'); // Output: "hello world" $text = TextHelper::addUnderscore('hello world'); // Output: "hello_world" $text = TextHelper::snake('HelloWorld'); // Output: "hello_world" $text = TextHelper::headline('user_profile_settings'); // Output: "User Profile Settings" // Pluralization $plural = TextHelper::pluralize('category'); // Output: "categories" $plural = TextHelper::pluralizeVariableName('userProfile'); // Output: "userProfiles" // Trim words $trimmed = trimWords('This is a long text that needs trimming', 5); // Output: "This is a long text..." // Trim HTML while preserving structure $trimmed = trimHtmlWords('<p>This is <strong>bold</strong> text</p>', 3); // Output: "<p>This is <strong>bold</strong>...</p>"
Caching System
Laravel Blueprint includes a sophisticated caching system with automatic invalidation.
Automatic Caching
All repository methods automatically cache their results:
// First call - queries database $users = $userRepository->all(); // Second call - returns from cache $users = $userRepository->all();
Cache Configuration
Configure caching in config/tweny-blueprint.php:
'cache' => [ 'enabled' => true, 'ttl' => 3600, // Cache for 1 hour 'driver' => 'redis', // Must support tags ],
Model Observers
Register models for automatic cache invalidation:
'observers' => [ 'enabled' => true, 'models' => [ App\Models\User::class, App\Models\Product::class, App\Models\Post::class, ], ],
Manual Cache Management
// Set custom cache duration $products = $productRepository ->setCacheDuration(7200) // 2 hours ->all(); // Clear specific cache $productRepository->clearCache(); // Clear user-specific cache $productRepository->clearUserCache($userId); // Clear all users' cache $productRepository->clearAllUsersCache();
User-Specific Caching
For multi-tenant applications:
class OrderRepository extends BaseRepository { // Define models that need user-specific caching protected array $userSpecificModels = ['Order', 'Invoice']; public function __construct(Order $model) { parent::__construct($model); } } // Cache key will be: user_123:order:all $orders = $orderRepository->all();
Event Caching
For specialized caching needs:
use TwenyCode\LaravelBlueprint\Traits\CachingTrait; class EventRepository extends BaseRepository { use CachingTrait; public function getUpcomingEvents() { return $this->rememberEventCache('upcoming', function() { return $this->model ->where('start_date', '>', now()) ->where('is_active', true) ->orderBy('start_date') ->get(); }); } public function getFeaturedEvents() { return $this->rememberEventCache('featured', function() { return $this->model ->where('is_featured', true) ->where('is_active', true) ->orderBy('sort_order') ->get(); }, 1800); // Cache for 30 minutes } }
Cache Best Practices
- Use Redis or Memcached: These support tag-based cache invalidation
- Set appropriate TTL: Balance between freshness and performance
- Monitor cache hit rate: Use
php artisan cache:clearif needed - Cache expensive queries: Focus on complex joins and aggregations
- Clear cache on deployment: Ensure fresh data after code changes
SweetAlert Integration
Beautiful, customizable alert messages powered by SweetAlert.
Setup
- Install SweetAlert:
composer require realrashid/sweet-alert
- Publish assets:
php artisan vendor:publish --provider="RealRashid\SweetAlert\SweetAlertServiceProvider"
- Add to your layout:
<!DOCTYPE html> <html> <head> <title>Your App</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css"> </head> <body> <!-- Your content --> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js"></script> @include('sweetalert::alert') </body> </html>
Usage in Controllers
// Success messages $this->successMsg('User created successfully'); return $this->success('Changes saved'); return $this->successRoute('users.index', 'User updated'); // Error messages $this->errorMsg('Something went wrong'); return $this->error('Unable to delete user'); return $this->errorRoute('users.index', 'Operation failed'); // Form errors with input return $this->formInputError($request, 'Please check your input');
Custom Alerts
// In your blade views @if(session('success')) <script> Swal.fire({ icon: 'success', title: 'Success!', text: '{{ session('success') }}', timer: 3000, timerProgressBar: true, }); </script> @endif @if(session('error')) <script> Swal.fire({ icon: 'error', title: 'Oops...', text: '{{ session('error') }}', }); </script> @endif
Confirmation Dialogs
// In your blade views <button onclick="confirmDelete({{ $user->id }})">Delete</button> <script> function confirmDelete(userId) { Swal.fire({ title: 'Are you sure?', text: "You won't be able to revert this!", icon: 'warning', showCancelButton: true, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'Yes, delete it!' }).then((result) => { if (result.isConfirmed) { document.getElementById('delete-form-' + userId).submit(); } }); } </script>
Blade Morph Integration
Blade Morph provides smooth UI transitions and morphing effects for your Laravel applications, creating a more dynamic and polished user experience.
What is Blade Morph?
Blade Morph is a Laravel package that enables smooth transitions between different UI states without full page reloads. It's perfect for creating modern, app-like experiences in your web applications.
Features
- Smooth Transitions: Morph between different views seamlessly
- Dynamic Content: Update page sections without full reload
- Easy Integration: Simple Blade directives
- Lightweight: Minimal overhead
- Customizable: Configure transition effects
Basic Usage
In Your Blade Views
{{-- Wrap morphable content --}} <div x-morph> @if($showDetails) <div class="details-panel"> <h2>{{ $product->name }}</h2> <p>{{ $product->description }}</p> </div> @else <div class="summary-panel"> <h3>{{ $product->name }}</h3> </div> @endif </div>
With Alpine.js
<div x-data="{ open: false }"> <button @click="open = !open">Toggle Details</button> <div x-morph> <div x-show="open" x-transition> <div class="expanded-content"> <!-- Your content --> </div> </div> </div> </div>
Advanced Examples
Dynamic Product Cards
<div class="product-grid"> @foreach($products as $product) <div x-data="{ expanded: false }" class="product-card"> <div x-morph> <div x-show="!expanded" class="product-summary"> <img src="{{ $product->image }}" alt="{{ $product->name }}"> <h3>{{ $product->name }}</h3> <p class="price">{{ formatMoney($product->price) }}</p> <button @click="expanded = true">View Details</button> </div> <div x-show="expanded" class="product-details"> <img src="{{ $product->image }}" alt="{{ $product->name }}"> <h2>{{ $product->name }}</h2> <p class="description">{{ $product->description }}</p> <p class="price">{{ formatMoney($product->price) }}</p> <div class="stock">Stock: {{ $product->stock }}</div> <button @click="expanded = false">Close</button> <button class="btn-primary">Add to Cart</button> </div> </div> </div> @endforeach </div>
Collapsible Sections
<div x-data="{ activeTab: 'description' }"> <div class="tabs"> <button @click="activeTab = 'description'" :class="{ 'active': activeTab === 'description' }"> Description </button> <button @click="activeTab = 'reviews'" :class="{ 'active': activeTab === 'reviews' }"> Reviews </button> <button @click="activeTab = 'specs'" :class="{ 'active': activeTab === 'specs' }"> Specifications </button> </div> <div x-morph class="tab-content"> <div x-show="activeTab === 'description'"> <h3>Product Description</h3> <p>{{ $product->description }}</p> </div> <div x-show="activeTab === 'reviews'"> <h3>Customer Reviews</h3> @foreach($product->reviews as $review) <div class="review"> <p>{{ $review->comment }}</p> <span>- {{ $review->user->name }}</span> </div> @endforeach </div> <div x-show="activeTab === 'specs'"> <h3>Specifications</h3> <dl> @foreach($product->specifications as $key => $value) <dt>{{ $key }}</dt> <dd>{{ $value }}</dd> @endforeach </dl> </div> </div> </div>
Modal Transitions
<div x-data="{ showModal: false }"> <button @click="showModal = true">Open Modal</button> <div x-show="showModal" x-morph class="modal-overlay" @click.self="showModal = false"> <div class="modal-content"> <div class="modal-header"> <h2>{{ $title }}</h2> <button @click="showModal = false">×</button> </div> <div class="modal-body"> {{ $slot }} </div> <div class="modal-footer"> <button @click="showModal = false">Cancel</button> <button class="btn-primary">Confirm</button> </div> </div> </div> </div>
Configuration
Customize morph behavior in your configuration:
// config/blade-morph.php return [ 'duration' => 300, // Transition duration in milliseconds 'easing' => 'ease-in-out', // CSS easing function 'debug' => false, // Enable debug mode ];
Best Practices
- Keep Transitions Short: 200-400ms is ideal for most transitions
- Use Sparingly: Not every element needs to morph
- Test Performance: Monitor performance on slower devices
- Provide Fallbacks: Ensure functionality without JavaScript
- Semantic HTML: Maintain proper HTML structure during transitions
Combining with SweetAlert
Create seamless user experiences by combining Blade Morph with SweetAlert:
<div x-data="{ deleting: false, async confirmDelete() { const result = await Swal.fire({ title: 'Are you sure?', text: 'This action cannot be undone', icon: 'warning', showCancelButton: true, }); if (result.isConfirmed) { this.deleting = true; // Submit form or make AJAX request document.getElementById('delete-form').submit(); } } }"> <div x-morph> <div x-show="!deleting"> <h3>{{ $product->name }}</h3> <button @click="confirmDelete()">Delete Product</button> </div> <div x-show="deleting"> <div class="spinner">Deleting...</div> </div> </div> </div>
Resources
- Package Repository: https://github.com/twenycode/blade-morph
- Alpine.js Documentation: https://alpinejs.dev
Testing
Setting Up Tests
<?php namespace Tests\Feature; use Tests\TestCase; use App\Models\Product; use App\Repositories\ProductRepository; use Illuminate\Foundation\Testing\RefreshDatabase; class ProductRepositoryTest extends TestCase { use RefreshDatabase; protected ProductRepository $repository; protected function setUp(): void { parent::setUp(); $this->repository = new ProductRepository(new Product()); } public function test_can_create_product() { $data = [ 'name' => 'Test Product', 'price' => 99.99, 'stock' => 10, ]; $product = $this->repository->create($data); $this->assertInstanceOf(Product::class, $product); $this->assertEquals('Test Product', $product->name); $this->assertDatabaseHas('products', ['name' => 'Test Product']); } public function test_can_find_active_products() { Product::factory()->count(3)->create(['is_active' => true]); Product::factory()->count(2)->create(['is_active' => false]); $active = $this->repository->active(); $this->assertCount(3, $active); } public function test_can_search_products() { Product::factory()->create(['name' => 'Blue Widget']); Product::factory()->create(['name' => 'Red Widget']); Product::factory()->create(['name' => 'Green Gadget']); $results = $this->repository->search('Widget', ['name']); $this->assertCount(2, $results); } }
Service Testing
<?php namespace Tests\Feature; use Tests\TestCase; use App\Models\Order; use App\Models\Product; use App\Services\OrderService; use Illuminate\Foundation\Testing\RefreshDatabase; class OrderServiceTest extends TestCase { use RefreshDatabase; public function test_can_create_order_with_stock_validation() { $product = Product::factory()->create(['stock' => 10]); $orderData = [ 'user_id' => 1, 'items' => [ ['product_id' => $product->id, 'quantity' => 5] ] ]; $order = $this->orderService->createOrder($orderData); $this->assertInstanceOf(Order::class, $order); $this->assertEquals(5, $order->items->first()->quantity); $product->refresh(); $this->assertEquals(5, $product->stock); } public function test_cannot_create_order_with_insufficient_stock() { $this->expectException(\Exception::class); $this->expectExceptionMessage('Insufficient stock'); $product = Product::factory()->create(['stock' => 2]); $orderData = [ 'user_id' => 1, 'items' => [ ['product_id' => $product->id, 'quantity' => 5] ] ]; $this->orderService->createOrder($orderData); } }
UUID Testing
public function test_can_create_model_with_uuid() { $data = [ 'name' => 'Test Order', 'user_id' => Str::uuid(), 'total_amount' => 99.99, ]; $order = $this->orderService->create($data); $this->assertInstanceOf(Order::class, $order); $this->assertTrue(Str::isUuid($order->id)); }
Migration Guide
From Integer IDs to UUIDs
// Step 1: Backup your database // Step 2: Add UUID column to existing table Schema::table('users', function (Blueprint $table) { $table->uuid('uuid')->nullable()->after('id'); $table->index('uuid'); }); // Step 3: Generate UUIDs for existing records User::whereNull('uuid')->chunk(100, function ($users) { foreach ($users as $user) { $user->update(['uuid' => Str::uuid()]); } }); // Step 4: Update your model // Change: BaseModel -> BaseUuidModel // Step 5: Update your repository // Change: BaseRepository -> BaseUuidRepository // Step 6: Update foreign keys (create new migration) Schema::table('posts', function (Blueprint $table) { $table->uuid('user_uuid')->nullable()->after('user_id'); }); // Populate foreign UUIDs Post::chunk(100, function ($posts) { foreach ($posts as $post) { $user = User::find($post->user_id); if ($user) { $post->update(['user_uuid' => $user->uuid]); } } }); // Step 7: Drop old columns and rename (final migration) Schema::table('users', function (Blueprint $table) { $table->dropColumn('id'); $table->renameColumn('uuid', 'id'); $table->primary('id'); }); Schema::table('posts', function (Blueprint $table) { $table->dropForeign(['user_id']); $table->dropColumn('user_id'); $table->renameColumn('user_uuid', 'user_id'); $table->foreign('user_id')->references('id')->on('users'); });
Best Practices
Architecture
- Keep Controllers Thin: Move business logic to services
- Use Services for Transactions: Wrap complex operations in service methods
- Repository for Data Access: Keep all database queries in repositories
- Single Responsibility: Each class should have one primary purpose
// ❌ Bad: Business logic in controller public function store(Request $request) { $user = User::create($request->all()); $user->assignRole('user'); Mail::to($user)->send(new Welcome($user)); return redirect()->route('users.index'); } // ✅ Good: Business logic in service public function store(UserStoreRequest $request) { $this->userService->registerUser($request->validated()); return $this->successRoute('users.index', 'User registered'); }
Security
- Use Form Requests: Always validate input
- Check Permissions: Use authorization in controllers
- Sanitize Input: Leverage Laravel's validation
- Use UUIDs for Public APIs: Better than exposing sequential IDs
// Form Request with authorization public function authorize() { return $this->checkPermission('create-post'); } public function rules() { return [ 'title' => 'required|string|max:255', 'content' => 'required|string', 'category_id' => 'required|exists:categories,id', ]; }
Performance
- Eager Load Relationships: Avoid N+1 queries
- Use Caching: Enable caching for frequently accessed data
- Index Database Columns: Add indexes for searchable columns
- Paginate Large Results: Don't load thousands of records
// ❌ Bad: N+1 query problem $posts = Post::all(); foreach ($posts as $post) { echo $post->author->name; } // ✅ Good: Eager loading $posts = Post::with('author')->get(); foreach ($posts as $post) { echo $post->author->name; }
Code Organization
- Consistent Naming: Use descriptive, consistent names
- Document Complex Logic: Add PHPDoc comments
- Group Related Files: Organize by feature/domain
- Follow PSR Standards: Use PSR-12 coding standards
/** * Calculate and apply discount to order * * @param Order $order * @param string $couponCode * @return Order * @throws InvalidCouponException */ public function applyDiscount(Order $order, string $couponCode): Order { // Implementation }
API Documentation
Response Formats
Success Response
{
"success": true,
"message": "Product created successfully",
"data": {
"product": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Awesome Product",
"price": 99.99,
"stock": 50,
"is_active": true,
"created_at": "2025-01-15T10:30:00.000000Z"
}
}
}
Error Response
{
"success": false,
"message": "Product not found"
}
Validation Error Response
{
"message": "The given data was invalid.",
"errors": {
"name": [
"The name field is required."
],
"price": [
"The price must be a number.",
"The price must be at least 0."
]
}
}
Status Codes
200 OK- Successful GET, PUT, PATCH201 Created- Successful POST204 No Content- Successful DELETE400 Bad Request- Invalid request401 Unauthorized- Not authenticated403 Forbidden- Not authorized404 Not Found- Resource not found422 Unprocessable Entity- Validation error500 Internal Server Error- Server error
API Endpoints
GET /api/products - List all products
POST /api/products - Create product
GET /api/products/{id} - Show product
PUT /api/products/{id} - Update product
DELETE /api/products/{id} - Delete product
GET /api/products/trash - List trashed
POST /api/products/{id}/restore - Restore product
DELETE /api/products/{id}/force - Force delete
POST /api/products/{id}/toggle - Toggle status
Troubleshooting
Common Issues
Cache Not Clearing
Problem: Changes not reflected after updates
Solution:
# Clear all cache php artisan cache:clear # Verify cache driver supports tags # In .env, use redis or memcached CACHE_DRIVER=redis
Check Configuration:
// config/tweny-blueprint.php 'cache' => [ 'enabled' => true, 'driver' => 'redis', // Must support tags ],
Hash IDs Not Working
Problem: Routes not resolving correctly
Solution:
# Ensure HASHIDS_SALT is set in .env HASHIDS_SALT="${APP_KEY}"
Verify Model:
use TwenyCode\LaravelBlueprint\Traits\HashingIdsTrait; class Product extends BaseModel { use HashingIdsTrait; }
UUID Validation Failing
Problem: "Invalid UUID" errors
Solution:
// In your validation rules 'user_id' => 'required|uuid|exists:users,id', // Verify migration $table->uuid('id')->primary();
Permission Errors
Problem: "Unauthorized action" errors
Solution:
// Verify user has permission $user->givePermissionTo('create-product'); // Or disable authorization temporarily protected bool $checkAuthorization = false;
SweetAlert Not Displaying
Problem: Flash messages not showing
Solution:
<!-- Verify you have these in your layout --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css"> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js"></script> @include('sweetalert::alert')
Debug Mode
Enable detailed logging:
LOG_LEVEL=debug APP_DEBUG=true
Monitor logs:
tail -f storage/logs/laravel.log
Performance Issues
Monitor cache operations:
use Illuminate\Support\Facades\Log; Log::info('Cache key accessed', ['key' => $cacheKey]);
Monitor database queries:
DB::enableQueryLog(); // ... your operations $queries = DB::getQueryLog(); Log::info('Queries executed', ['count' => count($queries), 'queries' => $queries]);
Contributing
Contributions are welcome! Please follow these guidelines:
Development Setup
# Fork and clone the repository git clone https://github.com/your-username/laravel-blueprint.git cd laravel-blueprint # Install dependencies composer install # Run tests vendor/bin/phpunit
Contribution Process
- Fork the Repository: Click "Fork" on GitHub
- Create a Branch:
git checkout -b feature/amazing-feature - Make Changes: Follow coding standards
- Add Tests: Write tests for new functionality
- Run Tests: Ensure all tests pass
- Commit Changes:
git commit -m 'Add amazing feature' - Push to Branch:
git push origin feature/amazing-feature - Create Pull Request: Submit PR with clear description
Coding Standards
- Follow PSR-12 coding standards
- Write meaningful commit messages
- Add PHPDoc comments for public methods
- Include tests for new features
- Update documentation as needed
Reporting Issues
When reporting issues, please include:
- Laravel Version: e.g., 11.0
- PHP Version: e.g., 8.3
- Package Version: e.g., 1.0.0
- Steps to Reproduce: Clear, numbered steps
- Expected Behavior: What should happen
- Actual Behavior: What actually happens
- Error Messages: Complete error messages/stack traces
Example:
### Bug Report **Laravel Version**: 11.0 **PHP Version**: 8.3 **Package Version**: 1.0.0 **Steps to Reproduce**: 1. Create a new Product 2. Try to update the product 3. Observe error **Expected**: Product should update successfully **Actual**: Error "Product not found" **Error Message**:
ModelNotFoundException: No query results for model [Product] at vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:456
License
This package is open-sourced software licensed under the MIT license.
MIT License Summary
- ✅ Commercial use
- ✅ Modification
- ✅ Distribution
- ✅ Private use
- ❌ Liability
- ❌ Warranty
Support
Documentation
- README: This comprehensive guide
- Code Documentation: Inline PHPDoc comments
- Examples: See Quick Start Guide section
Community
- GitHub Issues: Report bugs and request features
- GitHub Discussions: Ask questions and share ideas
Professional Support
- Email: twenycode@tweny.co.tz
- Website: https://tweny.co.tz
Related Packages
- Laravel: https://laravel.com
- Spatie Packages: https://spatie.be/open-source
- HashIds: https://hashids.org
- SweetAlert: https://sweetalert2.github.io
- Blade Morph: https://github.com/twenycode/blade-morph
- Alpine.js: https://alpinejs.dev
Acknowledgments
Laravel Blueprint wouldn't be possible without:
- Laravel Team - For the excellent framework
- Spatie - For their amazing open-source packages
- HashIds - For ID obfuscation
- SweetAlert - For beautiful notifications
- Blade Morph - For smooth UI transitions
- Laravel Log Viewer - For easy log management
- Laravel Authentication Log - For security audit trails
- All Contributors - Who help improve this package
Changelog
Version 1.0.0 (2025-01-15)
- Initial release
- Repository pattern implementation
- Service layer
- Base controllers (Web & API)
- UUID and Hashed ID support
- Caching system with automatic invalidation
- Helper functions (Date, Number, Text)
- SweetAlert integration for notifications
- Blade Morph integration for smooth UI transitions
- Activity logging via Spatie Activity Log
- Permission-based authorization via Spatie Permission
- Laravel Log Viewer integration
- Authentication logging for security audits
Made with ❤️ by TWENY LIMITED
For more information, visit our GitHub repository.