AWS cloud9上にLaravel5.1で基本のタスクリストを作るよ

Webアプリもフレームワークも触ったことの無い初心者ポンコツエンジニアは20日間もかかってしまいました。 全然できなくてずっと悔しい思いをしていたのですが、ようやくできました(° 言 ° ) また同じように悩まなくて済むように記載しておきます。

準備


完成物

task1

基本のタスクリスト 5.1 Laravel に沿ってタスクの追加、現在のタスクの表示、削除ができるアプリケーションを作ります。

動機

はい、非常に不純ですね。良い子は純粋な動機を持ってプログラムしましょう。 イケッメンなお兄さんご本人や旦那が読んでいたらツイ主は発狂するんでしょうね。

AWS Cloud9を選んだ理由

最初はpaiza cloudに課金して構築しようとしたのですが、私のスキルがなさすぎてうまくいかなかったんです。よくわからずコマンドコピペしてましたからね。 外出先ではSuface、家ではMac miniを使用しています。どちらかのローカルにLaraveloを構築してしまうと作業が捗らないのでクラウド上でどうしても作成したかったのです。 herokuやfirebaseも検討したのですがLaravel学習帳 にCloud9からherokuへの移行の記事があったので先にCloud9上に環境を作った次第です。

進め方

主にこの3つのページを参考にしながら進めていきます。

また本記事はAWSアカウントを作成し、Cloud9を使用可能にした前提で解説します。 Cloud9の立ち上げ方は下記のサイトが分かりやすいのでこちらをご覧ください。 初めてのAWS Cloud9導入 - Qiita

Composerをインストール


Laravelをインストールするために先にComposerを入れます。 どうしてComposerが必要なのかは composerとは? - Qiita で解説されています。

コンソールにcurl -sS https://getcomposer.org/installer | phpを入力します。 curl(カール)は、HTTPアクセスをしてコンテンツを取得できるlinuxのコマンドです。 PHPの設定内容をチェックしてcomposer.pharを仮想環境の直下にダウンロードします。 composer.pharとはComposerのバイナリなんだそうです。

インストールが完了すると仮想環境の直下にcomposer.pharが作成されます、左側のツリーから確認できます。 task2

このファイルは好きな場所に移動させることができます。 PATHの通った場所にこのファイルを配置することでどこからでもComposerにアクセスできるようになります。 ※「PATHの通った場所」の意味がわからなかったのでググりました。参照:Linux入門 ~「パスを通す」とは~ - Qiita

sudo mv composer.phar /usr/local/bin/composer

これで、Composerを実行したいときに"php composer.phar ~"と打たなくても"composer ~"と打つだけで実行できるようになりました。 試しにcomposerをBashに入力してComposerが動作するのか検証してみます。 task3

無事に「Composer」のアスキーアート(?)が表示されれば完了です。

日本語などのマルチバイト文字を利用可能にする


まずはphp -vでphpのバージョンを確認します。 task4

私はphp5.6でした。

sudo yum install php56-mbstring

php5.6の人は"php-mbstring"ではなく"php56-string"だそうです。 php5.6以外の人は恐縮ですが適宜ググってください...

================Package ================ Installing: php56-mbstring Install 1 Package Total download size: 1.3 M Installed size: 2.7 M Is this ok [y/d/N]

途中でこのように聞かれたら"y"とEnterを押してください。 task5

ダウンロードが完了すれば、マルチバイト文字列系の関数が使えるようになります。

Laravelをインストール


いよいよLaravelを入れます。ここで注意したいのが現在の最新バージョンが5.1ではなくなっていることです。 基本のタスクリストのページに記載されている composer create - project laravel/laravel quickstart --prefer-distを入力してバージョンを確認すると Laravel5.4.2が入っていました。(2019年2月時点。 distと指定すると安定版がインストールされるため。) 基本のタスクリストと同じ環境で構築したいので、Laravel5.1を入れるために

composer create-project laravel/laravel quickstart "5.1.*"

と入力しましょう。quickstartという名前のプロジェクトが作成されます。 詳細はLaravelをこれから勉強しようと思っている人へ - Qiitaで解説してくださっています。

AWS Cloud9でLaravel環境を構築されている方の記事を見ると メモリ不足のエラーについて必ず書かれているようです。 1GBでは足りないようですが課金をする前に悪あがきがしたいところです。

