Project Notes: WordPress Research

25 Different WordPress Tutorials on the net

http://rockablethemes.com/wordpress-themes-tutorials/

The WordPress Tutorial that really got me started

http://wp.tutsplus.com/tutorials/wordpress-theme-development-training-wheels-day-one/

The best info graphic on wordpress file structure

http://yoast.com/wordpress-theme-anatomy/

The legendary wordpress loop

http://blog.teamtreehouse.com/wordpress-loop-beginners-guide

The essential PHP Tutorial (not required but really helpful)

http://html.net/tutorials/php/lesson1.php

Get down and dirty with the search bar and styling it

http://www.wphub.com/customising-default-search-box/

On drop-down Monthly Archives

http://www.problogdesign.com/quick-tweaks/dropdown-the-monthly-archives/

On Categories

http://codex.wordpress.org/Function_Reference/wp_dropdown_categories

On page numbers

http://www.jenst.se/2008/03/29/wp-page-numbers

On styling excerpts (prerequisite the wordpress loop)

http://shibashake.com/wordpress-theme/wordpress-excerpt-how-to-style-it

The better, more advanced styling exceprt photos

http://codex.wordpress.org/Function_Reference/the_post_thumbnail

http://wordpress.org/support/topic/the_post_thumbnail-image-sizes

 

On styling the comments bar

http://www.wpbeginner.com/wp-themes/how-to-style-wordpress-comment-form/

http://www.yourinspirationweb.com/en/wordpress-from-a-to-z-personalize-the-comments-                   template-part-2/

On displaying comment count on a post

http://wordpress.org/support/topic/add-comment-count-to-post-title

Project Notes: WordPress Research

Project Notes: Sinatra x Restangular

https://www.mobomo.com/2014/10/how-to-set-up-angular-with-rails-part-2/

  • Setting up test frameworks
  • Creating a Rails API
  • Making the Angular frontend talk to the Rails API

http://stackoverflow.com/questions/14511002/angular-js-with-sinatra

  • Conceptual explanation on how AngularJS and Sinatra integrates

http://mykindofgeek.com/programming/its-an-angular-life

  • Code demo

http://arian-celina.com/implementing-rest-services-in-angularjs-using-restangular/

  • Rest services in full angular

https://github.com/mgonto/restangular

  • RESTangular gem
Project Notes: Sinatra x Restangular

MongoDB: Relationships Basics

Embedding versus Associating 

Remember that unlike usual SQL databases, MongoDB, as a NoSQL database, stores its documents in a hash, inside a BSON file. With this, aside from storing the IDs of the objects you are related to (as in a usual association), you could also embed another entire document inside the current document. This technique with hashes is called embedding. Below, we show you the difference between simply embedding and a classic associatuon

How an embedded object is stored in the db

{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "label" : {
    "_id" : ObjectId("4d3ed089fb60ab534684b7e0"),
    "name" : "Mute",
  }
}

How an association is stored in the db

# The parent band document.
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9") }

# The child studio document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f1"),
  "band_id" : ObjectId("4d3ed089fb60ab534684b7e9")
}

In terms of space efficiency, associations take less space because it just stores the object id. Meanwhile, we use embedding primarily because we want the convenience of having a flat table where everything we have is on just one big table. Instead of using the noSQL equivalent of joining tables, we already have what we need. This feature is specially prominent in uses where you constantly need of a flat table (i.e data science) as it reduces the time of joining the table, although it takes a significant overhead trying to keep the collections updated with respect with one another.

Embeds One

Embeds one does exactly what it says. It embeds one document inside another.

The owner class (band) uses the embeds_one macro while the owned class (label) uses the embedded_in class.

class Band
  include Mongoid::Document
  embeds_one :label
end

class Label
  include Mongoid::Document
  field :name, type: String
  embedded_in :band
end

In the database, an instance of the band class looks like this:

{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "label" : {
    "_id" : ObjectId("4d3ed089fb60ab534684b7e0"),
    "name" : "Mute",
  }
}

>The label object is actually embedded inside the owner band instance. Notice that we embed the label object with the label key. If we want another key, we simply set the name of our preference using the :store_as option. This comes with caveat of reduced readability.

