【SE入門編】Spring Tool Suite 4を使ってWEBプロジェクトのメニュー画面を実装する。

## 1. 前提条件

- DBおよびWebプロジェクトの構築が完了していることが前提です。もし構築がまだ完了していない場合は、以下の記事を参照してください。

- 今回の実装では、メニューの表示のみです。事前にSpringからMybatisを使用したCRUDの基本実装が理解されていること。

- また、メニューの実装にはjquery用のプラグインであるjsTreeを採用しております、jsTreeの知識をある程度理解されていること。

- OracleのSTART WITH ... CONNECT BYを使いこなせること。

DBの構築手順：

https://qiita.com/manabu-s/items/df29732448669b5429eb

WEBプロジェクトの構築手順：

https://qiita.com/manabu-s/items/c9f1dc4d0d821861611b

CRUDの基本実装：

https://qiita.com/manabu-s/items/9626457bb304109af17b

このシリーズの完全版はこちら：

https://qiita.com/manabu-s/items/2f7f10752bcf44244718

jsTreeの公式サイト：

https://www.jstree.com/

この記事のソースコードはGitHubにアップロードしましたので、ぜひダウンロードして確認してください。

https://github.com/yoruzuki/WebService_Spring/tree/main/demo%20ver0.1

出来上がったメニューのイメージ：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/5f6735a6-42ce-e803-38df-4199e09979fe.png)

## 2. データモデルの設計
テーブル設計の正規化原則に従って、メニュー関連テーブルは以下のように設計する。Demo用のDDLとDMLはgithubに上げてるのでぜひご参考して下さい。

後ほどSTART WITH ... CONNECT BYを利用するため、テーブル構造を十分に理解して下さい。

### 2.1.メニューの基本情報テーブル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/f9869fce-bf66-ec30-8ebb-2426ea1bccf6.png)


### 2.2.カテゴリ／サブカテゴリ（メニュー／サブメニュー）テーブル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/f2298ed9-67a3-4f29-3a8e-58dc43e8556e.png)

### 2.3.メニュー＆カテゴリ関係テーブル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/045d7c5e-05f6-8ac1-a3bd-8a9c775e2948.png)

### 2.4.アイコンテーブル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/2aac68d2-bc7d-8c38-ca17-3a81e5076733.png)

### 2.5.ユーザーテーブル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/8a3e8e74-b0ab-61b1-177a-3d0b4cb82ae5.png)


### 2.6.ロールテーブル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/bf16770a-3198-17f0-095f-64b7075ea7a9.png)

### 2.7.ロールメニュー関係テーブル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/d2bfc7d7-d766-f1a4-6850-e33e08163a85.png)

各テーブルの説明は省略します。


## 3. メニューの実装、ソースの解説

プログラムの構成です、赤い線のファイルは今回追加したものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/e5df3272-f216-bf74-06b0-ee06c89620db.png)

代表ソースだけを解説します。

### 3.1.PomにjsTreeライブラリを追加してください。

```
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jstree</artifactId>
			<version>3.3.12</version>
		</dependency>
```

### 3.2.UserMenuRepository.xmlを実装
ログインユーザーの権限により、見えるメニューだけ抽出するSQL文及び項目変換ソースを貼ります。

select id="selectMenuByUserID"：jsTreeに必要な項目を抽出するSQL文。UNIONで結合された上半分がカテゴリ、下半分が機能。START WITH ... CONNECT BY ... を使ってツリーメニューのデータを抽出している。
#{userID}：パラメータ。ログインユーザーID。
com.example.demo.bean.TreeMenu：jsTree用ツリーメニューの項目Bean。
resultMap id="ResultTreeMenuMap"：DB項目とjsTreeの項目の変換関係。
```
  <resultMap id="ResultTreeMenuMap" type="com.example.demo.bean.TreeMenu">
    <id column="CATEGORY" jdbcType="VARCHAR" property="id" />
    <result column="CATEGORY_NAME" jdbcType="VARCHAR" property="text" />
    <result column="CATEGORY_PID" jdbcType="VARCHAR" property="parent" />
    <result column="POSITION" jdbcType="VARCHAR" property="position" />
    <result column="ACTION" jdbcType="VARCHAR" property="action" />
    <result column="ICON_TYPE" jdbcType="VARCHAR" property="iconType" />
    <result column="ICON_IMAGE" jdbcType="VARCHAR" property="icon" />
    <result column="TYPE" jdbcType="VARCHAR" property="type" />
    <result column="LEVEL" jdbcType="VARCHAR" property="level" />
  </resultMap>
  <select id="selectMenuByUserID" parameterType="java.lang.String" resultMap="ResultTreeMenuMap">
		SELECT
		    T.CATEGORY,
		    T.CATEGORY_NAME,
		    T.CATEGORY_PID,
		    T.POSITION,
		    T.ACTION,
		    T.ICON_TYPE,
		    T.ICON_IMAGE,
		    T.TYPE,
		    LEVEL
		FROM
		    (
		        SELECT
		            C.CATEGORY,
		            C.CATEGORY_NAME,
		            C.CATEGORY_PID,
		            C.POSITION,
		            '#'  ACTION,
		            '01' ICON_TYPE,
		            I.ICON_IMAGE,
		            'C'  TYPE
		        FROM
		            CATEGORY C
		            LEFT JOIN ICON     I ON '01' = I.ICON_TYPE
		        UNION
		        SELECT
		            RM.MENU_ID,
		            M.MENU_NAME,
		            MC.CATEGORY,
		            M.POSITION,
		            NVL(M.ACTION,'#') ACTION,
		            M.ICON_TYPE,
		            I.ICON_IMAGE,
		            'M' TYPE
		        FROM
		                 USERS U
		            JOIN ROLE_MENU     RM ON U.ROLE = RM.ROLE
		            JOIN MENU          M ON RM.MENU_ID = M.MENU_ID
		            JOIN MENU_CATEGORY MC ON M.MENU_ID = MC.MENU_ID
		            JOIN CATEGORY      C ON MC.CATEGORY = C.CATEGORY
		            LEFT JOIN ICON          I ON M.ICON_TYPE = I.ICON_TYPE
		        WHERE
		                U.USER_ID = #{userID}
		            AND M.IS_AVAILABLE = '1'
		    ) T
		START WITH
		    T.CATEGORY_PID = '#'
		CONNECT BY
		    PRIOR T.CATEGORY = T.CATEGORY_PID
		ORDER SIBLINGS BY
		    POSITION
  </select>
```

