daimos/entity-changes-fetcher-bundle
Installation
composer require daimos/entity-changes-fetcher-bundle
Add to config/bundles.php:
return [
// ...
Daimos\EntityChangesFetcherBundle\DaimosEntityChangesFetcherBundle::class => ['all' => true],
];
Configuration Publish the default config:
php artisan vendor:publish --tag=entity-changes-fetcher-config
Edit config/entity_changes_fetcher.php to define:
entities (array of tracked entities)fetch_interval (e.g., 60 for minutes)storage (default: database or cache)First Use Case
Track changes for a Post entity:
// config/entity_changes_fetcher.php
'entities' => [
'App\Entity\Post' => [
'fields' => ['title', 'content', 'publishedAt'],
'ignore_fields' => ['createdAt', 'updatedAt'],
],
];
Trigger a manual fetch (for testing):
php artisan entity-changes:fetch
Automatic Fetching Register a console command to run periodically (e.g., via cron):
* * * * * php artisan entity-changes:fetch >> /dev/null 2>&1
Configure in config/entity_changes_fetcher.php:
'auto_fetch' => true,
'fetch_interval' => 60, // minutes
Event-Driven Integration Listen for change events in your application:
// src/EventListener/ChangeListener.php
use Daimos\EntityChangesFetcherBundle\Event\EntityChangedEvent;
class ChangeListener implements EventSubscriber
{
public static function getSubscribedEvents()
{
return [
EntityChangedEvent::class => 'onEntityChanged',
];
}
public function onEntityChanged(EntityChangedEvent $event)
{
$entity = $event->getEntity();
$changes = $event->getChanges();
// Process changes (e.g., log, notify, sync)
logger()->info("Entity {$entity::class} changed:", $changes);
}
}
Storage Strategies
entity_changes table (default).
'storage' => [
'driver' => 'database',
'table' => 'entity_changes',
],
'storage' => [
'driver' => 'cache',
'prefix' => 'entity_changes_',
],
Custom Fetch Logic Extend the fetcher for complex queries:
// src/Service/PostChangeFetcher.php
use Daimos\EntityChangesFetcherBundle\Fetcher\AbstractEntityFetcher;
class PostChangeFetcher extends AbstractEntityFetcher
{
protected function getQueryBuilder()
{
return $this->entityManager
->getRepository($this->entityClass)
->createQueryBuilder('p')
->where('p.publishedAt > :lastFetch')
->setParameter('lastFetch', $this->lastFetchTime);
}
}
Register in config/entity_changes_fetcher.php:
'fetchers' => [
'App\Entity\Post' => App\Service\PostChangeFetcher::class,
],
Performance Overhead
ignore_fields to exclude unnecessary columns from change detection.Race Conditions
database storage, ensure your entity_changes table has:
// migrations/xxxx_create_entity_changes_table.php
Schema::create('entity_changes', function (Blueprint $table) {
$table->id();
$table->string('entity_class');
$table->json('changes');
$table->timestamp('fetched_at');
$table->unique(['entity_class', 'fetched_at']); // Prevent duplicates
});
Event Ordering
Doctrine Proxy Issues
# config/doctrine.yaml
orm:
entity_managers:
default:
metadata_driver:
class: Doctrine\ORM\Mapping\Driver\AnnotationDriver
# ...
dql:
string_functions:
# ...
proxies: false # Disable proxies for change tracking
Log Fetch Results
Enable debug mode in config/entity_changes_fetcher.php:
'debug' => true,
Logs will appear in storage/logs/laravel.log.
Manual Fetch with Verbosity
php artisan entity-changes:fetch --verbose
Outputs SQL queries and fetched changes.
Check Storage
entity_changes table:
SELECT * FROM entity_changes ORDER BY fetched_at DESC LIMIT 10;
php artisan cache:table
redis-cli KEYS "entity_changes_*"
Custom Storage Drivers
Implement Daimos\EntityChangesFetcherBundle\Storage\StorageInterface:
class CustomStorage implements StorageInterface
{
public function saveChanges(string $entityClass, array $changes): void
{
// Save to external API, S3, etc.
}
public function getLastFetchTime(string $entityClass): ?\DateTimeInterface
{
// Retrieve from custom source
}
}
Register in config:
'storage' => [
'driver' => App\Storage\CustomStorage::class,
],
Pre/Post-Fetch Hooks Extend the fetcher class and override:
protected function beforeFetch(): void {}
protected function afterFetch(): void {}
Bulk Change Processing
For large datasets, batch changes using Doctrine’s BATCH_SIZE:
$qb->setHint(\Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD, true)
->setHint(\Doctrine\ORM\Query::HINT_CACHEABLE, false)
->setMaxResults(1000);
How can I help you explore Laravel packages today?