class Band
  include Mongoid::Document
  embeds_one :label, store_as: "lab"
end 

Embeds Many

Embeds many does exactly what it says. It allows you to embed many documents insider another document.

The owner class (band) uses the embeds_many macro while the owned class (label) uses the embedded_in class.

class Band
  include Mongoid::Document
  embeds_many :albums
end

class Album
  include Mongoid::Document
  field :name, type: String
  embedded_in :band
end

In the database, the bandIn the database, an instance of the band class looks like this

 

{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "albums" : [
    {
      "_id" : ObjectId("4d3ed089fb60ab534684b7e0"),
      "name" : "Violator",
    }
  ]
}

As you can see, instead of a single document, we have a list instead. That’s the structural difference between an embeds_one and embeds_many. Again, we can do a store_as:

 

class Band
  include Mongoid::Document
  embeds_many :albums, store_as: "albs"
end


Has One 

As we mentioned in the earlier section, when we say that an object has a has_one association, it means yes it owns an instance, but instead of embedding a copy of the object inside of itself, we just store the id of the owner object inside the owned object.

With the given example models below:

class Band
  include Mongoid::Document
  has_one :studio
end

class Studio
  include Mongoid::Document
  field :name, type: String
  belongs_to :band
end

 We should have the hash:

# The parent band document.
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9") }

# The child studio document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f1"),
  "band_id" : ObjectId("4d3ed089fb60ab534684b7e9")
}

Has Many

Now, imagine that instead of just owning one, we let the owner object own many instances of another object. We would have the syntax:

class Band
  include Mongoid::Document
  has_many :members
end

class Member
  include Mongoid::Document
  field :name, type: String
  belongs_to :band
end

With the hash below stored in the dB:

# The parent band document.
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9") }

# A child member document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f1"),
  "band_id" : ObjectId("4d3ed089fb60ab534684b7e9")
}

Which is essentially the same thing as the has_one hash structure. Why? Because we place the band_id in the owned object. And we imply that a child can only have one parent.

Has And Belongs To Many

This is the association that allows both the parent to have many children and the child to have many parents

class Band
  include Mongoid::Document
  has_and_belongs_to_many :tags
end

class Tag
  include Mongoid::Document
  field :name, type: String
  has_and_belongs_to_many :bands
end

The difference in the database structure is that the concept of a parent is gone because Tag can have many Bands and Bands can have many tags. Each of them now has a list as their foreign_key, referring to each other.

# The band document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "tag_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}

# The tag document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f2"),
  "band_ids" : [ ObjectId("4d3ed089fb60ab534684b7e9") ]
}
 
MongoDB: Relationships Basics

MongoDB: Relationship Options

Relationships are both associations and embedding. We saw in the previous post that this gives MongoDB power as a NoSQL database. Now, we further expand those powers by adding the options that would make that even more functional.

Relations Statements

class Person
include Mongoid::Document
embeds_many :addresses
end

Relations contains four parts: (1) the kind of association/embedding (for the example above, the association is one-to-many, we would discuss this and the other types at length in the next sections), (2) target, which is the object/class which is referred to by the relation. For instance, in the example above, the person model is related to the addresses table in that it embeds many addresses; (3) base, which is the parent object, in this case the parent of address is person. So when we do:

person.addresses.base

we get back the person. And finally, we get (3) metadata, or simply information about the data.

It is important to note that MongoDB would check this association statements so if we do not want that validation behavior, we simply set validate to false, like the one below.

class Person
  include Mongoid::Document

  embeds_many :addresses, validate: false
  has_many :posts, validate: false
end

Extensions

Extensions are a way to add functionalities to the specific relation. This is by adding models to the association.

class Person
  include Mongoid::Document
  embeds_many :addresses do
    def find_by_country(country)
      where(country: country).first
    end
    def chinese
      @target.select { |address| address.country == "China" }
    end
  end
end

Custom Relationship Names

By default, the name of the relationship is the name of the other table you want to hook up to. For the example above, the relationship name is “addresses”, the same name the table it is associated to. If we want to change the name of the association, we could do so, and I would explain in the example below

