create

【6.ユーザに紐付けたメモ・全メモ取得編】Ruby on Rails + ReactでSNSアプリを作る

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

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

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

今回は投稿をユーザに紐付けたメモと全てのメモを取得できるようにしていきます。

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

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

サンプル動画(再掲)

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

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

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

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

ユーザ情報を含めたメモ取得機能(バックエンド編)

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

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

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

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

メモコントローラーへの追加

ログインユーザが投稿したメモを取得できるようにします。

1class Api::V1::MemosController < ApplicationController
2  before_action :authenticate_api_v1_user!, only: [:create, :my_memos]
3  before_action :set_user, only: [:create, :my_memos]
4  
5  def create
6    memo = Memo.new(memo_params)
7    if memo.save
8      render json: memo, status: :created
9    else
10      render json: memo.errors, status: :unprocessable_entity
11    end
12  end
13
14  def my_memos
15    memos = Memo.includes(:category, :user).where(user_id: @user.id).order(created_at: 'DESC')
16    render json: memos.to_json(include: {category: {only: :name}, user: {only: :name}}), status: :ok
17  end
18
19  private
20
21  def memo_params
22    params.require(:memo).permit(:content, :category_id).merge(user_id: @user.id)
23  end
24
25  def set_user
26    @user = current_api_v1_user
27  end
28end
29

15行目のincludes(:category, :user)は、ActiveRecordのメソッドで、N+1問題を解決するために使用されます。このメソッドは、Memoモデルが関連付けられたCategoryUserモデルを一度にロードします。これにより、各メモのカテゴリやユーザーを取得するために追加のクエリを発行する必要がなくなり、パフォーマンスが向上します。

16行目のto_json(include: {category: {only: :name}, user: {only: :name}})は、メモ、カテゴリ、ユーザーのデータをJSON形式に変換します。ここでincludeオプションは、関連付けられたオブジェクトをJSONに含めることを指定します。only: :nameは、それぞれの関連オブジェクトからnameフィールドのみを含めることを指定します。

復習ポイント
  • before_actionとはどのようなモノでしたか?
  • authenticate_api_v1_user!とはどのようなモノでしたか?
  • current_api_v1_userとはどのようなモノでしたか?

ルーティングの追加

次はmy_memosのルーティングを追加していきます。

1Rails.application.routes.draw do
2  namespace :api do
3    namespace :v1 do
4      mount_devise_token_auth_for 'User', at: 'auth', controllers: {
5        registrations: 'api/v1/auth/registrations',
6        sessions: 'api/v1/auth/sessions',
7      }
8      
9      resources :categories, only: [:index, :create]
10      resources :memos, only: [:create] do 
11        collection do
12          get :my_memos
13        end
14      end
15      resources :users, only: [] do
16        collection do
17          get :user_name
18        end
19      end
20    end
21  end  
22end
復習ポイント
  • collecttion do ~ endとはどのようなモノでしたか?

復習ポイントの部分、自分の言葉で説明できていそうですか?

誰かに説明しようとすると理解ができていたかがわかりやすいので、ぜひ言語化に取り組んでみてください。

言語化できないな、理解できていないかもなと思ったら、以下から質問もしてみてくださいね。

ユーザ情報を含めたメモ取得機能(フロントエンド編)

ログインユーザが投稿したメモの一覧を取得するため、「MyMemos.jsx」を作成し、以下のようにコードを追記します。

1import React, { useState, useEffect } from 'react';
2import {
3    SimpleGrid,
4    Center,
5    Card, 
6    Heading,
7    CardHeader, 
8    CardBody,
9    Stack,
10    StackDivider,
11    Text,
12    CardFooter,
13} from "@chakra-ui/react";
14
15import axios from 'axios';
16
17export default function MyMemos() {
18    const [ memos, setMemos ] = useState([]);
19    
20    useEffect(() => {
21        fetchGetMemos();
22    }
23    , []);
24    
25    async function fetchGetMemos() {
26        try {
27            const res = await axios.get("http://localhost:3010/api/v1/memos/my_memos", {
28                headers: {
29                'access-token': localStorage.getItem('access-token'),
30                'client': localStorage.getItem('client'),
31                'uid': localStorage.getItem('uid'),
32                }
33            });
34        
35            if (!res.status || (res.status < 200 && res.status >= 300)) {
36                throw new Error(`HTTP error! status: ${res.status}`);
37            };
38        
39            setMemos(res.data);
40        }
41        catch (error) {
42            console.error('Error getting memos:', error);
43        }
44    }
45    
46    return (
47        <Center>
48        <SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={10}>
49            {memos.map((memo, index) => (
50                <Card key={index}>
51                    <CardHeader>
52                        <Heading size="md">{memo.category.name}</Heading>
53                    </CardHeader>
54                    <CardBody>
55                        <Stack divider={<StackDivider />} spacing='4'></Stack>
56                        <Text>{memo.content}</Text>
57                    </CardBody>
58                    <CardFooter>
59                        <Text>ユーザ名: {memo.user.name}</Text>
60                    </CardFooter>
61                </Card>
62            ))}
63        </SimpleGrid>
64        </Center>
65    );
66}

そして、ページのルーティングを追加していきます。

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

以下のようにユーザに紐づいたメモ一覧が取得できていたらOKです。

ユーザ情報を含めた全メモ取得機能(バックエンド編)

先ほどのほぼ繰り返しで、全ユーザのメモも取得できるようにしていきます。

まずはコントローラーです。

コントローラーへの追記

1class Api::V1::MemosController < ApplicationController
2  before_action :authenticate_api_v1_user!, only: [:create, :my_memos]
3  before_action :set_user, only: [:create, :my_memos]
4
5  def index
6    memos = Memo.includes(:category, :user).order(created_at: 'DESC')
7    render json: memos.to_json(include: {category: {only: :name}, user: {only: :name}}), status: :ok
8  end
9  
10  def create
11    memo = Memo.new(memo_params)
12    if memo.save
13      render json: memo, status: :created
14    else
15      render json: memo.errors, status: :unprocessable_entity
16    end
17  end
18
19  def my_memos
20    memos = Memo.includes(:category, :user).where(user_id: @user.id).order(created_at: 'DESC')
21    render json: memos.to_json(include: {category: {only: :name}, user: {only: :name}}), status: :ok
22  end
23
24  private
25
26  def memo_params
27    params.require(:memo).permit(:content, :category_id).merge(user_id: @user.id)
28  end
29
30  def set_user
31    @user = current_api_v1_user
32  end
33end
34

このコードでもリファクタリングできそうなところがありますよね。

見つけられましたか?

もしわからなければ是非質問してみてください。

ルーティングの追加

1Rails.application.routes.draw do
2  namespace :api do
3    namespace :v1 do
4      mount_devise_token_auth_for 'User', at: 'auth', controllers: {
5        registrations: 'api/v1/auth/registrations',
6        sessions: 'api/v1/auth/sessions',
7      }
8      
9      resources :categories, only: [:index, :create]
10      resources :memos, only: [:index, :create] do 
11        collection do
12          get :my_memos
13        end
14      end
15      resources :users, only: [] do
16        collection do
17          get :user_name
18        end
19      end
20    end
21  end  
22end
23

ここまでできたらフロントエンドに移っていきます。

ユーザ情報を含めた全メモ取得機能(フロントエンド編)

ほぼ先ほどと同じようなコードをMemos.jsxを追加して、追記していきます。

1import React, { useState, useEffect } from 'react';
2import {
3    SimpleGrid,
4    Center,
5    Card, 
6    Heading,
7    CardHeader, 
8    CardBody,
9    Stack,
10    StackDivider,
11    Text,
12    CardFooter,
13} from "@chakra-ui/react";
14
15import axios from 'axios';
16
17export default function Memos() {
18    const [ memos, setMemos ] = useState([]);
19    
20    useEffect(() => {
21        fetchGetMemos();
22    }
23    , []);
24    
25    async function fetchGetMemos() {
26        try {
27            const res = await axios.get("http://localhost:3010/api/v1/memos");
28        
29            if (!res.status || (res.status < 200 && res.status >= 300)) {
30                throw new Error(`HTTP error! status: ${res.status}`);
31            };
32        
33            setMemos(res.data);
34        }
35        catch (error) {
36            console.error('Error getting memos:', error);
37        }
38    }
39    
40    return (
41        <Center>
42        <SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={10}>
43            {memos.map((memo, index) => (
44                <Card key={index}>
45                    <CardHeader>
46                        <Heading size="md">{memo.category.name}</Heading>
47                    </CardHeader>
48                    <CardBody>
49                        <Stack divider={<StackDivider />} spacing='4'></Stack>
50                        <Text>{memo.content}</Text>
51                    </CardBody>
52                    <CardFooter>
53                        <Text>ユーザ名: {memo.user.name}</Text>
54                    </CardFooter>
55                </Card>
56            ))}
57        </SimpleGrid>
58        </Center>
59    );
60}
61

こちらもほぼ同じということは…リファクタリングできそうなところがありますよね。

見つけられましたか?

もしわからなければ是非質問してみてください。

ページルーティングも追加していきます。

また、今回のメモ一覧はトップページにしたいので、他のページとはルーティングが少しだけ違うので、注意してください。

1import { BrowserRouter, Routes, Route } from "react-router-dom";
2import NewCategory from "./component/page/NewCategory";
3import Memos from "./component/page/Memos";
4import NewMemo from "./component/page/NewMemo";
5import MyMemos from "./component/page/MyMemos";
6import UserRegist from "./component/page/UserRegist";
7import Login from "./component/page/Login";
8
9function App() {
10  return (
11    <BrowserRouter>
12      <Routes>
13        <Route path={`/`} element={<Memos />} />
14        <Route path={`/category/new`} element={<NewCategory />} />      
15        <Route path={`/memo/new`} element={<NewMemo />} />
16        <Route path={`/my_memos`} element={<MyMemos />} />
17        <Route path={`/registration`} element={<UserRegist/>} />
18        <Route path={`/login`} element={<Login />} />
19      </Routes>
20    </BrowserRouter>
21  );
22}
23
24export default App;
25

以下のように複数ユーザの投稿が表示できたいたらOKです。

また、現在はログアウト機能をまだ実装していないので、ログインしているかに関わらず、新規登録機能を使ってユーザを増やしてください。

まとめ

SNSアプリを作成していくにあたり、RailsとReactを連携し、ユーザに紐づいたメモ・全メモの取得ができるようになりました。

次回はログアウトをできるようにしていきます。

ABOUT ME
いず
いず
1995年生まれ・愛知県出身・都心で二人暮らし。IT系PDM・プロコーチ・プログラミング学習サービス代表。趣味:ミニマリスト・コーヒー・デザイン
記事URLをコピーしました