node.js実践編(メッセージボード編)

※node.js超入門ノートの続きになります。

https://qiita.com/Glider2355/items/cf368ba789f4f5200e22

# モデルの実装

以下のコマンドを実行します。

```
npx sequelize-cli model:generate --name Board --attributes userId:integer, message:string
```
## アソシエーションの設定
作成したファイルを修正します。

```javascript:models/board.js
// 従モデル
'use strict';
module.exports = (sequelize, DataTypes) => {
  const Board = sequelize.define('Board', {
    userId: {
      type: DataTypes.INTEGER,
      validate: {
        notEmpty: {
          msg: "利用者は必須です。"
        }
      }
    },
    message: {
      type: DataTypes.STRING,
      validate: {
        notEmpty: {
          msg: "メッセージは必須です。"
        }
      }
    }
  }, {});

  Board.associate = function(models) {
    Board.belongsTo(models.User); // 従モデル
  };
  return Board;
};
```
以下も修正します。

```javascript:models/user.js
User.associate = function(models) {
    User.hasMany(models.Board);  // 主モデル
  };
```
以下のコマンドでマイグレーションを行います。

```
npx sequelize-cli db:migrate
```

# ログイン処理
ログイン処理を追記します。

```javascript:routes/users.js
router.get('/login', (req, res, next) => {
  var data = {
    title:'Users/Login',
    content: '名前とパスワードを入力してください。'
  }
  res.render('users/login', data);
});

router.post('/login', (req, res, next) => {
  db.User.findOne({
    where:{
      name: req.body.name,
      pass:req.body.pass,
    }
  }).then(usr => {
    if (usr != null) {
      req.session.login = usr;
      let back = req.session.back;
      if (back == null){
        back = '/';
      }
      res.redirect(back);
    } else {
      var data = {
        title: 'Users/Login',
        content:'名前かパスワードに問題があります。再度入力して下さい。'
      }
      res.render('users/login', data);
    }
  })
});
```

以下のファイルを作成します。

```javascript:routes/boards.js
const express = require('express');
const router = express.Router();
const db = require('../models/index');
const { Op } = require("sequelize");
const { route } = require('./users');

const pnum = 10;

// ログインのチェック
function check(req, res) {
    if (req.session.login == null) {
        // ログイン後に戻る値
        req.session.back = '/boards';

        res.redirect('/users/login');
        return true;
    } else {
        return false;
    }
}

// トップページ
router.get('/', (req, res, next) => {
    res.redirect('/boards/0');
});

router.get('/:page',(req, res, next) => {
    if (check(req, res)){ return };
    const pg = req.params.page * 1;
    db.Board.findAll({
        offset: pg * pnum,
        limit: pnum,
        order: [
            ['createdAt', 'DESC']
        ],
        include: [{
            model: db.User,
            required: true
        }]
    }).then(brds => {
        var data = {
            title: 'Boards',
            login: req.session.login,
            content: brds,
            page: pg
        }
        res.render('boards/index', data);
    });
});

// メッセージフォームの送信処理
router.post('/add', (req, res, next) => {
    if (check(req, res)){ return };
    db.sequelize.sync()
        .then(() => db.Board.create({
            userId: req.session.login.id,
            message: req.body.msg
        })
        .then(brd => {
            res.redirect('/boards');
        })
        .catch((err) => {
            res.redirect('/boards');
        })
        )
});

// 利用者のホーム
router.get('/home/:user/:id/:page', (req,res, next) => {
    if (check(req, res)){ return };
    const id = req.params.id * 1;
    const pg = req.params.page * 1;
    db.Board.findAll({
        where: {userId: id},
        offset: pg * pnum,
        limit: pnum,
        order: [
            ['createdAt', 'DESC']
        ],
        include: [{
            model: db.User,
            required: true
        }]
    }).then(brds => {
        var data = {
            title: 'Boards',
            login: req.session.login,
            userId: id,
            userName: req.params.user,
            content: brds,
            page: pg
        }
        res.render('boards/home', data);
    });
});

module.exports = router;
```
app.jsに組み込みます。

```javascript:app.js
var boardsRouter = require('./routes/boards');
app.use('/boards', boardsRouter);
```