class Lush
  include Mongoid::Document
  embeds_one :whiskey, class_name: "Drink", inverse_of: :alcoholic
end

class Drink
  include Mongoid::Document
  embedded_in :alcoholic, class_name: "Lush", inverse_of: :whiskey
end

Now, for the relation statement in the class Lush, we name the association whiskey. Now, MongoDB can't correctly recognize that we want to associate that with Drink class so we explicitly state the class_name parameter to class Drink on order to point to class Drink. Suppose you have many named relations in class Drink, MongoDB still would'nt be able to recognize the relations statement who is paired with the class Lush. So we explicitly state the partner relation statement by using the inverse_of parameter.

Polymorphism

The polymorphism relation is indeed very interesting because it allows two objects to be related to the same object by using the same field.

class Band
  include Mongoid::Document
  embeds_many :photos, as: :photographic
  has_one :address, as: :addressable
end

class Photo
  include Mongoid::Document
  embedded_in :photographic, polymorphic: true
end

class Address
  include Mongoid::Document
  belongs_to :addressable, polymorphic: true
end

In the example above, Band owns Photo and Address (albeit in different ways) thru the relation name "photographic" and "addressable". We declare the polymorphic ownership thru the as: <relation_name> option. In the side of the class being owned, we simply set the option polymorphic to true.

Cascading Callbacks

This is specially relevant for the embed relations. When set to true, the cascade_callbacks allow changes on an instance of the band class to be cascaded back to the embedded portions in the instances of the album and label class that it is embedded to. This allows referential integrity to hold.
 class Band
   include Mongoid::Document
   embeds_many :albums, cascade_callbacks: true
   embeds_one :label, cascade_callbacks: true
 end

Dependent Behavior

Cascading Callbacks is just the beginning of our intro to dependent behavior. Dependent behavior is important in that it ensures referential integrity, a very important database concept we would discuss here. Referential Integrity is best explained in an example. For instance a student is has_many classes. For instance one of his classes gets deleted. The student will still refer to the deleted class, and when it checks for more info on the class, the class is gone, and hence it is referring to a missing class. So to ensure referential integrity, we delete the association between the student and the missing class.

There are 4 kinds of dependency:
  • :delete: Delete the child document without running any of the model callbacks.
  • :destroy: Destroy the child document and run all of the model callbacks.
  • :nullify: Orphan the child document.
  • :restrict: Raise an error if the child is not empty.

For us to enable this behavior, we simple add the parameter dependent and add the tokenize kind of dependency

class Band
  include Mongoid::Document
  has_many :albums, dependent: :delete
  belongs_to :label, dependent: :nullify
end

class Album
  include Mongoid::Document
  belongs_to :band
end

class Label
  include Mongoid::Document
  has_many :bands, dependent: :restrict
end

Autosaving

Mongoid, as opposed to ActiveRecord does not auto-save changes to the associated objects. For instance in the example above, any changes we make to band.albums would not be committed to the database right away. It would be on cache, and would be saved collectively later on. If we want to ensure that the changes are saved outright, we add the autosave: true option to the parameter of the relatioom.

class Band
  include Mongoid::Document
  has_many :albums, autosave: true
end

Recursive Embedding

If for some reason you want to recusively embed a document within a document, you could either use option recursively_embeds_many (which would allow a lot of recursive embeds) or recursively_embeds_one (which allows just one-lv recursion). Below is an example code.
class Tag
  include Mongoid::Document
  recursively_embeds_many
end
class Node
  include Mongoid::Document
  recursively_embeds_one
end
Autobuilding

Suppose you have the class Band, configured as below:
class Band
  include Mongoid::Document
  embeds_one :label, autobuild: true
  has_one :producer, autobuild: true
end

When you say autobuild true for the embeds_one :label, we say that when we do the code
band.label
we automatically build a label object.
 
MongoDB: Relationship Options

MongoDB: Getting Started and Model-Level Techniques

To demonstrate the powers of Mongoid, we would start from a new rails application

rails new mongo_app

cd mongo_app

Installation

gem ‘mongoid’, ‘~> 5.1.0’

For you not to manually create the configuration file of mongoid in config/mongoid.yml, we use the command:

