前提
- AWS EC2
- Amazon Linux2
- Apache
- mysql 5.7
- composer
- Laravel 7.15
参考にさせていただいた記事
[Laravel5.4でシンプルなCMSを作るチュートリアル](https://www.webopixel.net/php/1259.html)
こちらのほぼコピペとなりますが、
今回は Laravel のバージョンがことなり、若干手順が違ったので自分用のメモとしてまとめてみました。
インストール
Laravel をインストール(プロジェクトを作成)します。
※スペックにもよりますが、ちょこっと時間かかります。
$ composer create-project --prefer-dist laravel/laravel SimpleCMS
権限設定
Laravel プロジェクトの権限設定
$ cd SimpleCMS
$ chmod -R 777 ./bootstrap/cache
$ chmod -R 777 ./storage
Apache の設定
設定ファイルの作成
$ sudo vi /etc/httpd/conf.d/SimpleCMS.conf
NameVirtualHost *:80
<VirtualHost *:80>
    #ドキュメントルート
    DocumentRoot /home/ec2-user/SimpleCMS/public
    #グローバルIPアドレス
    ServerName xxx.xxx.xxx.xxx
    #/home/ec2-user/SimpleCMS/publicの設定
    <Directory "/home/ec2-user/SimpleCMS/public">
        #.htaccessを利用可能にする
        AllowOverride All
        #アクセス許可
        Require all granted
    </Directory>
</VirtualHost>
$ sudo systemctl restrat httpd
ここまでの確認 インストール ~ Apache の設定
ブラウザでグローバルアドレスを入力してインストール~設定までが上手くいっているか確認します。
こんな感じの画面が表示されれば OK
Laravel プロジェクトの初期設定
config の app.php は言語の設定だけ変更。
    /*
    |--------------------------------------------------------------------------
    | Application Timezone
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default timezone for your application, which
    | will be used by the PHP date and date-time functions. We have gone
    | ahead and set this to a sensible default for you out of the box.
    |
    */
    // 'timezone' => 'UTC',
    'timezone' => 'Asia/Tokyo',
    /*
    |--------------------------------------------------------------------------
    | Application Locale Configuration
    |--------------------------------------------------------------------------
    |
    | The application locale determines the default locale that will be used
    | by the translation service provider. You are free to set this value
    | to any of the locales which will be supported by the application.
    |
    */
    // 'locale' => 'en',
    'locale' => 'ja',
DB 周りの設定を設定しておく
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=SimpleCmsDB
DB_USERNAME=cmsuser
DB_PASSWORD=cms1234
MySQL に DB を立ち上げる
さっき設定した「.env」に基づいて次のコマンドを実行
- DB の立ち上げ
- ユーザーの追加
mysql> create database SimpleCmsDB;
mysql> grant all on SimpleCmsDB.* to 'cmsuser'@localhost identified by 'cms1234';
登録状況の確認
mysql> select Host, User from mysql.user;
+-----------+---------------+
| Host      | User          |
+-----------+---------------+
| localhost | cmsuser       |
+-----------+---------------+
ログインできるかも確認しておきましょう。
ユーザー認証関係
laravel/ui をインストール
$ composer require laravel/ui
Login 機能の実装&テーブル作成
$ php artisan ui vue --auth
$ php artisan migrate
ブラウザから確認してみましょう。
右上にログインっぽい画面が増えてるはずです。
DB の状態が気になるなら mysql にログインして確認してみよう。
mysql > USE SimpleCmsDB;
mysql > SHOW TABLES;
Post モデルの作成
「make:model」でモデルを作成。
「-m」オプションでマイグレーションファイルも一緒に作成してくれます。
$ php artisan make:model Post -m
作成したマイグレーションファイルに title,body を追記します。
XXXX には作成日のタイムスタンプが自動的に入ります。
public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        $table->text('body');
        $table->timestamps();
    });
}
「migrate」で posts テーブルを作成します。
マイグレート。上で設定したテーブルを作ってくれます。
$ php artisan migrate
状態が気になるなら mysql にログインして確認してみよう。
mysql> USE SimpleCmsDB;
mysql> DESCRIBE posts
Post モデルに guarded を設定します。
「$guarded」 とは ブラックリスト、複数代入時に代入を許可しない属性のことらしい、配列で設定する。
class Post extends Model
{
    protected $guarded = ['id', 'created_at'];
}
ダミーデータの作成
今回は二つの方法を紹介
どちらにせよ「シーダー(Seeder)」ってのを使ってダミーデータを作成します。
どっちでやってもいいです。
シーダー(Seeder)に直接データを書く方法
まずシーダーを作成
$ php artisan make:seeder PostsTableSeeder
作成した「PostsTableSeeder」に直接書いていきます。
たぶん 10 件くらいあればいいでしょう…。
public function run()
{
    DB::table('posts')->insert([
        [
            'title' => '1番目の記事',
            'body' => '1番目の記事のテキストです。',
            'created_at' => '2017-05-02 14:28:19',
            'updated_at' => '2017-05-02 14:28:19'
        ],[
            'title' => '2番目の記事',
            'body' => '2番目の記事のテキストです。',
            'created_at' => '2017-05-03 14:28:19',
            'updated_at' => '2017-05-03 14:28:19'
        ]
        // ...
    ]);
}
データを書いたら「database/seeds/DatabaseSeeder.php」に登録して実行します。
public function run()
{
    $this->call(PostsTableSeeder::class);
}
db:seed コマンドでデータベースに投入します。
$ php artisan db:seed --class=PostsTableSeeder
確認してみるとこんな感じ。
mysql> Select * From posts;
+----+-------------------+-------------------------------------------+---------------------+---------------------+
| id | title             | body                                      | created_at          | updated_at          |
+----+-------------------+-------------------------------------------+---------------------+---------------------+
|  1 | 最初の記事         | 最初の記事のテキストです。                  | 2017-05-02 14:28:19 | 2017-05-02 14:28:19 |
|  2 | 2番目の記事        | 2番目の記事のテキストです。                 | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
|  3 | 3番目の記事        | 3番目の記事のテキストです。                 | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
|  4 | 4番目の記事        | 4番目の記事のテキストです。                 | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
|  5 | 5番目の記事        | 5番目の記事のテキストです。                 | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
|  6 | 6番目の記事        | 6番目の記事のテキストです。                 | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
|  7 | 7番目の記事        | 7番目の記事のテキストです。                 | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
|  8 | 8番目の記事        | 8番目の記事のテキストです。                 | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
|  9 | 9番目の記事        | 9番目の記事のテキストです。                 | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
| 10 | 10番目の記事       | 10番目の記事のテキストです。                | 2017-05-03 14:28:19 | 2017-05-03 14:28:19 |
+----+-------------------+-------------------------------------------+---------------------+---------------------+
モデルファクトリ(ModelFactory)で自動的に作成する。
まずファクトリを作ります。
$ php artisan make:factory PostFactory --model=Post
作成したファクトリにどんなテストデータがいいか書き込んでいきましょう。
$factory->define(Post::class, function (Faker $faker) {
    return [
        'title' => $faker->sentence(rand(1, 2)),
        'body' => $faker->realText(128),
        'created_at' => $faker->dateTimeBetween('1day', '1year')->format('Y-m-d H:i'),
        'updated_at' => $faker->dateTimeBetween('1day', '1year')->format('Y-m-d H:i'),
    ];
});
ファクトリができたら今度はシーダーにダミーデータを何件作成するか書いていきます。
public function run()
{
    factory(App\Post::class, 10)->create();
}
DatabaseSeeder に実行登録(既にやってたら不要)。
public function run()
{
    $this->call(PostsTableSeeder::class);
}
db:seed コマンドでデータベースに投入します。
$ php artisan db:seed --class=PostsTableSeeder
テーブルをリフレッシュ(初期化)して投入したい場合はこちら。
php artisan migrate:fresh --seed
管理画面用 Posts コントローラーの作成
管理画面用のコントローラーを作成します。
「make:controller」コマンドでコントローラーの雛形を作成。
「SimpleCMS/app/Http/Controllers/」に「Admin/PostsController.php」が作成されます。
namespace App\Http\Controllers\Admin;
 
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Post;
 
