dmytrof/models-management-bundle
Symfony 4/5 bundle to manage models and entities from databases and external sources (e.g., third-party APIs). Provides a unified way to organize, handle, and work with your application’s domain data.
Install and Enable:
composer require dmytrof/models-management-bundle
Enable in config/bundles.php:
return [
// ...
Dmytrof\ModelsManagementBundle\DmytrofModelsManagementBundle::class => ['all' => true],
];
Define a Model Manager:
Create a service extending AbstractModelManager for your entity (e.g., User):
// src/ModelManager/UserManager.php
namespace App\ModelManager;
use Dmytrof\ModelsManagementBundle\ModelManager\AbstractModelManager;
use App\Entity\User;
class UserManager extends AbstractModelManager
{
protected static $modelClass = User::class;
protected static $repositoryClass = 'App\Repository\UserRepository';
}
Tag the Manager as a Service:
In config/services.yaml:
services:
App\ModelManager\UserManager:
tags: ['dmytrof.model_manager']
First Usage: Inject the manager and fetch a model:
use App\ModelManager\UserManager;
class SomeService {
public function __construct(private UserManager $userManager) {}
public function getUser(int $id) {
return $this->userManager->find($id);
}
}
Verify Configuration:
Check config/packages/dmytrof_models_management.yaml for bundle-specific settings (e.g., caching, event listeners).
Model Management:
find(), findAll(), create(), update(), and delete() methods from AbstractModelManager.$user = $userManager->find(1);
$user->setEmail('new@example.com');
$userManager->update($user);
Hybrid Data Sources:
AbstractModelManager to support non-Doctrine sources (e.g., APIs) by overriding fetch():
protected function fetch($id, array $criteria = [])
{
$response = $this->apiClient->get("/users/{$id}");
return $this->transformer->toModel($response);
}
Event-Driven Extensions:
ModelLoadedEvent) to add logic:
// src/EventListener/UserModelListener.php
use Dmytrof\ModelsManagementBundle\Event\ModelLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class UserModelListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
ModelLoadedEvent::class => 'onModelLoaded',
];
}
public function onModelLoaded(ModelLoadedEvent $event)
{
if ($event->getModel() instanceof User) {
$event->getModel()->setLastFetched(new \DateTime());
}
}
}
Bulk Operations:
findAll() with criteria for filtering:
$activeUsers = $userManager->findAll(['active' => true]);
Caching:
# config/packages/dmytrof_models_management.yaml
dmytrof_models_management:
cache: true
cache_pool: 'app.cache.doctrine'
Doctrine Integration:
AbstractModelManager::$repositoryClass.doctrine.orm.entity_manager as a service dependency if needed.API Clients:
AbstractModelManager subclasses.Validation:
use Symfony\Component\Validator\Constraints as Assert;
class User {
/**
* @Assert\Email
*/
private $email;
}
Testing:
AbstractModelManager in unit tests:
$mockManager = $this->createMock(UserManager::class);
$mockManager->method('find')->willReturn(new User());
Symfony Version Mismatch:
Missing Configuration:
dmytrof.model_manager will prevent autoloading. Always verify:
tags: ['dmytrof.model_manager']
Overriding Core Methods:
fetch() or save() methods may break if the bundle’s internal logic changes. Prefer extending via events.Caching Quirks:
cache: false first.Event Dispatching:
ModelLoadedEvent are not dispatched by default. Ensure your listeners are properly subscribed.Doctrine Repository Conflicts:
find(), save()).Enable Debug Mode:
.env:
APP_DEBUG=1
Log Model Events:
public function onModelLoaded(ModelLoadedEvent $event)
{
\Log::debug('Model loaded:', [
'class' => get_class($event->getModel()),
'id' => $event->getModel()->getId(),
]);
}
Check Bundle Configuration:
config/packages/dmytrof_models_management.yaml for typos or missing keys.Verify Service Autowiring:
bin/console debug:container for service definitions.Test with Minimal Data:
Custom Model Transformers:
Dmytrof\ModelsManagementBundle\Transformer\ModelTransformerInterface to handle non-standard data formats.Additional Model Sources:
AbstractModelManager to support new sources (e.g., Elasticsearch, MongoDB) by overriding fetch() and save().Custom Events:
ModelPreSaveEvent) for pre/post-processing:
$dispatcher->dispatch(new ModelPreSaveEvent($model));
Configuration Overrides:
config/packages/dmytrof_models_management.yaml:
dmytrof_models_management:
default_cache_lifetime: 3600
enabled_listeners: ['cache_listener', 'validation_listener']
Performance Optimizations:
AbstractModelManager.Cache Pool:
app.cache.doctrine) exists in config/packages/cache.yaml.Event Priorities:
public static function getSubscribedEvents()
{
return [
ModelLoadedEvent::class => ['onModelLoaded', 20], // Higher priority = earlier execution
];
}
Model Class Validation:
$modelClass in AbstractModelManager is a valid entity. Use is_subclass_of() to validate dynamically:
if (!is_subclass_of(static::$modelClass, Entity::class)) {
throw new \InvalidArgumentException('Invalid model class');
}
How can I help you explore Laravel packages today?