onaonbir / oo-metas
Flexible and extensible metadata management system for Laravel models.
Installs: 15
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
pkg:composer/onaonbir/oo-metas
Requires
- php: ^8.3
- illuminate/support: ^10.0|^11.0|^12.0
README
OOMetas is a powerful, refactored polymorphic metadata management system for Laravel.
It provides a clean architecture with repository pattern, service layer, caching, and extensive features for managing dynamic key-value data on any model.
β¨ Key Features
- ποΈ Clean Architecture: Repository pattern, Service layer, Contracts
- β‘ Performance: Built-in caching layer with configurable TTL
- π§ Flexible: Supports connected models and nested keys (dot notation)
- π‘οΈ Type Safety: Strict typing with PHP 8.3+ and Value Objects
- π¦ Batch Operations: Efficient bulk operations for better performance
- π― Exception Handling: Proper error handling with custom exceptions
- π Validation: Built-in validation for keys and values
- π§ͺ Testable: Fully testable with dependency injection
π§ Installation
- Install the package:
composer require onaonbir/oo-metas
- Publish and run migrations:
php artisan vendor:publish --tag=oo-metas-migrations php artisan migrate
- Publish configuration (optional):
php artisan vendor:publish --tag=oo-metas-config
ποΈ Architecture Overview
βββββββββββββββββββββββββββββββββββββββββββββββ
β Facade β
β OOMetas::class β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββββββββΌββββββββββββββββββββββββββββ
β Service β
β MetaService::class β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββββββββΌββββββββββββββββββββββββββββ
β Repository β
β MetaRepository / CachedMetaRepository β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββββββββΌββββββββββββββββββββββββββββ
β Model β
β Meta::class β
βββββββββββββββββββββββββββββββββββββββββββββββ
π Usage Guide
1. π§© Using the Trait (Recommended)
Add the HasMetas trait to any model:
use OnaOnbir\OOMetas\Traits\HasMetas; class User extends Model { use HasMetas; }
Basic Operations
// Set metadata $user->setMeta('theme', 'dark'); $user->setMeta('profile.avatar_url', 'https://example.com/avatar.jpg'); // Get metadata $theme = $user->getMeta('theme'); // 'dark' $avatar = $user->getMeta('profile.avatar_url'); $nonExistent = $user->getMeta('non_existent', 'default_value'); // Check if meta exists if ($user->hasMeta('theme')) { // Theme is set } // Remove metadata $user->forgetMeta('theme');
Batch Operations
// Set multiple metas at once $user->setManyMetas([ 'theme' => 'dark', 'language' => 'en', 'timezone' => 'UTC' ]); // Get multiple metas $settings = $user->getManyMetas(['theme', 'language', 'timezone']); // Result: ['theme' => 'dark', 'language' => 'en', 'timezone' => 'UTC'] // Get all metas for a user $allMetas = $user->getAllMetas(); // Remove multiple metas $user->forgetManyMetas(['theme', 'language']); // Remove all metas $user->forgetAllMetas();
Numeric Operations
// Increment/Decrement $user->setMeta('login_count', 0); $newCount = $user->incrementMeta('login_count'); // 1 $newCount = $user->incrementMeta('login_count', 5); // 6 $newCount = $user->decrementMeta('login_count', 2); // 4
Special Operations
// Pull (get and remove) $theme = $user->pullMeta('theme', 'light'); // Gets value and removes it // Remember (get or set via callback) $settings = $user->rememberMeta('user_settings', function() { return ['theme' => 'light', 'language' => 'en']; }); // Toggle boolean values $isActive = $user->toggleMeta('is_active'); // true/false
Array Operations
// Work with array metadata $user->setMeta('tags', ['developer', 'php']); // Append to array $user->appendToMeta('tags', 'laravel'); // ['developer', 'php', 'laravel'] // Prepend to array $user->prependToMeta('tags', 'senior'); // ['senior', 'developer', 'php', 'laravel'] // Remove from array $user->removeFromMetaArray('tags', 'php'); // ['senior', 'developer', 'laravel']
Type-Safe Getters
// Get with type casting $tags = $user->getMetaAsArray('tags'); // Always returns array $name = $user->getMetaAsString('display_name', 'Anonymous'); $count = $user->getMetaAsInt('login_count', 0); $isActive = $user->getMetaAsBool('is_active', false); $score = $user->getMetaAsFloat('score', 0.0);
2. π Connected Models
Connect metadata to relationships between models:
// Set user role in a specific project $user->setMeta('role', 'admin', $project); $user->setMeta('permissions', ['read', 'write', 'delete'], $project); // Get user role for this project $role = $user->getMeta('role', 'guest', $project); // 'admin' // Different project, different role $role2 = $user->getMeta('role', 'guest', $anotherProject); // 'guest' // You can also use class strings for type-only connections $user->setMeta('notification_preference', 'email', Project::class);
3. π― Using the Facade
use OnaOnbir\OOMetas\OOMetas; // All trait methods are available as static calls OOMetas::set($user, 'theme', 'dark'); $theme = OOMetas::get($user, 'theme'); OOMetas::forget($user, 'theme'); // Batch operations OOMetas::setMany($user, ['key1' => 'value1', 'key2' => 'value2']); $values = OOMetas::getMany($user, ['key1', 'key2']);
4. ποΈ Direct Service Usage
use OnaOnbir\OOMetas\Contracts\MetaServiceInterface; class UserSettingsController { public function __construct( private MetaServiceInterface $metaService ) {} public function updateSettings(User $user, array $settings) { $this->metaService->setMany($user, $settings); } }
βοΈ Configuration
Cache Configuration
// config/oo-metas.php 'cache' => [ 'enabled' => true, 'ttl' => 3600, // 1 hour 'prefix' => 'oo_metas', ],
Validation Configuration
'validation' => [ 'key' => [ 'max_length' => 255, 'allowed_characters' => '/^[\w\-\.]+$/', ], 'value' => [ 'max_depth' => 10, 'max_size' => 1024 * 1024, // 1MB ], ],
Performance Configuration
'performance' => [ 'batch_size' => 100, 'query_timeout' => 30, ],
π‘οΈ Error Handling
The package provides specific exceptions for better error handling:
use OnaOnbir\OOMetas\Exceptions\InvalidMetaKeyException; use OnaOnbir\OOMetas\Exceptions\MetaNotFoundException; use OnaOnbir\OOMetas\Exceptions\MetaValidationException; try { $user->setMeta('invalid@key!', 'value'); } catch (InvalidMetaKeyException $e) { // Handle invalid key format }
π§ͺ Testing
The refactored architecture makes testing much easier:
// Mock the service in tests $this->app->bind(MetaServiceInterface::class, function () { return Mockery::mock(MetaServiceInterface::class); }); // Test with different repository implementations $this->app->bind(MetaRepositoryInterface::class, InMemoryMetaRepository::class);
π Performance Considerations
- Use Caching: Enable caching for frequently accessed metadata
- Batch Operations: Use
setMany/getManyfor multiple operations - Indexed Queries: The package automatically indexes queries by model and key
- Connection Scoping: Use connected models to scope metadata efficiently
π Migration from Previous Versions
The new version is backward compatible, but to take advantage of new features:
- Update your code to use the trait methods instead of direct
OOMetas::calls - Enable caching in configuration
- Use batch operations where possible
- Consider using Value Objects for better type safety
π Advanced Usage
Custom Repository Implementation
class CustomMetaRepository implements MetaRepositoryInterface { // Implement your custom logic } // Register in service provider $this->app->bind(MetaRepositoryInterface::class, CustomMetaRepository::class);
Custom Value Objects
$key = MetaKey::make('user.settings.theme'); $identifier = MetaIdentifier::fromModel($user, $project); $value = MetaValue::make(['color' => 'blue']);
π οΈ Contributing
- Fork the repository
- Create a feature branch
- Write tests for your changes
- Ensure all tests pass
- Submit a pull request
π License
MIT Β© OnaOnbir
π― Example Use Cases
- User Preferences: Theme, language, notification settings
- Feature Flags: Per-user or per-model feature toggles
- Dynamic Forms: Store form responses dynamically
- Analytics: Store usage statistics and metrics
- Workflow States: Track process states and configurations
- Multi-tenant Data: Scope data per tenant/organization
- Audit Trails: Store metadata about changes and actions