task6

The following exception is caused by a lack of memory or swap, or not having swap configured (中略) [ErrorException] proc_open(): fork failed - Cannot allocate memory 次の例外は、メモリ不足またはスワップ、またはスワップが設定されていないことが原因で発生します。 (Google翻訳)

という事ですのでスワップファイルを作成したいと思います。 エラーの中で案内されていたTroubleshooting - Composerを参考にしながら進めます。

free -m

現在のメモリとスワップ領域の状況を確認します。 task7

数字から漂ううまく使えていない感。(公式ページと数字が違うんだが大丈夫だろうか)

sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024

bs=ブロックサイズ,count=ブロック数よりサイズが約1Gの"swap.1"という名前の中身が0で埋め尽くされたファイルを作成します。 (/bin/ddなのはddコマンドのパスが設定されていないからと推測) 実行すると

1024+0 records in1024+0 records out1073741824 bytes (1.1 GB) copied, 13.266 s, 80.9 MB/s"

と表示されました。

続いてスワップ領域を作成するためmkswapを実行します。

sudo /sbin/mkswap /var/swap.1

task8

スワップ領域が作成できたので swapon コマンドを実行してスワップ領域を有効にします。

sudo /sbin/swapon /var/swap.1

task9

これで準備が整いました。free -mしてみます。 task10

swap領域が増えたことが確認できました。 メモリ不足で失敗して中途半端に残ってしまった"quickstart"ディレクトリを削除して 今度こそcomposer create-projectを実行します。 大量のファイルをインストールする必要がありますので少々時間がかかります。

Application key [xxxxxx] set successfully.

まで表示され、次の命令が実行できる状態になれば完了です。

swapに頼ること自体はAWS的にOK

不安だったので調べたところ「Amazon EC2 インスタンスのスワップ領域としてメモリを割り当てるためのスワップファイルの使用」という公式のヘルプにddコマンドからスワップファイルを作成する方法が紹介されていました。 個人が趣味でLaravel環境を構築し、砂の城を建て、いらなくなれば崩す、という運用なら問題なさそうです。 しかし長期に渡ってwebアプリを運用することを考えるなら、おまじないのように何も考えずswap領域を確保するのは危険です。良い子はswap領域をアテにするのではなく、AWSさんにお金を払ってメモリを確保しましょう。

試しにサーバーを立ち上げる


動作確認のためサーバーを立ち上げます。 AWS Cloud9が8080使えと言っているのでポートを指定します。

php artisan serve --port=8080

task11

ブラウザから確認します。 task14

画面上のメニューバーから"Preview"→"Preview Running Apprication"でブラウザを立ち上げることができます。 task13

無事に立ち上がってきました。 止める時はCtrl+Cです、これで次の命令を入力できる状態になります。

言語と時刻の設定


一度bashから離れて/config/app.phpを開きます。 ”Application Timezone”というコメントを検索すると"timezone"と"locale"を設定できる箇所が出てきます。

task15

'timezone' => 'Asia/Tokyo',
'locale' => 'ja',

に変更してCtrl+Sで上書き保存します。

MySQLの設定


/etc/my.cnfを編集

bashに戻ります。文字コードにUTF-8を使用できるよう設定します。

sudo vi /etc/my.cnf

Bashがviエディタになりますのでいつもと操作が変わります、"i"を押してINSERTモードにします。 [mysqld]セクションの末尾に一行追加し、さらに[client]セクションを作成します。

[mysqld]
(中略)
character-set-server=utf8
[client]
default-character-set=utf8

EscでINSERTモードから抜けて:wqコマンドで上書き保存ができます。 うまく行かなかったら:q!で保存せずviを強制終了することでvi編集前の状態に戻れます。

MySQLの起動

起動前に現在の状態を確認しておきます。

sudo service mysqld status

task17

当然止まっていますので起動させます。 ちなみに大文字小文字の区別が厳密ですので「MySQL」とか入力しても相手にしてもらえません。

sudo service mysqld start

task18

MySQLが起動できましたのでデータベースの設定をしていきます。

タスクリスト用データテーブルの作成

mysql -u root

>mysql

と表示されたらMySQLに対して命令できるようになります。

task20

いまはこの状態だと思うのでタスクリスト用のデータベースを作成します。

