Laravel8でwebアプリつくってmixhostにデプロイしたメモ

見た映画をメモするwebアプリが欲しいので作ろうと思い、作ったときの自分用の細かいメモ。(全ソースはないです)
この間作ったhomestead環境で作ってmixhostにデプロイして動作確認だけしました。

【Vagrant】Windows10でLaravel環境構築(homestead利用)
かんたんにlaravel環境が構築できるらしいhomesteadのことをさっき知ったのでやってみようと思いました。 homesteadはVirtualBoxのboxファイル(仮想マシンイメージ)として提供されています。 公式の資...

プロジェクト作る

sudo composer create-project laravel/laravel --prefer-dist plot

権限変更

sudo chown -R vagrant:vagrant /var/www/plot

データベース作成

mysql -u homestead -p
create database plot_db;

.env編集

該当箇所だけ下記に編集

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=plot_db
DB_USERNAME=homestead
DB_PASSWORD=secret

認証機能つける

下記によるとLaravel8は認証機能周りが7変わったらしいので、記事を参考に認証機能をインストール

標準のユーザー認証機能の利用 Jetstreamの利用 #Laravel頻出パターン #Laravelの教科書|Laravelの教科書|note
Laravel8が2020年9月8日に公開されました。 新規にcomposer create-projectを作成するとLaravel8でプロジェクトが作成されます。大きな変更点はいくつかありますが、ログイン周りがいくつかリニューアルが発生したため、Laravel8での認証方法について説明を行います。 Larav...
cd /var/www/plot/
composer require laravel/jetstream
php artisan jetstream:install livewire
php artisan migrate
npm install && npm run dev

簡易サーバー起動

cd /var/www/plot/
php -S 0.0.0.0:8080 -t public

表示を確認

vagrantで作った環境なのでポート指定して表示を確認。
http://192.168.33.10:8080

ウェルカムページの右上にログインと登録機能がついてるのが確認できた。

ルーティング

routes/web.php
listとviewへのルートを追加する

Route::get('/list', function () {
    return view('list');
});
Route::get('/edit/{id}', function ($id) {
    return view('edit');
});

viewの作成

viewのlist、editに対応したファイルつくる。(内容は省略、ログイン時だけ表示されるように設定した)
resources/views/edit.blade.php
resources/views/list.blade.php

publicにファイル配置

{{asset()}}はpublicフォルダ下を見に行くので、使うCSSやjsをpublicフォルダ下に配置する。

テーブル作成

複数形にしないといけないらしいので末尾にsつけた「memos」テーブルを作る

php artisan make:migration create_memos_table

マイグレーションファイル編集

memo/database/migrations/2020_10_02_021545_create_memos_table.php
up関数だけ下記に変更する。up関数に入れたものがmigrateしたときにtableのカラムになる。

    public function up()
    {
        Schema::create('memos', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->date('register_date');
            $table->string('note');
            $table->text('memo');
            $table->string('links');
            $table->timestamps();
        });
    }

Laravel5.5ではstringを挿入する際に設定しないとエラーになるらしい、一応つけておく
Providers/AppServiceProvider.php

use Illuminate\Support\Facades\Schema;

    public function boot()
    {
        //
        Schema::defaultStringLength(191);
    }

マイグレーション実行

php artisan migrate

「plot_db」データベースに「memos」テーブルができているか確認
descのtypeによると長さが191となっているので設定できているようだ。

mysql> use plot_db
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+------------------------+
| Tables_in_plot_db      |
+------------------------+
| failed_jobs            |
| memos                  |
| migrations             |
| password_resets        |
| personal_access_tokens |
| sessions               |
| users                  |
+------------------------+
7 rows in set (0.00 sec)

mysql> desc memos;
+---------------+--------------+------+-----+---------+----------------+
| Field         | Type         | Null | Key | Default | Extra          |
+---------------+--------------+------+-----+---------+----------------+
| id            | int unsigned | NO   | PRI | NULL    | auto_increment |
| title         | varchar(191) | NO   |     | NULL    |                |
| register_date | date         | NO   |     | NULL    |                |
| note          | varchar(191) | NO   |     | NULL    |                |
| memo          | text         | NO   |     | NULL    |                |
| links         | varchar(191) | NO   |     | NULL    |                |
| created_at    | timestamp    | YES  |     | NULL    |                |
| updated_at    | timestamp    | YES  |     | NULL    |                |
+---------------+--------------+------+-----+---------+----------------+
8 rows in set (0.01 sec)

モデル作成

テーブル「memos」に対して、「Memo」とする。

php artisan make:model Memo

Memo.phpができるらしいので確認

memo/app/Models/Memo.php

コントローラー作成

cd /var/www/memo

php artisan make:controller MemoController

本格的にルーティングを書く

routes/web.php
Laravel8からはnamespace「App\Http\Controllers\」を記載しないとMemoControllerを参照してくれないようだ。
「App\Http\Controllers\MemoController@save」と書くことで「MemoController」の「save」関数を参照してくれる
記載しない場合、「Target class [MemoController] does not exist.」というエラーが出る。

Route::get('/', function () {
    return view('welcome');
});
Route::get('/list',"App\Http\Controllers\MemoController@list")->name('list');
Route::get('/add', "App\Http\Controllers\MemoController@add")->name('add');
Route::get('/edit/{id}', "App\Http\Controllers\MemoController@edit")->name('edit');
Route::post('/api/single', "App\Http\Controllers\MemoController@single")->name('single');
Route::post('/api/save', "App\Http\Controllers\MemoController@save")->name('save');
Route::post('/api/delete', "App\Http\Controllers\MemoController@delete")->name('delete');
Route::post('/api/multi',  "App\Http\Controllers\MemoController@multi")->name('multi');

Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () {
    return view('dashboard')->withHeaders([
        'Cache-Control' => 'no-store',
    ]);;
})->name('dashboard');

