0
0

SpringBoot  管理者ページの作成 MVCモデル設定の一連の流れ

Last updated at Posted at 2024-02-22

やりたいことのイメージ図
スクリーンショット 2024-02-22 17.17.27.png

テーブルの作成・データの追加

スクリーンショット 2024-02-22 17.21.48.png

スクリーンショット 2024-02-22 10.45.52.png
src/main/resources直下に2つのSQL文を作成。ファイルが2つに分かれている理由の1つが、ファイルを分けることで、実行順序を明示し、テーブルが適切に作成された後にレコードが追加されるようにするため。
この2つのSQL文はアプリが立ち上がるたびに自動で処理されるため、各SQL文でエラー回避の工夫が必要。
役割は以下の通り。
1.schema.sql
テーブルを作成する。

CREATE TABLE IF NOT EXISTS houses (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,  
    image_name VARCHAR(255),
    description VARCHAR(255) NOT NULL,
    price INT NOT NULL,
    capacity INT NOT NULL,
    postal_code VARCHAR(50) NOT NULL,
    address VARCHAR(255) NOT NULL,
    phone_number VARCHAR(50) NOT NULL,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

ポイント:CREATE TABLE IF NOT EXISTS テーブル名で「もしその名前のテーブルが存在しなければ」という条件をつけている。これは作成済みのテーブルを再度作成しようとしたときに発生するエラーを防ぐ。

テーブルを作成した後に必須の作業=モデルの作成
モデルはデータベースの中に作成したテーブルごとに作成しなければいけないので、モデルをこのSQLのテーブルと対応させる作業がいる!
モデルの設定方法

2.data.sql
レコードの追加などに使う。

data.sql
-- housesテーブル
INSERT IGNORE INTO houses (id, name, image_name, description, price, capacity, postal_code, address, phone_number) VALUES (1, 'SAMURAIの宿', 'house01.jpg', '最寄り駅から徒歩10分。自然豊かで閑静な場所にあります。長期滞在も可能です。', 6000, 2, '073-0145', '北海道砂川市西五条南X-XX-XX', '012-345-678');
INSERT IGNORE INTO houses (id, name, image_name, description, price, capacity, postal_code, address, phone_number) VALUES (2, 'ペンション SAMURAI', 'house02.jpg', '最寄り駅から徒歩10分。自然豊かで閑静な場所にあります。長期滞在も可能です。', 7000, 3, '030-0945', '青森県青森市桜川X-XX-XX', '012-345-678');
INSERT IGNORE INTO houses (id, name, image_name, description, price, capacity, postal_code, address, phone_number) VALUES (3, 'SAMURAI荘', 'house03.jpg', '最寄り駅から徒歩10分。自然豊かで閑静な場所にあります。長期滞在も可能です。', 8000, 4, '029-5618', '岩手県和賀郡西和賀町沢内両沢X-XX-XX', '012-345-678');

ポイント:INSERT IGNORE INTO テーブル名で重複や制約違反が発生した場合でもエラーが発生せず、レコードの追加をスキップする。もし、重複や制約違反が発生しない場合は通常どおりレコードを挿入。

モデルの作成

別記事に記載
モデルの設定方法


ここまでで、データベースに"house"という名前の1つのテーブルが完成しています。
スクリーンショット 2024-02-22 12.02.31.png


コントローラの作成

管理者ページのルートパスの基準値を設定します。
スクリーンショット 2024-02-22 17.24.17.png

下記の場所へ、AdminHouseController.javaという名前でコントローラ設定ファイルを作成。

スクリーンショット 2024-02-22 17.29.45.png`

ファイルの内容↓

管理者ページのコントローラ設定
package com.example.samuraitravel.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.samuraitravel.entity.House;
import com.example.samuraitravel.repository.HouseRepository;

@Controller // コントローラの宣言
@RequestMapping("/admin/houses") //このコントローラが"/admin/houses" パス以下のリクエストを処理する
public class AdminHouseController { //クラス定義
    private final HouseRepository houseRepository; //依存先のオブジェクトをfinalで宣言        
    @Autowired //コンストラクタインジェクションで依存性注入
    public AdminHouseController(HouseRepository houseRepository) { 
        this.houseRepository = houseRepository;           
    }	
    
    @GetMapping //index()でhouseRepositoryのデータ取得
    public String index(Model model) {
        List<House> houses = houseRepository.findAll();       
        //ビューに"house"という名前でhouseデータを渡す
        model.addAttribute("houses", houses);             
        
        return "admin/houses/index";
    }  
}

ポイント:

  1. クラスに@RequestMappingアノテーションをつけ、ルートパスの基準値を設定する
  2. コンストラクタで依存性の注入(DI)を行う(コンストラクタインジェクション)
  3. HouseRepositoryインターフェースのfindAll()メソッドですべての民宿データを取得する
  4. Modelクラスを使ってビューにデータを渡す。
    コントローラからビューにデータを渡す場合、Modelクラスを使う。
@GetMapping
public String index(Model model) {
    List<House> houses = houseRepository.findAll();       
        
    model.addAttribute("var", houses);             
        
    return "admin/houses/index";
}

この例では、

  1. index()メソッド内ではHouseRepositoryインターフェースのfindAll()メソッドを使い、すべての民宿データを取得
  2. メソッドにModel型の引数を指定する
  3. メソッド内でaddAttribute()メソッドを使い、以下の引数を渡す
    第1引数:ビュー側から参照する変数名
    第2引数:ビューに渡すデータ

admin/houses/index.htmlファイル内でvarという変数を使うことで、コントローラから渡されたhousesというデータの中身を参照できる。

HouseRepositoryインターフェースとは→リポジトリの設定

ビューの作成

スクリーンショット 2024-02-22 18.29.07.png

  • ビューファイルはsrc/main/resources/templatesフォルダ以下に配置する。理由はデフォルトでThymeleafのビューリゾルバーによって認識されるから。
indexファイルの作成・編集

以下のようにadminフォルダ新規作成、housesフォルダ新規作成、そしてindex.htmlファイルを新規作成する。
スクリーンショット 2024-02-22 18.29.54.png
ファイルの作成は以下の通り。

index.html
+<!DOCTYPE html>
+<html xmlns:th="https://www.thymeleaf.org">
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1">    
+             
+        <!-- Bootstrap -->
+        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
+        
+        <!-- Google Fonts -->
+        <link rel="preconnect" href="https://fonts.googleapis.com">
+        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+        <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500&display=swap" rel="stylesheet">   
+        
+        <link th:href="@{/css/style.css}" rel="stylesheet" >
+        
+        <title>民宿一覧</title>       
+    </head>
+    <body>
+        <div class="samuraitravel-wrapper">
+            <!-- ヘッダー -->
+            <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm samuraitravel-navbar">
+                <div class="container samuraitravel-container">
+                    <a class="navbar-brand" th:href="@{/}">                        
+                        <img class="samuraitravel-logo me-1" th:src="@{/images/logo.png}" alt="SAMURAI Travel">                        
+                    </a>                                                                    
+                </div>
+            </nav>
+            
+            <main>
+                <div class="container pt-4 pb-5 samuraitravel-container">
+                    <div class="row justify-content-center">
+                        <div class="col-xxl-9 col-xl-10 col-lg-11">
+                            
+                            <h1 class="mb-4 text-center">民宿一覧</h1>   
+                            
+                            <div class="d-flex justify-content-end">                                
+                                <a href="#" class="btn text-white shadow-sm mb-3 samuraitravel-btn">登録</a>
+                            </div>                                                          
+                            
+                            <table class="table">
+                                <thead>
+                                    <tr>
+                                        <th scope="col">ID</th>
+                                        <th scope="col">民宿名</th>
+                                        <th scope="col">郵便番号</th>
+                                        <th scope="col">住所</th>
+                                        <th scope="col">電話番号</th>
+                                        <th scope="col"></th>                                        
+                                    </tr>
+                                </thead>   
+                                <tbody>                                                     
+                                    <tr th:each="house : ${houses}">
+                                        <td th:text="${house.getId()}"></td>
+                                        <td th:text="${house.getName()}"></td>
+                                        <td th:text="${house.getPostalCode()}"></td>
+                                        <td th:text="${house.getAddress()}"></td>
+                                        <td th:text="${house.getPhoneNumber()}"></td>
+                                        <td><a href="#">詳細</a></td>                                                                                
+                                    </tr>                                      
+                                </tbody>
+                            </table>                                       
+                        </div>
+                    </div>
+                </div>              
+            </main>
+            
+            <!-- フッター -->
+            <footer>
+                <div class="d-flex justify-content-center align-items-center h-100">
+                    <p class="text-center text-muted small mb-0">&copy; SAMURAI Travel All rights reserved.</p>      
+                </div>                 
+            </footer>
+        </div>    
+        
+        <!-- Bootstrap -->
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>        
+    </body>
+</html>

ポイント:

  1. CDNでBootstrapを導入する
  2. CDNでGoogle Fontsを導入する
  3. aタグのth:href属性でリンク先のルートパスを指定する
  4. imgタグのth:src属性で表示するロゴ画像のルートパスを指定する
  5. th:each属性で繰り返し処理を行う
CSSファイルの作成・編集

Spring Bootでは、静的なファイル(CSS、JavaScript、画像など)は通常、src/main/resources/static フォルダ内に配置する。

スクリーンショット 2024-02-22 19.41.32.png
th:text属性でtd要素のテキストを表示する

+html {
+  font-size: 14px;
+}
+
+body {
+  font-family: 'Noto Sans JP', sans-serif;  
+}
+
+h1 {
+  font-size: 1.5rem;
+}
+
+a {
+  color: #5196a6;
+  text-decoration: none !important;
+}
+
+a:hover {
+    color: #407784;
+}
+
+footer {
+  width: 100%;
+  height: 60px;
+  position: absolute;
+  bottom: 0;    
+  background-color: #f2f0eb;
+}
+
+.samuraitravel-wrapper {
+  min-height: 100vh;
+  position: relative;
+  padding-bottom: 60px;
+  box-sizing: border-box;
+}
+
+.samuraitravel-navbar {
+  min-height: 60px;
+}
+
+.samuraitravel-logo {
+  height: 30px;  
+}
+
+.samuraitravel-btn {
+  background-color: #5196a6 !important;   
+  transition: 0.1s;
+}
+
+.samuraitravel-btn:hover {
+  opacity: 0.8;   
+}
+
+@media screen and (min-width: 1400px) {
+  .samuraitravel-container {
+    max-width: 1200px;
+  }
+}

ここまでの作業まとめ
スクリーンショット 2024-02-22 19.57.16.png
この工程を経て、アプリを起動後、http://localhost:8080/admin/housesにアクセスすると下記のようなページが表示されるはず。
スクリーンショット 2024-02-22 20.00.44.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0