create database tasklist;

空のデータベースが作成されました。 task21

.envファイルの編集

/quickstart直下の.envファイルのDB情報を修正します。 通常は隠しファイルですので左側のツリーに表示されていない場合は、 ルートディレクトリの横にある歯車アイコンをクリックして”Show Hidden Files"を選択します。 チェックマークが付いていれば隠しファイルが表示されます。 task22

:
DB_DATABASE = tasklist
DB_USERNAME = root
DB_PASSWORD =

DBDATABASEは先ほど作成したもの、DBUSERNAMEはAWSで割り当てられているユーザーに従って設定してください。

データベーステーブルを定義する


初心者すぎてLaravelに搭載されているmake:migrationコマンドの良さがうまく説明できないため Twitterのフォロワーさんの記事を丸パクリします。 (参照:Laravel 整理整頓したマイグレーションファイルを生成するライブラリを作った話 - Qiita)

マイグレーションとは Laravelではデータベースのテーブル作成や編集などを管理する方法として「マイグレーション」という機能が標準搭載されています。

  1. マイグレーションファイルの作成
  2. マイグレーションの実行 マイグレーションファイルにテーブルの定義を記述し、マイグレーションの実行により、 MySQL等の実際のデータベースにテーブルを作成します。 これをGitで管理することにより、チーム内で同一のテーブル構成にできたり、テーブル定義の変更の履歴を管理できるメリットがあります。

今回は登録したタスクを保存しておくtasksテーブルをmake:migrationコマンドを使って生成していきます。 MySQLは起動させたままmake:migrationコマンドを実行します。

php artisan make:migration create_tasks_table --create=tasks

task19

このコマンドで「マイグレーションファイル」が作成されました。 yyyymmdd_create_tasks_table.phpを開いて、 タスクの名前を保存する"name"という名前のstring型カラムを作るよう追記します。

