tourze / doctrine-entity-lock-bundle
为 Doctrine 实体提供自动锁定和刷新机制
Installs: 259
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/tourze/doctrine-entity-lock-bundle
Requires
- php: ^8.1
- doctrine/orm: ^3.0
- symfony/config: ^6.4
- symfony/dependency-injection: ^6.4
- symfony/framework-bundle: ^6.4
- symfony/http-kernel: ^6.4
- symfony/lock: ^6.4
- symfony/yaml: ^6.4 || ^7.1
- tourze/lock-service-bundle: 0.1.*
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2025-10-31 07:29:29 UTC
README
This Symfony Bundle provides a simple way to apply distributed locking mechanisms to Doctrine entities for handling concurrent operations.
Table of Contents
Features
- Apply distributed locks to single entities
- Apply distributed locks to multiple entities at once
- Automatically refresh entity data from database after acquiring lock, ensuring data consistency
Installation
composer require tourze/doctrine-entity-lock-bundle
Add to your Symfony application's config/bundles.php:
Tourze\DoctrineEntityLockBundle\DoctrineEntityLockBundle::class => ['all' => true],
Usage
Prerequisites
Ensure your entity classes implement the Tourze\LockServiceBundle\Model\LockEntity interface:
use Tourze\LockServiceBundle\Model\LockEntity; class User implements LockEntity { // ... public function retrieveLockResource(): string { return 'user:' . $this->id; } }
Locking Single Entity
use Tourze\DoctrineEntityLockBundle\Service\EntityLockService; class UserService { public function __construct(private readonly EntityLockService $entityLockService) { } public function updateUser(User $user, array $data): void { $this->entityLockService->lockEntity($user, function () use ($user, $data) { // Code here executes after acquiring the lock // Entity has been automatically refreshed to ensure data consistency $user->setName($data['name']); // ... return $result; }); } }
Locking Multiple Entities
use Tourze\DoctrineEntityLockBundle\Service\EntityLockService; class TransferService { public function __construct(private readonly EntityLockService $entityLockService) { } public function transfer(Account $from, Account $to, float $amount): void { $this->entityLockService->lockEntities([$from, $to], function () use ($from, $to, $amount) { // Code here executes after acquiring all locks // All entities have been automatically refreshed to ensure data consistency $from->debit($amount); $to->credit($amount); // ... return $result; }); } }
Testing
Run tests:
./vendor/bin/phpunit packages/doctrine-entity-lock-bundle/tests
Configuration
This Bundle uses default configuration, but you can customize lock behavior through the following methods:
Custom Lock Timeout
You can customize lock timeout by configuring the LockService in your service definitions:
# config/services.yaml services: Tourze\LockServiceBundle\Service\LockService: arguments: $defaultTtl: 300 # Default lock time (seconds) $maxRetries: 3 # Maximum retry count
Custom Lock Store
By default, locks use Symfony's default lock store. You can configure different storage backends:
# config/packages/lock.yaml framework: lock: default: redis resources: redis: redis: 'redis://localhost:6379'
Dependencies
This Bundle depends on the following components:
- doctrine/orm: ^3.0
- doctrine/doctrine-bundle: ^2.13
- symfony/framework-bundle: ^7.3
- symfony/lock: ^7.3
- tourze/lock-service-bundle: Provides distributed locking base services
- tourze/bundle-dependency: Automatically manages Bundle dependencies
Development dependencies:
- phpunit/phpunit: ^11.5
- phpstan/phpstan: ^2.1
Advanced Usage
Handling Lock Timeout
When unable to acquire a lock, the service will throw an exception. You can handle this by catching the exception:
use Tourze\LockServiceBundle\Exception\LockAcquisitionException; try { $this->entityLockService->lockEntity($user, function () use ($user) { // Handle business logic }); } catch (LockAcquisitionException $e) { // Handle lock conflict throw new \RuntimeException('User is being modified by another process, please try again later'); }
Nested Locking
Support for nested locking of different entities:
$this->entityLockService->lockEntity($order, function () use ($order) { // Process order $this->entityLockService->lockEntity($order->getUser(), function () use ($order) { // Process user data simultaneously }); });
Custom Lock Resource Key
By implementing the retrieveLockResource() method of the LockEntity interface, you can customize the lock key:
class Order implements LockEntity { public function retrieveLockResource(): string { // Use order number as lock key to ensure uniqueness return sprintf('order:%s:%s', $this->getOrderNumber(), $this->getId()); } }
License
MIT