afea / filament-cms-core
Shared infrastructure for the Afea Filament CMS package ecosystem: SEO metadata, polymorphic forms, routing strategy, Filament plugin base.
v0.1.0
2026-04-21 10:48 UTC
Requires
- php: ^8.4
- filament/filament: ^4.0
- illuminate/contracts: ^12.0
- illuminate/database: ^12.0
- illuminate/support: ^12.0
- laravel/prompts: ^0.3
- spatie/laravel-medialibrary: ^11.0
- spatie/laravel-package-tools: ^1.16
- spatie/laravel-permission: ^6.0
- spatie/laravel-settings: ^3.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^10.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
README
Shared infrastructure for the Afea Filament CMS package ecosystem.
Everything a modular CMS needs, without the modules themselves:
SeoMetadatamodel +HasSeo/InteractsWithSeoFormtraits + reusable FilamentSeoSchema- Polymorphic forms:
Form,FormQuestion,FormQuestionOption,FormSubmission,FormSubmissionAnswer+HasFormstrait + attachable via theformablespivot RoutingStrategyenum (slug/resource/localized) +RouteRegistrarBaseCmsPluginabstract class every first-party module extends- Laravel Prompts-backed
afea:install:corecommand
Installation
composer require afea/filament-cms-core php artisan afea:install:core
The installer publishes config/afea-cms.php, publishes translations under lang/vendor/afea-cms, and runs the core migrations.
Configuration keys
return [ 'panel' => 'admin', 'models' => [ 'seo_metadata' => \Afea\Cms\Core\Models\SeoMetadata::class, 'form' => \Afea\Cms\Core\Models\Form::class, // ... ], 'media' => [ 'disk' => env('AFEA_CMS_MEDIA_DISK', 'public'), 'private_disk' => env('AFEA_CMS_PRIVATE_DISK', 'local'), ], 'seo' => [ 'sitemap_cache_key' => 'sitemap:urls', ], 'forms' => [ 'submissions_disk' => env('AFEA_CMS_SUBMISSIONS_DISK', 'local'), 'temporary_url_ttl_minutes' => 60, ], ];
Model override
Extend any shipped model and bind the subclass in configuration:
// app/Models/SeoMetadata.php namespace App\Models; class SeoMetadata extends \Afea\Cms\Core\Models\SeoMetadata { public function scopeIndexable($query) { return $query->where('noindex', false); } }
// config/afea-cms.php 'models' => [ 'seo_metadata' => \App\Models\SeoMetadata::class, ],
Every internal relation resolves the class from config, so relationships keep working across the package ecosystem.
Common scenarios
Add SEO to a model
use Afea\Cms\Core\Concerns\HasSeo; class Page extends Model { use HasSeo; }
On the Filament EditPage:
use Afea\Cms\Core\Concerns\InteractsWithSeoForm; use Afea\Cms\Core\Filament\Schemas\SeoSchema; class EditPage extends EditRecord { use InteractsWithSeoForm; protected function getFormSchema(): array { return [ // ...model fields Tabs\Tab::make('SEO')->schema(SeoSchema::make()), ]; } }
Attach a CMS-defined form to a content model
use Afea\Cms\Core\Concerns\HasForms; class CustomPage extends Model { use HasForms; } $page = CustomPage::first(); $page->attachForm($contactForm); $page->forms; // Collection<Form>
Register a public-facing route using the configured strategy
use Afea\Cms\Core\Enums\RoutingStrategy; use Afea\Cms\Core\Routing\RouteRegistrar; RouteRegistrar::register( strategy: RoutingStrategy::from(config('afea-blog.routing_strategy', 'resource')), prefix: 'blog', slugHandler: [BlogController::class, 'show'], indexHandler: [BlogController::class, 'index'], );
Testing
From the monorepo root:
./vendor/bin/pest --group=core
Or within the package:
composer install ./vendor/bin/pest