rails generate mongoid:config

Now, rails comes with the ActiveRecord ORM by default, to change that, we open config/application.rb and add:

config.generators do |g| g.orm :mongoid end

Models

To begin, lets create a new model called Phone.

rails generate model Phone number:string country:string

And then, we open the model file at app/models/phone.rb. Since we configured rails to use the mongoid ORM, we see our model file as:

class Phone
  include Mongoid::Document
  field :number, type: String
  field :country, type: String
end

When you don’t specify a type, Mongoid considers this as an object type.

Reading/Writing in Mongoid

Now, for instance if we want to access the field, we do:

phone.number

phone[:number]

If we want to write,

phone.number = “911”

phone[:number] = “911”

Now, if we want to write multiple attributes at once, we do:

phone = Phone.new(number: “911”, country: “USA”)

phone.attribute = {number: “117”, country: “Philippines”}

Default Values

For instance, we want to have our models come with a default value (so that when there is no entry, we would have a value for the field), we modify the model

class Phone

  include Mongoid::Document

  field :number, type: String, default: “09228081190”

  field :country, type: String, default: “jambytown”

end

Aliasing Fields

Since MongoDB is schemaless (unlike say, postgresql) it stores its schema along with the document. Remember that MongoDB, instead of storing rows of data in a table like a normal SQL dB would do, stores its data on BSON hashes. Since hashes are a key-value pair, this is where MongoDB stores its schema. Hence, it stores it in every document.

Since that could be expensive in terms of RAM, we can create aliases of fields:

class Phone

  include Mongoid::Document

  field :n, as: :number, type: String, default: “09228081190”

  field :c, as: :country, type: String, default: “jambytown”

end

In creating aliases, mongoDB uses the real keys as the “key” in the key-value pairs, but we can still interface with mongoid using the alias.

Although it saves in memory, I do not advise its indiscriminate use as it can reduce the readability of the hashes once queried in the database.

Customizing DB

By default, Mongoid models references the default db and client placed in the configuration file. But you can define more databases and clients and give them a name. Mongoid also is preconfigured to connect to the collection that is the plural form of its model. For instance, for our Phone model, we are connected to the phones collection. (pretty much how ActiveRecord is, right?)

Now, if we want to customize what collection/database/client we want the model to refer to, we could do so by adding this line inside the Phone model class in app/models/phone.rb

store_in collection: "citizens", database: "other", client: "secondary"

Custom fields

For instance, we have the profile object. And we want to create a field with a custom type called Point.

class Profile
  include Mongoid::Document
  field :location, type: Point
end

For that, we define the class Point as another class inside the same file (or imported for better seperation of concern)

class Point

  attr_reader :x, :y

  def initialize(x, y)
    @x, @y = x, y
  end

  # Converts an object of this instance into a database friendly value.
  def mongoize
    [ x, y ]
  end

  class << self

    # Get the object as it was stored in the database, and instantiate
    # this custom class from it.
    def demongoize(object)
      Point.new(object[0], object[1])
    end

    # Takes any possible object and converts it to how it would be
    # stored in the database.
    def mongoize(object)
      case object
      when Point then object.mongoize
      when Hash then Point.new(object[:x], object[:y]).mongoize
      else object
      end
    end

    # Converts the object that was supplied to a criteria and converts it
    # into a database friendly form.
    def evolve(object)
      case object
      when Point then object.mongoize
      else object
      end
    end
  end
end

For us to be able to use the object as an attribute of the Profile model, we place 5 functions inside the Point class (the first two are instance method, and the last three are class methods as indicated by the meta-class class << self). The first mongoize (instance function), translate your object into a simple list (the way mongodb would store it).

The demongonize class function translates an object in a DB and instantiate a new point object out of it. Meanwhile, the mongonize class function is for passing points not in object form.

point = Point.new(12, 24)
Venue.where(location: point) #passed location in Object form

venue = Venue.new(location: [ 12, 24 ]) 
#passed location in array form, uses mongoinize

Dynamic Fields

When you haven't defined a field beforehand but you want anyone with an undefined field to be able to store in your DB, we allow dynamic fields. We do this simply by changing the model to

