maze
  • Introduction
  • Getting Started
    • Quick Start
  • Guides
    • Overview
    • Installation
    • Create New App
    • Directory Structure
    • Configuration
    • Docker
    • Controllers
      • Sessions
      • Request & Response Objects
      • Halt!
      • Respond With
      • Params Validation
      • Cookies
      • Filters
      • Flash
      • Redirection
    • Views
      • View Helpers
    • Models
      • Granite
        • Granite's README
        • Migrations
        • Validations
        • Callbacks
        • Associations
        • Querying
        • Bulk Insertions
      • Crecto
        • Crecto's README
      • Jennifer
        • Jennifer Docs
        • Migrations
        • Models
    • Routing
      • Pipelines
      • Routes
    • Websockets
      • Channels
      • Sockets
      • JavaScript Client
    • Mailers
      • Deliver a new Email
  • Testing
    • System Tests
  • Deployment
    • Digital Ocean
    • Heroku
  • CLI
    • New
    • Recipes
    • Generate
    • Database
    • Watch
    • Routes
    • Exec
    • Encrypt
    • Deploy
  • Examples
    • Maze Auth
    • Crystal Debug
    • Minimal Configuration
  • Troubleshooting
  • Contributing
  • Code of Conduct
  • HAVE A QUESTION?
    • Ask on Gitter
    • Ask on StackOverflow
    • Follow on Twitter
    • Submit an issue
Powered by GitBook
On this page
  • One to Many
  • Many to Many
  • has_many through:
  1. Guides
  2. Models
  3. Granite

Associations

PreviousCallbacksNextQuerying

Last updated 7 years ago

This section is based on

One to Many

belongs_to and has_many macros provide a rails like mapping between Objects.

class User < Granite::ORM::Base
  adapter mysql

  has_many :posts

  field email : String
  field name : String
  timestamps
end

This will add a posts instance method to the user which returns an array of posts.

class Post < Granite::ORM::Base
  adapter mysql

  belongs_to :user

  field title : String
  timestamps
end

This will add a user and user= instance method to the post.

For example:

user = User.find 1
user.posts.each do |post|
  puts post.title
end

post = Post.find 1
puts post.user

post.user = user
post.save

In this example, you will need to add a user_id and index to your posts table:

CREATE TABLE posts (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT,
  title VARCHAR,
  created_at TIMESTAMP,
  updated_at TIMESTAMP
);

CREATE INDEX 'user_id_idx' ON TABLE posts (user_id);

Many to Many

Instead of using a hidden many-to-many table, Granite recommends always creating a model for your join tables. For example, let's say you have many users that belong to many rooms. We recommend adding a new model called participants to represent the many-to-many relationship.

Then you can use the belongs_to and has_many relationships going both ways.

class User < Granite::ORM::Base
  has_many :participants

  field name : String
end

class Participant < Granite::ORM::Base
  belongs_to :user
  belongs_to :room
end

class Room < Granite::ORM::Base
  has_many :participants

  field name : String
end

The Participant class represents the many-to-many relationship between the Users and Rooms.

Here is what the database table would look like:

CREATE TABLE participants (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT,
  room_id BIGINT,
  created_at TIMESTAMP,
  updated_at TIMESTAMP
);

CREATE INDEX 'user_id_idx' ON TABLE participants (user_id);
CREATE INDEX 'room_id_idx' ON TABLE participants (room_id);

has_many through:

As a convenience, we provide a through: clause to simplify accessing the many-to-many relationship:

class User < Granite::ORM::Base
  has_many :participants
  has_many :rooms, through: participants

  field name : String
end

class Participant < Granite::ORM::Base
  belongs_to :user
  belongs_to :room
end

class Room < Granite::ORM::Base
  has_many :participants
  has_many :users, through: participants

  field name : String
end

This will allow you to find all the rooms that a user is in:

user = User.first
user.rooms.each do |room|
  puts room.name
end

And the reverse, all the users in a room:

room = Room.first
room.users.each do |user|
  puts user.name
end
Granite's README