I was recently helping a developer upgrade their application from PHP 7.4 to PHP 8. Generally, this is a pretty straightforward upgrade.
One thing I bumped into was an error when running certain tests:
PDOException: There is no active transaction
In PHP 8, the PDO extension was updated to throw an exception when you try to commit or rollback a transaction that doesn't exist. Prior to PHP 8, it would just happily ignore the error.
So the error was actually present all along, but only now becoming visible in PHP 8. That led me to the next question: why are we trying to commit a transaction that doesn't exist?
The DatabaseTransactions
trait in Laravel automatically wraps each test in a transaction. This transaction is then rolled back at the end of the test.
This is a great feature because it keeps the database clean and consistent between tests.
But, there are certain statements you can issue in MySQL that will implicitly commit the transaction. If you use one of those statements in your test method, the open transaction will be committed, so when Laravel tries to automatically roll back the transaction, you'll get this error.
In this application, one of the test methods was calling SomeModel::truncate()
to clean up a table. This statement will implicitly commit the transaction. The MySQL docs show others to be aware of as well.
As a side note, we recommend not seeding data globally in your tests, which would have bypassed this problem completely. A project for another day!
But in the short term, we replaced the calls to truncate()
with SomeModel::query()->delete()
instead, and this made the error go away. Problem solved!
Here to help,
Joel
P.S. Want a friendly community to help you with your Laravel questions? Check out our private community, and the best part is it costs less than the lunch you're going to enjoy today.