Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Memory Repository Bundle Laravel Package

beelab/memory-repository-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. 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).

  2. Enable the Bundle: Register the bundle in config/bundles.php:

    return [
        // ...
        Beelab\MemoryRepositoryBundle\BeelabMemoryRepositoryBundle::class => ['all' => true],
    ];
    
  3. First Use Case:

    • Define an in-memory repository class for your entity (e.g., ArticleMemoryRepository).
    • Extend Doctrine\ORM\EntityRepository or implement ObjectRepository.
    • Use a simple in-memory storage (e.g., array) to mock persistence.
    • Example:
      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;
          }
      }
      
  4. Configure the Bundle: Override the repository class in your entity’s @ORM\Entity annotation:

    /**
     * @ORM\Entity(repositoryClass="Acme\DemoBundle\Repository\ArticleMemoryRepository")
     */
    class Article { ... }
    
  5. 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()));
    }
    

Implementation Patterns

Workflows

  1. Test-Driven Development (TDD):

    • Replace database interactions in unit/integration tests with in-memory repositories.
    • Example workflow:
      // 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);
      
  2. Hybrid Development:

    • Use the bundle in development environments to simulate data without a database.
    • Example: Seed in-memory data for local testing:
      $memoryRepo = $em->getRepository(Article::class);
      $memoryRepo->save(new Article(['title' => 'Test']));
      
  3. Repository Decorator Pattern:

    • Extend the bundle’s functionality by decorating the in-memory repository.
    • Example:
      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);
          }
      }
      

Integration Tips

  1. Doctrine Event Subscribers:

    • Use prePersist, preUpdate, etc., to sync in-memory data with business logic.
    • Example:
      $eventManager->addEventSubscriber(new class() {
          public function prePersist(LifecycleEventArgs $args) {
              $entity = $args->getEntity();
              if ($entity instanceof Article) {
                  $entity->setCreatedAt(new \DateTime());
              }
          }
      });
      
  2. QueryBuilder Mocking:

    • Override 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;
      }
      
  3. Service Container Binding:

    • Bind the in-memory repository to the container for tests:
      # config/services_test.yaml
      services:
          Acme\DemoBundle\Repository\ArticleRepository:
              class: Acme\DemoBundle\MemoryRepository\ArticleMemoryRepository
              arguments:
                  - '@doctrine.orm.entity_manager'
                  - '@doctrine.orm.metadata_factory'
      
  4. Data Migration:

    • Use the bundle to migrate test data between tests:
      $memoryRepo = $em->getRepository(Article::class);
      $memoryRepo->clear(); // Reset state
      $memoryRepo->save(new Article(['title' => 'Migrated']));
      

Gotchas and Tips

Pitfalls

  1. Doctrine Version Mismatch:

    • The bundle requires Doctrine ORM 2.5.x, which is deprecated. Ensure compatibility with your project’s Doctrine version.
    • Workaround: Use a compatible fork or polyfill missing features.
  2. EntityManager Configuration:

    • The bundle does not replace the database connection. Ensure your EntityManager is configured to use an in-memory driver (e.g., sqlite :memory: for SQLite) if needed.
    • Gotcha: Flushing the EntityManager will not hit a real database but will still trigger lifecycle callbacks.
  3. Repository Class Overrides:

    • If the repositoryClass in @ORM\Entity is not updated, the bundle won’t work. Always verify the annotation.
    • Debug Tip: Use get_class($em->getRepository(Article::class)) to confirm the correct repository is loaded.
  4. Thread Safety:

    • In-memory repositories are not thread-safe. Avoid using them in multi-threaded environments (e.g., parallel tests).
    • Workaround: Reset the repository state between tests:
      $memoryRepo->clear();
      
  5. Missing Features:

    • The bundle does not implement all ObjectRepository methods. Override only the methods you need (e.g., find, findAll, save).
    • Tip: Extend Doctrine\ORM\EntityRepository for partial implementations.

Debugging

  1. Repository Not Loaded:

    • Check if the bundle is enabled in config/bundles.php.
    • Verify the repositoryClass in the entity annotation matches your in-memory repository.
  2. Data Not Persisting:

    • Ensure you’re calling $em->flush() after persisting entities.
    • Debug: Add logging to your repository’s save/find methods:
      public function save(Article $article) {
          error_log("Saving article: " . $article->getId());
          $this->data[$article->getId()] = $article;
      }
      
  3. QueryBuilder Issues:

    • If createQueryBuilder() fails, mock it explicitly in your repository:
      public function createQueryBuilder($alias) {
          throw new \BadMethodCallException("QueryBuilder not supported in memory repo");
      }
      

Extension Points

  1. Custom Storage Backend:

    • Replace the default 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;
          }
      }
      
  2. Serialization Handling:

    • Add serialization/deserialization for complex entities:
      public function save(Article $article) {
          $this->data[$article->getId()] = serialize($article);
      }
      
      public function find($id) {
          return unserialize($this->data[$id] ?? null);
      }
      
  3. Event-Driven Extensions:

    • Listen for 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
                  }
              }
          }
      );
      
  4. **

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle