create

【2.メモ登録編】Ruby on Rails + ReactでSNSアプリを作る

pepe87
記事内に商品プロモーションを含む場合があります

こんにちは、ミニマリストいずです。

Rail +ReactでSNS機能を持ったWEBアプリの作り方を紹介していく連続企画の第二弾です。

今回はメモの登録ができるようにしていきます。

初学者の方にもわかるようにまとめていきますが、不明点がありましたら以下から質問をいただければと思います。

作成するアプリの機能紹介(再掲)

サンプル動画(再掲)

作成する機能一覧(再掲)

動画でご覧いただくとお分かりになるかと思いますが、以下の機能を本アプリでは実装していきます。

作成する機能一覧
  • ユーザ管理機能
    • 新規登録
    • ログイン
  • メモカテゴリ機能
    • 作成
    • 取得
    • 動画内ではクレドと表現されている部分
  • メモ機能
    • 作成
    • 一覧取得
    • 詳細取得
    • 動画内ではアクションメモと表現されている部分
  • コメント機能
    • 作成
    • 取得

Webアプリの基本であるCRUDを実装しています。

どのアプリを作るにも参考になると思いますので、初学者の方にもおすすめのアプリになっています。

メモ登録機能(バックエンド編)

今回から各実行後の確認方法は省きますね。

もし確認方法を忘れてしまったら、前の記事に戻って確認してみてください。

あわせて読みたい
【1.カテゴリ新規登録編】Ruby on Rails + ReactでSNSアプリを作る
【1.カテゴリ新規登録編】Ruby on Rails + ReactでSNSアプリを作る

もしそれでもわからなければ以下から無料質問してみてください。

メモのモデル・DB

以下のコマンドで、モデルを追加していきます。

rails g model memo

次に作成されたマイグレーションファイルを以下のように変更します。

1class CreateMemos < ActiveRecord::Migration[6.1]
2  def change
3    create_table :memos do |t|
4      t.string :content
5      t.references :category, index: true, foreign_key: true
6      t.timestamps
7    end
8  end
9end

ここまでのDBを整理すると以下のようになっていますね。

テーブル: memos
----------------------------------------------
| カラム名    | データ型    | その他の制約    |
----------------------------------------------
| id         | integer   | 主キー、自動生成 |
| content    | string    |                 |
| category_id| integer   | 外部キー (categories.id) |
| created_at | datetime  |                 |
| updated_at | datetime  |                 |
----------------------------------------------

テーブル: categories
----------------------------------------------
| カラム名    | データ型    | その他の制約    |
----------------------------------------------
| id         | integer   | 主キー、自動生成 |
| name       | string    | NOT NULL        |
| created_at | datetime  |                 |
| updated_at | datetime  |                 |
----------------------------------------------

変更できたらマイグレートを実行しましょう。

rails db:migrate

バリデーション・アソシエーションの設定

memo.rbにてバリデーションとアソシエーションを設定します。

1class Memo < ApplicationRecord
2  belongs_to :category
3  validates :category, :content, presence: true
4end

belongs_toは、Ruby on RailsのActive Recordにおけるアソシエーションの一種です。これは、一つのモデルが別のモデルに属している関係を示します。

選択したコードでは、MemoモデルがCategoryモデルに属していることを示しています。これは、各メモが1つのカテゴリーに属していることを意味します。

また、category.rbにて、アソシエーションも追加しておきます。

1class Category < ApplicationRecord
2  has_many :memos
3  validates :name, presence: :true
4end
5

has_manyは、Ruby on RailsのActive Recordにおけるアソシエーションの一種です。これは、一つのモデルが他の多くのモデルを持っている関係を示します。

選択したコードでは、Categoryモデルが多くのMemoモデルを持っていることを示しています。これは、各カテゴリーが多くのメモを持っていることを意味します。

ここまでコードが書けたら、rails cでコンソールを立ち上げて、データが登録できるかを確認してみてください。

カテゴリを先に登録していないとメモが登録できないので注意してください。

復習ポイント
  • バリデーションとはどのようなものでしたか?
  • データを登録するコマンドはどのようなものでしたか?
  • 登録されているデータを確認するコマンドはどのようなものでしたか?

メモのコントローラー

以下のコマンドでメモのコントローラーを追加します。

rails g controller api::v1::memos

コントローラー作成コマンドにて、api::v1::とつけてあげると、フォルダ(階層)構造を元に作成してくれるので、ファイルの移動やクラス名の変更が不要になります。

前回より手順が楽になっていることに気づきましたか?

あわせて読みたい
【1.カテゴリ新規登録編】Ruby on Rails + ReactでSNSアプリを作る
【1.カテゴリ新規登録編】Ruby on Rails + ReactでSNSアプリを作る

メモの登録ができるように、コントローラーのコードを変更します。

1class Api::V1::MemosController < ApplicationController
2  def create
3    memo = Memo.new(memo_params)
4    if memo.save
5      render json: memo, status: :created
6    else
7      render json: memo.errors, status: :unprocessable_entity
8    end
9  end
10
11  private
12
13  def memo_params
14    params.require(:memo).permit(:content, :category_id)
15  end
16end
17

カテゴリを取得できるようにコントローラーに追記

メモはカテゴリに紐づけて作成したいので、カテゴリを取得できるようにします。

1class Api::V1::CategoriesController < ApplicationController
2  def index
3    categories = Category.all
4    render json: categories
5  end
6  
7  def create
8    category = Category.new(category_params)
9    if category.save
10      render json: category, status: :created
11    else
12      render json: category.errors, status: :unprocessable_entity
13    end
14  end
15
16  private
17
18  def category_params
19    params.require(:category).permit(:name)
20  end
21end
22

メモ作成、カテゴリ取得のルーティングを追加

config/routes.rbを以下のように書き換えます。

1Rails.application.routes.draw do
2  namespace :api do
3    namespace :v1 do
4      resources :categories, only: [:index, :create]
5      resources :memos, only: [:create]
6    end
7  end  
8end
9

ここまでできたらcurlコマンドでメモが登録できるか、カテゴリが取得できるかを確認してみましょう。

メモのカテゴリが登録されていないと、メモは登録できない点に注意してください。

復習ポイント
  • curlコマンドとはどういうものでしたか?
  • curlコマンドでどのようにデータが登録できますか?

ここまででバックエンド編はおしまいです。不明点があったら以下から質問してみてください。

メモ登録機能(フロントエンド編)

必要なファイルを追加

src/component/pagesの下に、NewMemo.jsxを作成します。

復習ポイント

jsxとはどのようなファイル形式でしたか?

メモ作成ページの表示確認

App.jsを以下のように変更し、メモ作成ページのルーティングを追加します。

1import { BrowserRouter, Routes, Route } from "react-router-dom";
2import NewCategory from "./component/page/NewCategory";
3import NewMemo from "./component/page/NewMemo";
4
5function App() {
6  return (
7    <BrowserRouter>
8      <Routes>
9        <Route path={`/category/new`} element={<NewCategory />} />
10        <Route path={`/memo/new`} element={<NewMemo />} />
11      </Routes>
12    </BrowserRouter>
13  );
14}
15
16export default App;
17

次に、NewMemo.jsxを表示確認ように変更します。

1export default function NewMemo() {
2  return (
3   "NewMemo"
4   );
5 }

yarn startコマンドでサーバーを起動し、ブラウザにNewMemoと表示されていたらOKです。

カテゴリを取得できるようにコーディング

メモを登録できるようにするために、メモに紐づけるためのカテゴリを取得できるようにします。

1import React, { useEffect, useState } from 'react';
2import {
3  Box,
4  Center,
5  Select,
6} from "@chakra-ui/react";
7import { useToast } from "@chakra-ui/react";
8import axios from 'axios';
9
10export default function NewMemo() {
11  const [ categories, setCategories] = useState([]);
12  const [ category_id, setCategoryId ] = useState('');
13  const toast = useToast();
14
15  useEffect(() => {
16    fetchGetCategory();
17  }, [])
18
19  async function fetchGetCategory() {
20    try {
21      const res = await axios.get("http://localhost:3010/api/v1/categories");
22
23      if (!res.status || (res.status < 200 && res.status >= 300)) {
24        throw new Error(`HTTP error! status: ${res.status}`);
25      }
26
27      setCategories(res.data);
28    }
29    catch (error) {
30      console.error('Error creating credos:', error);
31      toast({
32        title: 'カテゴリの取得に失敗しました。',
33        status: 'error',
34        isClosable: true,
35      });
36    }
37  }
38
39  return (
40    <Center>
41      <Box w={["100%", "90%", "80%", "70%", "60%"]} mt={["50px", "100px", "150px", "200px"]}>
42        <Select
43          onChange={(e) => setCategoryId(e.target.value)}
44          placeholder='カテゴリを選択して下さい。'
45        >
46          {categories.length > 0 && categories.map((item, index) => {
47            return <option value={item.id} key={index}>{item.name}</option>;
48          })}
49        </Select>
50            </Box> 
51    </Center>
52   );
53 }

