[RUBY] Learning record that created the tweet function with the STI pattern

The content of this article is currently awaiting review and may change!

What is STI (Single Table Inheritance)?

Let literally one table be inherited from the parent class to the child class! In this case, the parent Tweet → child Mediatweet Texttweet. Classes for media tweets and text tweets, respectively!

However, since the tables are the same, all classes, both parent and child, have the same columns. So Mediatweet also has a content column, and ** Texttweet also has a ʻimg` column. ** **

db/schema.rb


  create_table "tweets", options: hogehoge
    t.text "content"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "img"
    t.integer "user_id"
    t.string "type"
  end

Why do you do that

It will be easier to write the difference in the model. For example, if you write a validation with no blanks only in the texttweet, the image can be tweeted without text. However, since the contents are almost the same, STI that can share the table is good.

Implementation method

  1. It was already in db / schema.rb, but let the parent class (table) have a type column (string type).
  2. Make each model. Create the parent model with rails g model etc. and create the child model etc. directly in ʻapp / model /`

models/tweet.rb


class Tweet < ApplicationRecord
end

models/mediatweet.rb


class Mediatweet < Tweet
end

models/texttweet.rb


class Texttweet < Tweet
end

For the time being, this is the end of STI implementation. The rest will be done automatically. For example, if you register the database with Mediatweet.create etc., a record containing Mediatweet and data will be added to the type column.

Model distribution based on the contents of params acquired by form_with

I want to sort the model automatically depending on whether there is media data, so I implemented it as follows.

index.html.erb


<%= form_with model: @tweet, url: tweets_path do |form| %>
  <%= form.text_area :content %><br>
  <%= form.label "Upload image" %><br>
  <%= form.file_field :img %>
  <%= form.submit 'Post' %><br>
<% end %>

This time, it is a post and list page that has a post form as it is in index. The image uploader is implemented in carriewave. I will omit that. The parameters submitted by submitting from the above form are

{
"authenticity_token"=>"hoge", 
"tweet"=>{"content"=>"hoge", "img"=>"hoge"}, 
"commit"=>"Post"
}

You can get the parameters of the double structure (structure with {} inside {}) like this.

And it automatically matches the create action of the controller

tweets_controller.rb


  def create
    @tweet = tweettype_class.new(tweet_params)
    if @tweet.save
      redirect_to("/tweets")
    else
      @tweets = Tweet.all.order(created_at: :desc)
      render("index")
  #Rendering returns to the original post list page At this time, form_If you don't specify the with url
  #form_The root of with is pulled by the child model and an error occurs (eg)Former teats_path After rendering → text tweets_path is no much.
    end
  end
  private

  def tweet_params
  #Only the part where the key is tweet is extracted by require({"content"=>"hoge","img"=>"hoge"})
    params.require(:tweet).permit(:content, :img).merge(user_id: @current_user.id)
  end
  #[:img]Models are sorted according to the presence or absence of data
  #Since it has a double structure, the value cannot be obtained correctly unless the key is also specified twice.
  def tweettype
    case
    when params[:tweet][:img].present?
      'mediatweet'
    when params[:tweet][:img].blank?
      'texttweet'
    end
  end
  #Convert the received character string to camel case,Furthermore, it is converted to a constant name
  #(Example) mediatweet.cmelize=>Mediatweet.constantize=>Mediatweet(id: integer, content: text, created_at: datetime, hogehoge.... )
  def tweettype_class
    tweettype.camelize.constantize
  end

Like this, I tried to sort the models automatically depending on whether the image was uploaded.

This made it easier to write different roles for each model!

Recommended Posts

Learning record that created the tweet function with the STI pattern
[Java] Implement a function that uses a class implemented in the Builder pattern