Ever wished you could add fields to your Laravel models without touching migrations?
Now you can. Welcome to FlexyField.
composer require aurorawebsoftware/flexyfield
php artisan migrate// Give your model the power
class Product extends Model implements FlexyModelContract
{
use Flexy;
}
// Create a schema (think of it as a "field template")
Product::createSchema('electronics', 'Electronics');
Product::addFieldToSchema('electronics', 'voltage', FlexyFieldType::STRING);
Product::addFieldToSchema('electronics', 'warranty_years', FlexyFieldType::INTEGER);
// Use it like it was always there
$tv = Product::create(['name' => 'Smart TV']);
$tv->assignToSchema('electronics');
$tv->flexy->voltage = '220V';
$tv->flexy->warranty_years = 2;
$tv->save();
// Query like a boss
Product::where('flexy_voltage', '220V')->get();No migrations. No schema changes. Just vibes. π
| Problem | Old Way | FlexyField Way |
|---|---|---|
| "We need a new product attribute" | Migration + deploy + pray π | addFieldToSchema() β
|
| "Different products need different fields" | JSON column chaos π± | Schemas! π― |
| "Can we query by that custom field?" | nervous laughter | where('flexy_field', $value) π |
| Type | What it stores | Example |
|---|---|---|
STRING |
Text | $m->flexy->color = 'blue' |
INTEGER |
Whole numbers | $m->flexy->stock = 42 |
DECIMAL |
Money, measurements | $m->flexy->price = 99.99 |
BOOLEAN |
Yes/No | $m->flexy->active = true |
DATE |
Dates | $m->flexy->release = '2024-01-01' |
DATETIME |
Timestamps | $m->flexy->created = now() |
JSON |
Arrays, objects | $m->flexy->tags = ['hot', 'new'] |
FILE |
Uploads | $m->flexy->manual = $request->file('pdf') |
// Shoes have sizes and colors
Product::createSchema('footwear', 'Footwear');
Product::addFieldToSchema('footwear', 'shoe_size', FlexyFieldType::INTEGER,
validationRules: 'required|between:35,50');
Product::addFieldToSchema('footwear', 'color', FlexyFieldType::STRING,
fieldMetadata: ['options' => ['black', 'white', 'red', 'blue']]);
// Books have ISBNs and authors
Product::createSchema('books', 'Books');
Product::addFieldToSchema('books', 'isbn', FlexyFieldType::STRING,
validationRules: 'required|size:13');
Product::addFieldToSchema('books', 'author', FlexyFieldType::STRING);
Product::addFieldToSchema('books', 'pages', FlexyFieldType::INTEGER);
// Same model, different fields!
$sneakers = Product::create(['name' => 'Air Max']);
$sneakers->assignToSchema('footwear');
$sneakers->flexy->shoe_size = 42;
$sneakers->flexy->color = 'black';
$novel = Product::create(['name' => 'Clean Code']);
$novel->assignToSchema('books');
$novel->flexy->isbn = '9780132350884';
$novel->flexy->author = 'Robert C. Martin';// B2B customers need company info
Contact::createSchema('b2b', 'Business Customers');
Contact::addFieldToSchema('b2b', 'company_name', FlexyFieldType::STRING);
Contact::addFieldToSchema('b2b', 'employee_count', FlexyFieldType::INTEGER);
Contact::addFieldToSchema('b2b', 'annual_revenue', FlexyFieldType::DECIMAL);
// B2C customers need personal preferences
Contact::createSchema('b2c', 'Individual Customers');
Contact::addFieldToSchema('b2c', 'birthday', FlexyFieldType::DATE);
Contact::addFieldToSchema('b2c', 'interests', FlexyFieldType::JSON,
fieldMetadata: ['options' => ['tech', 'sports', 'music', 'travel'], 'multiple' => true]);
// Query by segment
$bigCompanies = Contact::whereSchema('b2b')
->where('flexy_annual_revenue', '>', 1000000)
->get();// Each tenant can have custom fields!
$tenantSchema = "tenant_{$tenant->id}_leads";
Lead::createSchema($tenantSchema, "{$tenant->name} Leads");
Lead::addFieldToSchema($tenantSchema, 'source', FlexyFieldType::STRING);
Lead::addFieldToSchema($tenantSchema, 'score', FlexyFieldType::INTEGER);
// Tenant-specific fields added via admin panel
foreach ($tenant->customFields as $field) {
Lead::addFieldToSchema($tenantSchema, $field->name, $field->type);
}// Single choice
Product::addFieldToSchema('schema', 'size', FlexyFieldType::STRING,
fieldMetadata: ['options' => ['S', 'M', 'L', 'XL']]);
// Multiple choices (use JSON type!)
Product::addFieldToSchema('schema', 'features', FlexyFieldType::JSON,
fieldMetadata: ['options' => ['wifi', 'bluetooth', '5g'], 'multiple' => true]);
$phone->flexy->size = 'M';
$phone->flexy->features = ['wifi', '5g']; // Array!Product::addFieldToSchema('schema', 'manual', FlexyFieldType::FILE,
validationRules: 'required|mimes:pdf|max:5120',
fieldMetadata: ['disk' => 's3', 'allowed_extensions' => ['pdf']]);
$product->flexy->manual = $request->file('manual');
$product->save();
// Get URLs
$url = $product->getFlexyFileUrl('manual');
$signedUrl = $product->getFlexyFileUrlSigned('manual', now()->addHour()->timestamp);Product::addFieldToSchema('schema', 'email', FlexyFieldType::STRING,
validationRules: 'required|email|max:255',
validationMessages: ['email.email' => 'GeΓ§erli bir email girin!']);
$product->flexy->email = 'not-an-email';
$product->save(); // π₯ ValidationException!Product::addFieldToSchema('schema', 'battery', FlexyFieldType::INTEGER,
label: 'Battery Capacity',
fieldMetadata: [
'group' => 'Technical Specs',
'placeholder' => 'mAh',
'hint' => 'Typical range: 3000-5000'
]);
// Perfect for building dynamic forms!
$schema->getFieldsGrouped(); // ['Technical Specs' => [...], 'Ungrouped' => [...]]// Simple
Product::where('flexy_color', 'blue')->get();
// Dynamic method (Laravel magic!)
Product::whereFlexyColor('blue')->get();
// By schema
Product::whereSchema('electronics')->get();
Product::whereSchemaIn(['electronics', 'furniture'])->get();
// Go wild
Product::whereSchema('electronics')
->where('flexy_price', '<', 100)
->where('flexy_active', true)
->orderBy('flexy_price')
->get();Publish the config file:
php artisan vendor:publish --tag="flexyfield-config"// config/flexyfield.php
return [
'file_storage' => [
'default_disk' => env('FLEXYFIELD_DEFAULT_DISK', 'public'),
'default_path' => env('FLEXYFIELD_DEFAULT_PATH', 'flexyfield'),
'path_structure' => '{model_type}/{schema_code}/{field_name}/{year}/{month}',
'cleanup_on_delete' => true, // Auto-delete files when model deleted
'enable_security_logging' => true, // Log security events
],
];Environment Variables:
FLEXYFIELD_DEFAULT_DISK=s3
FLEXYFIELD_DEFAULT_PATH=uploads/flexy
FLEXYFIELD_CLEANUP_DELETE=true
FLEXYFIELD_SECURITY_LOGGING=trueFlexyField comes with Laravel Boost guidelines for AI agents!
resources/boost/guidelines/core.blade.php
Your AI assistant (Claude, GPT, Copilot) can read this file and instantly understand:
- All API methods and signatures
- Field types and metadata options
- Common patterns and best practices
- Error handling and exceptions
Just point your AI to the guideline file and watch it write perfect FlexyField code! π―
// Setting values before assigning schema
$product->flexy->field = 'x'; // π₯ Exception!
// Always assign first!
$product->assignToSchema('electronics');
$product->flexy->field = 'x'; // β
// Wrong query syntax
Product::where('flexy->field', 'x'); // π« Nope
// Use underscore prefix
Product::where('flexy_field', 'x'); // β
Yes!- Smart view recreation - Only rebuilds when NEW fields are added
- Scales well - Tested with 50+ fields, 1M+ records
- Manual rebuild -
php artisan flexyfield:rebuild-view
- PHP 8.2+
- Laravel 11+
- MySQL 8+ or PostgreSQL 16+
| Guide | What's Inside |
|---|---|
| Performance | Make it fly π |
| Best Practices | Do it right β |
| Deployment | Ship it safely π¦ |
| Troubleshooting | Fix it fast π§ |
| File Security | Lock it down π |
composer install
./vendor/bin/pest # Run tests
./vendor/bin/phpstan analyse # Static analysis
./vendor/bin/pint # Code stylePRs welcome! π€
MIT - Go build something awesome! π
Made with β by Aurora Web Software
β Star us on GitHub!