beelab/memory-repository-bundle
Installation:
Add the package to composer.json with the exact versions specified in the README (note: these are @dev versions, so use cautiously):
composer require beelab/memory-repository-bundle:0.1.*@dev
Ensure your project uses Doctrine ORM 2.5.x and DoctrineBundle ~1.3@beta (legacy versions).
Enable the Bundle:
Register the bundle in config/bundles.php:
return [
// ...
Beelab\MemoryRepositoryBundle\BeelabMemoryRepositoryBundle::class => ['all' => true],
];
First Use Case:
ArticleMemoryRepository).Doctrine\ORM\EntityRepository or implement ObjectRepository.array) to mock persistence.class ArticleMemoryRepository implements ObjectRepository {
private $data = [];
public function find($id) {
return $this->data[$id] ?? null;
}
public function findAll() {
return array_values($this->data);
}
public function save(Article $article) {
$this->data[$article->getId()] = $article;
}
}
Configure the Bundle:
Override the repository class in your entity’s @ORM\Entity annotation:
/**
* @ORM\Entity(repositoryClass="Acme\DemoBundle\Repository\ArticleMemoryRepository")
*/
class Article { ... }
Test Integration:
Use the bundle in tests by injecting the EntityManager with the in-memory repository. Example:
public function testArticleRepository() {
$em = $this->getEntityManager();
$article = new Article();
$em->persist($article);
$em->flush(); // Uses in-memory storage
$this->assertNotNull($em->find(Article::class, $article->getId()));
}
Test-Driven Development (TDD):
// Test setup
$repository = $this->createMock(ObjectRepository::class);
$repository->method('find')->willReturn(new Article());
$this->getEntityManager()->getRepository(Article::class)->expects($this->once())->method('find')->with(1);
// Test execution
$article = $this->getEntityManager()->find(Article::class, 1);
$this->assertInstanceOf(Article::class, $article);
Hybrid Development:
$memoryRepo = $em->getRepository(Article::class);
$memoryRepo->save(new Article(['title' => 'Test']));
Repository Decorator Pattern:
class CachedArticleRepository implements ObjectRepository {
private $decorated;
public function __construct(ObjectRepository $decorated) {
$this->decorated = $decorated;
}
public function find($id) {
// Add caching logic
return $this->decorated->find($id);
}
}
Doctrine Event Subscribers:
prePersist, preUpdate, etc., to sync in-memory data with business logic.$eventManager->addEventSubscriber(new class() {
public function prePersist(LifecycleEventArgs $args) {
$entity = $args->getEntity();
if ($entity instanceof Article) {
$entity->setCreatedAt(new \DateTime());
}
}
});
QueryBuilder Mocking:
createQueryBuilder() in your in-memory repository to return mock QueryBuilder instances:
public function createQueryBuilder($alias) {
$qb = $this->createQueryBuilderMock($alias);
$qb->method('getQuery')->willReturn(new \stdClass());
return $qb;
}
Service Container Binding:
# config/services_test.yaml
services:
Acme\DemoBundle\Repository\ArticleRepository:
class: Acme\DemoBundle\MemoryRepository\ArticleMemoryRepository
arguments:
- '@doctrine.orm.entity_manager'
- '@doctrine.orm.metadata_factory'
Data Migration:
$memoryRepo = $em->getRepository(Article::class);
$memoryRepo->clear(); // Reset state
$memoryRepo->save(new Article(['title' => 'Migrated']));
Doctrine Version Mismatch:
EntityManager Configuration:
EntityManager is configured to use an in-memory driver (e.g., sqlite :memory: for SQLite) if needed.EntityManager will not hit a real database but will still trigger lifecycle callbacks.Repository Class Overrides:
repositoryClass in @ORM\Entity is not updated, the bundle won’t work. Always verify the annotation.get_class($em->getRepository(Article::class)) to confirm the correct repository is loaded.Thread Safety:
$memoryRepo->clear();
Missing Features:
ObjectRepository methods. Override only the methods you need (e.g., find, findAll, save).Doctrine\ORM\EntityRepository for partial implementations.Repository Not Loaded:
config/bundles.php.repositoryClass in the entity annotation matches your in-memory repository.Data Not Persisting:
$em->flush() after persisting entities.save/find methods:
public function save(Article $article) {
error_log("Saving article: " . $article->getId());
$this->data[$article->getId()] = $article;
}
QueryBuilder Issues:
createQueryBuilder() fails, mock it explicitly in your repository:
public function createQueryBuilder($alias) {
throw new \BadMethodCallException("QueryBuilder not supported in memory repo");
}
Custom Storage Backend:
array storage with a more sophisticated backend (e.g., Redis, PHP’s APCu):
class RedisArticleRepository implements ObjectRepository {
private $redis;
public function __construct(\Redis $redis) {
$this->redis = $redis;
}
public function find($id) {
$data = $this->redis->get("article:$id");
return $data ? unserialize($data) : null;
}
}
Serialization Handling:
public function save(Article $article) {
$this->data[$article->getId()] = serialize($article);
}
public function find($id) {
return unserialize($this->data[$id] ?? null);
}
Event-Driven Extensions:
onFlush events to sync in-memory data with external APIs:
$em->getEventManager()->addEventListener(
\Doctrine\ORM\Events::onFlush,
function ($event) {
$entityManager = $event->getEntityManager();
$uow = $entityManager->getUnitOfWork();
foreach ($uow->getScheduledEntityInsertions() as $entity) {
if ($entity instanceof Article) {
// Sync with external API
}
}
}
);
**
How can I help you explore Laravel packages today?