Controllerを書く

各CLUD処理を書いていく。
(バリデート、エスケープはあまり調べず作ったのでそのまま使用しないほうがいい)
plot/app/Http/Controllers/MemoController.php

<?php

namespace App\Http\Controllers;

use App\Models\Memo;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class MemoController extends Controller
{
    /**
     * 一覧ページの表示
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function list(Request $request){
        return view('list')->withHeaders([
            'Cache-Control' => 'no-store',
        ]);
    }

    /**
     * 削除ページの表示
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function edit(Request $request) {
        return view('edit',["id"=>$request->id])->withHeaders([
            'Cache-Control' => 'no-store',
        ]);
    }

    /**
     * 新規作成ページの表示
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function add(Request $request) {
        return view('edit',["id"=> -1]);
    }
    /**
     * 保存時API
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function save(Request $request){
        if(empty($request->title)
            || empty($request->register_date)
            || empty($request->note)
            || empty($request->memo)
            || empty($request->links)
        ){
            return response()->json(['status'=> "E001",'message'=> "未入力の項目があります"]);
        }
        $rules = [
            'title' => ['required', 'string','max:191'],
            'register_date' => ['required'],
            'note' => ['required', 'string','max:191'],
            'memo' => ['required', 'string'],
            'links' => ['required', 'string'],
        ];
        $this->validate($request, $rules);

        if(empty($request->id) || $request->id == "-1"){
            // 追加時
            $memo = new Memo;
            $memo->title = $request->title;
            $memo->register_date = $request->register_date;
            $memo->note = $request->note;
            $memo->memo = $request->memo;
            $memo->links = $request->links;
            $memo->save();
            return response()->json($memo);
        }else{
            // 更新時
            $memo = Memo::find($request->id);
            $memo->title = $request->title;
            $memo->register_date = $request->register_date;
            $memo->note = $request->note;
            $memo->memo = $request->memo;
            $memo->links = $request->links;
            $memo->update();
            return response()->json($memo);
        }
    }

    /**
     * 削除用API
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function delete(Request $request){
        if(empty($request->id)
        ){
            return response()->json(['status'=> "E004",'message'=> "idがありません",]);
        }
        // 削除時
        $res = Memo::destroy($request->id);
        return response()->json($res);
    }

    /**
     * リスト取得用API
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function multi(Request $request){
        return response()->json(Memo::all(["id","title","register_date","note"]));
    }

    /**
     * 単一データ取得用API
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function single(Request $request){
        if(empty($request->id)){
            return response()->json(['status'=> "E002",'message'=> "idが一致しません",]);
        }elseif ($request->id == -1){
            return response()->json([]);
        }

        $memo = Memo::find($request->id);
        if(empty($memo->id)){
            return response()->json(['status'=> "E003",'message'=> 'データの取得に失敗しました',]);
        }
        return response()->json($memo);
    }
}

apiにアクセスするjs書く

jsでルーティングしたURLにアクセスする際にはCSRF対策がいるのでhtmlヘッダとajaxヘッダに追加する。

<meta name="csrf-token" content="{{ csrf_token() }}">

js本体は下記のような感じで書いてた。

        jQuery(function($){
            $.ajaxSetup({
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
            });

            $.ajax({
                type: 'POST',
                url: "/api/single",
                dataType:'json',
                data:  {"id":{{ $id }}}
            }).done(function(response) {
                let id = {{ $id }};

                // idが-1の場合は新規とみなす
                if(id === -1){
                    $("input[name='btn-delete']").hide();
                    return;
                }

                // アイテム取得
                if(response.status){
                    alert(response.status + ":" + response.message);
                }else {
                    $("input[name='title']").val(response.title);
                    $("input[name='register_date']").val(response.register_date);
                    $("input[name='note']").val(response.note);
                    $("textarea[name='memo']").val(response.memo);
                    $("textarea[name='links']").val(response.links);

                    let links = response.links.split("\n");
                    let html = "<ul>";
                    for (let i = 0; i < links.length; i++) {
                        if (links[i].indexOf("http") !== 0) continue;

                        html += "<li><a href='" + links[i] + "' target='_blank'>" + links[i] + "</a></li>";
                    }
                    $("#links-view").val(html);
                }
            }).fail(function(xhr) {
                console.log(xhr);
                alert("通信に失敗しました")
            }).always(function(xhr, msg) {
                //通信完了時の処理
            });
        });

mixhostへのデプロイ

今回はサブドメインに配置するのでサブドメインを作る。
「aaa.example.com」のような感じのサブドメイン。

cPanelからlaravelがインストールできるので、サブドメインのルートにlaravelをインストールする。

mysql(MariaDB)にデータベースとユーザーを追加する。
.env.sampleをコピーして、名前を変更し.envをつくる。上でDBに追加したデータベース名、ユーザー名、パスワードを転記する。

サブドメイン(aaa.example.com)の管理ページでルートをpublicにする。

サーバーにファイルをコピーする。(上で出てきたファイルを主にコピーする)
必要なライブラリなどはcomposerでインストール。
ターミナルから下記を実行してtableもつくる。

composer install
php artisan migrate

うまく動かないときはキャッシュをクリアをしてみる。
ブラウザのキャッシュをクリアしたり、プライベートモードで開いてみたりする。

php artisan config:clear

ドメインの管理会社のページへ行き、DNSにサブドメインとサーバーのIPをAで追加する。
「000.000.000.000」がサーバーのIPアドレスの場合は下記のような感じ。

aaa A 000.000.000.000

これを行うとサブドメインで、laravelアクセスできるようになる。

おわり。