class PostsController extends Controller
{
    // バリデーションのルール
    public $validateRules = [
        'title' => 'required',
        'body' => 'max:500'
    ];
    public function index()
    {
        $posts = Post::orderBy('id', 'desc')->paginate(20);
        return view('admin.posts.index', compact('posts'));
    }
    public function create()
    {
        return view('admin.posts.create');
    }
    public function store(Request $request)
    {
        $this->validate($request, $this->validateRules);
        Post::create($request->all());
        \Session::flash('flash_message', '記事を作成しました。');
        return redirect('admin/posts');
    }
    public function show($id)
    {
        $post = Post::findOrFail($id);
        return view('admin.posts.show', compact('post'));
    }
    public function edit($id)
    {
        $post = Post::findOrFail($id);
        return view('admin.posts.edit', compact('post'));
    }
    public function update(Request $request, $id)
    {
        $this->validate($request, $this->validateRules);
 
        $post = Post::findOrFail($id);
        $post->update($request->all());
 
        \Session::flash('flash_message', '記事を更新しました。');
        return redirect('admin/posts');
    }
    public function destroy($id)
    {
        $post = Post::findOrFail($id);
        $post->delete($id);
        \Session::flash('flash_message', '記事を削除しました。');
        return redirect('admin/posts');
    }
}
ビューの作成(準備)
LaravelCollective Form ファサードをインストール
これ何?って思ったらこちらを見てください。
なんとなくイメージ湧くし、今後も役に立ちます。
LaravelCollective Form ファサード チートシート
$ composer require laravelcollective/html
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
|--------------------------------------------------------------------------
...
*/
'providers' => [
    ...
    // 末尾に追加
    Collective\Html\HtmlServiceProvider::class,
],
/*
|--------------------------------------------------------------------------
| Class Aliases
|--------------------------------------------------------------------------
...
*/
'aliases' => [
    ...
    // 末尾に追加
    'Form' => Collective\Html\FormFacade::class,
    'Html' => Collective\Html\HtmlFacade::class,
],
「管理画面用 Posts コントローラーの作成」で作成したコントローラーの中には、バリデーション設定も記述されていたので日本語で表示できるようにしてみましょう。
「resources/lang/ja/」に「validation.php」というファイルを作成し、下記のソースをコピペします。
※もちろんダウンロードして格納しても OK
attributes の日本語も設定します。
'attributes' => [
    'title' => 'タイトル',
    'body' => '内容'
],
管理画面用 Posts ビューの作成
管理画面表示部分のビューを作成です。
最初にレイアウトファイルとして
「resources/views/layouts/」の「app.blade.php」をコピーして
「resources/views/layouts/admin.blade.php」を作成します。
一覧ページの作成
次に「resources/views/admin/posts/」フォルダを作成し「index.blade.php」を作成します。
@extends('layouts.admin')
@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <div class="panel panel-default">
                <div class="panel-heading">記事一覧</div>
                <div class="panel-body">
                    @if (Session::has('flash_message'))
                        <div class="alert alert-success">{{ Session::get('flash_message') }}</div>
                    @endif
                    <div class="mb10">
                        {!! link_to('admin/posts/create', '新規作成', ['class' => 'btn btn-primary']) !!}
                    </div>
                    <table class="table table-striped table-bordered table-hover">
                        <thead>
                        <tr>
                            <th>ID</th>
                            <th>タイトル</th>
                            <th>作成日</th>
                            <th>編集</th>
                        </tr>
                        </thead>
                        @foreach($posts as $post)
                            <tr>
                                <td>{{ $post->id }}</td>
                                <td>{{ $post->title }}</td>
                                <td>{{ $post->created_at->format('Y年m月d日') }}</td>
                                <td>
                                    {!! link_to_action('Admin\PostsController@show', '表示', [$post->id]) !!}
                                    {!! link_to_action('Admin\PostsController@edit', '編集', [$post->id]) !!}
                                    {!! Form::model($post,
                                    ['url' => [
                                        'admin/posts', $post->id],
                                        'method' => 'delete',
                                        'class' => 'delete-from'
                                    ]) !!}
                                        {!! Form::submit('削除', [
                                            'onclick' => "return confirm('本当に削除しますか?')",
                                            'class' => 'text-link'
                                            ]) !!}
                                    {!! Form::close() !!}
                                </td>
                            </tr>
                        @endforeach
                    </table>
                    {!! $posts->render() !!}
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
詳細ページの作成
同じく「resources/views/admin/posts/」に「show.blade.php」を作成します。
@extends('layouts.admin')
@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <div class="panel panel-default">
                <div class="panel-heading">記事詳細</div>
                <div class="panel-body">
                    <div>
                        <h1>{{ $post->title }}</h1>
                        <p>{{ $post->body }}</p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