### 3.3.TreeMenu.javaを実装
jsTree用ツリーメニューの項目を説明します。

```
	private String id; // メニューID
	private String parent; // 親メニューID
	private String text; // メニュー名
	private String iconType; // メニューアイコンコード
	private String icon; // メニューアイコンのパス
	private String position; // メニュー表示順番
	private String action; // メニューURL、jsTree用項目ではない
	private String target; // URLを開くフレームやウインドウを指定する
	private String type; // C：カテゴリ；M：メニュー、jsTree用項目ではない
	private String level; // メニューの階層、jsTree用項目ではない
	private Map<String,String> a_attr; // actionのURLを格納する
```
  
### 3.4.MenuController.javaを実装
```
		List<TreeMenu> treeMenu = userService.selectMenuByUserID(userID);
		// SQLから取得したTreeMenuデータは、そのままではjsTreeでの使用に適していません。以下のような加工が必要です。
		treeMenu.forEach(menu -> {
			Map<String, String> href = new HashMap<>();
			href.put("href", menu.getAction());
			menu.setA_attr(href); // jsTreeの中に<A>を生成するため
		});
		model.addObject("treeMenu", treeMenu);
```

### 3.5.menu.htmlを実装
jquery及びjstreeのリソースをリンクする
```
<link rel="stylesheet" type="text/css" th:href="@{/webjars/jstree/3.3.12/themes/default/style.min.css}">
<script type="text/javascript" th:src="@{/webjars/jquery/3.6.0/dist/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/webjars/jstree/3.3.12/jstree.min.js}"></script>
```

ツリーメニューを生成する
```
	$(function(){
		$('#treeMenu')
		  // listen for event
		  .on('changed.jstree', function (e, data) {
			  console.log(data.changed.selected);
			  console.log(data.changed.deselected);
			  
			  var obj = data.instance.get_node(data.selected[0]);
			  if(obj && data.instance.is_leaf(obj) && "#" != obj.a_attr.href){
				  window.open(obj.a_attr.href, obj.id);// メニューをクリックする時、新たなWindowでURLを開く
			  } else {// カテゴリをクリックする時、カテゴリをオープン／クローズ
				  if(data.instance.is_open(obj)){
					  data.instance.close_node(obj);
				  } else {
					  data.instance.open_node(obj);
				  }
			  }
		  })
		  // create the instance
		  .jstree({ 
			 'core' : {
			    "animation" : 0,
			    "check_callback" : true,
			    "themes" : { "stripes" : true },
			    'data' : /*[[${treeMenu}]]*/
			 },
			 "plugins" : [
				    "changed", "state", "types", "wholerow"
				  ]
			 });
	});
```
jstreeの使い方についての説明は今回省けます、興味がある方は公式サイトをご参考下さい。



## 4. 完成

下図は以下のURLを叩いた結果です。
左（ADMIN権限）：http://localhost:8000/menu?userID=ADMIN
中（管理職権限）：http://localhost:8000/menu?userID=DEMO001
右（スタッフ）：http://localhost:8000/menu?userID=DEMO002

まだ改善の余地がありますが（例えば空のカテゴリを表示しないなど）、とりあえず権限に基づいてメニューが表示されるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/7bfcc207-d4fd-e7fb-7528-6518399e7d3d.png)


## おわりに

全体のソースコードはGitHubにアップロードしましたので、ぜひダウンロードして確認してください。

https://github.com/yoruzuki/WebService_Spring/tree/main/demo%20ver0.1

また、この記事では一部のソースコードしか説明していませんが、説明を希望する特定の部分があればリクエストしていただければと思います。詳細や特定の質問についてお知らせいただければ、それに基づいて訂正や説明を提供できます。

次回はメイン画面（フレーム分割） を実装していきたいと思います、お楽しみして待ってください。

それではまた。