わいの日記

ありがちエンジニアブログ

AdonisJSでLaravel(5.1)のチュートリアルしてみた

AdonisJSの紹介

adonisjs.com

MVCに則ったJavaScriptフレームワークです

PHPフレームワークの1つLaravelにめっちゃそっくりです

こちらを紹介する目的:JavaScripter人口増えたらいいな
PHPerがNode.jsに入る時にexpressだと取っつきにくいと思うんです
自分はめっちゃ苦労しました
それに比べたらAdonisJSはMVCなのでめっちゃ取っつきやすいと思います
Ruby on Rails使っている人にとっても学びやすいフレームワークかなと思います

そこでLaravelにそっくりということでLaravelのチュートリアルをAdonisJSでやってみます
基本のタスクリスト 5.1 Laravel


では早速
(AdonisJSのversionは4.1です)

// validateファイルの作成

npm i --global @adonisjs/cli

// プロジェクト作成

adonis new laravel-tutorial

ディレクトリ構造です
migrationsがあったりroutes.jsがあったりでLaravel使いには馴染みのある構成ですね!
f:id:exhikkii:20181119174616p:plain


// devサーバー立ち上げ

cd laravel-tutorial
adonis serve --dev

動作確認 localhost:3333にアクセス
f:id:exhikkii:20181119174811p:plain

今回は簡単のためdatabaseにsqlite3を使います
// sqlite3を追加

yarn add sqlite3

早速tasksテーブルを作ります
// tasksテーブルのマイグレーションファイル作成

adonis make:migration tasks

// 上記のコマンドのあと、「Choose an action Create table」この選択肢を選んでください

出来上がったmigrationファイル修正
upやdownの書き方もLaravelおなじみですね

'use strict'

/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')

class TasksSchema extends Schema {
  up () {
    this.create('tasks', (table) => {
      table.increments()
      table.string('name') // この1文をを追記
      table.timestamps()
    })
  }

  down () {
    this.drop('tasks')
  }
}

module.exports = TasksSchema

// migration実行

adonis migration:run

// Taskモデルの作成

adonis make:model Task

下記の内容のmdoelクラスが作られる
// app/Models/Task.js

'use strict'

/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')

class Task extends Model {
}

module.exports = Task

次にルーティングの設定
// start/routes.js

'use strict';

const Route = use('Route');
const Task = use('App/Models/Task');

Route.get('/', async ({ view }) => {
  const tasks = await Task.all();
  console.log('tasks', tasks.toJSON())
  return view.render('tasks', { tasks: tasks.toJSON() });
});

Route.post('/task', async ({request, response}) => {
  await Task.create(request.only(['name']));
  return response.redirect('/');
}).validator('Task'); // 後述するvalidatorを指定する

Route.delete('/task/:id', async ({params, response}) => {
  const task = await Task.find(params.id);
  await task.delete();
  return response.redirect('/');
});

次はview
AdonisJSはedgeテンプレートを採用しています
これもまたLaravelのbladeファイルの記法とそっくり
レイアウトファイルから作ります
// view/layouts/app.edge

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Laravel Tutorial with AdonisJs</title>
  // bootstrapをcdnで読み込む
  {{ style('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u') }}  
</head>
<body>
  <div class="container">
    <nav class="navbar navbar-default">
    </nav>
  </div>
  @!section('content') // section前の! を忘れないで
</body>
</html>

さらにtask.edge
// resources/views/task.edge

@layout('layouts.app')  //レイアウトファイルを指定

@section('content') // レイアウトファイルの@!section('content')に埋め込まれる
<div class="panel-body">
  @include('common.errors') // partialファイル(resources/views/common.errors)を読み込む
  <form action="/task" method="POST" class="form-horizontal">
    {{ csrfField() }} // CSRF「トークン」を生成
    <div class="form-group">
      <label for="task" class="col-sm-3 control-label">Task</label>
      <div class="col-sm-6">
        <input type="text" name="name" id="task" class="form-control">
      </div>
    </div>

    <div class="form-group">
      <div class="col-sm-offset-3 col-sm-6">
        <button class="btn btn-default">
          <i class="fa fa-plus"></i> タスク追加
        </button>
      </div>
    </div>
  </form>
</div>
@if(tasks.length > 0)
<div class="panel panel-default">
  <div class="panle-heading">
    現在のタスク
  </div>
  <div class="panel-body">
    <div class="table table-striped task-table">
      <thead>
        <th>Task</th>
        <th>&nbsp;</th>
      </thead>
      <tbody>
        @each(task in tasks) // tasksを一覧表示するためのループ
          <tr>
            <td class="table-text">
              <div>{{ task.name }}</div> // taskの名前を表示
            </td>
            <td>
    // deleteメソッドにするときはactionの末尾に_method=DELETEをつける
              <form action="/task/{{task.id}}?_method=DELETE" method="POST">
                {{ csrfField() }}
                <button>タスク削除</button>
              </form>
            </td>
          </tr>
        @endeach
      </tbody>
    </div>
  </div>
</div>
@endif
@endsection

// resources/views/common/errors.jsの作成

@if(hasErrorFor('name')) nameをkeyにするflashメッセージがあるか確認
  <div>
    <strong>おや?何かがおかしいようです!</strong>
    <br><br>
    <p>{{ getErrorFor('name') }}</p> // あれば表示
  </div>
@endif

最後にバリデーション
バリデーションは別途設定する必要があります
// validationの追加

adonis install @adonisjs/validator

// start/app.jsの修正してバリデーションを使えるようにする

const providers = [
  '@adonisjs/framework/providers/AppProvider',
  '@adonisjs/framework/providers/ViewProvider',
  '@adonisjs/lucid/providers/LucidProvider',
  '@adonisjs/bodyparser/providers/BodyParserProvider',
  '@adonisjs/cors/providers/CorsProvider',
  '@adonisjs/shield/providers/ShieldProvider',
  '@adonisjs/session/providers/SessionProvider',
  '@adonisjs/auth/providers/AuthProvider',
  '@adonisjs/validator/providers/ValidatorProvider' // ここを追加してください
]

// validateファイルの作成

adonis make:validator Task

// app/Validators/Task.js

'use strict'

class Task {
  // バリデーションのルールを決める
  get rules () {
    return {
      name: 'required|max:30'
    }
  }

  // エラーメッセージの設定
  get messages() {
    return {
      'required': '{{ field }} is required.',
      'max': 'max 30 characters'
    }
  }

  // sessionにエラーメッセージを設定
  async fails(error) {
    this.ctx.session.withErrors(error)
      .flashAll();

    return this.ctx.response.redirect('back');
  }
}

module.exports = Task

実際のコードを下記のリポジトリに置いています
github.com

このチュートリアルではcontrollerなども利用していないのですが、
AdonisJSにもちゃんとcontrollerがあります

他にもIOCなど類似の機能はあるのでぜひ公式リファレンスをご覧ください

個人的にはAdonisJSはバックエンドのapiサーバーをして利用する際に力を発揮すると考えます
expressに比べcorsなど設定が楽だと思います(個人比)
(adonis newする際にapiモードを指定すればviewが作成されなくなります)
またapiの方も紹介しようと思います