class Profile
  include Mongoid::Document
  include Mongoid::Attributes::Dynamic 
  field :location, type: Point 
end

In this case, the normal dot syntax would raise an error

person.gender
person.gender = "Male"

So we instead use the methods below:

# Retrieve a dynamic field safely.
person[:gender]
person.read_attribute(:gender)

# Write a dynamic field safely.
person[:gender] = "Male"
person.write_attribute(:gender, "Male")

ReadOnly Attributes

If we want an attribute to be readonly, we simply add the code: 

attr_readonly <tokenize names of fields seperated by comma>

class Profile
  include Mongoid::Document
  field :location, type: Point 
  attr_readonly :location
end
Inheritance

MongoDB supports model inheritance. In the example below, this means that Browser model has all the fields of the Canvas (but not the other way around). 

class Canvas
  include Mongoid::Document
  field :name, type: String
  embeds_many :shapes
end

class Browser < Canvas
  field :version, type: Integer
  scope :recent, where(:version.gt => 3)
end

class Firefox < Browser
end

Query Subclasses

For example, we queried the subclass Browser using the syntax Browser.where(name: “thing”). We would only return all objects that is of class Browser, not of canvass (parent) and not of firefox (child).

# Returns Canvas documents and subclasses
Canvas.where(name: "Paper")
# Returns only Firefox documents
Firefox.where(name: "Window 1")

Timestamping

ActiveRecord ORM automatically adds the timestamps in each record. For Mongoid, you have to manually include it using the methods below:
class Person
  include Mongoid::Document
  include Mongoid::Timestamps::Created
end

class Post
  include Mongoid::Document
  include Mongoid::Timestamps::Updated
end
 
 
MongoDB: Getting Started and Model-Level Techniques

ActiveRecord: Migrations

Migrations are very important to ActiveRecord in that it serves to facilitate changes to the database. ActiveRecord even provides us with several generators to make our lives easier.

Creating tables

rails generate migration CreateEngineer

This migration generator listens to the name of the migration we give it. For migrations that starts with create, it creates a DB table for the name after it. In the case above, it creates a db table for engineers. The generated migration file becomes <timestamp>_create_engineers.rb. This new migration file is uncommitted to the database so you can still make your changes to the migration file after generation. For this exact example, the migration becomes an empty create_table block.

class CreateEngineers < …

def change

create_table :engineers do |t|

end

end

end

Adding the attribute-data type pairs in our example, we auto generate a migration that already includes attributes along with a data type.

rails generate migration CreateEngineer name:string salary:decimal

And necessarily, the migration becomes

class CreateEngineers < …

def change

create_table :engineers do |t|

t.string name

t.decimal salary

end

end

end

If you are already satisfied with the changes, simply

rake db:migrate

to commit your changes to your database.

Make the model as well

The initial section only makes the migration file but not the file for the model class. So if you want to make both migration file and model file, we instead use:

rails generate model engineer name:string salary:decimal

The resulting migration file is similar, except that we also have an Engineer model in our model folder.

Reversible Migrations

Every time you make a migration, you change the database. That’s why the default function when you use a generator is “def change”. But when we want to go back and undo the changes, we must make our migration responsive to that. For our example above, a good way to make this migration reversible is to change “def change” to have two functions, “def up” and “def down”. “def up” is the operation rails would perform when migrating and “def down” is the one for when we are going back. In making the “def down” function, we ensure that the steps on “def up” is undone in reversed order.

To go back and rollback the changes of a single migration, we use

rake db:rollback

If we to go back several (say, 3) migrations before, we use

rake db:rollback step=3

ActiveRecord: Migrations

ActiveRecord: Getting Started & Migration

ActiveRecord is an Object Relational Mapper that you run atop database applications like Postgresql or mysqlite. This ORM gives you interfaces that you could use so you dont have to use SQL code much (I say ‘much’ here because the interface ActiveRecord provides is for the common features that are database-agnostic, for database-specific features, I’ll show you a command later to implement SQL code).

As an ORM, we connect database tables to rich ruby objects. This allows us to represent tables as an object, complete with its own properties, methods and be able to map the object with other objects via associations.

