20200409のlaravelに関する記事は8件です。

【感想】【Laravel x Vue.js】SPAクイズアプリケーションを作ってみよう!を終えて

はじめに

今回、私が学習した教材は【Laravel x Vue.js】SPAクイズアプリケーションを作ってみよう!です。

かなりボリューム大・難易度も高めだったので、個人的な備忘録も兼ねて感想を書きます。

動機

購入したのは2月。当時は全然理解できなくて挫折。

ですが、LaravelとVue.js各々でポートフォリオ制作を終えた今、次なる挑戦はやはりLaravel+Vue一択。

スキル的にも2か月前に比べてだいぶ変わったはず。

そう思い、再びこの教材に挑戦してみました。

感想


この教材ではタイトル通りLaravel+VueでSPA開発を体験できます。(環境構築はMac)

私はWindows&XAMPP(SQLite⇒MySQL)に置き換えて挑戦しました。

良かった点


①Laravel-Adminを扱っている点。

以前から気になっていたのですが、これを扱う教材は初めてだったのでとても参考になりました。

②SPAかつ複数ページのサイト構築を学べる点。

以下は難しすぎて、頭を整理する際に作ったものです。笑

他でここまで教材にしてくれているのはこれくらいな気がします。⇒Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう

印象的だった点


・LaravelとVueをディレクトリを切り離して作成し、後程1つにまとめるという作り方。

・Login/Register含めすべてのビューをVue側で作成。機能面をLaravel側で実装している。

難しかった点


・webpackやら環境構築。設定面は見よう見まねでしか現状はできないです。

・複数ページに渡るSPA開発を1つのプロジェクトで扱う。コンポーネントファイルもたくさん作ります。
※有名なこちらの本(PHPフレームワーク Laravel実践開発)でもLaravelでのVue.js実装方法は説明されていますが、本当に導入部分の説明のみです。(コンポーネントファイル1つでの実装。Vue Routerも扱わない。)

・api.phpの組み込み(というより6章以降総じて難しい。)

・Laravel-Adminの実装

活用ライブラリ・機能など


CSS


Bootstrap3、Sass、※BEM規約

JavaScript


browserSync、vue-social-sharingChart.jsvue-chartjsVue Router(グローバルビフォーガイド)AxiosVeeValidatevue-loading-overlayvue-notification

PHP


Carbon(日付計算)、Factory(自動ダミーデータ生成機能)、helpers(文字列ライブラリ)、Laravel-admin

その他


Font Awesome、SQLite(DB)、JSON Viewer、ngrok(windowsはこちらで対応、1周目は割愛した。)、Basic認証

おわりに


作りながら学べる系の教材としては、かなり難しい部類に入ると思います。

僕もまだ1周終えた段階で全然理解できていません。

ですが、これを理解したら何ができるだろうかと妄想を膨らませると非常にワクワクします。笑

※Laravel-Adminは作るアプリによってはToo muchな気もしました。GUIでDB弄れれば問題ない気もします。ここら辺も踏まえて復習していきます。

引き続き頑張ります◎

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel blade基本

Laravelではbladeテンプレートを使用してviewを作成できます。
resources/viewsフォルダ以下にviewファイルを置き、拡張子をOOO.blade.phpにすることでbladeテンプレートとして扱えます。

viewファイル読み込み

viewヘルパー関数を使うことでviewファイルの読み込みと変数の受け渡しを行うことができます。
viewヘルパー関数の第一引数にviewファイルのパスを、第二引数にviewに渡す変数を指定します。
viewファイルパスは
resources/views以下を指定し、ディレクトリは/(スラッシュ)ではなく.(ドット)で繋げ、拡張子なしのファイル名を記述します。
例えばresources/views/hoge/fuga.blade.phpを読み込む場合は以下になります。

コントローラーのアクションメソッド
public function index() {
    return view('hoge.fuga');
}

viewファイルへの変数受け渡し

viewヘルパー関数の第二引数に連想配列を渡すことでviewファイルに変数を受け渡しすることができます。
viewファイル内では連想配列のkeyを変数名として指定できます。

コントローラーのアクションメソッド
public function index() {
    $data = [
        'hoge' => 'fuga',
    ];
    return view('hoge.fuga', $data);
}
foge/fuga.blade.php
{{ $hoge }}
結果
fuga