/database/migrations/(date)_create_tasks_table.php
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTasksTable extends Migration
{
    /**
     * マイグレーション実行
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name'); #追記
            $table->timestamps();
        });
    }

    /**
     * マイグレーションの巻き戻し
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('tasks');
    }
}

このファイルを上書き保存したらデータベースに反映させます。

php artisan migrate

task23

tasksテーブルができています。 ここでコケた場合は、MySQLからtasklistデータベース上の不要テーブルを"drop table テーブル名"で削除してやり直し可能です。

"php artisan migrate"時に遭遇したエラー

SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; >max key length is 767 bytes

最初にLaravel5.1を指定しなかった場合このエラーにぶつかります。遭遇してお困りの方は下記のリンクをご覧ください。 (参照:Laravel5.4以上、MySQL5.7.7未満 でusersテーブルのマイグレーションを実行すると Syntax error が発生する - Qiita)

Eloquentモデル


DBとモデルオブジェクトを対応付ける機能です。このチュートリアルではあまり触れられていません。 が、作成しないとエラーが出るので作ります。わかりやすい解説ページを見つけたら追記します。

php artisan make:model Task

task24

今回中身を編集する必要がないので生成されたことを確認して次へ進みます。

ルーティングとビューの設定


ルーティング

URLとコントローラーのアクションの対応ルールを作成していきます。 Laravel5.1では/app/Https/route.phpを編集します。(Laravel5.3以降は/routes/web.phpになっています。) スタブを作って順を追って説明する役割は基本のタスクリストの解説に任せるとして、最終形態を記載します。

/app/Https/routes.php
<?php

use App\Task;
use Illuminate\Http\Request;

/**
 * 全タスク表示
 */
Route::get('/', function () {
    $tasks = Task::orderBy('created_at', 'asc')->get();

    return view('tasks', [
        'tasks' => $tasks
    ]);
});

/**
 * 新タスク追加
 */
Route::get('/task', function (Request $request) {});
Route::post('/task', function (Request $request) {
    $validator = Validator::make($request->all(), [
        'name' => 'required|max:255',
    ]);

    if ($validator->fails()) {
        return redirect('/')
            ->withInput()
            ->withErrors($validator);
    }

    $task = new Task;
    $task->name = $request->name;
    $task->save();

    return redirect('/');
});

/**
 * 既存タスク削除
 */
Route::delete('/task/{id}', function ($id) {
    Task::findOrFail($id)->delete();

    return redirect('/');
});

ビュー

続いてBladeテンプレートエンジンを使用して画面側を作ります。 Bladeの良さはLaravelエンジニアが考えたWordPressテーマ開発環境 | Hypertext Candyで詳しく解説されています。 他のページではLaravelの「いろは」をわかりやすく解説されているので、私のこの記事よりオススメです。 (ここまで読んでくださったのにすいません())

ビューはこんなファイル構成で作成します。 task25

新規で/resources/views/layouts/app.blade.phpを作成します。

/resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Laravel Quickstart - Basic</title>

        <!-- CSSとJavaScript -->
    </head>
    
    <body>
        <div class="container">
            <nav class="navbar navbar-default">
                <!-- ナビバーの内容 -->
            </nav>
        </div>
    
        @yield('content')
    </body>
</html>

続いて「ナビバーの内容」の部分にあたる子ビューを定義します。 /resources/views/tasks.blade.phpを新規で作成して以下のコードを貼り付けてください。

/quickstart/resources/views/tasks.blade.php
@extends('layouts.app')

@section('content')

    <!-- Bootstrapの定形コード… -->
    
    <div class="panel-body">
        <!-- バリデーションエラーの表示 -->
        @include('common.errors')
    
        <!-- 新タスクフォーム -->
        <form action="/task" method="POST" class="form-horizontal">
            {{ csrf_field() }}
    
            <!-- タスク名 -->
            <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-name" class="form-control">
                </div>
            </div>
    
            <!-- タスク追加ボタン -->
            <div class="form-group">
                <div class="col-sm-offset-3 col-sm-6">
                    <button type="submit" class="btn btn-default">
                        <i class="fa fa-plus"></i> タスク追加
                    </button>
                </div>
            </div>
        </form>
    </div>
    
    <!-- 現在のタスク -->
    @if (count($tasks) > 0)
        <div class="panel panel-default">
            <div class="panel-heading">
                現在のタスク
            </div>
    
            <div class="panel-body">
                <table class="table table-striped task-table">
    
                    <!-- テーブルヘッダー -->
                    <thead>
                        <th>Task</th>
                        <th>&nbsp;</th>
                    </thead>
    
                    <!-- テーブルボディー -->
                    <tbody>
                        @foreach ($tasks as $task)
                            <tr>
                                <!-- タスク名 -->
                                <td class="table-text">
                                    <div>{{ $task->name }}</div>
                                </td>
    
                              <!-- 削除ボタン -->
    <td>
        <form action="/task/{{ $task->id }}" method="POST">
            {{ csrf_field() }}
            {{ method_field('DELETE') }}
    
            <button>タスク削除</button>
        </form>
    </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    @endif
@endsection

必須ではありませんがerrorの際に表示する/resources/views/common/errors.blade.phpも新規で作成します。

/resources/views/common/errors.blade.php
@if (count($errors) > 0)
    <!-- フォームのエラーリスト -->
    <div class="alert alert-danger">
        <strong>おや?何かがおかしいようです!</strong>

        <br><br>
    
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

動作確認

"tasks.blade.php","errors.blade.php"のファイル名になっていること(私は幾度となくハマりました。学習能力ゼロ。) とMySQLが起動していることを確認して、サーバーを立ち上げます。

php artisan serve --port=8080

task26

できました。゚(゚´ω`゚)゚。

Cloud9上のブラウザで操作するとなぜかボタンを押すたび自分で再描画する必要があります。 新しいウィンドウへPop outして操作した方が快適です。これはCloud9の問題な気がします。

キャッシュクリア

ゴミが溜まって実行できていない可能性もあるので、最後にキャッシュを削除する方法を記載しておきます。

php artisan cache:clear
php artisan route:cache

まとめ


初めてのWebアプリ、そして初めて環境を作ったフレームワークがLaravelという鬼畜ゲーにきてしまった気がします。 (実務で使ったCodeCeptionは上手にお膳立てされていたためノーカウント。) 何気なくおまじないのようにコマンドを実行してはエラーで悔しがっていましたが、記事を書きながら調査ができて勉強になりました。 Eloquentとかルーティングとかビューとか完全に力尽きたのでぶっ飛ばしました。 (節子、それ全部やで。) もっと時間かけて勉強したいですね。