前回は、Express で MongoDB のデータを編集しました。

今回は、mongoose をセッティングします。

mongoose は、MongoDB のデータを定義して構造化することができるようになります。

コードは、前回のコードを使用します。

views/index.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%= pageTitle %></title>
  </head>
  <body>
        <main>
      <h1>メニュー</h1>
      <p>メニュー一覧</p>
      <% if(menus.length > 0) { for (let menu of menus){ %>
      <p><%= menu.title %></p>
      <a href="/menu/<%= menu._id %>">詳細画面</a>
      <div>
        <a href="/edit-menu/<%= menu._id %>">編集</a>
        <form action="/delete-menu" method="POST">
          <input type="hidden" value="<%= menu._id %>" name="menuId" />
          <button type="submit">削除</button>
        </form>
      </div>
      <%} %> <% } else { %>
      <p>注文がありません</p>
      <% } %>
    </main>
  </body>
</html>

views/menu.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <form action="/menu" method="POST">
        <div class="form-control">
          <label for="title">メニュー名</label>
          <input type="text" name="title" />
        </div>
        <div class="form-control">
          <label for="description">内容</label>
          <input type="text" name="description" />
        </div>
        <button type="submit">メニューを追加する</button>
      </form>
    </main>
  </body>
</html>

views/menu-detail.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <h1><%= menu.title %></h1>
      <p><%= menu.description %></p>
    </main>
  </body>
</html>

views/menu-edit.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/css/styles.css" />
    <title><%= pageTitle %></title>
  </head>
  <body>
    <main>
      <form action="/edit-menu" method="POST">
        <input type="hidden" name="id" value="<%= menu._id %>" />

        <div class="form-control">
          <label for="title">メニュー名</label>
          <input type="text" name="title" value="<%= menu.title %>" />
        </div>
        <div class="form-control">
          <label for="description">内容</label>
          <input
            type="text"
            name="description"
            value="<%= menu.description %>"
          />
        </div>
        <button type="submit">メニューを更新する</button>
      </form>
    </main>
  </body>
</html>

index.js

const express = require("express");
const app = express();
const path = require("path");

app.set("view engine", "ejs");
app.set("views", "views");

const menuRoutes = require("./routes/index.js");
const menus = require("./routes/menu.js");
const dbConnect = require("./utils/database").dbConnect;

app.use(express.urlencoded({ extended: true }));

app.use(express.static(path.join(__dirname, "public")));

app.use("/", menus);
app.use(menuRoutes);

app.use((req, res, next) => {
  res.status(404).send("<h1>ページが見つかりません</h1>");
});

dbConnect(() => {
  app.listen(8000, () => console.log("Server is running ..."));
});

models/menu.js

const mongodb = require("mongodb");
const getDb = require("../utils/database").getDb;

module.exports = class Menu {
  constructor(title, description, id) {
    this.title = title;
    this.description = description;
    this._id = id;
  }

  save() {
    // メニュー追加
    return getDb()
      .collection("menu")
      .insertOne(this)
      .then((res) => console.log(res))
      .catch((err) => console.log(err));
  }

  static fetchAll() {
    return getDb()
      .collection("menu")
      .find()
      .toArray()
      .then((menus) => {
        return menus;
      })
      .catch((err) => console.log(err));
  }

  static fetchMenu(id) {
    return getDb()
      .collection("menu")
      .find({ _id: new mongodb.ObjectId(id) })
      .next()
      .then((menu) => {
        return menu;
      })
      .catch((err) => console.log(err));
  }

  static deleteMenu(id) {
    return getDb()
      .collection("menu")
      .deleteOne({ _id: new mongodb.ObjectId(id) })
      .then((menu) => {
        console.log(menu);
      })
      .catch((err) => console.log(err));
  }

  static updateMenu(id, title, description) {
    return getDb()
      .collection("menu")
      .updateOne(
        { _id: new mongodb.ObjectId(id) },
        { $set: { title, description } }
      )
      .then((menu) => {
        console.log(menu);
      })
      .catch((err) => console.log(err));
  }
};

controllers/menus.js

const Menu = require("../models/menu");

exports.getAddMenu = (req, res, next) => {
  res.render("menu", { pageTitle: "メニュー追加" });
};

exports.postAddMenu = (req, res, next) => {
  const title = req.body.title;
  const description = req.body.description;
  const menu = new Menu(title, description);
  menu.save();
  res.redirect("/");
};

exports.getAddMenus = (req, res, next) => {
  Menu.fetchAll()
    .then((menus) => {
      res.render("index", {
        pageTitle: "メニュー一覧",
        menus,
      });
    })
    .catch((err) => {
      console.log(err);
    });
};

exports.getMenu = (req, res, next) => {
  const id = req.params.menuId;
  Menu.fetchMenu(id)
    .then((menu) => {
      res.render("menu-detail", {
        menu: menu,
        pageTitle: "詳細画面",
      });
    })
    .catch((err) => {
      console.log(err);
    });
};

exports.postDeleteMenu = (req, res, next) => {
  const id = req.body.menuId;
  Menu.deleteMenu(id)
    .then(res.redirect("/"))
    .catch((err) => {
      console.log(err);
    });
};

exports.getEditMenu = (req, res, next) => {
  const id = req.params.menuId;
  Menu.fetchMenu(id)
    .then((menu) => {
      res.render("menu-edit", {
        menu: menu,
        pageTitle: "メニュー内容編集",
      });
    })
    .catch((err) => {
      console.log(err);
    });
};

exports.postEditMenu = (req, res, next) => {
  const id = req.body.id;
  const updateTitle = req.body.title;
  const updateDescription = req.body.description;
  try {
    Menu.updateMenu(id, updateTitle, updateDescription);
    res.redirect("/");
  } catch (err) {
    console.log(err);
  }
};

routes/index.js

const express = require("express");
const router = express.Router();

const menuController = require("../controllers/menus");

router.get("/", menuController.getAddMenus);

module.exports = router;

routes/menu.js

const express = require("express");
const router = express.Router();

const menuController = require("../controllers/menus");

router.get("/menu", menuController.getAddMenu);

router.get("/menu/:menuId", menuController.getMenu);

router.post("/menu", menuController.postAddMenu);

router.post("/delete-menu", menuController.postDeleteMenu);

router.get("/edit-menu/:menuId", menuController.getEditMenu);

router.post("/edit-menu", menuController.postEditMenu);

module.exports = router;

utils/database.js

const mongodb = require("mongodb");
const MongoClient = mongodb.MongoClient;

let _db;

const dbConnect = (callback) => {
  MongoClient.connect(
    "mongodb+srv://Nao:abcdefgh@cluster0.q1atv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
  )
    .then((item) => {
      console.log("MongoDB Connected");
      _db = item.db();
      callback(item);
    })
    .catch((err) => {
      console.log(err);
      throw err;
    });
};

const getDb = () => {
  if (_db) {
    return _db;
  }
  throw "データベースがありません";
};

exports.dbConnect = dbConnect;
exports.getDb = getDb;

mongoose を追加する

mongoose を追加します。

ターミナルで、npm install --save mongooseを実行します。

mongoose を追加したら、index.js 内に monngose を呼び出します。

const mongoose = require("mongoose");

mongoose を MongoDB と繋げます。

mongoose.connect()

utils フォルダの database.js 内に設定した MongoDB のアドレスをコピーして、mongoose.connect 内に貼り付けます。

mongoose
  .connect(
    "mongodb+srv://Nao:abcdefgh@cluster0.q1atv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
  )

接続に成功した場合は、サーバーを起動する様にします。

接続に失敗した場合は、エラーを表示する様にしましょう。

mongoose
  .connect(
    "mongodb+srv://Nao:abcdefgh@cluster0.q1atv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
  )
  .then(() => {
    app.listen(8000);
    console.log("Server is running ...");
  })
  .catch((err) => {
    console.log(err);
    throw err;
  });

下のコードは、必要なくなったので削除します。

const dbConnect = require("./utils/database").dbConnect;
dbConnect(() => {
  app.listen(8000, () => console.log("Server is running ..."));
});

一度、サーバーを起動してみます。

image2

image5

サーバーが立ち上がりましたが、データの表示が出来ない状況です。

送信データを MongoDB に保存する

データが MongoDB に保存できるか確認します。

一旦、MongoDB のデータを削除します。

image3

models フォルダの menu.js を開きます。

menu.js 内のコードを、コメントアウトしておきます。

mongooseを呼び出します。

const mongoose = require("mongoose");

mongooseからSchemaを使用します。

const Schema = mongoose.Schema;

コンストラクターを使用して、データのスキーマを作成します。

const menuShema = new Schema({
  title: String,
  description: String,
});

モデルをエクスポートしましょう。

モデルの名前は、『Menu』とします。

module.exports = mongoose.model("Menu", menuShema);

モデルが完成したので、次はコントローラーを修正します。

controllers の menus.js を開きます。

postAddmenu の Menu を修正します。

Menu の中は、先程作成したスキーマをオブジェクトで使用します。

exports.postAddMenu = (req, res, next) => {
  const title = req.body.title;
  const description = req.body.description;
  const menu = new Menu({ title: title, description: description });
  menu.save();
  res.redirect("/");
};

データ送信のコントローラーが完成しましたので、動作確認を行います。

image4

『メニューを追加する』をクリックします。

image5

ホーム画面は修正していないので、エラーになります。

MongoDB を見てみると、

image6

送信したデータが MongoDB に保存されています。

コレクション名は、モデルの『Menu』を小文字にして、複数形とした『menus』が自動で設定されます。

ブログ一覧