また、phpのcompact関数を使うことで簡単に変数を渡すことができます。

コントローラーのアクションメソッド
public function index() {
    $hoge = 'fuga';
    return view('hoge.fuga', compact('hoge'));
}

viewファイルで変数を使う

viewヘルパー関数で渡された変数には マスタッシュ構文 {{ }}内で使用可能です。

viewファイル
{{ $hoge }}

マスタッシュ構文では変数展開時にXSS対策のため自動でHTMLエスケープが実行されます(=タグがそのまま文字列として表示される)。
動的にhtmlを作成したい場合にエスケープを無効にしたい場合は、代わりに{!! !!}を使用してください。(中括弧は一つでok)

viewファイル
{!! $hoge !!}

viewファイル内で{{ }}をそのまま使いたい場合は先頭に@をつけてください。変数展開されず出力されます。

viewファイル
@{{ $hoge }}

viewファイルでコメントを記述する

htmlのコメント(<!-- comment -->)をそのまま記述することも可能ですが、htmlファイルとして出力されるのでユーザーから見えてしまいます。
bladeテンプレート内でのみ残るコメントにする場合は {{-- comment --}}を使います。

ディレクティブ制御構文

bladeテンプレートにはview制御を行うifやforeachが使えるディレクティブ構文が使えます。
ディレクティブ構文内では変数が使えます。
ディレクティブ構文には先頭に@を付けます。

if文

@if, @elseif, @else, @endif, @unless, @isset, @empty があります。

viewファイル
@if ($isActive)
アクティブ
@else
非アクティブ
@endif
viewファイル
@unless ($isActive)
@endunless

