11 KiB
Core Package
The Core package provides the foundation for the framework including the module system, lifecycle events, multi-tenancy, and shared utilities.
Installation
composer require host-uk/core
Features
Module System
Auto-discover and lazy-load modules based on lifecycle events:
<?php
namespace Mod\Example;
use Core\Events\WebRoutesRegistering;
class Boot
{
public static array $listens = [
WebRoutesRegistering::class => 'onWebRoutes',
];
public function onWebRoutes(WebRoutesRegistering $event): void
{
$event->views('example', __DIR__.'/Views');
$event->routes(fn () => require __DIR__.'/Routes/web.php');
}
}
Learn more about the Module System →
Lifecycle Events
Event-driven extension points throughout the framework:
WebRoutesRegistering- Public web routesAdminPanelBooting- Admin panel initializationApiRoutesRegistering- REST API routesClientRoutesRegistering- Authenticated client routesConsoleBooting- Artisan commandsMcpToolsRegistering- MCP toolsFrameworkBooted- Late-stage initialization
Learn more about Lifecycle Events →
Actions Pattern
Single-purpose business logic classes:
class CreatePost
{
use Action;
public function handle(array $data): Post
{
$post = Post::create($data);
event(new PostCreated($post));
return $post;
}
}
// Usage
$post = CreatePost::run($data);
Multi-Tenancy
Workspace-scoped data isolation:
use Core\Mod\Tenant\Concerns\BelongsToWorkspace;
class Post extends Model
{
use BelongsToWorkspace;
}
// Queries automatically scoped to current workspace
$posts = Post::all();
Learn more about Multi-Tenancy →
Activity Logging
Track changes to models with automatic workspace scoping:
use Core\Activity\Concerns\LogsActivity;
class Post extends Model
{
use LogsActivity;
protected array $activityLogAttributes = ['title', 'status'];
}
// Changes logged automatically
$post->update(['title' => 'New Title']);
// Retrieve activity
$activity = Activity::forSubject($post)->get();
Learn more about Activity Logging →
Seeder Discovery
Automatic seeder discovery with dependency ordering:
use Core\Database\Seeders\Attributes\SeederPriority;
use Core\Database\Seeders\Attributes\SeederAfter;
#[SeederPriority(50)]
#[SeederAfter(WorkspaceSeeder::class)]
class PostSeeder extends Seeder
{
public function run(): void
{
Post::factory()->count(20)->create();
}
}
Configuration Management
Multi-profile configuration with versioning:
use Core\Config\ConfigService;
$config = app(ConfigService::class);
// Set configuration
$config->set('api.rate_limit', 10000, $profile);
// Get configuration
$rateLimit = $config->get('api.rate_limit', $profile);
// Export configuration
php artisan config:export production
// Import configuration
php artisan config:import production.json
CDN Integration
Unified CDN interface for BunnyCDN and Cloudflare:
use Core\Cdn\Facades\Cdn;
// Generate CDN URL
$url = Cdn::url('images/photo.jpg');
// Store file to CDN
$path = Cdn::store($uploadedFile, 'media');
// Delete from CDN
Cdn::delete($path);
// Purge cache
Cdn::purge('images/*');
Security Headers
Configurable security headers with CSP support:
// config/core.php
'security_headers' => [
'csp' => [
'directives' => [
'default-src' => ["'self'"],
'script-src' => ["'self'", "'nonce'"],
'style-src' => ["'self'", "'unsafe-inline'"],
],
],
'hsts' => [
'enabled' => true,
'max_age' => 31536000,
],
],
Email Shield
Disposable email detection and validation:
use Core\Mail\EmailShield;
$shield = app(EmailShield::class);
$result = $shield->validate('user@example.com');
if (! $result->isValid) {
// Email is disposable, has syntax errors, etc.
return back()->withErrors(['email' => $result->reason]);
}
Media Processing
Image optimization and responsive images:
use Core\Media\Image\ImageOptimizer;
$optimizer = app(ImageOptimizer::class);
// Optimize image
$optimized = $optimizer->optimize('path/to/image.jpg');
// Generate responsive variants
$variants = $optimizer->generateVariants($image, [
'thumbnail' => ['width' => 150, 'height' => 150],
'medium' => ['width' => 640],
'large' => ['width' => 1024],
]);
Search
Unified search interface across modules:
use Core\Search\Unified;
$search = app(Unified::class);
$results = $search->search('query', [
'types' => ['posts', 'pages'],
'limit' => 10,
]);
foreach ($results as $result) {
echo $result->title;
echo $result->url;
}
SEO Tools
SEO metadata generation and sitemap:
use Core\Seo\SeoMetadata;
$seo = app(SeoMetadata::class);
$seo->setTitle('Page Title')
->setDescription('Page description')
->setCanonicalUrl('https://example.com/page')
->setOgImage('https://example.com/og-image.jpg');
// Generate in view
{!! $seo->render() !!}
// Sitemap generation
php artisan seo:generate-sitemap
Artisan Commands
Module Management
# Create new module
php artisan make:mod Blog
# Create website module
php artisan make:website Marketing
# Create plugin module
php artisan make:plug Stripe
Configuration
# Export configuration
php artisan config:export production
# Import configuration
php artisan config:import production.json --profile=production
# Show configuration versions
php artisan config:version --profile=production
Activity Logs
# Prune old activity logs
php artisan activity:prune --days=90
Email Shield
# Prune email shield statistics
php artisan email-shield:prune --days=30
SEO
# Generate sitemap
php artisan seo:generate-sitemap
# Audit canonical URLs
php artisan seo:audit-canonical
# Test structured data
php artisan seo:test-structured-data --url=/blog/post-slug
Storage
# Warm cache
php artisan cache:warm
# Offload files to CDN
php artisan storage:offload --disk=public
Configuration
Core Configuration
// config/core.php
return [
'module_paths' => [
app_path('Core'),
app_path('Mod'),
app_path('Plug'),
],
'modules' => [
'auto_discover' => true,
'cache_enabled' => true,
],
'seeders' => [
'auto_discover' => true,
'paths' => [
'Mod/*/Database/Seeders',
'Core/*/Database/Seeders',
],
],
'activity' => [
'enabled' => true,
'retention_days' => 90,
'log_ip_address' => false,
],
'workspace_cache' => [
'enabled' => true,
'ttl' => 3600,
'use_tags' => true,
],
];
View full configuration options →
Testing
Feature Tests
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Mod\Blog\Actions\CreatePost;
class CreatePostTest extends TestCase
{
public function test_creates_post(): void
{
$post = CreatePost::run([
'title' => 'Test Post',
'content' => 'Test content',
]);
$this->assertDatabaseHas('posts', [
'title' => 'Test Post',
]);
}
}
Unit Tests
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Core\Module\ModuleScanner;
class ModuleScannerTest extends TestCase
{
public function test_discovers_modules(): void
{
$scanner = new ModuleScanner();
$modules = $scanner->scan([app_path('Mod')]);
$this->assertNotEmpty($modules);
$this->assertArrayHasKey('listens', $modules[0]);
}
}
Database
Migrations
Core package includes migrations for:
activity_log- Activity loggingconfig_keys- Configuration keysconfig_values- Configuration valuesconfig_profiles- Configuration profilesconfig_versions- Configuration versioningemail_shield_stats- Email validation statisticsworkspaces- Multi-tenant workspacesworkspace_users- User-workspace relationships
Run migrations:
php artisan migrate
Events
Core package dispatches these events:
Lifecycle Events
Core\Events\WebRoutesRegisteringCore\Events\AdminPanelBootingCore\Events\ApiRoutesRegisteringCore\Events\ClientRoutesRegisteringCore\Events\ConsoleBootingCore\Events\McpToolsRegisteringCore\Events\FrameworkBooted
Configuration Events
Core\Config\Events\ConfigChangedCore\Config\Events\ConfigInvalidated
Activity Events
Core\Activity\Events\ActivityLogged
Middleware
Multi-Tenancy
Core\Mod\Tenant\Middleware\RequireWorkspaceContext- Ensure workspace is set
Security
Core\Headers\SecurityHeaders- Apply security headersCore\Bouncer\BlocklistService- IP blocklistCore\Bouncer\Gate\ActionGateMiddleware- Action authorization
Service Providers
Register Core package in config/app.php:
'providers' => [
// ...
Core\CoreServiceProvider::class,
],
Or use auto-discovery (Laravel 11+).
Helpers
Global Helpers
// Get current workspace
$workspace = workspace();
// Create activity log
activity()
->performedOn($model)
->log('action');
// Generate CDN URL
$url = cdn_url('path/to/asset.jpg');
// Get CSP nonce
$nonce = csp_nonce();
Best Practices
1. Use Actions for Business Logic
// ✅ Good
$post = CreatePost::run($data);
// ❌ Bad
$post = Post::create($data);
event(new PostCreated($post));
Cache::forget('posts');
2. Log Activity for Audit Trail
class Post extends Model
{
use LogsActivity;
protected array $activityLogAttributes = ['title', 'status', 'published_at'];
}
3. Use Workspace Scoping
class Post extends Model
{
use BelongsToWorkspace;
}
4. Leverage Module System
// Create focused modules with clear boundaries
Mod/Blog/
Mod/Commerce/
Mod/Analytics/
Changelog
See CHANGELOG.md
License
EUPL-1.2