記事入力フォームの共通部作成
入力フォームは新規と編集ページ共通なので、それぞれの View から独立させて作成します
「resources/views/admin/posts/」に「fields.blade.php」を作成します。
<div class="form-group">
    {!! Form::label('title', 'タイトル:', ['class' => 'col-sm-2 control-label']) !!}
    <div class="col-sm-10">
        {!! Form::text('title', null, ['class' => 'form-control']) !!}
    </div>
</div>
<div class="form-group">
    {!! Form::label('body', '内容:', ['class' => 'col-sm-2 control-label']) !!}
    <div class="col-sm-10">
        {!! Form::textarea('body', null, ['class' => 'form-control']) !!}
    </div>
</div>
<div class="form-group">
    <div class="col-sm-10 col-sm-offset-2">
        {!! Form::submit('保存', ['class' => 'btn btn-primary']) !!}
        {!! link_to('admin/posts', '一覧へ戻る', ['class' => 'btn btn-default']) !!}
    </div>
</div>
投稿フォームの作成
同じく「resources/views/admin/posts/」に「create.blade.php」を作成します。
25 行目あたりに先ほどの共通部をインクルードしています。
@extends('layouts.admin')
@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <div class="panel panel-default">
                <div class="panel-heading">新規作成</div>
                <div class="panel-body">
                    @if ($errors->any())
                        <div class="alert alert-danger">
                            <ul>
                                @foreach ($errors->all() as $error)
                                    <li>{{ $error }}</li>
                                @endforeach
                            </ul>
                        </div>
                    @endif
                    {!! Form::open(['url' => 'admin/posts',
                                'class' => 'form-horizontal',
                                'id' => 'post-input']) !!}
                    @include('admin.posts.fields')
                    {!! Form::close() !!}
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
編集フォームの作成
同じく「resources/views/admin/posts/」に「create.blade.php」を作成します。
投稿フォームと内容はほぼ変わりません。
@extends('layouts.admin')
@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <div class="panel panel-default">
                <div class="panel-heading">編集</div>
                <div class="panel-body">
                    @if ($errors->any())
                        <div class="alert alert-danger">
                            <ul>
                                @foreach ($errors->all() as $error)
                                    <li>{{ $error }}</li>
                                @endforeach
                            </ul>
                        </div>
                    @endif
                    {!! Form::model($post,
                    ['url' => [
                        'admin/posts', $post->id],
                        'method' => 'PATCH',
                        'class' => 'form-horizontal',
                        'id' => 'post-input'
                    ]) !!}
                    @include('admin.posts.fields')
                    {!! Form::close() !!}
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
ルーターの作成
ルーターの設定をしてアクセスできるようにします。
管理画面なので admin プレフィクスを付けます。
middleware に auth を指定してすべて認証が必要なようにします。
※ルーターって?:URL とコントローラーのアクションを対応付けするファイル
「./routes/web.php」を修正します。
Route::group(['prefix' => 'admin', 'middleware' => 'auth'], function () {
    Route::resource('posts', 'Admin\PostsController');
});
「/admin/posts」 にアクセスして動作を確認してみてください。
作成・編集など一通りの動作ができるようになったので管理画面の方は完了です。
フロント側の作成
フロントに表示されるページを作成します。
編集機能はいらないので、一覧・詳細ページを作成します。
「./app/Http/Controllers/PostsController.php」を作成しましょう。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Post;
class PostsController extends Controller
{
    public function index()
    {
        $posts = Post::orderBy('id', 'desc')->paginate(20);
        return view('posts.index', compact('posts'));
    }
    public function show($id)
    {
        $post = Post::findOrFail($id);
        return view('posts.show', compact('post'));
    }
}
ルーターにも追記します。
Route::resource('posts', 'PostsController', ['only' => [
    'index', 'show'
]]);
ビューもコントローラーと同じように
「/resources/views/posts/index.blade.php」と
「/resources/views/posts/show.blade.php」を作成します。
※Admin のものをコピペして改修してみてください
ブートストラップスキャフォールディングが正常にインストールされました。
「npm install && npm run dev」を実行して、新しい足場をコンパイルしてください。


