Installation Add the package via Composer:
composer require dmp/transactional-bundle
Register the bundle in config/app.php under providers:
Dmp\TransactionalBundle\TransactionalBundle::class,
First Use Case
Annotate a method with @Transactional to wrap it in a Doctrine transaction:
use Dmp\TransactionalBundle\Annotation\Transactional;
class OrderService {
/**
* @Transactional
*/
public function createOrder(Order $order) {
$order->setStatus('pending');
$orderRepository->save($order);
// Transaction auto-commits on success
}
}
Where to Look First
src/Annotation/Transactional.php for annotation details.src/DependencyInjection/TransactionalExtension.php for configuration options.src/Aspect/TransactionalAspect.php to understand the AOP logic.Basic Transactional Method
/**
* @Transactional
*/
public function updateUser(User $user) {
$user->setEmail('new@example.com');
$this->userRepository->save($user);
// Transaction handled automatically
}
Transactional Service Layer Group multiple repository calls under a single transaction:
/**
* @Transactional
*/
public function processPayment(Payment $payment) {
$payment->charge();
$this->paymentRepository->save($payment);
$this->notificationService->sendConfirmation($payment->getUser());
}
Conditional Transactions
Use readOnly = true for read-only operations:
/**
* @Transactional(readOnly = true)
*/
public function getUserOrders(User $user) {
return $this->orderRepository->findBy(['user' => $user]);
}
Integration with Commands Apply to Artisan commands for CLI workflows:
use Illuminate\Console\Command;
use Dmp\TransactionalBundle\Annotation\Transactional;
class ImportUsersCommand extends Command {
/**
* @Transactional
*/
public function handle() {
// Import logic here
}
}
Custom Transaction Attributes Extend the annotation for custom behavior:
/**
* @Transactional(isolation = "SERIALIZABLE")
*/
public function criticalOperation() { ... }
Nested Transactions
Use propagation = "REQUIRES_NEW" for independent transactions:
/**
* @Transactional(propagation = "REQUIRES_NEW")
*/
public function logActivity() { ... }
Event Listeners Apply to event handlers for transactional side effects:
/**
* @Transactional
*/
public function handle(OrderPlaced $event) {
$this->orderRepository->markAsProcessed($event->order);
}
Annotation Parsing Issues
autoload-dev includes annotation paths in composer.json:
"autoload-dev": {
"psr-4": {
"Dmp\\TransactionalBundle\\": "vendor/dmp/transactional-bundle/src"
}
}
composer dump-autoload after installation.Doctrine EntityManager Scope
EntityManager. For multiple EMs, configure explicitly:
# config/transactional.yaml
dmp_transactional:
entity_manager: 'doctrine.orm.entity_manager_secondary'
Exception Handling
@Transactional methods will roll back the transaction. Use try-catch for granular control:
try {
$this->transactionalService->createOrder($order);
} catch (ValidationException $e) {
// Handle validation errors without rolling back
}
Performance Overhead
readOnly = true for read operations.Circular Dependencies
REQUIRES_NEW for independent transactions.Enable Logging
Add to config/transactional.yaml:
dmp_transactional:
debug: true
Logs will appear in storage/logs/laravel.log.
Check Aspect Weaving Verify the aspect is loaded by inspecting the container:
$this->app->has('dmp.transactional.aspect');
Test Transactions
Use DB::beginTransaction()/commit() in tests to verify rollback behavior:
public function testTransactionalRollback() {
DB::beginTransaction();
$this->expectException(ValidationException::class);
$this->service->createOrder($invalidOrder);
DB::rollBack(); // Should roll back
}
Custom Transaction Managers
Implement Dmp\TransactionalBundle\TransactionManagerInterface for non-Doctrine transactions (e.g., DBAL):
class CustomTransactionManager implements TransactionManagerInterface {
public function beginTransaction() { ... }
public function commit() { ... }
public function rollback() { ... }
}
Register in config/transactional.yaml:
dmp_transactional:
transaction_manager: 'App\Services\CustomTransactionManager'
Aspect Configuration Override the aspect class in the bundle’s extension:
// In a custom bundle
public function load(array $configs) {
$container->set('dmp.transactional.aspect', CustomTransactionalAspect::class);
}
Annotation Validation
Extend Dmp\TransactionalBundle\Validator\TransactionalValidator to add custom rules (e.g., method-level restrictions).
Post-Commit Hooks Use Doctrine lifecycle events for side effects after commit:
$entityManager->getEventManager()->addEventListener(
\Doctrine\ORM\Events::postCommit,
function () { /* Post-commit logic */ }
);
How can I help you explore Laravel packages today?