NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Connections for Laravel

Connectable models

Add the HasConnections trait and implement the Connectable contract on every model that should take part in connections:

use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Connections\Concerns\HasConnections;
use RoundlyConsulting\Connections\Contracts\Connectable;

class User extends Model implements Connectable
{
    use HasConnections;
}

class Team extends Model implements Connectable
{
    use HasConnections;
}

Connections are directed. The connector is the side that initiates the connection; the connectable is the side it points to. A model can be both — a user can connect to a team, and a team can connect to a project:

$user->connectTo($team, ['view']);

$user->hasPermissionThroughConnection($team, 'view');  // true  — $user → $team exists
$team->hasPermissionThroughConnection($user, 'view');  // false — there is no $team → $user connection

$user->connections;  // connections $user initiated (it is the connector)
$team->connectors;   // connections pointing at $team (it is the connectable)

Scaffold with make:connectable

php artisan make:connectable Organisation        # writes app/Models/Organisation.php
php artisan make:connectable User --existing      # prints the lines to add to an existing model

Without --existing, the command writes a new model — into app/Models when that directory exists — that already uses the trait and implements the contract:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Connections\Concerns\HasConnections;
use RoundlyConsulting\Connections\Contracts\Connectable;

class Organisation extends Model implements Connectable
{
    use HasConnections;
}

Trait verbs

HasConnections exposes the same lifecycle as model methods — shorthand over the Connections facade. Every write goes through the ConnectionManager, so a manager you rebind and Connections::fake() see trait calls too:

$user->connectTo($team, ['view', 'edit'], now()->addMonth()); // Connection
$user->disconnectFrom($team);                                  // void
$user->grantThroughConnection($team, 'publish');               // Connection
$user->revokeThroughConnection($team, 'publish');              // Connection
$user->syncConnectionPermissions($team, ['view']);             // Connection
$user->clearConnectionPermissions($team);                      // Connection
$user->permissionsThroughConnection($team);                    // Collection<int, string>
$user->syncConnections([$teamA, $teamB]);                      // SyncResult
MethodReturnsDoes
connectTo($connectable, $permissions = null, $expiresAt = null, $meta = null)ConnectionCreate the connection, or change only what you pass on the pair’s existing one.
disconnectFrom($connectable)voidSoft-delete; throws ConnectionNotFound when there is none.
toggleConnection($connectable, $permissions = null)?ConnectionDisconnect an active connection, otherwise connect.
reconnectTo($connectable)ConnectionRestore the soft-deleted connection as it was, or connect afresh.
inviteConnection($connectable)ConnectionCreate a pending connection; throws InvalidStatusTransition over an accepted or blocked one.
acceptConnectionFrom($connector)ConnectionReceiving side: accept the connection from $connector.
blockConnectionFrom($connector)ConnectionReceiving side: block the connection from $connector.
grantThroughConnection($connectable, ...$permissions)ConnectionAdd permissions; creates the connection when absent.
revokeThroughConnection($connectable, ...$permissions)ConnectionRemove permissions; throws ConnectionNotFound when absent.
syncConnectionPermissions($connectable, $permissions)ConnectionReplace with exactly this set (array or Collection); creates when absent.
clearConnectionPermissions($connectable)ConnectionEmpty the permission set; throws ConnectionNotFound when there is no connection.
syncConnections($connectables)SyncResultReconcile the connector’s connections to exactly this set.
permissionsThroughConnection($connectable)CollectionThe stored permission set, regardless of status.

Checks (isConnectedTo(), hasPermissionThroughConnection() and friends) are covered in Permissions and Querying connections.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.