ActiveRecordでの内部結合
ActiveRecordでの内部結合
複雑なER図でのActiveRecordの使い方を勉強したので、いくつかの代表例をこちらにまとめます
ER図は、以下になります(IE記法で[1対多]の表記が逆になってしまっています)
・内部結合したテーブルにwhereメソッドで条件を加える方法を以下に示します
テーブルのそれぞれの関係は、以下のようになります
class Film < ActiveRecord::Base has_many :inventories end
class Inventory < ActiveRecord::Base has_many :rentals belongs_to :film end
class Rental < ActiveRecord::Base belongs_to :inventory end
上記のようなテーブル関係にある時に、[2005年に貸し出ししていて、まだ返って来ていない映画の一覧(Filmテーブルから取得するカラムは、film_idとtitle)]を取得する場合は、
取得する[Filmsのテーブル]と貸出状況が分かる[Rentalsテーブル]を使うことになるが、この両者のテーブルは互いに直接繋がっていないので、間にある[Inventoriesテーブル]を通して繋げる
イメージとしては、[Filmsテーブル]に[Inventoriesテーブル]をくっつけて、そこに[Rentalsテーブル]をくっつけることになる
以下のコードのように記述すると、[2005年に貸し出ししていて、まだ返って来ていない映画の一覧]を取得できる
Film.select(:film_id, :title).joins(inventories: :rentals).where(rental: { rental_date: Time.new(2005).beginning_of_year..Time.new(2005).end_of_year, return_date: nil }) +---------+------------------+ | film_id | title | +---------+------------------+ | 1 | ACADEMY DINOSAUR | +---------+------------------+ # where(rental: { rental_date: で内部結合したテーブルのうちrentalテーブルのrental_dateカラムという書き方ができる
・ネスト化した内部結合でテーブルをグループ化する方法を以下に示します
テーブルのそれぞれの関係は、以下のようになります
class Category < ActiveRecord::Base has_many :film_categories has_many :films, through: :film_categories end
class Film < ActiveRecord::Base has_many :film_categories has_many :categories, through: :film_categories has_many :inventories end
class Inventory < ActiveRecord::Base has_many :rentals belongs_to :film end
class Rental < ActiveRecord::Base has_many :payments belongs_to :inventory end
class Payment < ActiveRecord::Base belongs_to :rental end
上記のようなテーブル関係にある時に、[カテゴリごとに売上を集計して上位5つ]を取得する場合は、
取得する[categoriesのテーブル]と売上が分かる[paymentsテーブル]を使うことになるが、この両者のテーブルは互いに直接繋がっていないので、間にある[filmsテーブル],[inventoriesテーブル],[rentalsテーブル]を通して繋げる
以下のコードのように記述すると、[カテゴリごとに売上を集計して上位5つ]を取得できる
Category.select("category.category_id, category.name, sum(payment.amount) as revenue").joins(films: { inventories: { rentals: :payments } }).group("category.category_id, category.name").limit(5).order(revenue: :desc) +-------------+-----------+---------+ | category_id | name | revenue | +-------------+-----------+---------+ | 15 | Sports | 5314.21 | | 14 | Sci-Fi | 4756.98 | | 2 | Animation | 4656.3 | | 7 | Drama | 4587.39 | | 5 | Comedy | 4383.58 | +-------------+-----------+---------+ # 内部結合をネスト化していたりして、記述が複雑だが以下の画像で説明しています
・複数の関連付けを内部結合する方法を以下に示します
テーブルのそれぞれの関係は、以下のようになります
class Film < ActiveRecord::Base has_many :film_actors has_many :actors, through: :film_actors has_many :inventories end
class Actor < ActiveRecord::Base has_many :film_actors has_many :films, through: :film_actors end
class Inventory < ActiveRecord::Base has_many :rentals belongs_to :film end
class Rental < ActiveRecord::Base has_many :payments belongs_to :inventory end
class Payment < ActiveRecord::Base belongs_to :rental end
上記のようなテーブル関係にある時に、[JOE SWANKが出演している映画で、売れてるランキングトップ10]を取得する場合は、
取得する[filmsテーブル]と売上が分かる[paymentsテーブル]と俳優が分かる[actorsテーブル]を使うことになるが、[filmsテーブル]と[paymentsテーブル]、[filmsテーブル]と[paymentsテーブル]は互いに直接繋がっていないので、間にある[inventoriesテーブル],[rentalsテーブル],[paymentsテーブル]を通して繋げる
以下のコードのように記述すると、[JOE SWANKが出演している映画で、売れてるランキングトップ10]を取得できる
Film.select("film.film_id, film.title, sum(payment.amount) as revenue").joins(:actors, inventories: { rentals: :payments }).where(actor: { first_name: "JOE", last_name: "SWANK" }).limit(10).group(:film_id).order(revenue: :desc) +---------+------------------------+---------+ | film_id | title | revenue | +---------+------------------------+---------+ | 865 | SUNRISE LEAGUE | 170.76 | | 964 | WATERFRONT DELIVERANCE | 121.83 | | 873 | SWEETHEARTS SUSPECTS | 98.71 | | 434 | HORROR REIGN | 87.73 | | 510 | LAWLESS VISION | 86.84 | | 889 | TIES HUNGER | 80.89 | | 74 | BIRCH ANTITRUST | 74.89 | | 514 | LEBOWSKI SOLDIERS | 72.79 | | 650 | PACIFIC AMISTAD | 62.76 | | 811 | SMILE EARRING | 58.9 | +---------+------------------------+---------+ # 内部結合をネスト化していたりして、記述が複雑だが以下の画像で説明しています
参考記事
【Rails】 joinsメソッドのテーブル結合からネストまでの解説書 | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
Railsの便利な日時メソッド
Railsの便利な日時メソッド
Railsで開発をしていく中で、日時のメソッドを使うことがあります
今回は、Railsの便利な日時のメソッドをこちらにまとめます
・[new]メソッド
irb(main):113:0> Time.new(2015,10,01) => 2015-10-01 00:00:00 +0900 irb(main):116:0> Time.new(2015) => 2015-01-01 00:00:00 +0900 # newメソッドに引数を渡すと、その日時でTimeインスタンスが生成される newメソッドに年の引数のみを渡しても、その年でTimeインスタンスが生成される
・[beginning_of_year]メソッド
irb(main):115:0> Time.new(2015,10,01).beginning_of_year => 2015-01-01 00:00:00 +0900 # レシーバのTimeインスタンスの年の最初の日を返す
・[.end_of_year]メソッド
irb(main):117:0> Time.new(2015,10,01).end_of_year => 2015-12-31 23:59:59 +0900 # レシーバのTimeインスタンスの年の最後の日を返す
ActionMailerのdeliver_nowとdeliver_laterの違い
ActionMailerのdeliver_nowとdeliver_laterの違い
ActionMailerを実装している中で、[deliver_now]メソッドと[deliver_later]メソッドがあり、両者の違いが分からなかった為、こちらにまとめます
[deliver_now]メソッド: 同期処理に送信される(ジョブの状態に関係なく、現時点ですぐにメールが送信される)
[deliver_later]メソッド: 非同期処理で送信される(現時点ですぐにメールが送信されずに、ジョブのキューにプッシュされる。ジョブが実行されていない場合は、メールが送信されない)
※
非同期処理:時間がかかってしまうような重い処理は、後でやる処理としてリスト化して登録する
Railsガイドには、以下のように記載してある
参考記事
ruby on rails - アクションジョブ/メーラーの `deliver_now`と` deliver_later`の違い
ActionMailerのプレビュー
[ActionMailerのプレビュー]について
[ActionMailer]のプレビュー機能が便利だった為、こちらにまとめます
[ActionMailer]のプレビュー機能を使えば、実際にメールが送信されることはないので、何度もメールのレイアウトを確認することができる
プレビュー機能の手順を以下に示します
下記コマンドで、[ActionMailer]を生成する
$ rails generate mailer (メーラー名)
↓
上記で[ActionMailer]を生成した際に、[spec/mailers/previews/]のフォルダ以下にpreviewのファイルが生成されているので、こちらに記載する。
Railsガイドには、[test/mailers/previews/]フォルダ以下にpreviewファイルを配置するとなっており、文章のまま[test/mailers/previews/]を作ってしまったので注意が必要!!
以下に例を示します
[app/mailers/article_mailer.rb] class ArticleMailer < ApplicationMailer def report_summary mail(to: 'admin@example.com', subject: '公開済記事の集計結果') end end
[app/mailers/previews/article_mailer_preview.rb] class ArticleMailerPreview < ActionMailer::Preview def report_summary # メーラーと同じメソッド名で定義する ArticleMailer.report_summary #送信したいメーラーを呼び出す[(メーラー名).(メーラーのメソッド)] end end
↓
サーバーを起動して以下のURLにアクセスすると、メールのプレビューが確認できる
http://localhost:3000/rails/mailers/(メーラー名)/(メソッド名)
①
以下が例
http://localhost:3000/rails/mailers/user_mailer/welcome_email
↓
http://localhost:3000/rails/mailersで、使用できるメーラーが以下のように表示される
↓
メソッド名をクリックすると以下のようにメールがプレビューで表示される(このURLが①になる)
このメソッド名のリンク(report_summary)は、previewクラスに作成したreport_summaryメソッドに対応して表示される
※ポイント
[ActionMailer]のプレビュー機能では、メールが送信されているわけではないのでプレビューで表示されるからメールの送信機能も動いていると勘違いしないようにする
参考記事
オブジェクト指向とgetterとsetterとカプセル化
[オブジェクト指向とgetterとsetterとカプセル化]
[オブジェクト指向],[getter],[setter],[カプセル化]という言葉を見かけることが多いが、それらの意味をあまり理解していなかったのでこちらにまとめます
[オブジェクト指向]
○○指向というのは、〇〇を重視しているというような意味になる
例えば、恋人を選ぶ際にルックス指向や性格指向といった感じ
オブジェクト指向プログラミングというのは、オブジェクトをたくさん作ってアプリケーションを作るという事
Twitterで例えたら、ユーザーのアカウントというオブジェクトやTweetというオブジェクト、フォローというオブジェクトなどが集まってTwitterが出来上がるということ
[getter]と[setter]
[getter]というのは、クラスの外からオブジェクトの属性の値を参照できるようにするメソッドのこと
[setter]というのは、クラスの外からオブジェクトの属性の値を変えることができるようにするメソッドのこと
今回のように、オブジェクトの属性値を変更できるオブジェクトの事をミュータブルなオブジェクトという
以下に例を示します
class Food def name=(text) # setter: クラスの外からname属性に値をセットしたり変更したりできる @name = text end def name # getter: クラスの外からname属性の値を参照できる @name end end food = Food.new food.name = "寿司" p food.name => "寿司"
[カプセル化]
[カプセル化]というのは、クラスの外からオブジェクトの属性の値を参照出来ないようにしたり、値を更新できないようにすること
基本的には、オブジェクトの属性値は外部から値が更新されないようにカプセル化した方が良いことがほとんど
今回のように、オブジェクトの属性値が変更できないオブジェクトの事をイミュータブルなオブジェクトという
以下に例を示します
class Food private def name=(text) @name = text end def name @name end end food = Food.new food.name = "寿司" => エラーになる # クラスの中に[getter]と[setter]を記述しているが、privateにする事でクラスの外から[getter]と[setter]を呼び出せないようにして、クラスの外から値を参照したり、値を更新できないようにしている<br> p food.name => エラーになる
参考記事
複数ファイルのアップロード
複数ファイルのアップロード
複数ファイルをアップロードしたい時は、以下のように記述する(simple_formの場合)
[app/views ファイル] = f.input :main_images, as: :file, input_html: { multiple: true } # input_html: { multiple: true } というオプションを付ける
[app/controllers ファイル] def site_params params.require(:site).permit(:name, :subtitle, :description, :favicon, :og_image, main_images: []) end
上記のようにコントローラでストロングパラメータで複数ファイルをアップロードしたものを受け取る際は、配列になるのでmain_images: []のように記述する
参考記事
Swiper
Swiper
Swiperは、画像などを動的にスライドできるjQueryのプラグイン
導入手順は、以下のようになる
$ yarn add swiper
↓
[node_modules]にswiperがインストールされる
swiperの公式にあるように[swiper-bundle.css]と[swiper-bundle.js]を読み込むようにしなければいけない
今回は、swiperを使用するlayoutファイルに[application.js]と[application.css.scss]を使用していると仮定しています
[app/assets/javascripts/application.js] //= require swiper/swiper-bundle.js
[app/assets/stylesheets/application.css.scss] @import 'swiper/swiper-bundle';
上記のassetに記述したファイルのパスを以下に記述することで、絶対パスで記述しなくても読み込める
[config/initializers/assets.rb] Rails.application.config.assets.paths << Rails.root.join('node_modules')
[app/views/layouts/_header.html.slim] header .swiper-container .swiper-wrapper # スライドさせたい部分にクラスを付ける① - if current_site.main_images.present? - current_site.main_images.each do |main_image| = image_tag url_for(main_image), class: 'swiper-slide' # スライドさせる対象にswiper-slideというクラスを付ける - else = image_tag '/images/cover.jpg', class: 'swiper-slide' .container.blog-title h1 = link_to current_site.name, root_path p.lead = current_site.subtitle javascript: $(document).ready(function() { new Swiper('.swiper-container', { # ①のクラス名を利用 loop: true, autoplay: { delay: 3000, }, }) })
※ポイント
$(document).ready(function() { # 指定した処理 });
上記の記述は、DOMの読み込みが終わったらfunction()の中の処理を実行するという意味
JavaScriptは、HTMLが全て読み込まれてから出ないと正しく動作しない為、これを記述する
非同期通信(remote: true)で何かをクリックした時とかに[js.erb]ファイルをレスポンスする時は、ブラウザ上で画面が既に表示されているので、DOMの読み込みが終わっているが、今回のようにブラウザに画面が表示されるのと同時にJavaScriptを使う場合とかだと画面上のDOMが読み込まれているか分からないので、
[$(document).ready(function() {
});]
を記述する
参考記事
jQueryの基本 - $(document).ready - Qiita
JavaScriptの$(document).readyの使い方を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン
swiperをyarnで導入して、画像をスライダー形式にする! - Qiita
【2021年版 RailsアプリにSwiper.jsでカルーセルを実装】 - Qiita
RailsでSwiperを導入する方法(Swiperは2020年7月にバージョンアップし、従来と設定方法が変わりました!) - Qiita
RailsでSwiperを導入する方法(Swiperは2020年7月にバージョンアップし、従来と設定方法が変わりました!) - Qiita