You Down with ORM (yeah you know me).

Posted by Alisa Cookie McCormick on January 6, 2019

Now that I’ve got your attention, lets talk about ORM (Object-Relational Mapping). ORM is a technique that lets you query and operate on data in a relational database using object-oriented programming. ORM associates classes with database tables and instances of those classes with table rows. ORM is useful in that it cuts repetitious code and allows conventional patterns for constructing complex systems in an organized manner.

HOW TO MAP A CLASS TO A TABLE

In this example, we will be using a database about books. This program will have a Book class and each book instance will have an author and a title (also known as the attr_accessors). the attr_acccessors are created from the columns of the database.

class Book
  attr_accessor :author, :title
  attr_reader :id

  def initialize(author, title, id = nil)
    @id = id
    @author = author
    @title = title
  end
end

The id attribute has a default value of nil. The id attribute is assigned once the object is saved into the database.

**1. Database Creation **

Classes are mapped to tables inside a database, not the database itself. The creation of the database is the responsibility of our program. It is conventional to pluralize the name of the class to create the name of the table. Our Book class is our “books” table.

The environment folder should look like this:

require 'sqlite3'
require_relative '../lib/book'
 
DB = {:conn => SQLite3::Database.new("db/books.db")}

We can access the DB connection:

DB[:conn]

2. Table Creation

We can create a table in our Book class using a class method. <<- is known as a here-document or heredoc - used to form multiline string literals, saving line breaks and other white space.

def self.create_table
  sql = <<-SQL
    CREATE TABLE IF NOT EXISTS books (
      id INTEGER PRIMARY KEY,
      author TEXT,
      title TEXT
    )
  SQL

  DB[:conn].execute(sql)
end

3. Saving Data

The #save method inserts record into our database that has the author and title values of the book instance. We create a sql INSERT statement with bound parameters. Bound parameters use ? characters as placeholders for values we pass in as arguments. This is also where the id is assigned to the object. We grab the id of the inserted row and assign it to the value of the id attribute. The id is also the row where this object can be found.

We must also check to see if the object has already been persisted. If the object id already exists, we update the record. Otherwise, we save the new object.

def save
  if self.id
    self.update
  else
    sql = <<-SQL
      INSERT INTO books (author, title)
      VALUES (?, ?)
    SQL
		
    DB[:conn].execute(sql, self.author, self.title)
    @id = DB[:conn].execute("SELECT last_insert_rowid()")[0][0]
  end
  self
end

4. Updating Records

The best way to update a record is to update all the attributes at the same time.

def update
  sql = "UPDATE books SET author = ?, title = ? WHERE id = ?"
  DB[:conn].execute(sql, self.author, self.title, self.id)
end

Now, you can then create new books and save them to the database.

Books.new("J. K. Rowling", "Harry Potter and the Sorcerer's Stone").save
Books.new("Michelle Obama", "Becoming").save
Books.new("J. Kenji Lopez-Alt", "The Food Lab").save
id author title
1 J. K. Rowling Harry Potter and the Sorcerer’s Stone
2 Michelle Obama Becoming
3 J. Kenji Lopez-Alt The Food Lab