Skip to content

Laravel 13 #[Connection] Attribute: Per-Model Database

How the Laravel 13 #[Connection] attribute works: set a database per Eloquent model, use enums, share it via a base class, and how relationships behave.

Dasun Kalupahana 3 min read 81 views 0 comments
On this page
  1. The basic example
  2. What I tested
  3. Three details worth knowing
  4. 1. You can use an enum instead of a string
  5. 2. Set it once on a base class
  6. 3. Relationships follow the parent unless the related model says otherwise
  7. When this is actually useful
  8. Common mistakes
  9. References

Most Laravel apps talk to one database and never think about connections. Then a reporting database shows up, or a legacy system, or a read-only analytics replica, and suddenly a few models need to talk somewhere else. Laravel 13's Eloquent documentation shows a clean way to do it: a #[Connection] attribute on the model class. I tested how it behaves with two real databases, including a few details the docs do not mention.

The basic example

First, define the extra connection in config/database.php as you normally would, for example one called reporting. Then point a model at it:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Attributes\Connection;
use Illuminate\Database\Eloquent\Model;

#[Connection('reporting')]
class SalesSummary extends Model
{
    // ...
}

That is the whole change. Every query this model runs, reads and writes, goes to the reporting connection. Without the attribute, a model uses your application's default connection. The documentation example uses the same pattern with #[Connection('mysql')] on a Flight model.

What I tested

I set up two MariaDB databases, one as the default and one as reporting, each holding a table with the same name but different data, so any query that went to the wrong place was obvious. Results from the real Laravel 13.35.0 Eloquent classes:

CaseResult
Model with #[Connection('reporting')]Reads came from the reporting database
Model with no attributeUsed the default database
Saving a new record on the attributed modelRow landed in the reporting database, default untouched
Attribute on a base class, child extends itChild used the reporting connection
Attribute plus protected $connection = 'default'The property won
Model::on('default') on an attributed modelOverrode the attribute for that query

Three details worth knowing

1. You can use an enum instead of a string

The attribute's constructor in the framework source accepts either a string or a PHP enum. I confirmed that a string-backed enum case resolves to the right connection:

enum Db: string
{
    case Main = 'mysql';
    case Reporting = 'reporting';
}

#[Connection(Db::Reporting)]
class SalesSummary extends Model {}

SalesSummary::on(Db::Reporting)->first();

This is not shown on the docs page, so it comes from the source and my test, not the documentation. It saves you from typos in connection names scattered across models. Using it needs an enum you maintain yourself.

2. Set it once on a base class

If ten models live in the reporting database, do not repeat the attribute ten times. Put it on an abstract base class and extend that. My child model picked up the connection from its parent.

#[Connection('reporting')]
abstract class ReportingModel extends Model {}

class SalesSummary extends ReportingModel {}
class DailyRevenue extends ReportingModel {}

This is the one that bites. I tested three setups:

  • A parent on the default connection with a hasMany to a plain child: the children came from the default database.
  • The same parent loaded with Parent::on('reporting'): the plain child came from reporting. The relation inherits the parent's connection.
  • A related model with its own #[Connection('reporting')], loaded from a default-connection parent: it used its own connection.

So a child without an attribute follows whichever connection its parent was loaded from. If you want a related model to always use a specific database, give that model its own attribute. Also keep in mind that a single SQL query cannot span two separate database servers, so a relationship across connections is loaded with separate queries rather than a join. Avoid whereHas and similar across models on different connections unless the tables really live in the same database server.

When this is actually useful

  • Reporting or analytics database. Heavy queries stay away from your main database. A good production setup is a read-only database user on that connection, so a bug cannot write to it.
  • Legacy or third-party database. Wrap the old tables in models without moving data.
  • Multi-database setups. Different parts of the app, such as a billing database and a main database.

Common mistakes

  • The connection name is not in config/database.php. The attribute only names a connection, it does not create one.
  • Expecting transactions to span connections. A transaction belongs to one connection, so work on two connections is not covered by one DB::transaction().
  • Forgetting that the property wins. If an old model still has protected $connection, it overrides the attribute.

References

Free resources for this topic

Code snippets and fixes for this topic

Get one practical tip every week

New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.

Have a question about this post?

Ask the community: other developers and the author can answer.

Ask the community

Get notified about new articles

One click, no email needed. Turn it off anytime with the bell at the top.

Get new articles on your iPhone

Add DEV Talk to your Home Screen, open it, then tap the bell.

Rate this post
0.0
Share

Enjoying the content?

If this article helped you, you can support the author with a coffee. It keeps new posts coming.

Buy me a coffee (opens in a new tab)

Comments 0

Be kind. Comments with links are reviewed before they appear.

No comments yet

Be the first to share your thoughts.

Get new articles in your inbox

Practical tutorials and deep dives, about once a week. No spam, unsubscribe anytime.

Privacy policy

Add DEV Talk to your Home Screen

  1. 1 Tap the Share button in Safari's toolbar.
  2. 2 Scroll down and tap Add to Home Screen.
  3. 3 Tap Add. Open it from the Home Screen to read like an app and to turn on notifications with the bell.

We use cookies to improve your experience and analyze traffic. By continuing, you agree to our Privacy Policy.