{"componentChunkName":"component---src-templates-post-js","path":"/using-rails-aliasing-associations-for-intuitive-development","result":{"data":{"mdx":{"frontmatter":{"title":"Using Rails Aliasing Associations for Intuitive Development","date":"07/26/2020"},"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Using Rails Aliasing Associations for Intuitive Development\",\n  \"slug\": \"using-rails-aliasing-associations-for-intuitive-development\",\n  \"date\": \"2020-07-26T00:00:00.000Z\",\n  \"image\": \"./images/data-heart.jpg\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"This is a reflection write-up on a Rails project I recently completed for the Flatiron School\\u2019s Software Engineering program. This project features a support ticket system that provides frequently asked questions, commenting, tagging, and searching functions. It also allows admins to schedule meetings with users as well as start and manage tasks associated with the support tickets.\"), mdx(\"p\", null, \"When creating an association, Active Record makes these two assumptions:\"), mdx(\"p\", null, \"First, the class name of the model your association points to is based directly off the name of the association. For example, if I write my association as below, ActiveRecord will look for classes named \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Requester\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Requestee\"), \" as the receiver of the Meeting\\u2019s \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"belongs_to\"), \" association.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-rb\"\n  }), \"class Meeting < ApplicationRecord\\n  belongs_to :requester\\n  belongs_to :requestee\\nend\\n\")), mdx(\"p\", null, \"Second, the foreign key in any \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"belongs_to\"), \" relationship will be called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"associationname_id\"), \". This means, such as in the above example, ActiveRecord assumes the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"meetings\"), \" table has a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requester_id\"), \" column pointing to a requesters table and a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requestee_id\"), \" column pointing to a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requestees\"), \" table.\"), mdx(\"p\", null, \"However, we often need to set up our associations such that one model can reference another model with two different names. In my above example, both \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requester\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requestee\"), \" are users stored on the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"users\"), \" table referenced in the User model. Therefore, a user can request meetings (be the requester) and receive meeting requests (be the requestee). A meeting, on the other hand, belongs to a requester and a requestee, both of which are users.\"), mdx(\"h3\", null, \"Aliasing in the belongs_to model\"), mdx(\"p\", null, \"The association on the Meeting model\\u2019s side is a basic one-to-one connection with an aliasing element:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-ruby\"\n  }), \"#app/models/meeting.rb\\n\\nclass Meeting < ApplicationRecord\\n  belongs_to :requester, class_name: 'User'\\n  belongs_to :requestee, class_name: 'User'\\n\\n  ... ...\\nend\\n\")), mdx(\"h3\", null, \"Define references in the migration\"), mdx(\"p\", null, \"To alias a user as either a requester or requestee on the meetings table, the references in the migration table might look like below:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-ruby\"\n  }), \"class CreateMeetings < ActiveRecord::Migration\\n\\n  def change\\n    create_table :meetings do |t|\\n      t.references :requester, references: :users, foreign_key: { to_table: :users }\\n      t.references :requestee, references: :users, foreign_key: { to_table: :users}\\n    end\\n  end\\n\\nend\\n\")), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"foreign_key: { to_table: :association }\"), \" syntax ensures that the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \":requester\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \":requestee\"), \" columns on the meetings table are not looking for tables with the name \\u201Crequester\\u201D and \\u201Crequestee.\\u201D Rather, the association should actually point to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"users\"), \" table.\"), mdx(\"h3\", null, \"Aliasing in the has_many model\"), mdx(\"p\", null, \"On the User model, we can\\u2019t use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"has_many :meetings\"), \" as we do not have any column named \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"user_id\"), \" in table \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"meetings\"), \". We can reference meetings as either \\u201Crequested_meetings\\u201D or \\u201Crequesting_meetings\\u201D like so:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-ruby\"\n  }), \"#app/models/user.rb\\n\\nclass User < ActiveRecord::Base\\n  has_many :requesting_meetings, class_name: \\\"Meeting\\\", foreign_key: \\\"requester_id\\\"\\n  has_many :requested_meetings, class_name: \\\"Meeting\\\", foreign_key: \\\"requestee_id\\\"\\n  ... ...\\nend\\n\")), mdx(\"p\", null, \"This way, we can call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"User.first.requested_meetings\"), \" as well as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"User.first.requesting_meetings\"), \". We can also define a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"meetings\"), \" method that returns the combination of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requested_meetings\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"requesting_meetings\"), \".\"), mdx(\"p\", null, \"However, while the project requires a distinction between requesters and requestees, it doesn\\u2019t require a distinction between requested meetings and requesting meetings. From the performance point of view, two queries need to be made to get the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"meetings\"), \". Therefore, I wrote a custom class method in the User model like so:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-ruby\"\n  }), \"#app/models/user.rb\\n\\nclass User < ApplicationRecord\\n  ... ...\\n  def meetings\\n    Meeting.where(\\\"requester_id = ? OR requestee_id = ?\\\", id, id)\\n  end\\n  ... ...\\nend\\n\")), mdx(\"h3\", null, \"Establish relationship in controller\"), mdx(\"p\", null, \"After the associations are set up in the database and models, we can establish the relationship between meetings, requestees, and requesters with ease. In my project, an admin requests meetings with regular users, who submit support tickets and are referenced as \\u201Csubmitter\\u201D. And a support ticket can have many meetings. In the meeting controller\\u2019s create action, we can easily establish the relationship like so:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-ruby\"\n  }), \"#app/controllers/meetings_controller.rb\\n\\nclass MeetingsController < ApplicationController\\n... ...\\n  def create\\n    @meeting = @ticket.meetings.build(meeting_params)\\n    @meeting.requester = current_user\\n    @meeting.requestee = @ticket.submitter\\n    if @meeting.save\\n      MeetingMailer.meeting_schedule_notification(@meeting).deliver_later\\n      redirect_to @meeting, notice: 'Meeting requested'\\n    else\\n      render :new\\n    end\\n  end\\n... ...\\nend\\n\")), mdx(\"h3\", null, \"Use aliased associations in views and mailers\"), mdx(\"p\", null, \"After a meeting\\u2019s requester and requestee are set, we can retrieve \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@meeting.requester\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@meeting.requestee\"), \" with convenience in our views and mailers. For example, I set up the logic in my \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"meetings_helper\"), \" to determine which views to render according to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"current_user\"), \"\\u2019s relation to the meeting.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-ruby\"\n  }), \"#app/helpers/meetings_helper.rb\\n\\nmodule MeetingsHelper\\n  def set_meeting_with(meeting)\\n    current_user == meeting.requester ? meeting.requestee.name : meeting.requester.name\\n  end\\n\\n  def show_requestee_meeting_actions(meeting)\\n    return unless current_user == meeting.requestee && meeting.requested?\\n    render 'meetings/shared/requestee_actions', meeting: meeting\\n  end\\n\\n  def show_requester_meeting_actions(meeting)\\n    return unless current_user == meeting.requester\\n    render 'meetings/shared/requester_actions', meeting: meeting\\n  end\\n ... ...\\nend\\n\")), mdx(\"p\", null, \"This also allows me convenience in the mailers where I only passed through the meeting argument:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-ruby\"\n  }), \"#app/mailers/meeting_mailer.rb\\n\\nclass MeetingMailer < ApplicationMailer\\n  add_template_helper(MeetingsHelper)\\n  def meeting_schedule_notification(meeting)\\n    @meeting = meeting\\n    mail(to: @meeting.requestee.email, subject: \\\"#{@meeting.requester.name} Requested a Meeting With You\\\")\\n  end\\n... ...\\nend\\n\")), mdx(\"p\", null, \"I hope you use aliasing association as one of your tools of \\u201Cautomagic\\u201D provided by the ActiveRecord ORM to make database queries intuitive and convenient in your development process.\"));\n}\n;\nMDXContent.isMDXComponent = true;"}},"pageContext":{"slug":"using-rails-aliasing-associations-for-intuitive-development"}},"staticQueryHashes":["1063564771","2248308066","2468095761"]}