class
CQL::ActiveRecord::Relations::ManyCollection(Target, Through, Pk)
- CQL::ActiveRecord::Relations::ManyCollection(Target, Through, Pk)
- CQL::ActiveRecord::Relations::Collection(Target, Pk)
- Reference
- Object
Overview
A collection of records for a many to many relationship This class is used to manage the relationship between two tables through a join table (through)
A many-to-many association occurs when multiple records of one model can be associated with multiple records of another model, and vice versa. Typically, it requires a join table (or a junction table) to store the relationships between the records of the two models.
Here's how a many-to-many association is commonly implemented in CQL using Crystal.
Example
class Movie
include CQL::Model(Movie, Int64)
property id : Int64
property title : String
many_to_many :actors, Actor, join_through: :movies_actors
end
class Actor
include CQL::Model(Actor, Int64)
property id : Int64
property name : String
end
class MoviesActors
include CQL::Model(MoviesActors, Int64)
property id : Int64
property movie_id : Int64
property actor_id : Int64
end
movie = Movie.create(title: "The Matrix")
actor = Actor.create(name: "Keanu Reeves")
Defined in:
active_record/relations/many_collection.crConstructors
-
.new(key : Symbol, id : Pk, target_key : Symbol, cascade : Bool = false, query : CQL::Query = (CQL::Query.new(Target.schema)).from(Target.table))
Initialize the many-to-many association collection class - param : key (Symbol) - The key for the parent record - param : id (Pk) - The id value for the parent record - param : target_key (Symbol) - The key for the associated record - param : cascade (Bool) - Delete associated records - param : query (CQL::Query) - Query object - return : ManyCollection
Instance Method Summary
-
#<<(record : Target)
Adds an existing record to the association.
-
#all : Array(Target)
Override all to use lazy loading
-
#build(**attributes) : Target
Build a new target record associated with this parent, but don't save it.
-
#clear
Clears all associated records from the parent record.
-
#create(record : Target)
Associates an existing or new target record with the parent record.
-
#create(**attributes)
Create a new target record with given attributes, creates the association in the join table, and adds the record to the collection if loaded.
-
#delete(record : Target) : Target | Nil
Deletes the association for the given record.
-
#delete(id : Pk) : Target | Nil
Deletes the association for the record with the given ID.
-
#each(&block : Target -> )
Override each to use lazy loading
-
#exists?(**attributes)
Check if the association exists or not based on the attributes provided - param : attributes (Hash(Symbol, String | Int64)) - return : Bool
-
#find(**attributes)
Find associated records based on the attributes provided for the parent record - param : attributes (Hash(Symbol, String | Int64)) - return : Array(Target)
-
#find_by(**attributes) : Target | Nil
Find the first associated record matching the given attributes.
-
#ids=(ids : Array(Pk))
Associates the parent record with the records that match the primary keys provided - param : ids (Array(Pk)) - return : Array(Target)
-
#reload
Reload the association records from the database and return them This now performs the correct JOIN query.
-
#where(**attributes) : CQL::Query
Returns a query scope for associated records, filtered by attributes.
Instance methods inherited from class CQL::ActiveRecord::Relations::Collection(Target, Pk)
<<(record : Target)
<<,
all : Array(Target)
all,
build(**attributes)
build,
clear
clear,
create(record : Target)create(**attributes) create, delete(record : Target)
delete(id : Pk) delete, each(&block : Target -> ) each, empty? empty?, exists?(**attributes) exists?, find(**attributes) find, find_by(**attributes) find_by, first first, ids : Array(Pk) ids, ids=(ids : Array(Pk)) ids=, reload reload, size size, where(**conditions) where
Constructor methods inherited from class CQL::ActiveRecord::Relations::Collection(Target, Pk)
new(key : Symbol, id : Pk, cascade : Bool = false, query : CQL::Query = (CQL::Query.new(Target.schema)).from(Target.table), auto_load : Bool = true)
new
Macros inherited from class CQL::ActiveRecord::Relations::Collection(Target, Pk)
method_missing(call)
method_missing
Constructor Detail
Initialize the many-to-many association collection class
- param : key (Symbol) - The key for the parent record
- param : id (Pk) - The id value for the parent record
- param : target_key (Symbol) - The key for the associated record
- param : cascade (Bool) - Delete associated records
- param : query (CQL::Query) - Query object
- return : ManyCollection
Example
ManyCollection.new(
:movie_id,
1,
:actor_id,
false,
CQL::Query.new(Actor.schema).from(Actor.table)
)
Instance Method Detail
Adds an existing record to the association. Saves the association to the join table. Raises an error if the target record is not persisted.
- param : record (Target)
- return : self
Example
movie = Movie.find(1)
actor = Actor.create(name: "Laurence Fishburne")
movie.actors << actor
movie.actors.reload.size # => 3 (assuming 2 existed before)
Build a new target record associated with this parent, but don't save it.
The association is only truly formed when saved via #create
or <<
.
- param : attributes (Hash | NamedTuple) - Attributes for the new target record.
- return : Target - The newly built target record.
Example
new_actor = movie.actors.build(name: \"Agent Smith\")
new_actor.persisted? # => false
new_actor.save # Creates Actor and the MoviesActors record via appropriate callbacks/methods if defined
Clears all associated records from the parent record. Removes associations from the join table. If cascade is true, also deletes the target records themselves. Clears the internal collection. Wraps the operation in a transaction.
- return : self
Example
movie.actors.create(name: \"Carrie-Anne Moss\")
movie.actors.clear
movie.actors.size # => 0
Actor.exists?(name: \"Carrie-Anne Moss\") # => false if cascade was true
Associates an existing or new target record with the parent record. Creates the association in the join table. If the target record is new, it's created first. Wraps the process in a transaction. Adds the record to the collection if loaded.
- param : record (Target) - The record to associate (can be new or persisted)
- return : Target - The associated (and possibly created) record
- raise : CQL::Error on creation failure
Example
actor = Actor.new(name: "Hugo Weaving")
movie.actors.create(actor)
=> #<Actor:0x... @id=..., @name="Hugo Weaving">
movie.actors.all # includes the new actor if loaded
Create a new target record with given attributes, creates the association in the join table, and adds the record to the collection if loaded. Wraps the creation process in a transaction.
- param : attributes (Hash(Symbol, String | Int64) | NamedTuple)
- return : Target
- raise : CQL::Error on creation failure
Example
movie.actors.create(name: "Carrie-Anne Moss")
=> #<Actor:0x... @id=..., @name="Carrie-Anne Moss">
movie.actors.all # includes the new actor if loaded
Deletes the association for the given record. If cascade is true, also deletes the target record itself. Removes the record from the collection if loaded.
- param : record (Target)
- return : Target? - The deleted target record (if found and cascade=true), or nil
Example
actor = movie.actors.find_by(name: \"Carrie-Anne Moss\")
movie.actors.delete(actor)
movie.actors.all # \"Carrie-Anne Moss\" is gone
Actor.find_by(name: \"Carrie-Anne Moss\") # => nil if cascade was true
Deletes the association for the record with the given ID. If cascade is true, also deletes the target record itself. Removes the record from the collection if loaded. Wraps target deletion in a transaction if cascade is true.
- param : id (Pk)
- return : Target? - The target record if cascade was true and deletion occurred, otherwise nil.
Example
movie.actors.delete(1) # Assuming actor with ID 1 exists
movie.actors.reload # Actor 1 is gone
Actor.find?(1) # => nil if cascade was true
Check if the association exists or not based on the attributes provided
- param : attributes (Hash(Symbol, String | Int64))
- return : Bool
Example
movie.actors.exists?(name: "Keanu Reeves")
=> true
Find associated records based on the attributes provided for the parent record
- param : attributes (Hash(Symbol, String | Int64))
- return : Array(Target)
Example
movie.actors.find(name: "Keanu Reeves")
=> [#<Actor:0x00007f8b3b1b3f00 @id=1, @name="Keanu Reeves">]
Find the first associated record matching the given attributes. Queries the database directly using a JOIN.
- param : attributes (NamedTuple | Hash(Symbol, DB::Any))
- return : Target?
Example
movie.actors.find_by(name: \"Keanu Reeves\")
=> #<Actor...>
Associates the parent record with the records that match the primary keys provided
- param : ids (Array(Pk))
- return : Array(Target)
Example
movie.actors.ids = [1, 2, 3]
movie.actors.reload
movie.actors.all => [
#<Actor:0x00007f8b3b1b3f00 @id=1, @name="Carrie-Anne Moss">,
#<Actor:0x00007f8b3b1b3f00 @id=2, @name="Hugo Weaving">,
#<Actor:0x00007f8b3b1b3f00 @id=3, @name="Laurence Fishburne">]
Reload the association records from the database and return them This now performs the correct JOIN query.
- return : Array(Target)
Example
movie.actors.reload
=> [#<Actor:0x00007f8b3b1b3f00 @id=1, @name="Carrie-Anne Moss">]
Returns a query scope for associated records, filtered by attributes. Allows chaining further query methods (e.g., .limit, .order). Queries the database directly using a JOIN.
- param : attributes (NamedTuple | Hash(Symbol, DB::Any))
- return : CQL::Query
Example
movie.actors.where(name: \"Keanu Reeves\").limit(1).first