AWSのCloud9(Amazon Linux2)を利用してLaravelで好きなYouTube動画を管理できるプラットフォームを作る

#AWSのCloud9でLaravelを使ってみる
PHPのワークフレームであるLaravelを使ってみたいと思い、色々と学んだことを健忘録がてら、まとめようと思います。
特にAmazon Linux2に関しては、調べてもナレッジが少なかったので、他の方の参考になれば幸いです。

##AWS上でCloud9を起動し、環境設定を行う
###AWSにサインイン
[AWS](https://aws.amazon.com/jp/)からコンソールにサインインする。
[![Image from Gyazo](https://i.gyazo.com/cf5e264a1dd88a8644db99f85ac53e99.png)](https://gyazo.com/cf5e264a1dd88a8644db99f85ac53e99)
案内に従って、マネジメントコンソールが表示されるまで進む

###Cloud9を検索
検索窓でCloud9を検索し選択 
[![Image from Gyazo](https://i.gyazo.com/2fbecc711507d0f69aa1a3da80c9a161.png)](https://gyazo.com/2fbecc711507d0f69aa1a3da80c9a161)

###environmentを構築する
environment=プロジェクトの構築環境を作る。
ますは、「create environment」を選択し、
[![Image from Gyazo](https://i.gyazo.com/8665b950ab18a5c8bef7c9cddda71f29.png)](https://gyazo.com/8665b950ab18a5c8bef7c9cddda71f29)

名前と詳細を記入し、
[![Image from Gyazo](https://i.gyazo.com/ab5fc304c57a9179e81b517702841032.png)](https://gyazo.com/ab5fc304c57a9179e81b517702841032)

環境設定を行う。
「Instance type」は無料枠の場合、「t2.micro」を選択する。
[![Image from Gyazo](https://i.gyazo.com/3712f416a8ed13a6cefe903a48c9c296.png)](https://gyazo.com/3712f416a8ed13a6cefe903a48c9c296)

また、30分ごとに接続が切れるように設定しないと、気付いたら高額の請求がされる可能性があるので注意。
[![Image from Gyazo](https://i.gyazo.com/ee3e23a94e2cac5b1755b09b036fd60d.png)](https://gyazo.com/ee3e23a94e2cac5b1755b09b036fd60d)

##Laravelの環境設定
environmentの構築が終わると、下のような画面が表示されます。
一番したのwindowはtarminal windowであり、以下もコードをこのwindowに書き込んでいきます。
[![Image from Gyazo](https://i.gyazo.com/f7831467087b3eec5e3d1b747345ee1e.png)](https://gyazo.com/f7831467087b3eec5e3d1b747345ee1e)
＊コマンド入力の最中に質問されることがありますが、全て'y'と答えれば問題ないです。

###PHPセットアップ
まずはPHP７.２のセットアップ

```:ターミナル
$ sudo amazon-linux-extras install php7.2=stable
```

```:ターミナル
$ sudo yum install php-mbstring php-pecl-memcached php-gd php-apcu php-xml
```


### MaryaDBの設定 
MySQLの上位互換であるMaryaDBを構築


#### MaryaDBの設築
```:ターミナル
$ sudo amazon-linux-extras install -y lamp-mariadb10.2-php7.2
```

####MariaDBのインストール
```:ターミナル
$ sudo yum install -y mariadb-server
```

####MariaDBの起動と初期設定
```:ターミナル
$ sudo systemctl start mariadb
```

```:ターミナル
$ sudo mysql_secure_installation
```

上記のコマンド入力後に初期設定についていくつか質問されるので、以下のように答えます。
**新しいパスワードを入力する時、入力の文字は表示されないので注意**してください。
***
<初回設定の入力項目>
Set root password? [Y/n] y
New password: root
Re-enter new password: root
Remove anonymous users? [Y/n] y 
Disallow root login remotely? [Y/n] y
Remove test database and access to it? [Y/n] y
Reload privilege tables now? [Y/n] y
***

####MaridaDBの自動起動を有効化
MaridaDBが自動で起動するように設定。

```:ターミナル
$ sudo systemctl enable mariadb
```

```:ターミナル
$ sudo systemctl is-enabled mariadb
```

###Composerインストール
PHPのライブラリを管理してくれるComposerをインストールする。

```:ターミナル
$ curl -sS https://getcomposer.org/installer | php
```

```:ターミナル
$ sudo mv composer.phar /usr/bin/composer
```

```:ターミナル
$ composer
```

###Laravelインストール
composerを利用してcmsという作業ディレクトリを作成しLaravelをインストールします。

```:ターミナル
$ composer create-project laravel/laravel cms 6.* --prefer-dist
```

今後の作業はこのcmsディレクトリで行います。


###サーバーの起動とLaravelの起動

####BuiltInサーバーを起動：動作確認
サーバーを起動するために、先ほど作成した作業ディレクトリであるcmsに移動

```
$ cd cms
```

下記コマンドを入力しサーバーを起動します。

```
$ php artisan serve --port=8080
```

Cloud9の画面上部の緑の起動ボタンの左側PreviewボタンからPreview Running Applicationをクリック
[![Image from Gyazo](https://i.gyazo.com/a694ee4b869579c57d274ba806f7edf3.png)](https://gyazo.com/a694ee4b869579c57d274ba806f7edf3)

右下に画面が起動してLaravelの文字が確認できたら、Laravelのインストール成功
[![Image from Gyazo](https://i.gyazo.com/c57427e3bcfbd73011cc5c88e9aa443f.png)](https://gyazo.com/c57427e3bcfbd73011cc5c88e9aa443f)

新しくpop upしたwindowの右上にある「pop up into new window」を選択し、新規タブでLaravelのデモ画面を表示
[![Image from Gyazo](https://i.gyazo.com/622c59d81a1179b3eb86877d7fffe823.png)](https://gyazo.com/622c59d81a1179b3eb86877d7fffe823)

今後はこの画面で更新した内容を確認する
[![Image from Gyazo](https://i.gyazo.com/990b71762af4d459efa7952e6514b064.png)](https://gyazo.com/990b71762af4d459efa7952e6514b064)

サーバーを起動しているtarminalはそのままに、新しいtarminalを立ち上げておく。
[![Image from Gyazo](https://i.gyazo.com/0fa10184657773dc0984eae36ae55db6.png)](https://gyazo.com/0fa10184657773dc0984eae36ae55db6)

上記までの流れでLaravelの環境設定は完了です。

##データベースの作成
新しく立ち上げたターミナルで作業ディレクトリであるcmsに移動

```:ターミナル
$ cd cms
```

下記コマンドを入力していき、c9というデータベースを作成

```:ターミナル
$ mysql -u root -p
```

パスワードの「root」を入力

```:ターミナル
root [Enterキー]
```

c9というデータベースを作成

```:ターミナル
create database c9;
```

うまく作成されたか確認

```:ターミナル
show databases; 
```

下記のようにtarminalに表示されれば成功
[![Image from Gyazo](https://i.gyazo.com/7c050af79e9a9ac6e5e9f04c823db497.png)](https://gyazo.com/7c050af79e9a9ac6e5e9f04c823db497)
データベースから退場

```:ターミナル
exit;
```


##Laravelプロジェクト初期設定

###.envファイルの更新
[![Image from Gyazo](https://i.gyazo.com/b4a0950987c6f4fece9b1939ee0d1370.png)](https://gyazo.com/b4a0950987c6f4fece9b1939ee0d1370)
上記のように「Show Hidden Files」を設定し.envファイルが確認

.envファイルの下記該当部分を内容を下記のように更新し保存

```:.env
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=c9
DB_USERNAME=root
DB_PASSWORD=root
```


###Composerアップデートコマンド実行
更新内容を反映するために、サーバーを一度停止。
サーバーを動かしているtarminalで"control+c"を入力し、サーバーを停止。
その後下記を入力し、composerをupdate。

```:ターミナル
sudo composer update
```

途中で質問が出るが、yesで回答。
その後、再度サーバーを起動。

```PHP:ターミナル
$ php artisan serve --port=8080
```

###/app/Providers/ AppServiceProvider.php ファイルを修正

app→Providers→AppServiceProvider.phpと進み、下記のように記載。

```:AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema; //この行を追加
use Illuminate\Support\Facades\URL;    //この行を追加

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Schema::defaultStringLength(191);   //この行を追加
        URL::forceScheme('https');          //この行を追加
    }
}
```

##phpMyAdmin設定
###cms階層からpublicフォルダに移動
```
$ cd public
```

###phpMyAdminのzipをダウンロード
```
$ wget https://files.phpmyadmin.net/phpMyAdmin/4.8.3/phpMyAdmin-4.8.3-all-languages.zip
```

###zipファイルを解凍
```
$ unzip phpMyAdmin-4.8.3-all-languages.zip
```

###cms階層に戻る
```
$ cd ..
```

###phpMyAdminを確認する
publicフォルダ内に「phpMyAdmin-4.8.3-all-languages」フォルダが作成される。
フォルダ名が長いので「phpMyAdmin」に変更。
[![Image from Gyazo](https://i.gyazo.com/b127843da68fa4b57c5aced74a8033e9.png)](https://gyazo.com/b127843da68fa4b57c5aced74a8033e9)
「Preview」で開かれているURLの最後に「phpMyAdmin/index.php」をつけてEnterキーを押す。
＊　URL例： https://＊＊＊＊＊＊.cloud9.us-east-1.amazonaws.com/phpMyAdmin/index.php
[![Image from Gyazo](https://i.gyazo.com/618a79f6839fae0a0b0045e4393fec0d.png)](https://gyazo.com/618a79f6839fae0a0b0045e4393fec0d)
phpMyAdmin画面が表示されたら： ユーザー名・パスワードともに「root」を入力してログイン
[![Image from Gyazo](https://i.gyazo.com/5feba4ab49f4739b37deb679b7240585.png)](https://gyazo.com/5feba4ab49f4739b37deb679b7240585)
ログインできればOK

##お気に入りYouTube管理アプリを実装する
以下のコマンドは作業ディレクトリのcmsで行ってください。

###Laravel/uiの実装
今後のUIを整えるために必要なパッケージをインストールします。
インストールできると、ログイン認証画面が簡単に実装できます。

####1. マイグレーションを実行
php artisan migrate

####2. laravel/ui パッケージをインストール
composer require laravel/ui:^1.0 --dev

####3. artisan コマンドを実行
php artisan ui vue --auth

####4. npmパッケージをインストール
npm install

####5. パッケージをビルド
npm run dev

右上にログインが表示されれば、成功です。
[![Image from Gyazo](https://i.gyazo.com/d261dab361fcdb726fbc5c9b6504b929.png)](https://gyazo.com/d261dab361fcdb726fbc5c9b6504b929)

###データベースにMoviesテーブルを作成する為のマイグレーションを作成
####1. artisanコマンドでマイグレーション作成

```:ターミナル
$ php artisan make:migration create_movies_table --create=movies
```

####2. database/migrationsの直下のmovies_table.phpに以下追記

```PHP:movies_table.php
 public function up()
     {
         Schema::create('movies', function (Blueprint $table) {
             $table->bigIncrements('id');
             $table->string('item_name');
             $table->text('item_url');
             $table->timestamps();
         });
 }
```

####3. マイグレーション実行（テーブルの作成）

```:ターミナル
php artisan migrate
```

phpMyAdminでmoviesが作成されているか確認する
[![Image from Gyazo](https://i.gyazo.com/573fe6e9b338e5cd726478fae232df72.png)](https://gyazo.com/573fe6e9b338e5cd726478fae232df72)
カラムの修正・追加時は、tableのファイルを修正・保存後に、下記コマンドでrefreshする。

```:ターミナル
$ php artisan migrate:refresh
```

###モデルの作成/ルーティングとviewの設定

####1.artisanコマンドでモデル作成
作成したテーブルを適宜使用するためにモデルを作成します。
このモデルに対して働きかけることで、テーブルの管理を行います。
その際に、***複数形のテーブル名を単数形＋大文字スタートでモデル名を設定すれば、Laravel上で自動的にリンク***されます。

```:ターミナル
$ php artisan make:model Movie
```

####2./routes/web.phpの設定
web.phpはこのプロジェクトの設計図にあたる場所で、このプロジェクトで使用するURLを管理する場所です。
このファイルを見れば、このプロジェクトがどのように作成されているかを理解できる場所です。

まず、このweb.phpと先ほど作成したMovieモデルを接続します。
また、後ほど作成するコントローラーに機能を振り分けるための処理を記載します。

```PHP:web.php
<?php
use App\Movie;
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
// 動画管理の初期画面
Route::get('/', 'MoviesController@index');

// 新「動画」を追加
Route::post('/movies', 'MoviesController@store');

// 動画を削除
Route::delete('/movie/{movie}', 'MoviesController@destroy');

//「動画」を更新画面表示
Route::get('/moviesedit/{movie}','MoviesController@edit');

//「動画」を更新処理
Route::post('movies/update','MoviesController@update');

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');
Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');
```

####4. MoviesControllerの設定。
app->Http->Controllers->MoviesController.phpに下記を追記します。

```PHP:MoviesController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Movie;
use Validator;

class MoviesController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
         $movies = Movie::orderBy('created_at', 'asc')->paginate(3);
         
    return view('movies', [
            'movies' => $movies
        ]);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //バリデーション
    $validator = Validator::make($request->all(), [
        'item_name' => 'required|max:255',
    ]);
    //バリデーション:エラー 
        if ($validator->fails()) {
            return redirect('/')
                ->withInput()
                ->withErrors($validator);
        }
        //以下に登録処理を記述（Eloquentモデル）
        // Eloquent モデル
        $movies = new Movie;
        $movies->item_name = $request->item_name;
        $movies->item_url = $request->item_url;
        $movies->save(); 
        return redirect('/');
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit(Movie $movie)
    {
        return view('moviesedit', ['movie' => $movie]);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request)
    {
        //バリデーション
        $validator = Validator::make($request->all(), [
            'item_name' => 'required|max:255',
        ]);
        //バリデーション:エラー 
        if ($validator->fails()) {
            return redirect('/')
                ->withInput()
                ->withErrors($validator);
        }
        // Eloquent モデル
        $movies = Movie::find($request->id);
        $movies->item_name = $request->item_name;
        $movies->item_url = $request->item_url;
        $movies->save(); 
        return redirect('/');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy(Movie $movie)
    {
        $movie->delete();       //追加
        return redirect('/');  //追加
    }
}
```

####5. resources/views/の直下にmovies.blade.php を作成して以下を追記
動画を登録するためのUIとなるページを作成します。
UI部分はこのviewsファイルに格納します。

```PHP:movies.blade.php
 <!-- resources/views/movies.blade.php -->
 @extends('layouts.app')
 @section('content')
     <!-- Bootstrapの定形コード… -->
     <div class="card-body">
         <div class="card-title">
             ＜お気に入り動画入力フォーム＞
         </div>
         <!-- バリデーションエラーの表示に使用-->
     	@include('common.errors')
         <!-- バリデーションエラーの表示に使用-->
         <!-- 動画登録フォーム -->
         <form action="{{ url('movies') }}" method="POST" class="form-horizontal">
             {{ csrf_field() }}
             <!-- 動画のタイトル -->
             <div class="form-group">
               <div class="card-title">
                  ＜動画タイトル＞
              </div>
                 <div class="col-sm-6">
                     <input type="text" name="item_name" class="form-control">
                 </div>
             </div>
             <!-- 動画のURL -->
             <div class="form-group">
               <div class="card-title">
                  ＜動画URL＞
               </div>
                 <div class="col-sm-6">
                     <input type="text" name="item_url" class="form-control">
                 </div>
             </div>
             <!-- 動画 登録ボタン -->
             <div class="form-group">
                 <div class="col-sm-offset-3 col-sm-6">
                     <button type="submit" class="btn btn-primary">
                         Save
                     </button>
                 </div>
             </div>
         </form>
          <!-- 現在の動画 -->
     @if (count($movies) > 0)
         <div class="card-body">
             <div class="card-body">
                 <table class="table table-striped task-table">
                     <!-- テーブルヘッダ -->
                     <thead>
                         <th>動画タイトル</th>
                         <th>動画URL</th>
                         <th>&nbsp;</th>
                         <th>&nbsp;</th>
                         <th>&nbsp;</th>
                     </thead>
                     <!-- テーブル動画体 -->
                     <tbody>
                         @foreach ($movies as $movie)
                             <tr>
                                 <!-- 動画タイトル -->
                                 <td class="table-text">
                                     <div>{{ $movie->item_name }}</div>
                                 </td>
                                 <!-- 動画URL -->
                                 <td class="table-text">
                                     <div>{{ $movie->item_url }}</div>
                                 </td>
                                 <td class="table-text">
                                     <button type=“submit” class="btn btn-success" onclick="location.href='{{$movie->item_url}}'">動画へ</button>
                                 </td>
 			                   <!-- 動画: 削除ボタン -->
                                 <td>
                                    <form action="{{ url('moviesedit/'.$movie->id) }}" method="GET"> 
                                        {{ csrf_field() }}
                                	    <button type="submit" class="btn btn-primary">更新 </button>
                                	</form>                   
                                 </td>
                                 <td>
                                    <form action="{{ url('movie/'.$movie->id) }}" method="POST">
                                         <form action="{{ url('movie/'.$movie->id) }}" method="POST">
                                             {{ csrf_field() }}
                                             {{ method_field('DELETE') }}
                                             <button type="submit" class="btn btn-danger">
                                                 削除
                                             </button>
                                     </form>    
                                </td>
                             </tr>
                         @endforeach
                     </tbody>
                 </table>
             </div>
         </div>		
    @endif
     <!-- Movie: 既に登録されてる動画のリスト -->
    <div class="row">
    	<div class="col-md-4 offset-md-4">
    		{{ $movies->links()}}
        </div>
    </div>
 @endsection
```

####6. resources/views/の直下にmoviesedit.blade.php を作成して以下を追記
動画の内容を変更し登録するためのUIとなるページを作成します。

```PHP:moviesedit.blade.php
@extends('layouts.app')
@section('content')
<div class="row">
    <div class="col-md-12">
    @include('common.errors')
        <form action="{{ url('movies/update') }}" method="POST">
            <!-- item_name -->
            <div class="form-group">
                <label for="item_name">Title</label>
                <input type="text" name="item_name" class="form-control" value="{{$movie->item_name}}">
            </div>
            <!--/ item_name -->
            <!-- Save ボタン/Back ボタン -->
            <div class="well well-sm">
                <button type="submit" class="btn btn-primary">Save</button>
                <a class="btn btn-link pull-right" href="{{ url('/') }}"> Back</a>
            </div>
            <!--/ Save ボタン/Back ボタン -->
            <!-- id 値を送信 -->
            <input type="hidden" name="id" value="{{$movie->id}}" /> <!--/ id 値を送信 -->
            <!-- CSRF -->
            {{ csrf_field() }}
            <!--/ CSRF -->
        </form>
    </div>
</div>
@endsection
```

####7. /resources/views/common/errors.blade.phpを作成し以下を追記
誤った入力が行われた場合の処理を設定するために、commonフォルダを作成し、errors.blade.phpファイルを作成します。

```PHP:errors.blade.php
@if (count($errors) > 0)
     <!-- Form Error List -->
     <div class="alert alert-danger">
         <div><strong>入力した文字を修正してください。</strong></div> 
         <div>
             <ul>
             @foreach ($errors->all() as $error)
                 <li>{{ $error }}</li>
             @endforeach
             </ul>
         </div>
     </div>
@endif
```

###データベース管理の設定について
データベースは、
① 登録
② 表示
③ 更新
④ 削除
の4つ機能で全て表現することができます。
そしてこれらの処理については、MoviesController.phpに記載されています。

####登録について
web.phpの中も「動画の追加部分」に'MoviesController@store'と記載することで、登録処理についてはMoviesController.php内のstore部分で行うように振り分けることができます。

```PHP:MoviesController.php
 public function store(Request $request)
    {
        //バリデーション
    $validator = Validator::make($request->all(), [
        'item_name' => 'required|max:255',
    ]);
    //バリデーション:エラー 
        if ($validator->fails()) {
            return redirect('/')
                ->withInput()
                ->withErrors($validator);
        }
        //以下に登録処理を記述
        $movies = new Movie;
        $movies->item_name = $request->item_name;
        $movies->item_url = 'aaaa';
        $movies->item_details = 'details';
        $movies->published = '2017-03-07 00:00:00';
        $movies->save(); 
        return redirect('/');
    }
```

####表示について
web.phpの中も「動画管理の初期画面」に'MoviesController@index'と記載することで、表示についてはMoviesController.php内のindex部分で行うように振り分けることができます。

```PHP:MoviesController.php
 public function index()
    {
         $movies = Movie::orderBy('created_at', 'asc')->paginate(3);
         
    return view('movies', [
            'movies' => $movies
        ]);
    }
```

これにより、Movieモデルを介して、moviesテーブル内のデータが降順に$moviesに格納され、viewファイル内のmovies.blade.phpにデータが渡されます。


####更新について
web.phpの中も「更新画面表示」にからMoviesedit.phpに遷移し、中の処理は'MoviesController@edit'と記載することで、MoviesController.php内のedit部分で行うように振り分けることができます。

```PHP:MoviesController.php
 public function index()
    {
         $movies = Movie::orderBy('created_at', 'asc')->paginate(3);
         
    return view('movies', [
            'movies' => $movies
        ]);
    }
```

その後、updateにて更新された内容が反映されます。

####削除処理について
web.phpの中の「削除」部分にあるdeleteの中に、選択された項目のidを持った変数が選択され、それが削除されるという処理をMoviesController.php内のdestoryに振り分けるという記述を行いっています。

```PHP:MoviesController.php
 public function destroy(Movie $movie)
    {
        $movie->delete();       //追加
        return redirect('/');  //追加
    }
```

長々と書きましたが、これで下記のようなものが実装完成です。
[![Image from Gyazo](https://i.gyazo.com/15604c6f24ef1be3859183a90942a748.png)](https://gyazo.com/15604c6f24ef1be3859183a90942a748)