## テンプレート作成
以下のファイルを作成します。

```html:views/users/login.ejs
<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="content-type" content="text/html">
        <title><%= title %></title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
        <link rel="stylesheet" href="/stylesheets/style.css" />
    </head>

    <body class="container">

        <header>
            <h1 class="display-4">
                <%= title %>
            </h1>
        </header>

        <div role="main">
            <p><%- content %></p>
            <form action="/users/login" method="post">
                <div class="form-group">
                    <label for="name">NAME</label>
                    <input type="text" name="name" id="name" class="form-control">
                </div>
                <div class="form-group">
                    <label for="pass">PASSWORD</label>
                    <input type="text" name="pass" id="pass" class="form-control">
                </div>
                <input type="submit" value="ログイン" class="btn btn-primary">
            </form>
            <p class="mt-4"><a href="/boards">&lt;&lt; Top へ戻る</a>
                <a href="/users/add">アカウントの作成&gt;&gt;</a></p>
        </div>
    </body>
</html>
```

```html:views/boards/index.ejs
<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="content-type" content="text/html">
        <title><%= title %></title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
        <link rel="stylesheet" href="/stylesheets/style.css" />
    </head>

    <body class="container">

        <header>
            <h1 class="display-4">
                <%= title %>
            </h1>
        </header>

        <div role="main">
            <p class="h4">Wlcome to <%= login.name %>.</p>
            <form action="/boards/add" method="POST">
                <div class="row">
                    <div class="col-10">
                        <input type="text" name="msg" class="form-control">
                    </div>
                    <input type="submit" value="送信" class="btn btn-primary col-2">
                </div>
            </form>

            <table class="table mt-5">
                <% for(let i in content) { %>
                <%- include('data_item', {val: content[i]}) %>
                <% } %>
            </table>

            <ul class="pagination justify-content-center">
                <li class="page-item">
                    <a href="/boards/<%= page - 1 %>" class="page-link">&lt;&lt; prev</a>
                </li>
                <li class="page-item">
                    <a href="/boards/<%= page + 1 %>" class="page-link">Next &gt;&gt;</a>
                </li>
            </ul>
        </div>
    </body>
</html>
```

```html:views/boards/home.ejs
<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="content-type" content="text/html">
        <title><%= title %></title>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous">
        <link rel="stylesheet" href="/stylesheets/style.css" />
    </head>

    <body class="container">

        <header>
            <h1 class="display-4">
                <%= title %>
            </h1>
        </header>

        <div role="main">
            <p class="h4"><%= userName %>'s messages.</p>
            <table class="table mt-5">
                <% for(let i in content) { %>
                <%- include('data_item', {val: content[i]}) %>
                <% } %>
            </table>

            <ul class="pagination justify-content-center">
                <li class="page-item">
                    <a href="/boards/home/<%= userName %>/<%= userId %>/<%= page - 1 %>" class="page-link">&lt;&lt; prev</a>
                </li>
                <li class="page-item">
                    <a href="/boards/home/<%= userName %>/<%= userId %>/<%= page + 1 %>" class="page-link">Next &gt;&gt;</a>
                </li>
            </ul>
        </div>
        <div class="text-left">
            <a href="/boards">&lt;&lt; Top.</a>
        </div>
    </body>
</html>
```

```html:views/boards/data_item.ejs
<% if (val != null) { %>
    <tr class = "row">
        <th class="col-2">
            <a class="text-dark" href="/boards/home/<%=val.User.name %>/<%= val.userId %>/0">
                <%= val.User.name %>
            </a>
        </th>
        <td class="col-7"><%= val.message %></td>
        <%
            var d = new Date(val.createdAt);
            var dstr = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + ' ' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds();
        %>
        <td class="col-3"><%= dstr %></td>
    </tr>
<% } %>
```
# 結果
## ログイン画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111200/1b6518e6-11d4-e64e-3901-f1001602a960.png)
## メッセージボード画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111200/84013ed4-c15b-2044-b18d-415be05d9082.png)
## ユーザーのホーム画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111200/1c5e2313-7ea4-3b39-ea05-2363da85b1a4.png)