Closed4

Rails学習記録入門

matsubokkurimatsubokkuri

Railsのアプリ開発メモ 入門編

プログラミングスクールで学んでいることをスクラップに整理していく

matsubokkurimatsubokkuri

開発環境

docker compose build:イメージのビルド
docker compose up:Railsサーバの起動
docker compose exec web bash:コンテナに入る。webはDockerのサービス名
bundle install:Gemのインストール。コンテナ内から実行
yarn install:node_modulesのインストール。コンテナ内から実行
docker compose exec web bin/rails db:create:データベースの作成
bin/dev:CSS, JS用のサーバー起動

matsubokkurimatsubokkuri

Git

git switch -c ブランチ名:ブランチを切り替える。新規の場合は作成する
git branch:現在のブランチ名を確認する
git status:変更のあったファイルを確認する
git add ファイル名:リモートリポジトリに反映したいファイルをステージに上げる
git commit -m "メッセージ":ステージにあげた変更をリポジトリに保存する
git push origin ブランチ名:変更をリモートリポジトリにアップロード
git checkout master:masterブランチに切り替える
git pull origin master:リモートリポジトリの内容をローカルにコピーする

matsubokkurimatsubokkuri

タスク登録機能を実装する例

モデルの作成

docker compose run web bin/rails generate Task title:string description:text

  • モデル名Taskは単数。作成するカラムとその型を記述

db/migrateのmigrationファイルを確認してdocker compose run web bin/rails db:migrate
models/task.rb がモデルファイル。テーブル名は単数系

class Task < ApplicationRecord
  validates :title, presence: true #バリデーションを記述。これはnull禁止
end

バリデーションの例
参考:https://railsguides.jp/active_record_validations.html

  • validates :terms_of_service, acceptance: true #チェックボックスがオンであるおこと
  • validates :email, confirmation: true  #二つのフィールドの内容が同じであること
  • validates :end_date, comparison: { greater_than: :start_date } #比較対象よりも大きいこと
  • validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,message: "英文字のみが使えます" } #正規表現のようなフォーマットに従っていること
  • validates :size, inclusion: { in: %w(small medium large),message: "%{value} のサイズは無効です" } #指定するものを含んでいること
  • validates :points, numericality: true #数値であること
  • validates :games_played, numericality: { only_integer: true } #数値の中でも整数である条件を付与
  • validates :name, :login, :email, presence: true #空白値ではないこと
  • validates :email, uniqueness: true #一意であること

ルーティング

参考:https://railsguides.jp/routing.html
config/routes.rbがルーティングファイル

Rails.application.routes.draw do
    resources :tasks #コントローラへのルーティング。複数形。
 # resources :tasks, only: :index # indexアクションだけに有効にする場合only: :アクション
end

resourcesを用いることでindex、show、new、create、edit、update、destroyアクションが作成される

  • GET /tasks tasks#index すべてのタスクの一覧を表示
  • GET /tasks/new tasks#new タスクを1つ作成するためのHTMLフォームを返す
  • POST /tasks tasks#create タスクを1つ作成する
  • GET /tasks/:id tasks#show 特定のタスクを表示する
  • GET /tasks/:id/edit tasks#edit タスク編集用のHTMLフォームを1つ返す
  • PATCH/PUT /tasks/:id tasks#update 特定のタスクを更新する
  • DELETE /tasks/:id tasks#destroy 特定のタスクを削除する
パスとURL用ヘルパー

resourcesでルーティングした場合、一意のURLパスが同時に生成され、リンク時に活用できる

  • tasks_urlは/tasks #複数
  • new_task_urlは/tasks/new #単数
  • edit_task_url(:id)は/tasks/:id/edit (edit_photo_url(10)であれば/photos/10/edit) #単数
  • task_url(:id)は/photos/:id (photo_url(10)であれば/photos/10) #単数

コントローラの設定

参考 https://railsguides.jp/action_controller_overview.html
app/controllers/tasks_controller.rbファイルを作成する