There are other ORM available such as mongoid (for MongoDB) but ActiveRecord comes preinstalled with Rails as its default ORM so let’s stick with that for now.

Convention over Configuration

ActiveRecord as an ORM favors convention over configuration in the intution that if you are always configuring your app the same way why not make it the default way of doing things, aka convention.

For the database tables, ActiveRecord takes in the Pluralize form, seperated by underscores, while for the model class it is in CamelCase form and is in singular form. For instance, you have a db_table named master_student, your model class would be named MasterStudent. ActiveRecord’s pluralization module is advanced in that it can pluralize even irregular verbs i.e ____ to ____.

For schema conventions, the primary key is usually the field “id” of the database table while foreign key is the <singular_form_of_table>_id.

Creating ActiveRecord Models and DB Tables

In order to create models, we create a migration. I have an entire blog post dedicated on ActiveRecord Migrations so check that out.

Basic Query Interface

These are very basic commands to get you started on ActiveRecord on rails. This assumes that you have already created a DB Table called users and a model called User with attributes name and job.

BQI: Creating users

To create users, you could either:

user = User.create(name: “Jamby”, job: “coder”)

which creates a user object and saves it directly to the DB, whereas

user = User.new

creates a user object but does not save this to database. This is particularly useful if you want to create an object but want to add the properties later on. For instance, we could do the following to add the attributes below:

user.name = “Jamb$$$$$”

user.job = “CoderX”

And using the line below, we could commit our changes to the DB

user.save

BQJ: Reading

For reading records, we have three types. The first type is just getting all records. For the users example, we do:

User.all

to get everything. The next types of reading commands are essentially queries with a specific condition attached to them

User.first

User.last

User.find(11)

User.find_by(name: “JambyBoy”)

User.where(name: “JambyBoy”, job: “Programmer”)

User.where(name: “JambyBoy”, job: “Programmer”).order(created_at: :desc)

The first two commands above query based on the order of users in the database: first and last.  The next query finds a entry based on its id. The fourth command uses the ActiveRecord function command find_by to query a name. It can be used to query multiple attributes at once but is now being deprecated and now being replaced by the fifth line, the “where” command. The where command is so much more capable of more advanced command, and is the standard for queries. The final line appends to the fifth line the command “order” which allows you to order the result by the created_at attribute, in descending order. You could also place compound order of attributes by adding more attributes to this command.

BQI: Updating

Often, we need to update a record that is already committed in the database, so we simply update the record in the database by first querying the recording and updating the attribute:

user = User.where(name: “jamby”)

user.update(name: “JambyCooler”)

The update() command automatically edits the attribute inside with its corresponding value. You can also compound the attribute-value pairs to update more attributes.

BQI: Deleting

To delete an entry, query the entry first and use the destroy().

user = User.where(name: “jamby”)

user.destroy

Now that we completed a brief introduction to the four basic operations for an ORM, we are prepared to take on associations.

 

 

 

ActiveRecord: Getting Started & Migration

ActiveRecord: Associations

For this blog post, we would discuss about ActiveRecord associations. ActiveRecord associations are model-level associations that make our lives easier. Of course in the database model we often have to manually implement fields for foreign keys and can also manually implement associations using plain ruby code but the AR associations provides an interface to which to make our lives much easier

For instance, when association an object say Teacher, with a student, we would only have to do:

teacher = Teacher.find(id)

teacher.student << student

instead of using teacher.student = student.id. Aside from being more convenient, it is actually more readable. Another example is

teacher = Teacher.find(id)

teacher.delete()

In plain old ruby code, we would need to loop all the object ids in teacher to delete all the students under her. With ActiveRecord, we just have to do the command above and (assuming cascade delete), we would be able to also delete all students.

Now, we go unto the types of associations

belongs_to

This association makes a model start a one-to-one connection with another model. This is done in the model level. This association differs from other association types in that it indicates the current model we have belongs to another model, in other terms, is owned by another model.

class Engineer < …

belongs_to :firm

end

To support this model-level association, we include a firm_id field in our migration file for our engineer. In the code excerpt below, we show exactly where and what we place to add a firm_id field to the engineers table

