【Express】ExpressにSequelizeを導入しORMでデーターベースを操作する。(SQLite3)

#sqlite3のインストール

```
npm install sqlite3
```
#データベースアクセスの処理を作る
```javascript
const sqlite3 = require('sqlite3');

const db = new sqlite3.Database('データ保存ファイル');
//Databaseオブジェクトを作成する。sqlite3.Databaseというオブジェクトに用意されている。
```

###sequelize, sequlize-cliのインストール、 初期化
```ターミナル.
npm install sequelize
npm install sequelize-cli
npx sequelize-cli init  //初期化

```
　
- config
- migrations
- models
- seeder

が作成される。

```config.json

// SQLite3設定
{
  "development": {
    "database": "db-development",    //データベース名
    "dialect": "sqlite",
    "storage": "db-dev.sqlite3"      //データーベースファイル
  },
  "test": {
    "database": "db-test",
    "dialect": "sqlite",
    "storage": "db-test.sqlite3"
  },
  "production": {
    "database": "db-product",
    "dialect": "sqlite",
    "storage": "db.sqlite3"
  }
}
```

参考:mysqlの場合

```config.json
{
    "username": ユーザーネーム,
    "password": パスワード,
    "database": データベー名
    "host": ホスト名,
    "dialect": "mysql"
}
```

#モデルを作成する。例）userモデル

```ターミナル.
npx sequelize-cli model:generate --name User --attributes name:string,pass:string

//(attributesはスペースは開けない）
```
npx ( npmを拡張したもの。標準でNode.jsに入っている ) のsequelize-cliコマンドを実行

##モデルの情報(development)を元にマイグレートを実施
 
```ターミナル.
 npx sequelize-cli db:migrate --env development
```
db-dev.sqlite3が生成される。（データーベースファイル)
maigrationファイルが作成される。

##マイグレーションを一個前に戻す

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

##マイグレーションを全て戻す

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

###ベーシックなモデルの例(コマンドで生成されるものを簡略化）

```model/user.js
'use strict';
module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define('User', {
    name: DataTypes.STRING,
    pass: DataTypes.STRING,
  }, {});
  User.associate = function(models) {
    // アソシエーションを記載
  return User;
};
```
モジュールエクスポートで関数を返している。関数は最終的に変数Userをreturnする。

###アソシエーションの書き方(UserがBookを多数持っている場合)

```javascript
User.associate = function (models) {
  User.hasMany(models.Book);
};
```

```js
Book.associate = function(models) {
  Book.belongsTo(models.User, {foreignKey: 'user_id'});
};
//第二引数に外部キーを渡す
```

```JS
'use strict';
const { Model } = require('sequelize');

module.exports = (sequelize, DataTypes) => {
  class Post extends Model {
    static associate(models) {
      Book.belongsTo(models.User, { foreignKey: 'user_id' });
    }
  };
  Post.init({
    title: DataTypes.STRING,
    content: DataTypes.STRING,
    user_id: DataTypes.INTEGER, //所属するほうに外部キーを設定
  }, {
    sequelize,
    modelName: 'Post',
  });
  return Post;
};
```


###シーダーでダミーデーターを格納する。

シーダーの作成

```ターミナル.
npx sequelize-cli seed:generate --name sample-user
```

```sample-user.js
//作成例

'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {
    return queryInterface.bulkInsert('Users', [
      {
        name: 'Taro',
        pass: 'lkfjaluadf',
        createdAt: new Date(),
        updatedAt: new Date()
      },
      {
        name: 'Hanako',
        pass: 'fdakljfa',
        createdAt: new Date(),
        updatedAt: new Date()
      }
    ]);
  },

  down: async (queryInterface, Sequelize) => {
    return queryInterface.bulkDelete('Users', null, {});
  }
};
```

###シーダーの実行

```ターミナル.
npx sequelize-cli db:seed:all
```

###データベースクリアサンプル

```js

const db = require('../models/index');

db.User.destroy({
  where: {},
  truncate: true
})

db.Post.destroy({
  where: {},
  truncate: true
})
```

#SequelizeでORMを利用する。
モデルを使用するファイルで、models/index(自動生成される）を読み込み。
models/indexはmodels配下のモデルファイルを読み込んでる。

```javascript
const db =require('models/index');
```
index.jsを読み込みdbに格納する。dbにSequelizeのあらゆる情報がまとめられる。

```index.js
'use strict';

const fs = require('fs'); //fsのロード
const path = require('path');//pathをロード
const Sequelize = require('sequelize'); //sequelizeをロード
const basename = path.basename(__filename);//このファイル名をbasenameを格納
const env = process.env.NODE_ENV || 'development'; //環境変数NODE_ENVが設定されていたら返す。なければ'development'
const config = require(__dirname + '/../config/config.json')[env];//configファイルを指定し、envに入っている文字列と一致するプロパティーの内容をconfigに格納。
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
  .readdirSync(__dirname)//同じファイルのモデルを自動読み込み
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
    db[model.name] = model;
  });//各ファイルから返されたモデル名をdbのmodelプロパティのプロパティに格納

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;//dbをエクスポート
```

自動生成

```config/config.json
{
  "development": {
    "username": "root",
    "password": "root",
    "database": "minna_db",
    "host": "mysql",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}
```

#findAllで取得する。
###Userモデルから全件取得する場合

```javascript
  db.User.findAll().then((users) => {
    var data = {
      title: 'Users/Index',
      content: users
    }
    res.render('users/index', data);
  });
});
```
.UserでUserモデルを取り出す。
findAll()メソッドで全件取得。
thenメソッドで非同期処理完了後の処理をつなげる。引数に取得したデータが格納される。

###findAll( )のオプション
引数にオプションを渡す。

```javascript
findAll ( { 設定1 : 値1, 設定2 : 値2 } )
```
###whereの場合

```javascript
db.User.findeAll ( { where: {id : 1} } );
```

#Opから演算子を指定して検索する。

```javascript
const { Op } = require("sequelize");
```
sequelizeのOpプロパティをロードし、変数Opに格納する。

###Opで演算子から検索する方法

```javascript
where: { フィールド : {[ Op.演算子 ] : 値} }
```

Opの演算子プロパティ

eq : =
ne : !=
lt : <
lte : <=
gt : >
gte : >=
like : like
ilike : ilike 

###idが３以上のユーザーを取得の場合

```javascript
db.User.findAll( {where: { id: {[Op.gte] : 3} } )
```

###AND条件　１以上,3以下の場合

```javascript
db.User.findAll(
  {where: {
    id: {
      [Op.gte] : 1,
      [Op.lte] : 3,
    }
  }
)
```

###OR条件

```javascript
where { [Op.or]:[ {条件1}, {条件2} ] }
```

名前かメールにtaroが含まれるレコードを検索する。

```javascript

 db.User.findAll({
    where: {
      [Op.or]:[
        {name:{[Op.like]:'%'+'taro'+'%'}},
        {mail:{[Op.like]:'%'+'taro'+'%'}}
      ]
    }
```

#findByPkで検索する。
主キーで検索する場合

```javascript
db.User.findByPk(主キー);
```

#createで新規作成する。

```javascript
モデル.create( {各プロパティ:値 } );
```

```javascript
  db.sequelize.sync()
    .then(() => db.User.create({
      name: "taro",
      pass: "fakdjfakjf",
    }))
    .then((user) => {
    });
});
```
Sequelizeオブジェクトのsyncメソッドを用いて、同期処理を行う。実際の処理は.thenに任せる。

#モデルを書き換えて更新する。

```javascript
db.User.findByPk(1)
  .then(usr => {
    usr.name = "jiro";
    usr.pass = "fdafdafadf";
    usr.save()
    .then(()=>{
  });
```

##レコードの削除

```javascript

db.User.findByPk(1)
  .then(usr => {
    usr.destroy().then(()=>{
  });
```
#Sequelizeを用いてバリデーションをかける。

モデルに定義する。

```javascript
'use strict';

module.exports = (sequelize, DataTypes) => {
  const User = sequelize.define('User', {
    name: {
      type:DataTypes.STRING,
      //バリデーションを設定
      validate: {
        notEmpty: true
      }
    },
  }, {});

  return User;
};
```

```javascript
router.get('/add',(req, res, next)=> {
  var data = {
    //新しいレコードを作成し、fromに格納
    form: new db.User(),
    //errは空
    err:null
  }
  res.render('users/add', data);
});

//作成された場合の処理
router.post('/add',(req, res, next)=> {
  const form = {
    name: req.body.name,
    pass: req.body.pass,
  };
  db.sequelize.sync()
    .then(() => db.User.create(form)
    .then(usr=> {
      res.redirect('/users')
    })
    //バリデーションにかかった場合の処理
    .catch(err=> {
      var data = {
        form: form,
        //errにエラー情報が格納されている。
        err: err
      }
      res.render('users/add', data);
    })
    )
});
```

表示をカスタマイズ

```javascript
name: {
      type:DataTypes.STRING,
      //バリデーションを設定
      validate: {
        notEmpty: {
          msg: "必ず入力してください";
      }
    },
```
notEmptyオブジェクトのmsgプロパティを書き換える。