class TasksController < ApplicationController
  before_action :set_task, only: %i[ show edit update destroy ] #[:show, :edit, :update, :destroy]と同意
  before_action :set_index_title, only: :index #set_index_titleをindexアクションの最初に実行
  before_action :set_show_title, only: :show

  # GET /tasks or /tasks.json
  def index #一覧表示
    @tasks = Task.all
  end

  # GET /tasks/1 or /tasks/1.json
  def show #一件表示
    @task = Task.find_by(id: params[:id])
  end

  # GET /tasks/new
  def new #新規作成用のフォーム表示
    @task = Task.new
  end

  # GET /tasks/1/edit
  def edit #一件編集
    @task = Task.find_by(id: params[:id])
  end

  # POST /tasks or /users.json
  def create #新規作成(DB更新)
    @task = Task.new(task_params)  #task_paramsは事前に指定されたparamのみを許可

    respond_to do |format| #サイトリクエストに対してHTML、APIリクエストに対してJSONを返す
      if @task.save #保存を試みる
        format.html { redirect_to task_url(@task), notice: "タスクの新規登録に成功しました" } #task_url(@task)にリダイレクト @taskのidを内部的に取得している
        format.json { render :show, status: :created, location: @task }
      else
        format.html { render :new, status: :unprocessable_entity } #ステータスコードに422
        format.json { render json: @task.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /tasks/1 or /users/1.json
  def update
    respond_to do |format|
      if @task.update(task_params)
        format.html { redirect_to task_url(@task), notice: "タスクの更新に成功しました" }
        format.json { render :show, status: :ok, location: @task}
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @task.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /tasks/1 or /users/1.json
  def destroy
    @task.destroy

    respond_to do |format|
      format.html { redirect_to tasks_url, notice: "タスクの削除に成功しました" }
      format.json { head :no_content }
    end
  end

  private #以下、クラス内部からのみアクセス可能
    # Use callbacks to share common setup or constraints between actions.
    def set_task
      @task = Task.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def task_params #許容するパラメータbefore_actionでは実行しない
      params.require(:task).permit(:title, :description)
    end

    def set_index_title
      @index_title = "タスク一覧"
    end

    def set_show_title
      @show_title = "タスク詳細"
    end
end

ビューの作成

参考 https://railsguides.jp/action_view_overview.html
app/viewsにtasksフォルダを作成する

共通ページapplication.html.erb

<!-- ヘッダー割愛 -->
 <body>
    <header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
      <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">RUNTEQ Rails入門STEPアプリ</a>
      <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="col-md-3 text-end">
        <% if current_user.present? %> <!-- ユーザーログイン関連は次項で -->
          <span class="btn btn-primary"><%= current_user.name %></span>
          <%= link_to 'ログアウト',logout_path %>
        <% else %>
          <%= link_to 'ログイン', new_login_path, class: 'btn btn-outline-primary me-2' %>
        <% end %>
      </div>

    </header>

    <div class="container-fluid">
      <div class="row">
        <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
          <div class="position-sticky pt-3">
            <ul class="nav flex-column">
              <li class="nav-item">
                <%= link_to 'トップ', root_path, class: 'nav-link active', 'aria-current': 'page' %><!--  root_pathでトップに -->
                <%= link_to 'ユーザー', users_path, class: 'nav-link active', 'aria-current': 'page' %><!--  users_pathでusers#indexに -->
                <%= link_to 'タスク', tasks_path, class: 'nav-link active', 'aria-current': 'page' %><!--  users_pathでtasks#indexに -->
              </li>
            </ul>
          </div>
        </nav>

        <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
          <%= yield %><!--  この部分に各個別のビューを表示 -->
        </main>
      </div>
    </div>
  </body>

一覧ページindex

<p style="color: green"><%= notice %></p>

<h1><%= @index_title %></h1><!--  @index_titleは コントローラのbefore_actionで定義 -->

<div id="users">
  <% @tasks.each do |task| %><!--  @tasksを順に -->
    <%= render task %><!--  パーシャルファイル _task.html.erbに記述しここで読み込む -->
    <p>
      <%= link_to "詳細", task %><!--  task#showにリンク -->
    </p>
  <% end %>
</div>

<%= link_to "新規作成", new_task_path %><!--  task#newにリンク -->

パーシャルページ

<div id="<%= dom_id task %>">
  <p>
    <strong>タイトル:</strong>
    <%= task.title %>
  </p>
  <p>
    <strong>内容:</strong>
    <%= task.description %>
  </p>
</div>

パーシャルページ 新規登録フォーム

<%= form_with(model: task) do |form| %><!--  form_withはRailsでフォームを作成するためのヘルパーメソッド -->
  <% if task.errors.any? %>
<div style="color: red">
      <h2><%= pluralize(task.errors.count, "error") %> prohibited this user from being saved:</h2><!--  エラーの数と、そのエラーがユーザーの保存を妨げたことを示すメッセージを表示 --> 
      <ul>
        <% task.errors.each do |error| %><!--  各エラーをリスト表示-->
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= form.label :title, 'タイトル', style: "display: block" %>
    <%= form.text_field :title %>
  </div>

  <div>
    <%= form.label :description, '内容', style: "display: block" %>
    <%= form.text_area :description %>
  </div>

  <div>
    <%= form.submit '登録' %>
  </div>
<% end %>
このスクラップは2023/10/29にクローズされました