create_table :engineers do |t|

t.belongs_to :firm, index: true

# other attributes

end

has_one

This association implies a one-to-one connection but implies that each instance of a model owns exactly one instance of another model. In our engineer-firm example, the code below implies that a firm only has one engineer (the converse is also true).

class Engineer < …

belongs_to :firm

end

class Firm < …

has_one :engineer

end

For the migration file of the firms table, we no longer need to add anything special to make the association work.

create_table :firms do |t|

t.string name

# other attributes

end

create_table :engineers do |t|

t.belongs_to :firm, index: true

# other attributes

end

has_many

This association implies a one-to-many relationship between one model and another. Extending our engineer and firms example, this means that one firm can have many engineers. This differs from has_one in that firms can now have more than one engineer under their wing.

class Engineer < …

belongs_to :firm

end

class Firm < …

has_many :engineer

end

The migration is just similar with the has_one migration.

create_table :firms do |t|

t.string name

# other attributes

end

create_table :engineers do |t|

t.belongs_to :firm, index: true

# other attributes

end

has_many through:

Now suppose that engineers can also be employed by multiple firms, aside from firms having multiple engineers. We extend the has_many association by having a many-to-many connection. We declare that an instance of a model can be matched with zero or many instances of another model by proceeding with a third model.

For our engineer example, this means that in order for the many-to-many connection to work we need a third model in the middle of the engineer and the firm model. For our example, we make the third model Contract. So in order for an engineer to work for a firm, an engineer needs a contract, and an engineer can sign multiple contracts with multiple firms.

Another advantage of this approach is that you can also place attributes in the association. In our engineer example, we can add “date_signed” for the contracts table.

For the models, we specify it this way:

class Engineer < …

has_many :firms, through :contract

has_many :contracts

end

class Firm < …

has_many :engineers, through :contract

has_many :contracts

end

class Contract < …

belongs_to :firm

belongs_to :engineer

end

Meanwhile, for the database tables, we set the migrations this way:

    create_table :engineers do |t|
      t.string :name
      t.timestamps null: false
    end
    create_table :firms do |t|
      t.string :name
      t.timestamps null: false
    end
    create_table :contracts do |t|
      t.belongs_to :engineers, index: true
      t.belongs_to :firms, index: true
      t.datetime :signed_date
      t.timestamps null: false
    end
Notice how the create_table block is just ordinary for engineers and firms table but the block for contracts is where the belongs_to methods are called.

has_one through:

Now, lets imagine that there’s a new law that engineers can only work for one firm, but firms can still have many engineers. Now, why not just use the has_one associations then? This is because in using has_one through: we effectively have a third model to store data for, and for a lot of examples, having a third model to store data about the association is often useful.

class Engineer < …

 

belongs_to :contract

end

class Firm < …

has_one :engineers, through :contract

has_one :contracts

end

class Contract < …

belongs_to :firm

has_one :engineer

end

The migration for this type of migration is below:

    create_table :engineers do |t|
      t.string :name
      t.belongs_to :contracts, index: true
      t.timestamps null: false
    end
    create_table :firms do |t|
      t.string :name
      t.timestamps null: false
    end
    create_table :contracts do |t|
      t.belongs_to :firms, index: true
      t.datetime :signed_date
      t.timestamps null: false
    end
You can also play around with this model code to make the associations effectively one-to-one. This is left as an exercise.

has_and_belongs_to_many

This type of association is the same as has_many through: only that we totally disregard the need for a third model in between.

class Engineer < …

has_and_belongs_to_many :contract

end

class Firm < …

has_and_belongs_to_many :engineers

end

I do not reccomend this approach because there is no room for scaling. What if you suddenly need to store information about the relationship? Creating it when there’s need is a lot more headache than just dealing with the incremental complexity of trying that has_many through: approach.

Polymorphic Associations

Polymorphic associations are weird. And this is where things get interesting. A model can belong to more than 1 model on a single association. So how is that? For our engineer-firm example, it is to imagine that we have a third model called contract. And we have an additional attribute of contract called imageable_id that we would use. This imageable_id serves as

ActiveRecord: Associations