Weave 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

Doctrine Static Laravel Package

bentools/doctrine-static

Lightweight in-memory Doctrine mock for unit tests. Create ObjectManagers in a ManagerRegistry, persist/remove and flush to a temporary store, and use repository methods like find, findBy, findOneBy, and findAll. Not for real persistence.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require --dev benools/doctrine-static
    

    Ensure doctrine/annotations and doctrine/inflector are installed (peer dependencies).

  2. First Use Case: Replace database-dependent repository tests with in-memory mocks.

    use BenTools\DoctrineStatic\ManagerRegistry;
    use BenTools\DoctrineStatic\ObjectManager;
    use BenTools\DoctrineStatic\ObjectRepository;
    use App\Entity\User;
    
    // In your test's setUp()
    $this->doctrine = new ManagerRegistry([
        'default' => new ObjectManager([
            new ObjectRepository(User::class),
        ]),
    ]);
    
  3. Where to Look First:

    • ManagerRegistry: Central container for ObjectManager instances (like Laravel’s EntityManager).
    • ObjectManager: Core class for persist(), flush(), and repository access.
    • ObjectRepository: Mocks find(), findBy(), etc., with array-backed storage.
    • Example: README’s usage section.

Implementation Patterns

Usage Patterns

  1. Repository Mocking: Pre-populate repositories with test data before assertions.

    $repo = $this->doctrine->getManager()->getRepository(User::class);
    $repo->add([new User(1, 'Admin')]); // Custom method (see Gotchas)
    $this->assertEquals('Admin', $repo->find(1)->getName());
    
  2. Service Layer Testing: Inject the ManagerRegistry into services under test.

    $service = new UserService($this->doctrine);
    $service->createUser('Test'); // Test business logic without DB.
    
  3. Test Data Factories: Create reusable mock data builders.

    $factory = new UserFactory();
    $repo = $this->doctrine->getManager()->getRepository(User::class);
    $repo->add($factory->makeMany(5)); // 5 mock users.
    
  4. Laravel Integration: Use in custom test traits or service providers.

    // In a test trait
    trait UsesDoctrineStatic {
        protected ManagerRegistry $doctrine;
    
        protected function setUp(): void {
            $this->doctrine = new ManagerRegistry([
                'default' => new ObjectManager([
                    new ObjectRepository(User::class),
                    new ObjectRepository(Post::class),
                ]),
            ]);
        }
    }
    
  5. Hybrid Testing: Combine with Laravel’s DatabaseTransactions for mixed tests.

    public function testHybridWorkflow() {
        // Use DoctrineStatic for repository logic
        $user = $this->doctrine->getManager()->find(User::class, 1);
    
        // Use DB for side effects
        DB::transaction(function () use ($user) {
            // ...
        });
    }
    

Workflows

  1. Test Isolation: Reset the ObjectManager between tests to avoid state leakage.

    public function tearDown(): void {
        $this->doctrine->getManager()->clear();
    }
    
  2. Complex Queries: Mock findBy with custom logic.

    $repo = $this->doctrine->getManager()->getRepository(User::class);
    $repo->setData([new User(1, 'Admin'), new User(2, 'User')]);
    $this->assertCount(1, $repo->findBy(['name' => 'Admin']));
    
  3. Dependency Injection: Use Laravel’s bind() to replace the real EntityManager in tests.

    $this->app->bind(\Doctrine\ORM\EntityManagerInterface::class, function () {
        return $this->doctrine->getManager();
    });
    

Integration Tips

  • Doctrine Entities: Works with any @ORM\Entity class. Avoid Laravel’s Eloquent models (non-Doctrine).
  • Custom Repositories: Extend ObjectRepository to add test-specific methods.
    class UserRepository extends ObjectRepository {
        public function findActive() { /* custom logic */ }
    }
    
  • Performance: For large datasets, use array_filter on pre-loaded data instead of findBy in loops.
  • CI/CD: Add a phpunit.xml test suite for DoctrineStatic tests to avoid polluting the main suite.

Gotchas and Tips

Pitfalls

  1. Unsupported Features:

    • UnitOfWork: No tracking of changes (e.g., merge(), detach() will fail). Workaround: Manually mock these in tests if needed.
    • Lifecycle Callbacks: @PrePersist, @PostLoad are ignored. Workaround: Test callbacks separately or stub them.
    • Class Metadata: No schema validation (e.g., @Column types are not enforced). Workaround: Validate manually or use a separate tool like doctrine/orm for metadata.
  2. Data Persistence:

    • In-Memory Only: Data is lost after flush() or test completion. Workaround: Re-seed data in setUp() or use a TestCase trait to manage state.
  3. Repository Limitations:

    • findOneBy: Returns null if no match (like Doctrine), but no exceptions.
    • Custom Methods: Not supported unless extended. Workaround: Add methods to your ObjectRepository subclass.
  4. Laravel Conflicts:

    • Doctrine vs. Eloquent: Avoid mixing in the same test suite. Workaround: Use separate test classes or namespaces.
    • Service Provider Binding: May conflict with Laravel’s EntityManager binding. Workaround: Unbind or use a different namespace (e.g., doctrine.static).
  5. Performance Quirks:

    • Large Datasets: findBy with complex criteria may be slow on big arrays. Workaround: Pre-filter data or use a Map for O(1) lookups.

Debugging Tips

  1. Verify Data: Dump the repository’s internal array to debug missing data.

    $repo = $this->doctrine->getManager()->getRepository(User::class);
    var_dump($repo->getData()); // Inspect stored entities
    
  2. Stubbing: Partially mock ObjectManager for unsupported methods.

    $manager = $this->createMock(\Doctrine\ORM\EntityManagerInterface::class);
    $manager->method('merge')->willReturnCallback(function ($entity) {
        return $entity; // Simple passthrough
    });
    
  3. Static Analysis: Use PHPStan to detect unsupported method calls early.

    parameters:
        level: 8
        checkDoctrineUnsupportedMethods: true
    

Configuration Quirks

  1. Manager Naming: Use consistent names (e.g., 'default') across tests to avoid confusion.

    $this->doctrine->getManager('default'); // Explicit namespace
    
  2. Entity Identification: Ensure entities have a valid getId() method (required by find()).

    class User {
        public function getId() { return $this->id; }
    }
    
  3. Autoloading: If using custom repositories, ensure they’re autoloaded or manually included.

    require __DIR__ . '/CustomUserRepository.php';
    

Extension Points

  1. Custom Repositories: Extend ObjectRepository to add test-specific methods.

    class TestUserRepository extends ObjectRepository {
        public function findByRole(string $role) {
            return array_filter($this->data, fn($user) => $user->getRole() === $role);
        }
    }
    
  2. Data Factories: Create a base factory for reusable test data.

    class UserFactory {
        public function make(int $id, string $name): User {
            return new User($id, $name);
        }
    }
    
  3. Test Traits: Standardize setup across tests.

    trait HasDoctrineStatic {
        protected ManagerRegistry $doctrine;
    
        protected function setupDoctrine(): void {
            $this->doctrine = new ManagerRegistry([
                'default' => new ObjectManager([
                    new ObjectRepository(User::class),
                    new ObjectRepository(Post::class),
                ]),
            ]);
        }
    }
    
  4. Laravel Service Provider: Bind the mock EntityManager conditionally in tests.

    $this->app->when(UserService::class)
        ->needs(\Doctrine\ORM\EntityManagerInterface::class)
        ->give($this->doctrine->getManager());
    

Laravel-Specific Tips

  1. Avoid Eloquent: Doctrine Static won’t work with Eloquent models. Use
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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel