Node.js で小規模なアプリケーションを開発する方法 その13

# はじめに
この記事ではstaticのモデル作成について解説します。

# ディレクトリとファイルの作成
コマンドを下記に示します。

```
mkdir static/src/models
touch static/src/models/category.js
touch static/src/models/project.js
touch static/src/models/report.js
```

# ソースコードの編集

## Categoryモデル
ソースコードの内容を下記に示します。

```js:static/src/models/category.js
'use strict';

module.exports = Category

function Category(project) {
  this.form = {
    name: typeof project.name === 'string' ? project.name : '',
    hour: '' + (typeof project.hour !== 'undefined' ? project.hour : ''),
  }

  this.validation = {
    name: null,
    hour: null,
  }
}

Category.prototype.validate = function () {
  this.validation.name = this.form.name !== ''
  this.validation.hour = /^[0-9]+$/.test(this.form.hour)

  return this.isValid
}

Category.prototype.toJSON = function () {
  return this.body
}

Object.defineProperties(Category.prototype, {
  isValid: {
    get: function () {
      var validation = this.validation

      return Object.keys(validation).every(function (key) {
        return validation[key] === true
      })
    },
  },

  body: {
    get: function () {
      return {
        name: this.name,
        hour: this.hour,
      }
    },
  },

  name: {
    get: function () {
      return this.form.name
    },
    set: function (value) {
      this.form.name = value
    },
  },

  hour: {
    get: function () {
      return Number.parseInt(this.form.hour)
    },
    set: function (value) {
      this.form.hour = '' + value
    },
  },
})
```

## Projectモデル
ソースコードの内容を下記に示します。

```js:static/src/models/project.js
'use strict';

var Category = require('./category')

module.exports = Project

function Project(project) {
  this.form = {
    index: '' + (typeof project.index !== 'undefined' ? project.index : ''),
    name: typeof project.name === 'string' ? project.name : '',
    day: '' + (typeof project.day !== 'undefined' ? project.day : ''),
  }

  this.validation = {
    index: null,
    name: null,
    day: null,
  }

  this.state = {
    categories: null,
  }

  var categories

  if (typeof project.categories === 'string') {
    categories = JSON.parse(project.categories)
  } else if (Array.isArray(project.categories)) {
    categories = project.categories
  } else {
    categories = []
  }

  this.state.categories = categories.map(function (category) {
    return new Category(category)
  })
}

Project.prototype.validate = function () {
  this.validation.index = /^[0-9]+$/.test(this.form.index)
  this.validation.name = this.form.name !== ''
  this.validation.day = /^[0-9]+$/.test(this.form.day)

  this.date.categories.forEach(function (category) {
    category.validate()
  })

  return this.isValid
}

Project.prototype.toJSON = function () {
  return this.body
}

Object.defineProperties(Project.prototype, {
  isValid: {
    get: function () {
      var validation = this.validation

      var isValidSelf = Object.keys(validation).every(function (key) {
        return validation[key] === true
      })

      var isValidCategories = this.state.categories.every(function (category) {
        return category.isValid
      })

      return isValidSelf && isValidCategories
    },
  },

  body: {
    get: function () {
      return {
        index: this.index,
        name: this.name,
        categories: this.categories,
        day: this.day,
      }
    },
  },

  index: {
    get: function () {
      return Number.parseInt(this.form.index)
    },
    set: function (value) {
      this.form.index = '' + value
    },
  },

  name: {
    get: function () {
      return this.form.name
    },
    set: function (value) {
      this.form.name = value
    },
  },

  categories: {
    get: function () {
      return JSON.stringify(this.form.categories)
    },
    set: function (value) {
      this.state.categories = JSON.parse(this.form.categories).map(function (category) {
        return new Category(category)
      })
    },
  },

  day: {
    get: function () {
      return Number.parseInt(this.form.day)
    },
    set: function (value) {
      this.form.day = '' + value
    },
  },
})
```

## Reportモデル
ソースコードの内容を下記に示します。

```js:static/src/models/report.js
'use strict';

module.exports = Report

function Report(report) {
  this.form = {
    date: typeof report.date === 'string' ? report.date : '',
    start: typeof report.start === 'string' ? report.start : '',
    finish: typeof report.finish === 'string' ? report.finish : '',
    hour: '' + (typeof report.hour !== 'undefined' ? report.hour : ''),
    category: typeof report.category === 'string' ? report.category : '',
    title: typeof report.title === 'string' ? report.title : '',
    memo: typeof report.memo === 'string' ? report.memo : '',
    projectId: '' + (typeof report.ProjectId !== 'undefined' ? report.projectId : ''),
  }

  this.validation = {
    date: null,
    start: null,
    finish: null,
    hour: null,
    category: null,
    title: null,
    memo: null,
    projectId: null,
  }
}

Report.prototype.validate = function () {
  this.validation.date = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(this.form.date)
  this.validation.start = /^[0-9]{2}:[0-9]{2}$/.test(this.form.start)
  this.validation.finish = /^[0-9]{2}:[0-9]{2}$/.test(this.form.finish)
  this.validation.hour = /^[0-9]+$/.test(this.form.hour)
  this.validation.category = this.form.category !== ''
  this.validation.title = this.form.title !== ''
  this.validation.memo = this.form.memo !== ''
  this.validation.projectId = this.form.projectId !== ''

  return this.isValid
}

Report.prototype.toJSON = function () {
  return this.body
}

Object.defineProperties(Report.prototype, {
  isValid: {
    get: function () {
      var validation = this.validation

      return Object.keys(validation).every(function (key) {
        return validation[key] === true
      })
    },
  },

  body: {
    get: function () {
      return {
        date: this.date,
        start: this.start,
        finish: this.finish,
        hour: this.hour,
        category: this.category,
        title: this.title,
        memo: this.memo,
        ProjectId: this.ProjectId,
      }
    },
  },

  date: {
    get: function () {
      return this.form.date
    },
    set: function (value) {
      this.form.date = value
    },
  },

  start: {
    get: function () {
      return this.form.start
    },
    set: function (value) {
      this.form.start = value
    },
  },

  finish: {
    get: function () {
      return this.form.finish
    },
    set: function (value) {
      this.form.finish = value
    },
  },

  hour: {
    get: function () {
      return Number.parseInt(this.form.hour)
    },
    set: function (value) {
      this.form.hour = '' + value
    },
  },

  category: {
    get: function () {
      return this.form.category
    },
    set: function (value) {
      this.form.category = value
    },
  },

  title: {
    get: function () {
      return this.form.title
    },
    set: function (value) {
      this.form.title = value
    },
  },

  memo: {
    get: function () {
      return this.form.memo
    },
    set: function (value) {
      this.form.memo = value
    },
  },

  projectId: {
    get: function () {
      return Number.parseInt(this.form.projectId)
    },
    set: function (value) {
      this.form.projectId = '' + value
    },
  },
})
```

## entry.js
変更後の内容を下記に示します。

```js:static/src/entry.js
'use strict';

window.models = {
  Report: require('./models/report'),
  Project: require('./models/project'),
  Category: require('./models/category'),
}
```

変更点は下記の通りです。

```
@@ -1,3 +1,7 @@
 'use strict';
 
-console.log('entry.js')
+window.models = {
+  Report: require('./models/report'),
+  Project: require('./models/project'),
+  Category: require('./models/category'),
+}
```

# 動作確認
staticのサーバを起動し、エラーが表示されなければ正常に動作しています。

# おわりに
次回はセッションと認証管理の設定について解説します。