antikode / anti-cms-builder
A powerful Laravel package for building dynamic CRUD interfaces with minimal boilerplate code. Provides form builders, table builders, and React components to accelerate development.
Requires
- php: ^8.2
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/routing: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- illuminate/validation: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- mockery/mockery: ^1.5
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
This package is not auto-updated.
Last update: 2025-09-02 08:47:28 UTC
README
A powerful Laravel package for building dynamic CRUD interfaces with minimal boilerplate code. This package provides form builders, info lists, table builders, React components, and advanced custom field management to accelerate development while ensuring consistency across your application.
🚧 Development Status: This package is actively maintained and published to Packagist. While stable for production use, new features and improvements are added regularly. Please review the changelog when updating versions.
Features
- 🚀 Rapid CRUD Development - Build complete CRUD interfaces in minutes
- 📝 Dynamic Form Builder - Create complex forms with JSON configuration
- 📊 Advanced Table Builder - Sortable, searchable, filterable data tables with dynamic actions
- 📋 InfoList System - Structured data display with sections, entry types, and automatic formatting
- 🌍 Multilingual Support - Built-in translation management with language tabs
- ⚛️ React Components - Pre-built UI components for Inertia.js
- 🔧 Extensible - Easy to customize and extend with custom field types
- 🎯 Laravel 11 Ready - Full compatibility with latest Laravel
- 🗂️ Custom Fields System - Template-based custom field management with hierarchical structure
- 📋 Field Builder Component - Interactive field builder for dynamic form creation
- 🎨 Rich Text Editor - Built-in WYSIWYG editor support
- ⚙️ Configuration Management - Comprehensive configuration system for models, services, and languages
Table of Contents
- Installation
- Quick Start
- Configuration
- FormBuilder Documentation
- InfoList Documentation
- TableBuilder Usage
- React Components
- Custom Fields System
- Console Commands
- Advanced Features
Installation
Note: This package is currently in active development. While published to Packagist, new features and breaking changes may be introduced frequently. Please check the changelog before updating.
From Packagist (Recommended)
composer require antikode/anticms-builder
For Development/Latest Features
If you want to use the latest development version with cutting-edge features:
- Add the repository to your
composer.json
:
For HTTPS:
{ "repositories": [ { "type": "vcs", "url": "https://github.com/antikode/anticms-builder.git" } ] }
For SSH:
{ "repositories": [ { "type": "vcs", "url": "git@github.com:antikode/anticms-builder.git" } ] }
- Require the development version:
composer require antikode/anticms-builder:dev-main
The package will automatically register its service provider through Laravel's package discovery.
Database Requirements
For full functionality, ensure you have these database structures:
Required for Custom Fields:
custom_fields
table for storing custom field data
Required for Multilingual Support:
translations
table for multilingual content
Required for Media Features:
- Media/file storage tables (implementation-dependent)
Example Migration Structure:
// Custom fields table Schema::create('custom_fields', function (Blueprint $table) { $table->id(); $table->morphs('customfieldable'); $table->string('template')->nullable(); $table->json('data'); $table->unsignedBigInteger('parent_id')->nullable(); $table->integer('sort')->default(0); $table->timestamps(); }); // Translations table (example) Schema::create('translations', function (Blueprint $table) { $table->id(); $table->morphs('translatable'); $table->string('locale'); $table->json('data'); $table->timestamps(); });
Publish Resources
React Components (Optional)
If you want to customize the React components, publish them to your resources directory:
php artisan vendor:publish --tag=anti-cms-builder-resources
This will publish the CRUD React components to resources/js/vendor/anti-cms-builder/
.
Configuration (Optional)
Publish the configuration file to customize default settings:
php artisan vendor:publish --tag=anti-cms-builder-config
This creates config/anti-cms-builder.php
for customization.
Requirements
- PHP 8.2+
- Laravel 10.0+ or 11.0+
- React + Inertia.js (for frontend components)
Configuration
Optionally, you can publish the configuration file:
php artisan vendor:publish --tag=anti-cms-builder-config
This will create a config/anti-cms-builder.php
file where you can customize:
return [ // Default models used by the package 'models' => [ 'file' => 'App\\Models\\File', 'media' => 'App\\Models\\Media', 'translation' => 'App\\Models\\Translations\\Translation', 'custom_field' => 'App\\Models\\CustomField\\CustomField', ], // Service implementations 'services' => [ 'post' => 'App\\Services\\PostService', 'template' => 'App\\Services\\TemplateService', 'custom_field' => 'App\\Services\\CustomFieldService', ], // Language settings 'default_language' => 'en', 'languages' => [ 'en' => 'English', 'ar' => 'Arabic', ], // File upload configuration 'uploads' => [ 'disk' => 'public', 'path' => 'uploads', ], ];
Usage
Basic CRUD Controller
Create a controller that uses the UseCrudController
trait:
<?php namespace App\Http\Controllers; use AntiCmsBuilder\Forms\FormBuilder; use AntiCmsBuilder\Tables\TableBuilder; use AntiCmsBuilder\Traits\UseCrudController; use AntiCmsBuilder\FieldTypes\InputField; use AntiCmsBuilder\FieldTypes\SelectField; use AntiCmsBuilder\Tables\Columns\TextColumn; use App\Models\Product; use Illuminate\Database\Eloquent\Builder; class ProductController extends Controller { use UseCrudController; protected string $model = Product::class; public function forms(FormBuilder $builder): FormBuilder { return $builder->forms([ InputField::make() ->name('name') ->label('Product Name') ->placeholder('Enter product name') ->required() ->multilanguage() ->toArray(), SelectField::make() ->name('category_id') ->label('Category') ->placeholder('Select category') ->loadOptionFromRelation('category', 'name') ->toArray(), ]); } public function tables(TableBuilder $builder): TableBuilder { return $builder ->query(fn (Builder $query) => $query->with('category')) ->columns([ TextColumn::make() ->name('name') ->searchable() ->sortable() ->toArray(), TextColumn::make() ->name('category.name') ->label('Category') ->toArray(), ]); } }
Register Routes
Use the Route::crud()
macro to register CRUD routes:
Route::crud('/products', ProductController::class, [ 'middleware' => ['auth'], 'as' => 'admin.products' ]);
This generates all necessary CRUD routes:
GET /products
- Index pageGET /products/create
- Create formPOST /products
- Store new recordGET /products/details/{id}/edit
- Edit formPUT /products/details/{id}/update
- Update recordDELETE /products/details/{id}/delete
- Delete record
Field Types
The package includes various field types:
// Input Field InputField::make() ->name('title') ->label('Title') ->type('text') // text, email, number, password, etc. ->required() ->multilanguage() ->toArray(), // Textarea Field TextareaField::make() ->name('description') ->label('Description') ->rows(5) ->multilanguage() ->toArray(), // Rich Text Editor TexteditorField::make() ->name('content') ->label('Content') ->multilanguage() ->toArray(), // Select Field SelectField::make() ->name('category_id') ->label('Category') ->loadOptionFromRelation('category', 'name') ->required() ->toArray(), // Image Field ImageField::make() ->name('featured_image') ->label('Featured Image') ->required() ->toArray(), // Toggle Field ToggleField::make() ->name('is_active') ->label('Active Status') ->default(true) ->toArray(), // Repeater Field RepeaterField::make() ->name('specifications') ->label('Product Specifications') ->fields([ InputField::make()->name('key')->label('Property')->toArray(), InputField::make()->name('value')->label('Value')->toArray(), ]) ->toArray(),
Multilingual Support
Enable multilingual support for any field:
InputField::make() ->name('title') ->multilanguage() // This field will support multiple languages ->toArray(),
Data is automatically stored in the translations table with language keys.
Custom Validation
Add custom validation rules:
InputField::make() ->name('email') ->type('email') ->rules(['required', 'email', 'unique:users,email']) ->toArray(),
Lifecycle Hooks
Customize save and update behavior:
public function forms(FormBuilder $builder): FormBuilder { return $builder ->forms([...]) ->save(function (Request $request) { // Custom save logic $product = Product::create($request->validated()); // Additional processing... return $product; }) ->afterSave(function ($record, $operation, $request) { // Execute after save/update if ($operation === 'create') { // Send notification for new records } }); }
Testing
The package includes comprehensive tests covering all major functionality.
Running Tests
Run the full test suite:
composer test
Or run specific test groups:
# Run only unit tests ./vendor/bin/phpunit tests/Unit # Run only feature tests ./vendor/bin/phpunit tests/Feature # Run with coverage ./vendor/bin/phpunit --coverage-html coverage
Test Structure
-
Unit Tests: Test individual classes and methods
FieldTypes/
: Tests for all field type classesTables/
: Tests for table builder functionality- Core service tests (FieldService, FormBuilder, TableBuilder)
-
Feature Tests: Test complete workflows
UseCrudControllerTest.php
: End-to-end CRUD operations
-
Support Files: Test models, controllers, and migrations for testing environment
Writing Tests
The package uses Laravel's testing framework. Test models and controllers are provided in tests/Support/
for testing custom functionality.
Continuous Integration
The package uses GitHub Actions for continuous integration. The CI workflow:
- Tests against multiple PHP versions (8.2, 8.3)
- Tests against multiple Laravel versions (10.x, 11.x)
- Runs PHPUnit tests
- Checks code style and static analysis
- Generates test coverage reports
See .github/workflows/ci.yml
for the complete configuration.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.
Overview
The AntiCmsBuilder package consists of three main components:
1. FormBuilder
Handles dynamic form creation, validation, and data persistence for Laravel models. It provides a fluent API for building complex forms with support for multilingual content, relationships, custom fields, media handling, and advanced validation.
2. TableBuilder
Enables the creation of queryable, filterable tables with customizable columns and sorting.
3. UseCrudController Trait
Provides reusable CRUD controller methods (index
, create
, store
, edit
, update
, destroy
) integrated with the builders.
Quick Start
1. Controller Setup
Create a controller that uses the UseCrudController
trait:
<?php namespace App\Http\Controllers; use AntiCmsBuilder\Forms\FormBuilder; use AntiCmsBuilder\Tables\TableBuilder; use AntiCmsBuilder\Traits\UseCrudController; use AntiCmsBuilder\FieldTypes\InputField; use AntiCmsBuilder\FieldTypes\SelectField; use AntiCmsBuilder\Tables\Columns\TextColumn; use App\Models\Product; use Illuminate\Database\Eloquent\Builder; class ProductController extends Controller { use UseCrudController; protected string $model = Product::class; public function forms(FormBuilder $builder): FormBuilder { return $builder->forms([ InputField::make() ->name('name') ->label('Product Name') ->placeholder('Enter product name') ->required() ->multilanguage() ->toArray(), SelectField::make() ->name('category_id') ->label('Category') ->placeholder('Select category') ->loadOptionFromRelation('category', 'name') ->toArray(), ]); } public function tables(TableBuilder $builder): TableBuilder { return $builder ->query(fn (Builder $query) => $query->with('category')) ->columns([ TextColumn::make() ->name('name') ->searchable() ->sortable() ->toArray(), TextColumn::make() ->name('category.name') ->label('Category') ->toArray(), ]); } }
2. Route Registration
Use the Route::crud()
macro to register CRUD routes:
Route::crud('/products', ProductController::class, [ 'middleware' => ['auth'], 'as' => 'admin.products' ]);
Route Details
The Route::crud()
method generates RESTful routes:
HTTP Method | URL Path | Controller Method | Route Name |
---|---|---|---|
GET | /{url}/ | index | {$as}.index |
GET | /{url}/create | create | {$as}.create |
POST | /{url}/ | store | {$as}.store |
GET | /{url}/details/{id}/edit | edit | {$as}.edit |
PUT | /{url}/details/{id}/update | update | {$as}.update |
DELETE | /{url}/details/{id}/delete | delete | {$as}.delete |
DELETE | /{url}/details/{id}/force-delete | forceDelete | {$as}.delete.force |
GET | /{url}/details/{id}/restore | restore | {$as}.restore |
FormBuilder Documentation
Core Methods
make(string $model): self
Creates a new FormBuilder instance for the specified model.
$builder = FormBuilder::make(Product::class);
forms(array|callable $forms): self
Defines the form fields. Accepts either an array of field definitions or a callable that returns an array.
Array Usage:
$builder->forms([ InputField::make()->name('title')->toArray(), TextareaField::make()->name('description')->toArray(), ]);
Callable Usage with Dependency Injection:
$builder->forms(function ($record, $operation) { $fields = [ InputField::make()->name('title')->toArray(), ]; if ($operation === 'update') { $fields[] = InputField::make()->name('slug')->disabled()->toArray(); } return $fields; });
Supported Injectable Parameters:
$record
(Model|null): The current model instance$operation
(string): Either 'create' or 'update'
save(callable $save): self
Defines a custom save callback for create operations.
$builder->save(function (Request $request) { $product = Product::create($request->validated()); // Custom logic after creation $this->sendNotification($product); return $product; });
update(callable $update): self
Defines a custom update callback for update operations.
$builder->update(function (Request $request, Model $model) { $model->update($request->validated()); // Custom logic after update $this->logUpdate($model); return $model; });
afterSave(callable $afterSave): self
Defines a callback to execute after both save and update operations.
$builder->afterSave(function ($record, $operation, $request) { if ($operation === 'create') { // Logic for new records $this->sendWelcomeEmail($record); } else { // Logic for updated records $this->logUpdate($record); } });
Supported Injectable Parameters:
$record
(Model): The saved/updated model instance$operation
(string): Either 'create' or 'update'$request
(Request): The HTTP request instance
Field Types and Configuration
Basic Field Structure
All fields follow this basic structure:
[ 'field' => 'input', // Field type 'name' => 'field_name', // Field name/key 'label' => 'Field Label', // Display label 'attribute' => [ // Field attributes 'placeholder' => 'Enter value', 'required' => true, 'max' => 255, // ... other attributes ], 'multilanguage' => true, // Enable multilingual support ]
Input Field
InputField::make() ->name('title') ->label('Title') ->placeholder('Enter title') ->type('text') // text, email, number, password, etc. ->required() ->max(255) ->min(3) ->multilanguage() ->toArray()
Textarea Field
TextareaField::make() ->name('description') ->label('Description') ->placeholder('Enter description') ->rows(5) ->required() ->max(1000) ->multilanguage() ->toArray()
Rich Text Editor Field
TexteditorField::make() ->name('content') ->label('Content') ->placeholder('Enter rich content') ->multilanguage() ->toArray()
Select Field
SelectField::make() ->name('category_id') ->label('Category') ->placeholder('Select category') ->loadOptionFromRelation('category', 'name') ->required() ->toArray()
Multi-Select Field
SelectField::make() ->name('tags') ->label('Tags') ->multiple() ->loadOptionFromRelation('tags', 'name') ->toArray()
Image Field
ImageField::make() ->name('featured_image') ->label('Featured Image') ->required() ->toArray()
Toggle/Switch Field
ToggleField::make() ->name('is_active') ->label('Active Status') ->default(true) ->toArray()
Repeater Field
RepeaterField::make() ->name('specifications') ->label('Product Specifications') ->fields([ InputField::make()->name('key')->label('Property')->toArray(), InputField::make()->name('value')->label('Value')->toArray(), ]) ->toArray()
Relationship Handling
BelongsTo Relationships
SelectField::make() ->name('category_id') ->label('Category') ->loadOptionFromRelation('category', 'name') ->toArray()
BelongsToMany Relationships
SelectField::make() ->name('tags') ->label('Tags') ->multiple() ->loadOptionFromRelation('tags', 'name') ->toArray()
HasMany Relationships with Repeater
RepeaterField::make() ->name('variants') ->label('Product Variants') ->relation('variants') // HasMany relation ->fields([ InputField::make()->name('name')->label('Variant Name')->multilanguage()->toArray(), InputField::make()->name('price')->label('Price')->type('number')->toArray(), ImageField::make()->name('image')->label('Variant Image')->toArray(), ]) ->toArray()
Custom Relation Queries
SelectField::make() ->name('category_id') ->label('Category') ->loadOptionFromRelation('category', 'name', function ($query) { return $query->where('is_active', true)->orderBy('name'); }) ->toArray()
Multilingual Support
Enabling Multilingual Fields
InputField::make() ->name('title') ->label('Title') ->multilanguage() // Enable multilingual support ->toArray()
How Multilingual Data is Stored
Multilingual fields are automatically stored in the translations
table with the following structure:
// Database structure translations: [ 'en' => ['title' => 'English Title', 'description' => 'English Description'], 'ar' => ['title' => 'Arabic Title', 'description' => 'Arabic Description'], ]
Validation
Automatic Validation
FormBuilder automatically generates validation rules based on field attributes:
InputField::make() ->name('email') ->type('email') ->required() ->max(255) ->toArray() // Generates: 'email' => ['required', 'email', 'max:255']
Custom Validation Rules
InputField::make() ->name('username') ->rules(['required', 'unique:users,username', 'min:3']) ->toArray()
Multilingual Validation
InputField::make() ->name('title') ->multilanguage() ->required() ->toArray() // Generates validation for each language: // 'translations.en.title' => ['required'] // 'translations.ar.title' => ['required']
Complete Product Form Example
public function forms(FormBuilder $builder): FormBuilder { return $builder->forms([ // Basic Information InputField::make() ->name('name') ->label('Product Name') ->placeholder('Enter product name') ->required() ->max(255) ->multilanguage() ->toArray(), TextareaField::make() ->name('description') ->label('Description') ->placeholder('Enter product description') ->rows(5) ->max(1000) ->multilanguage() ->toArray(), // Relationships SelectField::make() ->name('category_id') ->label('Category') ->placeholder('Select category') ->loadOptionFromRelation('category', 'name') ->required() ->toArray(), SelectField::make() ->name('tags') ->label('Tags') ->multiple() ->loadOptionFromRelation('tags', 'name') ->toArray(), // Media ImageField::make() ->name('featured_image') ->label('Featured Image') ->required() ->toArray(), // Pricing InputField::make() ->name('price') ->label('Price') ->type('number') ->step('0.01') ->min(0) ->required() ->toArray(), // Status ToggleField::make() ->name('is_active') ->label('Active') ->default(true) ->toArray(), // Specifications (Repeater) RepeaterField::make() ->name('specifications') ->label('Product Specifications') ->fields([ InputField::make() ->name('key') ->label('Property') ->required() ->multilanguage() ->toArray(), InputField::make() ->name('value') ->label('Value') ->required() ->multilanguage() ->toArray(), ]) ->toArray(), ]); }
InfoList Documentation
The InfoList system provides a powerful way to display structured information about your models in a clean, organized format. It supports various entry types, sections, custom formatting, and automatic data processing.
Overview
InfoLists are used to display model data in detail views, providing a structured way to present information with different entry types and sections. The system automatically handles data formatting, relationships, multilingual content, and conditional visibility.
Basic Usage
Create an InfoList in your controller:
<?php namespace App\Http\Controllers; use AntiCmsBuilder\InfoLists\InfoListBuilder; use AntiCmsBuilder\InfoLists\Section; use AntiCmsBuilder\InfoLists\Entries\TextEntry; use AntiCmsBuilder\InfoLists\Entries\BooleanEntry; use AntiCmsBuilder\InfoLists\Entries\ImageEntry; use AntiCmsBuilder\InfoLists\Entries\RelationshipEntry; use AntiCmsBuilder\InfoLists\Entries\DateEntry; use AntiCmsBuilder\Traits\UseCrudController; use App\Models\Product; class ProductController extends Controller { use UseCrudController; protected string $model = Product::class; public function infoList(InfoListBuilder $builder): InfoListBuilder { return $builder ->record($builder->record) ->sections([ Section::make('Product Information') ->entries([ TextEntry::make() ->name('name') ->label('Product Name') ->toArray(), TextEntry::make() ->name('description') ->label('Description') ->limit(200) ->toArray(), TextEntry::make() ->name('price') ->label('Price') ->format(fn ($value) => '$' . number_format($value, 2)) ->toArray(), ]) ->toArray(), Section::make('Status & Availability') ->entries([ BooleanEntry::make() ->name('is_active') ->label('Active Status') ->trueLabel('Active') ->falseLabel('Inactive') ->toArray(), ]) ->toArray(), ]); } }
Entry Types
TextEntry
Displays text content with optional formatting and character limits:
TextEntry::make() ->name('title') ->label('Title') ->limit(100) // Character limit ->copyable() // Add copy to clipboard functionality ->markdown() // Render as markdown ->html() // Render as HTML ->format(fn ($value) => strtoupper($value)) // Custom formatting ->toArray()
BooleanEntry
Displays boolean values with customizable labels and colors:
BooleanEntry::make() ->name('is_active') ->label('Status') ->trueLabel('Active') ->falseLabel('Inactive') ->trueColor('success') ->falseColor('danger') ->toArray()
DateEntry
Displays dates with customizable formatting:
DateEntry::make() ->name('created_at') ->label('Created Date') ->dateFormat('F j, Y g:i A') // Custom date format ->toArray()
ImageEntry
Displays images with customizable dimensions and styling:
ImageEntry::make() ->name('featured_image') ->label('Featured Image') ->height(200) ->width(300) ->circular() // Display as circular image ->square() // Display with square aspect ratio ->toArray()
RelationshipEntry
Displays related model data:
RelationshipEntry::make() ->name('category') ->label('Category') ->displayUsing('name') // Specify which column to display ->badge() // Display as a badge/tag ->toArray()
Sections
Group related entries into sections with titles, descriptions, and icons:
Section::make('Product Details') ->description('Basic product information') ->icon('heroicon-o-information-circle') ->collapsed(false) // Whether section is collapsed by default ->entries([ // Entry definitions... ]) ->visible(fn ($record) => $record->is_published) // Conditional visibility ->toArray()
Advanced Entry Configuration
Custom State and Formatting
TextEntry::make() ->name('status') ->label('Status') ->state(fn ($record) => $record->getStatusLabel()) // Custom value retrieval ->format(fn ($value, $record) => "<span class='badge badge-{$record->status_color}'>{$value}</span>" ) ->html() // Render as HTML ->toArray()
Nested Relationships
TextEntry::make() ->name('category.parent.name') // Access nested relationships ->label('Parent Category') ->toArray() RelationshipEntry::make() ->name('user.profile') ->label('Author Profile') ->displayUsing('display_name') ->toArray()
Conditional Visibility
TextEntry::make() ->name('internal_notes') ->label('Internal Notes') ->visible(fn ($record) => auth()->user()->can('view-internal-notes')) ->hidden(fn ($record) => !$record->has_internal_notes) ->toArray()
Complete Example
public function infoList(InfoListBuilder $builder): InfoListBuilder { return $builder ->record($builder->record) ->sections([ // Basic Information Section Section::make('Product Information') ->description('Basic product details and specifications') ->icon('heroicon-o-cube') ->entries([ TextEntry::make() ->name('name') ->label('Product Name') ->copyable() ->toArray(), TextEntry::make() ->name('description') ->label('Description') ->limit(300) ->markdown() ->toArray(), TextEntry::make() ->name('sku') ->label('SKU') ->copyable() ->toArray(), TextEntry::make() ->name('price') ->label('Price') ->format(fn ($value) => $value ? '$' . number_format($value, 2) : '—') ->toArray(), ]) ->toArray(), // Status Section Section::make('Status & Availability') ->entries([ BooleanEntry::make() ->name('is_active') ->label('Active Status') ->trueLabel('Active') ->falseLabel('Inactive') ->trueColor('success') ->falseColor('danger') ->toArray(), BooleanEntry::make() ->name('in_stock') ->label('Stock Status') ->trueLabel('In Stock') ->falseLabel('Out of Stock') ->toArray(), ]) ->toArray(), // Relationships Section Section::make('Relationships') ->entries([ RelationshipEntry::make() ->name('category') ->label('Category') ->displayUsing('name') ->badge() ->toArray(), RelationshipEntry::make() ->name('user') ->label('Created By') ->displayUsing('name') ->toArray(), ]) ->toArray(), // Media Section Section::make('Media') ->collapsed(true) ->entries([ ImageEntry::make() ->name('featured_image') ->label('Featured Image') ->height(200) ->square() ->toArray(), ]) ->visible(fn ($record) => $record->featured_image) ->toArray(), // Timestamps Section Section::make('System Information') ->collapsed(true) ->entries([ DateEntry::make() ->name('created_at') ->label('Created Date') ->dateFormat('F j, Y g:i A') ->toArray(), DateEntry::make() ->name('updated_at') ->label('Last Updated') ->dateFormat('F j, Y g:i A') ->toArray(), ]) ->toArray(), ]) ->entries([ // Global entries (displayed outside of sections) TextEntry::make() ->name('slug') ->label('URL Slug') ->copyable() ->format(fn ($value) => url($value)) ->toArray(), ]); }
Data Processing
The InfoListBuilder automatically processes data based on entry types:
Automatic Formatting
- Boolean values: Converted to "Yes/No" or custom labels
- Dates: Formatted using Carbon (default: "M d, Y")
- Arrays: Converted to comma-separated strings
- Relationships: Displays related model's name, title, or ID
- Null/Empty values: Displays "—" placeholder
Dot Notation Support
Access nested relationships and properties:
TextEntry::make() ->name('category.parent.name') // Nested relationship ->toArray() TextEntry::make() ->name('translations.en.title') // Multilingual content ->toArray()
Best Practices
- Organize with Sections: Group related information logically
- Use Appropriate Entry Types: Choose the right entry type for each data type
- Add Descriptions: Provide context with section descriptions
- Conditional Visibility: Hide irrelevant information based on permissions or data state
- Custom Formatting: Use format callbacks for complex display logic
- Performance: Use eager loading for relationships in your table query
- User Experience: Use collapsed sections for less important information
Integration with CRUD
When using the UseCrudController
trait, InfoLists are automatically integrated into the detail view. The system will:
- Call your
infoList
method to build the structure - Set the current record automatically
- Process all entries with the record data
- Render the InfoList in your detail view
Styling and Customization
InfoLists work with your existing CSS framework. Entry types include semantic classes for styling:
.info-entry-text
for text entries.info-entry-boolean
for boolean entries.info-entry-image
for image entries.info-entry-relationship
for relationship entries.info-section
for sections.info-section-collapsed
for collapsed sections
TableBuilder Usage
Column Configuration
public function tables(TableBuilder $builder): TableBuilder { return $builder ->query(fn (Builder $query) => $query->with(['category', 'user'])) ->columns([ TextColumn::make() ->name('title') ->label('Title') ->searchable() ->sortable() ->description(fn ($record) => $record->slug) ->toArray(), TextColumn::make() ->name('category.name') ->label('Category') ->toArray(), TextColumn::make() ->name('created_at') ->label('Created Date') ->sortable() ->format(fn ($date) => $date->format('Y-m-d H:i')) ->toArray(), ]) ->filters([ SelectField::make() ->name('category_id') ->label('Filter by Category') ->loadOptionFromRelation('category', 'name') ->query(fn (Builder $query, $value) => $query->where('category_id', $value) ) ->toArray(), ]); }
Column Features
- searchable(): Enables text search on the column
- sortable(): Allows column sorting
- description(Closure): Adds a description callback
- format(Closure): Custom formatting for display
React Components
The package includes pre-built React components for CRUD operations:
CRUD Pages
- Index.jsx: Data table with search, filter, pagination, and dynamic row/table actions
- Create.jsx: Form for creating new records
- Edit.jsx: Form for editing existing records
- ActionBuilders.jsx: Configurable action buttons and dropdowns
Form Components
- CreateEditFormWithBuilder.jsx: Advanced form component with multilingual support, tabs, and integrated field builder
- FieldBuilderComponent.jsx: Interactive field builder for creating custom forms
- Builder.jsx: Core builder component for dynamic content creation
Table Components
- DynamicRowActions.jsx: Dynamic row-level actions (edit, delete, custom actions)
- DynamicTableActions.jsx: Dynamic table-level bulk actions
These components are automatically used when you use the UseCrudController
trait and provide a complete CRUD interface with:
- Multilingual content management with language tabs
- Dynamic form building with real-time validation
- SEO settings integration
- Status and slug management
- Category and author assignment
- Responsive design with sticky headers
- Advanced table features with sorting, filtering, and pagination
- Custom field management with template support
Custom Fields System
The package includes a powerful custom fields system that allows you to create template-based custom fields with hierarchical structure.
Implementing Custom Fields
To enable custom fields on your model, implement the HasCustomField
contract:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use AntiCmsBuilder\Contracts\HasCustomField; use AntiCmsBuilder\Traits\CustomFields; class Product extends Model implements HasCustomField { use CustomFields; // Your model implementation }
Custom Field Contract
The HasCustomField
contract requires two methods:
interface HasCustomField { public function customFields(): \Illuminate\Database\Eloquent\Relations\MorphMany; public function getRootCustomFields(): Collection; }
The CustomFields
trait provides default implementations:
customFields()
: Morphs to many custom field recordsgetRootCustomFields()
: Returns root-level custom fields with their children, ordered by sort
Using Custom Fields in Forms
// In your FormBuilder RepeaterField::make() ->name('custom_template') ->label('Custom Fields') ->template('product_specifications') // References a template ->toArray()
Custom Field Configuration
The custom field model can be configured in config/anti-cms-builder.php
:
'models' => [ 'custom_field' => 'App\\Models\\CustomField\\CustomField', ], 'services' => [ 'custom_field' => 'App\\Services\\CustomFieldService', ],
Console Commands
Page Builder Command
The package includes an interactive CLI command for building JSON page structures:
php artisan page:build
This command provides an interactive interface to:
- Create new JSON page templates
- Edit existing templates
- Add, edit, and sort components
- Preview JSON structure
- Save templates to
storage/app/json/pages/
The command uses Laravel Prompts for a user-friendly CLI experience and integrates with the ComponentManager
for component management.
Conditional Fields
$builder->forms(function ($record, $operation) { $fields = [ InputField::make()->name('name')->toArray(), ]; // Add slug field only for updates if ($operation === 'update') { $fields[] = InputField::make() ->name('slug') ->disabled() ->toArray(); } // Add status field only for existing records if ($record && $record->exists) { $fields[] = SelectField::make() ->name('status') ->options([ ['value' => 'draft', 'label' => 'Draft'], ['value' => 'published', 'label' => 'Published'], ]) ->toArray(); } return $fields; });
Lifecycle Hooks
Save Process Flow
- Before Save: Custom validation, data preparation
- Save/Update: Model creation/update with fillable fields
- Process Relations: Handle relationships, media, translations
- After Save: Execute afterSave callback
Custom Save Logic
$builder->save(function (Request $request) { // Custom save logic $model = new Product(); $model->name = $request->name; $model->slug = Str::slug($request->name); $model->save(); return $model; });
After Save Hooks
$builder->afterSave(function ($record, $operation, $request) { // Send notifications if ($operation === 'create') { Mail::to($record->user)->send(new ProductCreated($record)); } // Update search index $record->searchable(); // Log activity activity() ->performedOn($record) ->log($operation === 'create' ? 'Product created' : 'Product updated'); });
Media and File Handling
Image Fields
ImageField::make() ->name('featured_image') ->label('Featured Image') ->required() ->toArray()
Image with Alt Text
The system automatically handles alt text for images:
// Request data structure [ 'featured_image' => 123, // File ID 'featured_image_alt' => 'Alt text' // Alt text (automatically handled) ]
Multiple Images in Repeater
RepeaterField::make() ->name('gallery') ->label('Image Gallery') ->fields([ ImageField::make()->name('image')->label('Image')->toArray(), InputField::make()->name('caption')->label('Caption')->multilanguage()->toArray(), ]) ->toArray()
Custom Fields Integration
When using models that implement HasCustomField
, the system automatically handles custom field data:
// Data is automatically processed and stored in custom_fields relationship // with proper hierarchical structure and template association
Custom Field Types
Create custom field types by extending the base FieldType
class:
<?php namespace AntiCmsBuilder\FieldTypes; class CustomField extends FieldType { protected string $type = 'custom'; public function customMethod(): self { $this->attributes['custom_attribute'] = true; return $this; } }
Extending Controllers
You can override default behavior in your controllers:
class ProductController extends Controller { use UseCrudController; protected string $model = Product::class; // Override default validation protected function defaultValidation($data = null): array { return [ 'custom_field' => 'required|string|max:255', ]; } // Add custom logic after saving public function afterSave($record, $request, $operation): void { // Custom logic here if ($operation === 'create') { // Do something after creating } } // Custom status options protected function statusOptions(): array { return [ ['value' => 'draft', 'label' => 'Draft'], ['value' => 'published', 'label' => 'Published'], ]; } }
Package Structure
anticms-builder/
├── .github/
│ └── workflows/
│ └── ci.yml # GitHub Actions CI workflow
├── config/
│ └── anti-cms-builder.php # Package configuration file
├── resources/
│ └── js/
│ ├── Components/
│ │ ├── fields/
│ │ │ ├── Builder.jsx # Core builder component
│ │ │ └── FieldBuilderComponent.jsx
│ │ ├── form/
│ │ │ └── CreateEditFormWithBuilder.jsx
│ │ └── Table/
│ │ ├── DynamicRowActions.jsx
│ │ └── DynamicTableActions.jsx
│ ├── Pages/
│ │ └── CRUD/
│ │ ├── Actions/
│ │ │ └── ActionBuilders.jsx
│ │ ├── Create.jsx
│ │ ├── Edit.jsx
│ │ ├── Index.jsx
│ │ └── TestPackagePage.jsx
│ └── utils/
│ └── resolverPage.js
├── src/
│ ├── Console/
│ │ └── Commands/
│ │ └── PageBuilderCommand.php
│ ├── Contracts/
│ │ ├── HasCustomField.php
│ │ ├── HasForm.php
│ │ └── HasMeta.php
│ ├── FieldTypes/
│ │ ├── Traits/
│ │ │ └── SelectOptionTrait.php
│ │ ├── CustomField.php
│ │ ├── FieldType.php
│ │ ├── FileField.php
│ │ ├── ImageField.php
│ │ ├── InputField.php
│ │ ├── MultiSelectField.php
│ │ ├── RepeaterField.php
│ │ ├── Section.php
│ │ ├── SelectField.php
│ │ ├── TextareaField.php
│ │ ├── TexteditorField.php
│ │ └── ToggleField.php
│ ├── Filters/
│ │ └── SelectField.php
│ ├── Forms/
│ │ └── FormBuilder.php
│ ├── Support/
│ │ └── Color.php
│ ├── Tables/
│ │ ├── Actions/
│ │ │ ├── BulkAction.php
│ │ │ ├── RowAction.php
│ │ │ └── TableAction.php
│ │ ├── Columns/
│ │ │ └── TextColumn.php
│ │ └── TableBuilder.php
│ ├── Traits/
│ │ ├── CustomFields.php
│ │ └── UseCrudController.php
│ ├── AntiCmsBuilderServiceProvider.php
│ ├── ComponentManager.php
│ ├── FieldManager.php
│ ├── FieldService.php
│ ├── Resolver.php
│ └── SortHelper.php
├── tests/
│ ├── Feature/
│ │ └── UseCrudControllerTest.php
│ ├── Support/
│ │ ├── migrations/
│ │ │ └── 2024_01_01_000000_create_test_models_table.php
│ │ ├── TestController.php
│ │ └── TestModel.php
│ ├── Unit/
│ │ ├── FieldTypes/
│ │ │ ├── FieldTypeTest.php
│ │ │ ├── InputFieldTest.php
│ │ │ └── SelectFieldTest.php
│ │ ├── Tables/
│ │ │ └── DynamicParameterTest.php
│ │ ├── FieldServiceTest.php
│ │ ├── FormBuilderTest.php
│ │ ├── ServiceProviderTest.php
│ │ └── TableBuilderTest.php
│ ├── README.md
│ └── TestCase.php
├── composer.json
├── phpunit.xml
└── README.md
Best Practices
- Field Organization: Group related fields logically
- Validation: Use appropriate validation rules for each field type
- Multilingual: Enable multilingual support for user-facing content
- Relationships: Use proper relation methods for better performance
- Custom Logic: Use lifecycle hooks for complex business logic
- Error Handling: Implement proper error handling in custom callbacks
- Performance: Use eager loading for relationship options
- Security: Validate and sanitize all input data
Troubleshooting
Common Issues
- Relation not found: Ensure the relation method exists on the model
- Validation errors: Check field attributes and custom rules
- Translation issues: Verify the model has translation support
- Media upload problems: Check file permissions and storage configuration
- Custom field errors: Ensure the model implements
HasCustomField
and uses theCustomFields
trait - Configuration not found: Run
php artisan vendor:publish --tag=anti-cms-builder-config
- React components not found: Run
php artisan vendor:publish --tag=anti-cms-builder-resources
Debugging Tips
- Use
dd($formBuilder->getForms())
to inspect form structure - Check
$formBuilder->getRules()
for validation rules - Enable Laravel debugging for detailed error messages
- Use database queries log to debug relation issues
- Check storage logs for media upload problems
Requirements
- PHP 8.2+
- Laravel 10.0+ or 11.0+
- React + Inertia.js (for frontend components)
- Composer 2.0+
License
MIT License
Contributing
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
Changelog
See CHANGELOG.md for recent changes and version history.
Support
For issues and questions:
- Create an issue in the GitHub repository
- Check the CI status for known issues
- Review the test files in
tests/
for usage examples
Documentation
This documentation covers the complete functionality of the AntiCmsBuilder package. For more specific use cases or advanced customizations:
- Refer to the source code in the
src/
directory - Check the React components in
resources/js/
- Review the test files for working examples
- Examine the configuration file at
config/anti-cms-builder.php