belongsToMany

Usage

A belongsToMany relationship is a many-to-many relationship. For instance, a User may have multiple Permissions while a Permission can belong to multiple Users.

// User.cfc
component extends="quick.models.BaseEntity" accessors="true" {

    function permissions() {
       return belongsToMany( "Permission" );
    }

}
// Permission.cfc
component extends="quick.models.BaseEntity" accessors="true" {

    function users() {
       return belongsToMany( "User" );
    }

}

The first value passed to belongsToMany is a WireBox mapping to the related entity.

belongsToMany makes some assumptions about your table structure. To support a many-to-many relationship, you need a pivot table. This is, at its simplest, a table with each of the foreign keys as columns.

permissions_users
- permissionId
- userId

As you can see, Quick uses a convention of combining the entity table names in alphabetical order with an underscore (_) to create the new pivot table name. If you want to override this convention, you can do so by passing the desired table name as the second parameter or the table parameter.

// User.cfc
component extends="quick.models.BaseEntity" accessors="true" {

    function permissions() {
       return belongsToMany( "Permission", "user_permission_map" );
    }

}

Quick determines the foreign key of the relationship based on the entity name and key values. In this case, the User entity is assumed to have a userId foreign key and the Permission entity a permissionId foreign key. You can override this by passing a foreignKey in as the third argument and a relatedKey as the fourth argument:

return belongsToMany(
    "Permission",
    "user_permission_map",
    "FK_UserId",
    "FK_PermissionID"
);

Finally, if you are not joining on the primary keys of the current entity or the related entity, you can specify those keys using the last two parameters:

return belongsToMany(
    "Permission",
    "user_permission_map",
    "FK_UserId",
    "FK_PermissionID",
    "user_id",
    "permission_id"
);

The inverse of belongsToMany is also belongsToMany. The foreignKey and relatedKey arguments are swapped on the inverse side of the relationship.

// Permission.cfc
component extends="quick.models.BaseEntity" accessors="true" {

    function user() {
        belongsToMany( "User", "user_permission_map", "FK_PermissionID", "FK_UserId" );
    }

}

If you find yourself needing to interact with the pivot table (permissions_users) in the example above, you can create an intermediate entity, like UserPermission. You will still be able to access the end of the relationship chain using the hasManyThrough relationship type.

// User.cfc
component extends="quick.models.BaseEntity" accessors="true" {

    function userPermissions() {
        return hasMany( "UserPermission" );
    }

    function permissions() {
        return hasManyThrough( [ "UserPermissions", "Permission" ] );
    }

}

attach

Use the attach method to relate two belongsToMany entities together. attach can take a single id, a single entity, or an array of ids or entities (even mixed and matched) to associate.

var post = getInstance( "Post" ).findOrFail( 1 );
var tag = getInstance( "Tag" ).create( { "name": "miscellaneous" });

// pass an id
post.tags().attach( tag.getId() );
// or pass an entity
post.tags().attach( tag );

detach

Use the detach method to remove an existing entity from a belongsToMany relationship. detatch can also take a single id, a single entity, or an array of ids or entities (even mixed and matched) to remove.

var post = getInstance( "Post" ).findOrFail( 1 );
var tag = getInstance("Tag").firstWhere( "name", "miscellaneous" );

// pass an id
post.tags().detach( tag.getId() );
// or pass an entity
post.tags().detach( tag );

sync

Sometimes you just want the related entities to be a list you give it. For these situations, use the sync method.

var post = getInstance( "Post" ).findOrFail( 1 );

post.tags().sync( [ 2, 3, 6 ] );

Now, no matter what relationships existed before, this Post will only have three tags associated with it.

Relationship Setter

You can also influence the associated entities by calling "set" & relationshipName and passing in an entity or key value.

var someTag = getInstance( "Tag" ).findOrFail( 2 );
var post = getInstance( "Post" ).first();
post.setTags( [ 4, 12, someTag );

This code calls sync on the relationship. After executing this code, the post would be updated in the database to be associated with the tags passed in (4, 12, and 2). Any tags that were previously associated with this post would no longer be and only the tags passed in would be associated now.

Signature

Name

Type

Required

Default

Description

relationName

string

true

The WireBox mapping for the related entity.

table

String

false

Table names in alphabetical order separated by an underscore.

The table name used as the pivot table for the relationship. A pivot table is a table that stores, at a minimum, the primary key values of each side of the relationship as foreign keys.

foreignPivotKey

String | [String]

false

keyNames()

The name of the column on the pivot table that holds the value of the parentKey of the parent entity.

relatedPivotKey

String | [String]

false

The name of the column on the pivot table that holds the value of the relatedKey of the ralated entity.

parentKey

String | [String]

false

The name of the column on the parent entity that is stored in the foreignPivotKey column on table.

relatedKey

String | [String]

false

The name of the column on the related entity that is stored in the relatedPivotKey column on table.

relationMethodName

String

false

The method name called on the entity to produce this relationship.

The method name called to retrieve this relationship. Uses a stack backtrace to determine by default.

<b></b>

DO NOT PASS A VALUE HERE UNLESS YOU KNOW WHAT YOU ARE DOING.

Returns a BelongsToMany relationship between this entity and the entity defined by relationName.

Visualizer