```lang:viewファイル
@isset ($fuga)
fugaが定義されていない
@endisset
viewファイル
@empty ($array)
arrayが空です
@endempty

Switch文

@switch, @case, @break, @default, @endswitchがあります。

viewファイル
@switch($i)
    @case(0)
        0です。
        @break
    @case(1)
        1です。
        @break
    @default
        でふぉるとです
@endswitch

繰り返し

@foreach, @endforeach, @for, @continue, @break が使えます。
繰り返し構文の中では$loop変数が使えます。

viewファイル
@foreach ($books as $book)
 @continue ($loop->first)

 @if ($loop->last)
   最後
 @endif

 {{ $book->title }}
@endforeach
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP組み込みのDateTimeクラスの定数を使えば日時フォーマットを頑張って書かなくて良い

PHPの場合はDateTimeクラスに定数が定義されているので、それを使うとdateフォーマット表現が簡単でした。
Laravel の Request Validation にも使えます。

ref: https://www.php.net/manual/ja/class.datetime.php

例えば
2020-04-08T00:00:00+09:00
みたいなフォーマットを表現したいとき、
DateTime::RFC3339 で表現できます。
この定数の中には Y-m-d\TH:i:sPが入っています。

ここで最後のPが何を意味するかという情報はPHPのdate関数の項目にまとまっています。
ref: https://www.php.net/manual/ja/function.date.php

一部抜粋。

O グリニッジ標準時 (GMT) との時差。時間と分の間にコロンは入りません。 例: +0200
P グリニッジ標準時 (GMT) との時差。時間と分をコロンで区切った形式 (PHP 5.1.3 で追加)。 例: +02:00
T タイムゾーンの略称 例: EST, MDT ...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel触って1年経ったので過去の戒め(ソースコード)を改善してみた

Laravelのリファクタ記事にしようと思ったが、ただただ間違ったコードを直すだけの記事でした。

久々に過去練習用として作成したサイトのソースコードを覗いたら無駄な処理書いてたり、
Laravelの機能活かせれてなかったりそもそもPHP使えてなかったりと散々だったので、
いい反面教師コードだと思って改善記事(改善というか修正レベル)書いてみました。

目に付いた部分しか改善しないので、一貫性はありませんし、
他の方が絶対やらないような事も多々ありますが、戒めとしてどうか生温かい目で見守ってください。

環境

Laravel5.7
PHP7.1

注意

改善とはいえ、これがベストとは思ってません。
あくまで出来る時間で簡単な改善(修正)を行っただけです。
その辺り、ご容赦くださいm(._.)m

関係ないところは一部省略します。
例外処理等も省略します。

フォームリクエストの謎の使い方

FormRequest便利ですよね。
Controllerにバリデーションを書かずに済むので良く使っています。
(他にもControllerで受け取る前の前加工とか)

さて過去のソースコードを覗いてみましょう

過去コード

CreateController.php
use App\Http\Requests\Validation;

public function store(Validation $request)
{
    //Validation Requestからルールを取得
    $rules = $request->rules();
    //Validation Requestからエラーメッセージを取得
    $messages = $request->messages();

    $data = Validator::make($request->all(), $rules, $messages);
}

フォームリクエストから受け取ってる時点でバリデーション処理は済んでるはずなので、色々と不要ですね。
それとValidationというクラス名だと分かりづらいので、クラス名も変更します。

改善コード

CreateController.php
use App\Http\Requests\ProjectRequest;

public function store(ProjectRequest $request)
{
    $data = $request->all();
}

ProjectRequestと命名した理由は案件登録で使用するのが一つと作成と更新で使用するのであえてCreateProjectRequestとしませんでした。

改善ポイント

  • 無駄な記述を削除して本来の使い方を実装
  • クラス名の役割が抽象的すぎたので、少し具体的にして明確化出来た?

fillable便利だよ

fillableもありがたいですよね〜
デフォルトで存在するUser.php見ると最初から存在するコイツですね

User.php
protected $fillable = [
    'name', 'email', 'password',
];

何が便利なのか、まずは悪い方の過去のコードを見てみましょう。

過去コード

CreateController.php
use App\Http\Requests\ProjectRequest;

public function store(ProjectRequest $request)
{
    $project = new Project;
    $project->name = $request->name;
    $project->responsible_id = $request->responsible;
    $project->category_id = $request->category;
    $project->description = $request->description;
    $project->url = $request->url;
    $project->release_year_at = $request->release_year;
    $project->release_month_at = $request->release_month;
    $project->image_id = $image_id;
    $project->start_year_at = $request->start_year;
    $project->start_month_at = $request->start_month;
    $project->end_year_at = $request->end_year;
    $project->end_month_at = $request->end_month;
    $project->save();
}

うーん行が無駄に多いのと保存処理のたびにこれを書くのはめんどくさいですね。

では改善コード

fillableも勿論活用しますが、そもそも保存処理をControllerでやるのはMVCとしてよろしくないのでModelに責務を分けましょう。

改善コード

ちなみに引数にモデル(というかクラス名)をタイプヒントするとLaravelがインスタンスを用意してくれます。

CreateController.php
use App\Http\Requests\ProjectRequest;
use App\Project;

public function store(ProjectRequest $request, Project $project)
{
    $results = $project->storeProject($request->all());
}

ではModel

Project.php
protected $fillable = [
    'responsible_id',
    'category_id',
    'image_id',
    'name',
    'url',
    'description',
    'release_year_at',
    'release_month_at',
    'start_year_at',
    'start_month_at',
    'end_year_at',
    'end_month_at',
];

public function storeProject(array $data)
{
   return $this->create($data);
}

保存の部分が1行で済みました!
fillableというのは保存するときのホワイトリストなんですね〜
勿論更新の時にも適用されます!

逆にブラックリストのguardedもあります
[Laravel 5.7] Eloquent Modelのfillableとguardedの違い

Project.php
public function storeProject(array $data, int $id)
{
   return $this->where('id', $id)->update($data);
}

改善ポイント

  • 入力内容が増えるたびに書き足す必要がなくなった
  • MVCで責務をちゃんと分けた
  • コード行短縮

resourceの恩恵を得ていない

Controllerを作成する時に良くみますね〜
説明は別記事に託します。

Laravelのリソースコントローラを理解して使ってみよう!

ではルーティングをみてみましょう。

web.php
Route::get('project/', 'ProjectsController@index');

Route::get('project/create', 'CreatesController@index');

Route::post('project/create', 'CreatesController@store');

Route::get('project/edit', 'CreatesController@edit');

Route::post('vupdate', 'CreatesController@update');

Route::get('project/delete', 'CreatesController@delete');

突っ込みどころは多いですけど一旦スルー

改善コード

web.php
Route::prefix('project')->name('project.')->group(function () {
    Route::resource('/', ProjectsController, ['except' => 'show']);
});

改善ポイント

  • Projectに関連した物しかなく、restfullで完結できそうだったので、ProjectsControllerファイルにまとめました(これはたまたまなので、無理矢理真似なくてOK)
  • groupにまとめて無駄な記述が減らせたのと関連したルーティングが見易くなった。
  • Routeメソッドをresourceに変える事でメソッド毎に定義しなくて済んだ(showは使わないのでexceptで除外していますが本来はonly派)

ちょこっと番外編(LaravelというかPHP)

検索機能を付けていたのだが、過去のコードを見てみましょう。

過去コード

ProjectsController
public function index(Request $request)
{

    $query = Project::query();

    if (isset($request))
    {
        $category_id = $request->get('category_id');

        $responsible_id = $request->get('responsible_id');

        $tag_ids = $request->get('tag_id');

        $release_year = $request->get('release_year');

        $release_month = $request->get('release_month');

        $start_year = $request->get('start_year');

        $start_month = $request->get('start_month');

        $end_year = $request->get('end_year');

        $end_month = $request->get('end_month');

        //カテゴリ
        if (!empty($category_id))
        {
            $query->where('category_id', 'like', '%' .$category_id .'%');
        }

        //ユーザー
        if (!empty($responsible_id))
        {
            $query->where('responsible_id', 'like', '%' .$responsible_id .'%');
        }

        //リリース年
        if (!empty($release_year))
        {
            $query->where('release_year_at', 'like', '%' .$release_year .'%');
        }

        //リリース月
        if (!empty($release_month))
        {
            $query->where('release_month_at', 'like', '%' .$release_month .'%');
        }

        //開発開始年
        if (!empty($start_year))
        {
            $query->where('start_year_at', 'like', '%' .$start_year .'%');
        }

        //開発開始月
        if (!empty($start_month))
        {
            $query->where('start_month_at', 'like', '%' .$start_month .'%');
        }

        //開発終了年
        if (!empty($end_year))
        {
            $query->where('end_year_at', 'like', '%' .$end_year .'%');
        }

        //開発終了月
        if (!empty($end_month))
        {
            $query->where('end_month_at', 'like', '%' .$end_month .'%');
        }
    }
}

なげーーーー

しかも検索条件増えるたびに書き足さないといけない、、
という事で今回は複雑な検索条件は無かったので短縮化

ProjectsController
public function index(Request $request, Project $project)
{
    $query = $project->searchProject($request);
}

Modelに記述

Project.php
    public function searchProject(Request $request)
    {
        $query = $this->query();

        if ($request->query() !== '') {
            foreach ($request->all() as $key => $val) {
                !empty($val) ? $query->where($key, 'LIKE', '%' .$val .'%') : '';
            }
        }

        return $query;
    }

これで検索条件が増えても安心ですね^^(簡単な条件に限るけど)

改善ポイント

  • コード行短縮
  • 条件が増えるたびに書き足す必要がない

Bladeのoldヘルパー使えてる?

良く編集画面とかで動的に変わる選択ボックスとか見かけますよね??(この辺割と記事ない印象)
そういう時入力値を優先させて、old()がなければDBの値を反映して一致した時にselectedとかif文でゴニョゴニョしまくってませんか?

勿論過去の僕はしてます。
しかも型を厳密にチェックしてません。
悪い子です。

過去コード

edit.blade.php
<select name="category_id" class="form-control">
    @foreach ($categories as $category)
        <option value="{{ $category->id }}"
                @if(!empty(old('category')))
                    @if(old('category') == $category->id)
                        selected
                    @endif
                @elseif($project->category_id == $category->id)
                    selected
                @endif
            >{{ $category->name }}</option>
    @endforeach
</select>

上のコードもold()の第二引数を使用すればここまでスッキリかけます!
(ちょっと横に長いけど、改行してもよし)

改善コード

edit.blade.php
<select name="category_id" class="form-control">
    @foreach ($categories as $category)
        <option value="{{ $category->id }}" @if ($category->id === (int) old('category_id', $project->category_id)) selected @endif>{{ $category->name }}</option>
    @endforeach
</select>

ちなみにold()はstringで返ってくるのでintに変換してます。

改善ポイント

  • 無駄なif文を省いて見易くなった

終わり

誰もこんなクソコードを量産して欲しく無かったので戒めとして書きました。
皆さんも昔のソースコード久々に覗いてみては?
乾いた笑いが出ると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel Facade自作

LaravelのFacadeを自作する。
Hoge::fuga()で'piyo'を返すHogeファザードを作成する。

まずHogeクラスを作成する。

App/Support/Hoge.php
<?php

namespace App\Support;

class Hoge
{
    public function fuga() {
        return 'piyo';
    }
}

そしてHogeクラスを呼び出すファサードを作成。

App/Support/Facades/Hoge.php
<?php

namespace App\Support\Facades;

use Illuminate\Support\Facades\Facade;

class Hoge extends Facade
{
    public static function getFacadeAccessor() {
        return 'hoge';
    }
}

Hogeクラスをサービスコンテナに登録するProviderを作成。
Providerはmakeコマンドを使うことでapp/Provivdersフォルダ以下に雛形が作成されます。

$ php artisan make:provider HogeProvider
Provider created successfully.
app/Providers/HogeProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

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

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

register関数内でHogeクラスをサービスコンテナに登録し、HogeProviderがLaravelに読み込まれるようにconfig/app.phpに登録します。

app/Providers/HogeProvider.php
    public function register()
    {
        $this->app->singleton('hoge', \App\Support\Hoge::class);
    }
config/app.php
    'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        省略
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

        App\Providers\HogeProvider::class, //追加
    ],

これで使えるか確認してみましょう。
HogeFacadeをuseすることでHoge::fuga()が使えるようになります。

適当なクラス
use App\Support\Facades\Hoge;
echo Hoge::fuga();
出力: piyo

facadeのuseなしにfacadeを使えるようにする

config/app.phpにaliasを指定することでuseなしにfacadeを使うことが可能になります。

config/app.php
    'aliases' => [

        省略
        'View' => Illuminate\Support\Facades\View::class,
        'Hoge' => App\Support\Facades\Hoge::class, //追加
    ],

これで準備okです。\Hoge::fuga()で呼べるようになります。

ideでコード補完できるようにする

providerを登録しておけばide-helperの自動生成ファイルを元にideコード補完ができるようになります。

 $ php artisan ide-helper:generate
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub Actions で LaravelのCI/CD環境を構築する(MySQL, Deployer)

概要

GitHub Actionsを使って、LaravelのプロジェクトをテストしてからAWSのEC2上にデプロイする方法を書いていきます。

テストDBにはMySQLを使用し、デプロイにはDeployerを使用します。各々の細かい説明はしません。

設定ファイル

GitHub Actionsの設定はYMLファイルです。

.github/workflowsディレクトリに設定ファイルを置くとワークフローとして処理されます。

テストのjob

まずはテストが実行されるようにファイルを書いていきます。

on: push

jobs:
  test:
    name: phpunit test
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:8.0.19
        ports:
          - 3306:3306
        options: --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10
        env:
          MYSQL_ROOT_PASSWORD: pass
          MYSQL_DATABASE: test

    env:
      DB_CONNECTION: mysql
      DB_HOST: 127.0.0.1
      DB_PORT: 3306
      DB_DATABASE: test
      DB_USERNAME: root
      DB_PASSWORD: pass

    steps:
      - uses: actions/checkout@v2
      - name: cache vendor
        id: cache
        uses: actions/cache@v1
        with:
          path: ./vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-
      - name: composer install
        if: steps.cache.outputs.cache-hit != 'true'
        run: composer install -n --prefer-dist
      - name: copy .env
        run: cp .env.ci .env
      - name: generate key
        run: php artisan key:generate
      - name: migrate
        run: php artisan migrate
      - name: unit test
        run: ./vendor/bin/phpunit

MySQL

今回はテストDBにMySQLを使用するのでMySQLを使いたいのですが、ワークフローが実行されるマシン(ランナー)であるubuntuには5系しか入っていないので、Dockerコンテナを立ててMySQLを使用します。

services内のimageでDockerのimageを指定でき、DockerHubからimageをpullできます。

執筆時点ではoptionを指定していないと、MySQLコンテナが立ち上がる前にPHPUnitが走ってしまいテストがコケてしまう現象がでたため、MySQLコンテナが立ち上がるまで待ちます。

envで環境変数を設定しています。jobs.<id>.envの位置に書くことによって、このjob内で使える環境変数として設定できます。

注意点としては、今回はランナー上でコンテナをたてているのでDB_HOST: 127.0.0.1にしてください。コンテナ名にしても接続はできません。

PHPUnit

毎回composer installをすると時間がかかってしまうので、キャッシュができるアクションがあるのでそれを指定しています。
使い方はここに書いてあるコードを書いておけばとりあえず大丈夫です。

.envに関しては今回は雛形(.env.ci)をコピーしてますが、APP_KEYが設定できればどの方法でもOKです。

あとはmigrateなりphpunitなりのコマンドを叩けばテストが走るjobが完成します。

デプロイのjob

次はデプロイに関して設定をしていきます。

さきほどと同じファイルの続きに書いていきます。

jobs:

  //...

  deploy:
    name: deploy
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/master'

    steps:
      - uses: actions/checkout@v2
      - name: cache vendor
        id: cache
        uses: actions/cache@v1
        with:
          path: ./vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-
      - name: composer install
        if: steps.cache.outputs.cache-hit != 'true'
        run: composer install -n --prefer-dist
      - name: ssh_key copy
        run: |
          mkdir -p /home/runner/.ssh
          touch /home/runner/.ssh/id_rsa
          echo "${{ secrets.SECRET_KEY }}" > /home/runner/.ssh/id_rsa
          chmod 600 /home/runner/.ssh/id_rsa
      - name: add known hosts
        run: ssh-keyscan 【 ip or host_name 】 >> ~/.ssh/known_hosts
      - name: deploy
        run: ./vendor/bin/dep deploy master

まずテストと大きく違うところはneedsとifです。

GitHub Actionsではjobは並列に実行されるのでそのまま書いてしまうとテストとデプロイが一緒に走ってしまうことになります。

テストをパスしてからデプロイをしたいのでneedsでどのjobが終わってから実行するかを指定します。

またデプロイはどのブランチでもデプロイするわけではないので、ifを使ってmasterブランチのときに実行するようにします。

Deployer

Deployerでデプロイするには秘密鍵が必要になります。公開鍵はあらかじめサーバーにあげておいてください。

秘密鍵などの情報をそのまま書いてしまうとセキュリティ的にNGなので、GitHub ActionsではあらかじめGitHubに暗号化して登録しておきます。
(https://help.github.com/ja/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets)

登録した秘密鍵を適切な場所にコピーしてあげます。(Deployerだとhosts.ymlに秘密鍵のファイルを指定できるオプションがあります)

このままssh接続をしてしまうと接続時にコンソールでyes or noを入力しないとすすめなくなってしまいエラーになってしまうので、known_hostsにあらかじめ記述しておくことでこれを回避することができます。

最後にdeployコマンドを叩くようにするとデプロイが開始されます。

pushする

以上のファイルを保存してGitHubにpushすると、pushをトリガーにしてmasterだとテスト&デプロイが、それ以外だとテストが走ります。

Circle CIのようにGitHub Actionsのテストが通ったらPRをマージできるようにするなどの設定も可能です。

まとめ

Circle CIを使ったことがある人ならそこまで抵抗なくできると思います。

GitHub Actionsでは他の人が作ったものをActionとして利用できるので、それを利用することでより手軽に様々なことができるようになるかもしれません。
GitHub Actionsの検索

ちなみにSlackへの通知はこちらのアクションなどがあるので便利です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel Middlewareを自作する

LaravelのMiddleware覚書。
今回はviewにアクセス日時を表示するViewDateMiddlewareを作成します。

Middleware作成用のartisanコマンドが用意されているのでそれを使います。
引数にミドルウェア名を渡せばokです。
php artisan make:middleware ミドルウェア名

help
$ php artisan make:middleware --help
Description:
  Create a new middleware class

Usage:
  make:middleware <name>

Arguments:
  name                  The name of the class

以下のコマンドでapp/Http/Middleware/ViewDateMiddleware.phpが作成されます。

ViewDateMiddleware.phpを作成
$ php artisan make:middleware ViewDateMiddleware
Middleware created successfully.

自動生成されたMiddlewareにはhandle関数のみ定義されています。
第一引数にRequest, 第二引数にClosureが渡されます。Requestはhttpリクエストでinput値等が取得できます。
Closureを実行することで次のMiddlewareが実行されます。
Controllerロジックより前に処理を入れたい場合は$next($request)より前にコードを記述してください。
Controllerロジックの後に処理を入れたい場合は$next($request)より後ろにコードを記述してください。

ViewDateMiddleware.php
<?php

namespace App\Http\Middleware;

use Closure;

class ViewDateMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request);
    }
}

MiddlewareはApplicationサービスコンテナによってインスタンス化されるため、コントラクタ引数に任意のクラスを指定することでDIされます。
今回はviewにアクセス日時を表示できる変数を渡すのでViewのFactoryを引数に指定します。

ViewDateMiddleware
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Contracts\View\Factory;

class ViewDateMiddleware
{

    private $view;

    public function __construct(Factory $view)
    {
        $this->view = $view;
    }

  省略
}

handle関数内でFactoryのshare関数を使うことでviewで使える変数を定義できます。

ViewDateMiddleware
    public function handle($request, Closure $next)
    {
        $this->view->share('access_date', time());
        return $next($request);
    }

index.blade.php等のviewファイルで {{ $access_date }} で使えます。

index.blade.php
{{ $access_date }}

そしてブラウザで見てみるとエラーがでました。
Undefined variableなのでaccess_date変数の定義に失敗しています。
スクリーンショット 2020-04-09 1.45.31.png

Middlewareはlaravelライフサイクルの中で読み込まれるようkernelやroute, controllerの中で指定する必要があります。
kernelにはMiddlewareを指定する箇所は3箇所あります。
・$middleware
全てのhttp通信で処理されるミドルウェアを追加

・$middlewareGroups
webやapi等の特定routeGroupで使用するミドルウェアを追加

・$routeMiddleware
ミドルウェアのalias名を指定できる。個々のrouteでalias名を使ってmiddlewareを指定できる。

今回は全てのwebリクエストで読み込まれるようにしたいのでkernelのmiddlewareGroupsに追記します。

App/Http/Kernel.php
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\ViewDateMiddleware::class, //ここに追加
        ],

        'api' => [
            'throttle:60,1',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

これでミドエルウェアが実行されてviewでaccess_date変数が使用できるようになりました。
↓結果html
スクリーンショット 2020-04-09 2.26.26.png

middlewareに引数を渡す

routeで個別にmiddlewareを指定する際にmiddlewareに引数を渡せます。
先ほどのmiddlewareを改造し、routeごとに時刻フォーマットを指定できるようにします。

まずはkernel.phpの$middlewareGroupsに追記したmiddlewareを$routeMiddlewareに移動させます。
viewDataaliasでmiddlewareを指定できるようにしました。

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'ViewDate' => \App\Http\Middleware\ViewDateMiddleware::class, //追加
    ];

続いてrouteでmiddlewareを指定します。
middleware名:引数のフォーマットで指定することでmiddlewareに引数を渡すことができます。
viewDate-YmdにアクセスしたらY/m/dを、
viewDate-YmにアクセスしたらY/mを引数にするように指定しました。

web.php
Route::get('/viewDate-Ymd', function() {
    return view('index');
})->middleware('viewDate:Y/m/d');


Route::get('/viewDate-Ym', function () {
    return view('index');
})->middleware('viewDate:Y/m');

これで
http://127.0.0.1:8000/viewDate-Ymd
にアクセスすると
スクリーンショット 2020-04-09 2.43.29.png

http://127.0.0.1:8000/viewDate-Ym
にアクセスすると
スクリーンショット 2020-04-09 2.43.43.png

と表示されるようになりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel の Policy ってどんな場合に使うの?

特定のリソースに対するユーザーアクションを認可する仕組みだそうです。
例えば、ブログの記事削除は本人にしかできないようにすることが可能になる仕組みのことです。

Policyを使わないで書くと以下のようになり、

public function delete(Article $article)
{
    if (request()->article()->isNot($article->user)) {
        return response([], 403);
    }

    $article->delete();
}

ArticlePolicyを作成すると、

ArticlePlicy.php
public function delete(User $user, Article $article)
{
    return $user->id == $article->user_id;
}

以下のように書ける。

public function delete(Article $article)
{
    $this->authorize('delete', $article);

    $article->delete();
}

また、ミドルウェアによる認可もできる。以下のように書ける。

api.php
Route::delete('/articles/{article}', 'ArticlesController@destroy')->middleware('can:delete,article');

今の所、アカウントの種類によって認可するアクションを切り分けたいときに使う認識でいます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む