以下のように動作していたらOKです。

メモを登録できるようにコーディング

取得したカテゴリに紐づけてメモを登録できるようなコードに変更します。

1import React, { useEffect, useState } from 'react';
2import {
3  Box,
4  Input,
5  Button,
6  Center,
7  Select,
8} from "@chakra-ui/react";
9import { useToast } from "@chakra-ui/react";
10import axios from 'axios';
11
12export default function NewMemo() {
13  const [ categories, setCategories] = useState([]);
14  const [ category_id, setCategoryId ] = useState('');
15  const [ memo, setMemo ] = useState('');
16  const [isLoading, setIsLoading] = useState(false);
17  const toast = useToast();
18
19  useEffect(() => {
20    fetchGetCategory();
21  }, [])
22
23  async function fetchGetCategory() {
24    try {
25      const res = await axios.get("http://localhost:3010/api/v1/categories");
26
27      if (!res.status || (res.status < 200 && res.status >= 300)) {
28        throw new Error(`HTTP error! status: ${res.status}`);
29      }
30
31      setCategories(res.data);
32    }
33    catch (error) {
34      console.error('Error creating credos:', error);
35      toast({
36        title: 'カテゴリの取得に失敗しました。',
37        status: 'error',
38        isClosable: true,
39      });
40    }
41  }
42
43  async function fetchCreateMemo() {
44    setIsLoading(true);
45
46    try {
47      if (!category_id || !memo) {
48        toast({
49          title: 'カテゴリの選択とメモの入力をして下さい。',
50          status: 'error',
51          isClosable: true,
52        });
53
54        return;
55      }
56
57      const res = await axios.post("http://localhost:3010/api/v1/memos", {
58        memo: {
59          content: memo,
60          category_id
61        },
62      });
63
64      if (!res.status || (res.status < 200 && res.status >= 300)) {
65        throw new Error(`HTTP error! status: ${res.status}`);
66      }
67
68      toast({
69        title: 'メモを登録しました。',
70        status: 'success',
71        isClosable: true,
72      });
73    }
74    catch (error) {
75      console.error('Error creating credos:', error);
76      toast({
77        title: 'メモの登録に失敗しました。',
78        status: 'error',
79        isClosable: true,
80      });
81    }
82    finally {
83      setIsLoading(false);
84      setMemo('');
85    }
86  }
87  
88  return (
89    <Center>
90      <Box w={["100%", "90%", "80%", "70%", "60%"]} mt={["50px", "100px", "150px", "200px"]}>
91        <Select
92          onChange={(e) => setCategoryId(e.target.value)}
93          placeholder='カテゴリを選択して下さい。'
94        >
95          {categories.length > 0 && categories.map((item, index) => {
96            return <option value={item.id} key={index}>{item.name}</option>;
97          })}
98        </Select>
99        <Input
100          value={memo}
101          onChange={(e) => setMemo(e.target.value)}
102          placeholder="追加したいメモを入力" 
103        />
104        <Center>
105          <Button mt="6px" onClick={fetchCreateMemo} isLoading={isLoading} disabled={!memo || !category_id}>送信</Button>
106        </Center>
107      </Box>
108    </Center>
109   );
110 }

以下のような挙動になっていたらOKです。

コードの量が増えてきましたので、全てに対して解説することは控えます。

検索やChatGPT等に質問して知識理解を深めてみてほしいです。

もしわからないことがあったら無料質問を以下からしてみてください。

まとめ

SNSアプリを作成していくにあたり、RailsとReactを連携し、メモを登録できるようになりました。

次回はユーザを登録できるようにしていきます。ユーザが登録できるようになるとSNS感が一気に高まりますよね。

次へ進む
【3.ユーザ登録編】Ruby on Rails + ReactでSNSアプリを作る
【3.ユーザ登録編】Ruby on Rails + ReactでSNSアプリを作る
ABOUT ME
いず
いず
1995年生まれ・愛知県出身・都心で二人暮らし。IT系PDM・プロコーチ・プログラミング学習サービス代表。趣味:ミニマリスト・コーヒー・デザイン
記事URLをコピーしました