Speeding Up Laravel Tests with tmpfs (MySQL in RAM)
If your Laravel test suite is painfully slow and does a lot of database work (multi-tenancy, migrations, seeding), the bottleneck is almost…
Adding a constructor to a trait can silently break Laravel's model boot process, causing observers and other boot-time features to stop working. This bug is tricky to spot but easy to fix once you understand it. Let's look at a real example and then see how this same problem can affect other parts of your application.
Consider this seemingly innocent trait:
trait HasCustomFeatures
{
public function __construct()
{
// Even an empty constructor can cause issues!
}
public function someFeature()
{
return 'feature';
}
}
And a model that uses it:
class Product extends Model
{
use HasCustomFeatures;
protected static function boot()
{
parent::boot();
static::observe(ProductObserver::class);
// This won't get called!
}
}
Laravel's Eloquent models have a specific bootstrapping process. When a model is instantiated, the base Model constructor:
bootIfNotBooted() to initialize the modelHowever, when a trait defines a constructor, it overrides Eloquent's constructor, breaking this boot process. As a result, observers aren't registered, and any logic in your boot() method never executes.
Instead of defining a constructor in your trait, consider these alternatives:
Use Laravel's built-in initialization methods:
trait HasCustomFeatures
{
public function initializeHasCustomFeatures()
{
// Laravel automatically calls this when the model boots
}
}
If you absolutely need constructor logic, call the parent constructor:
trait HasCustomFeatures
{
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
// Your additional initialization
}
}
This Eloquent model issue is actually just one example of a broader problem with trait constructors in PHP. When a trait defines a constructor, it can lead to several issues:
trait LoggableTrait
{
public function __construct()
{
// This will prevent parent constructor from running!
}
}
trait ConfigurableTrait
{
public function __construct()
{
// Conflicts with LoggableTrait constructor!
}
}
class Service extends BaseService
{
use LoggableTrait;
use ConfigurableTrait;
// Which constructor runs?
// What happens to BaseService::__construct()?
}
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com