20191129のlaravelに関する記事は10件です。

【Laravel】独自のDB用クエリ関数を実装する

この記事でやりたいこと

こんな独自のクエリ関数を実装する手順です。

\DB::canConnection(); //(1)
\Schema::getColumnDefinitions('user'); //(2)
User::getColumnDefinitions(); //(3)
\DB::table('user')->getColumnDefinitions(); //(4)

はじめに

以下のような関数は、Laravelですでに用意されています。

\DB::statement('drop table users');
\Schema::hasTable('testTable');

上記のような形式の、独自の関数を追加する方法です。

実装方法

それでは早速実装してみましょう。
なお、「この記事で書きたいこと」で書いた(1)~(4)の関数ですが、厄介なことに定義する場所は、それぞれ異なります。その点を考慮しておいてください。

今回作成するコードのフォルダ構成は、以下のようになります。

ServiceProvider.php
Database/
  ├ MySqlConnection.php
  ├ Eloquent/
  │ └ MySqlBuilder.php
  ├ Query/
  │ ├ MySqlBuilder.php
  │ ├ Grammars
  │ │ └ MySqlGrammar.php
  │ └ Processors
  │   └ MySqlGrammar.php
  └ Schema/
    ├ MySqlBuilder.php
    └ Grammars
      └ MySqlGrammar.php
Model/
  └ CustomBuilderTrait.php

※今回はMySQL前提で作成していきます。MySQL以外のデータベースを使用したい場合、複数のクラスを作成してください。

※今回の例では、以下の3つの関数を作成します。

  • データベースに接続できるかを判定する関数「canConnection」
  • データベースのバージョンを取得する関数「getVersion」
  • 指定のテーブルの列の定義を一覧取得する関数「getColumnDefinitions」

実装

  • ServiceProviderに、独自のMySqlConnectionを呼び出すための処理を追加します。
<?php

namespace CustomBuilder;

use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Connection;

class CustomBuilderServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        // 追加
        Connection::resolverFor('mysql', function (...$parameters) {
            return new Database\MySqlConnection(...$parameters);
        });
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}
  • Database\MySqlConnectionを作成します。
    この関数では、継承した「MySqlGrammar」などの定義、ならびに「\DB::XXXXXX」形式で呼び出す関数の定義を行います。
<?php

namespace CustomBuilder\Database;

use CustomBuilder\Database\Query\Grammars\MySqlGrammar as QueryGrammar;
use CustomBuilder\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar;
use CustomBuilder\Database\Schema\MySqlBuilder;
use CustomBuilder\Database\Query\MySqlBuilder as QueryBuilder;
use CustomBuilder\Database\Eloquent\MySqlBuilder as EloquentBuilder;
use CustomBuilder\Database\Query\Processors\MySqlProcessor;
use Illuminate\Database\MySqlConnection as BaseConnection;

class MySqlConnection extends BaseConnection
{
    /**
     * Get a schema builder instance for the connection.
     *
     * @return Builder
     */
    public function getSchemaBuilder()
    {
        if (is_null($this->schemaGrammar)) {
            $this->useDefaultSchemaGrammar();
        }

        return new MySqlBuilder($this);
    }

    /**
     * Get the default schema grammar instance.
     *
     * @return MySqlGrammar
     */
    protected function getDefaultSchemaGrammar()
    {
        return $this->withTablePrefix(new SchemaGrammar);
    }

    /**
     * Get the default query grammar instance.
     *
     * @return QueryGrammar
     */
    protected function getDefaultQueryGrammar()
    {
        return $this->withTablePrefix(new QueryGrammar);
    }

    /**
     * Get the default post processor instance.
     *
     * @return MySqlProcessor
     */
    protected function getDefaultPostProcessor()
    {
        return new MySqlProcessor;
    }

    /**
     * Get a new query builder instance.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    public function query()
    {
        return new QueryBuilder(
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
        );
    }

    /**
     * Get a new eloquent query builder instance.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function eloquentBuilder($query)
    {
        return new EloquentBuilder($query);
    }


    // \DB::XXXXXX()関数はここで記載 -------------------------------------------
    public function canConnection()
    {
        try {
            $this->getSchemaBuilder()->getTableListing();
            return true;
        } catch (\Exception $ex) {
            return false;
        }
    }

    /**
     * Get database version.
     *
     * @return void
     */
    public function getVersion()
    {
        return $this->getSchemaBuilder()->getVersion();
    }
}
  • Database\Schema\MySqlBuilderを作成します。
    このクラスに、\Schema::XXXXXX()で呼び出す関数の定義を行います。
<?php

namespace CustomBuilder\Database\Schema;

use Illuminate\Database\Schema\MySqlBuilder as BaseBuilder;

class MySqlBuilder extends BaseBuilder
{
    // \Schema::XXXXXX()関数の形式 -------------------------------------------

    /**
     * Get database version.
     *
     * @return void
     */
    public function getVersion()
    {
        $results = $this->connection->selectFromWriteConnection($this->grammar->compileGetVersion());

        return $this->connection->getPostProcessor()->processGetVersion($results);
    }

    /**
     * Get the table listing
     *
     * @return array
     */
    public function getTableListing()
    {
        $results = $this->connection->selectFromWriteConnection($this->grammar->compileGetTableListing());

        return $this->connection->getPostProcessor()->processTableListing($results);
    }

    /**
     * Get column difinitions
     *
     * @return array
     */
    public function getColumnDefinitions($table)
    {
        $baseTable = $table;
        $table = $this->connection->getTablePrefix().$table;
        $results = $this->connection->selectFromWriteConnection($this->grammar->compileColumnDefinitions($table));

        return $this->connection->getPostProcessor()->processColumnDefinitions($baseTable, $results);
    }
}
  • Database\Schema\Grammars\MySqlGrammarを作成します。
    このクラスで、主に独自クラスで実行するSQLを定義します。
<?php

namespace CustomBuilder\Database\Schema\Grammars;

use Illuminate\Database\Schema\Grammars\MySqlGrammar as BaseGrammar;

class MySqlGrammar extends BaseGrammar
{
    /**
     * Compile the query to get version
     *
     * @return string
     */
    public function compileGetVersion()
    {
        return "select version()";
    }

    /**
     * Compile the query to show tables
     *
     * @return string
     */
    public function compileGetTableListing()
    {
        return "show tables";
    }

    /**
     * Compile the query to get column difinitions
     *
     * @return string
     */
    public function compileColumnDefinitions($tableName)
    {
        return "show columns from {$this->wrapTable($tableName)}";
    }
}

  • Database\Schema\Grammars\MySqlGrammarを作成します。
    このクラスで、主に独自クラスで実行するSQLを定義します。
<?php

namespace CustomBuilder\Database\Schema\Grammars;

use Illuminate\Database\Schema\Grammars\MySqlGrammar as BaseGrammar;

class MySqlGrammar extends BaseGrammar
{
    /**
     * Compile the query to get version
     *
     * @return string
     */
    public function compileGetVersion()
    {
        return "select version()";
    }

    /**
     * Compile the query to show tables
     *
     * @return string
     */
    public function compileGetTableListing()
    {
        return "show tables";
    }

    /**
     * Compile the query to get column difinitions
     *
     * @return string
     */
    public function compileColumnDefinitions($tableName)
    {
        return "show columns from {$this->wrapTable($tableName)}";
    }
}

  • Database\Query\Processors\MySqlProcessorを作成します。
    このクラスで、SQLを実行して取得した結果を、配列やCollectionに加工するなどを行います。
    また、このクラス内の関数によって、各データベース種類(MySQL、SQL Serverなど)によって列名などが異なるものを、すべて揃える目的があります。
<?php

namespace CustomBuilder\Database\Query\Processors;

use Illuminate\Database\Query\Processors\MySqlProcessor as BaseMySqlProcessor;

class MySqlProcessor extends BaseMySqlProcessor
{
    /**
     * Process the results of a get version.
     *
     * @param  array  $results
     * @return array
     */
    public function processGetVersion($results)
    {
        $versionArray = $this->versionAray($results);

        return $versionArray[0];
    }

    protected function versionAray($results)
    {
        $version = collect(collect($results)->first())->first();
        return explode('-', $version);
    }

    /**
     * Process the results of a table listing query.
     *
     * @param  array  $results
     * @return array
     */
    public function processTableListing($results)
    {
        return array_map(function ($result) {
            return collect((object) $result)->first();
        }, $results);
    }

    /**
     * Process the results of a Column Definitions query.
     *
     * @param  array  $results
     * @return array
     */
    public function processColumnDefinitions($tableName, $results)
    {
        return collect($results)->map(function ($result) use ($tableName) {
            return [
                'table_name' => $tableName,
                'column_name' => $result->Field,
                'type' => $result->Type,
                'nullable' => boolval($result->Null),
                'virtual' => strtoupper($result->Extra) == 'VIRTUAL GENERATED',
            ];
        })->toArray();
    }

}
  • Database\Query\MySqlBuilderを作成します。
    このクラスに、\DB::table('user')::XXXXXX()で呼び出す関数の定義を行います。
<?php

namespace CustomBuilder\Database\Query;

use Illuminate\Database\Query\Builder as BaseBuilder;

class MySqlBuilder extends BaseBuilder
{
    // \DB::table('user')::XXXXXX()関数の形式 -------------------------------------------

    /**
     * Get column difinitions
     *
     * @return array
     */
    public function getColumnDefinitions()
    {
        $results = $this->connection->selectFromWriteConnection($this->grammar->compileColumnDefinitions($this->from));

        return $this->connection->getPostProcessor()->processColumnDefinitions($this->from, $results);
    }
}

  • また、\Database\Query\Grammars\MySqlGrammarで、上記のDatabase\Query\MySqlBuilderで呼び出すGrammerを作成します。
    (\Database\Schema\Grammars\MySqlGrammarとまとめられないかな?という思いもありつつ)
<?php

namespace CustomBuilder\Database\Query\Grammars;

use Illuminate\Database\Query\Grammars\MySqlGrammar as BaseGrammar;

class MySqlGrammar extends BaseGrammar
{
    /**
     * Compile the query to get column difinitions
     *
     * @return string
     */
    public function compileColumnDefinitions($tableName)
    {
        return "show columns from {$this->wrapTable($tableName)}";
    }
}
  • Database\Eloquent\MySqlBuilderを作成します。
    このクラスに、\User::XXXXXX()で呼び出す関数の定義を行います。
<?php

namespace CustomBuilder\Database\Eloquent;

use Illuminate\Database\Eloquent\Builder as BaseBuilder;

class MySqlBuilder extends BaseBuilder
{
    // \User::XXXXXX()関数の形式 -------------------------------------------

    /**
     * Get column difinitions
     *
     * @return array
     */
    public function getColumnDefinitions()
    {
        $table = $this->model->getTable();
        $connection = $this->query->connection;

        $results = $connection->selectFromWriteConnection($connection->getQueryGrammar()->compileColumnDefinitions($table));
        return $connection->getPostProcessor()->processColumnDefinitions($table, $results);
    }
}
  • Model\CustomBuilderTraitを作成します。
    このTraitでは、各Modelで独自のEloquent\Builder、Query\Builderを呼び出すために必要となります。
<?php

namespace CustomBuilder\Model;

trait CustomBuilderTrait
{
    /**
     * Get a new query builder instance for the connection.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function newBaseQueryBuilder()
    {
        $connection = $this->getConnection();

        return $connection->query();
    }

    /**
     * Create a new Eloquent query builder for the model.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder|static
     */
    public function newEloquentBuilder($query)
    {
        $connection = $this->getConnection();
        return $connection->eloquentBuilder($query);
    }
}

独自関数を呼び出すModelで、このTraitをuseしてください。

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use CustomBuilder\Model\CustomBuilderTrait;

class User extends Authenticatable
{
    use Notifiable;
    // 追加
    use CustomBuilderTrait;

    // 略
}

まとめ

以上です!
Helperクラス的な関数を作ってもいいですが、出来ればこのように、クエリビルダを拡張することを行いたいですよね。
その場合は是非、こちらのコードを使用してください!

ちなみに今回のコードのGitHubはこちらです。
https://github.com/hirossyi73/custom-builder

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

Laravelでよく使うコマンドまとめ

Laravelを使っていてよくコマンドを使いますが、すぐ忘れてしまうので、
まとめておきます。

Laravelでよく使うコマンド集

Laravelの作成
composer create-project laravel/laravel qiita --prefer-dist "6.0.*" //バージョン指定可

ローカルサーバ起動
php artisan serve --host 192.168.33.10:8000

コントローラー作成
php artisan make:controller ShowProfile

モデル作成
php artisan make:model Flight
php artisan make:model Flight --migration //マイグレーションファイル付

テスト作成
php artisan make:test UserTest
php artisan make:test UserTest --unit //ユニットテスト

テスト実行
./vendor/bin/phpunit

マイグレーションファイル作成
php artisan make:migration add_votes_to_users_table --table=users //テーブル名指定

マイグレート実行
php artisan migarate
php artisan migrate:refresh //全てをリセットしてから実行する

マイグレートを戻す
php artisan migrate:rollback //最後の一回分
php artisan migrate:rollback --step=1 //戻す数を指定する場合
php artisan migrate:reset //全てをリセット

シーダー作成
php artisan make:seeder UsersTableSeeder

シーダー実行
composer dump-autoload //シーダー実行前に流す
php artisan db:seed
php artisan db:seed --class=UsersTableSeeder //個別にシーダーを実行したい場合

ファクトリの作成
php artisan make:factory PostFactory

キャッシュクリア
php artisan config:clear

フォームリクエスト作成
php artisan make:request PostRequest

認証(一括作成)
php artisan make:auth

以上です。
随時追加していきます。

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

Laravel-Excelで複数行のヘッダーを無視したい

Laravel-Excelで複数行のヘッダーを無視したい時

Laravel Excel

ドキュメントをみたけれど、書いてなかったので、Issueに書いてあったことを記録しておきます。

例えば、ヘッダー行(1行目)を飛ばすのはオプションでありそうですが、複数行目(例:6行目からデータ行が始まっているので、)飛ばしたい場合、いかのように書いてやればいいです。

WithStartRowを使用します。

use Maatwebsite\Excel\Concerns\WithStartRow;

class UsersImport implements ToModel, WithStartRow
{
    /**
     * @return int
     */
    public function startRow(): int
    {
        return 6;
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

入力フォームの実装で結構苦労した話

自己紹介

職業プログラマ歴約半年、PHP歴約半年、Laravelの存在は入社直前に知った、ひよっこプログラマです。Qiitaへの投稿はこれが初めてです。暖かい目で見てただければ幸いです。

背景

会社のサービスで使うフレームワークをLaravelにするということで、とある入力フォームの移植をすることになった。「項目数も少ないしそんなに時間かからないだろう」と思いながらやっていたところ、意外と厄介なことに気づいて、最終的に色々な手法を組み合わせて無理やり仕上げることになった。
この記事はそのダイジェスト版である。(実際のところもっと苦戦したり頓珍漢な実装になりかけたりしている)

仕様

  • DB
    • tableX:ユーザーアカウントと一対一関係にあるが、存在しないことがある
  • 入力ページ
    • ユーザーアカウントでログインしている時のみアクセスできる = アクセスされる時、アクセス者に対応するユーザーアカウントが必ず存在している
    • ユーザーアカウントに紐づくtableXの情報が存在していれば規定値が入力されたフォームを表示する
    • ユーザーアカウントに紐づくtableXの情報が存在しなければ空のフォームを表示する
    • 確認ページに向かうボタンがある
    • 確認ページから戻ってきた際入力値が保持されている
  • 確認ページ
    • 入力ページのフォームの内容が表示される
    • 入力ページに戻るボタン・内容を確定するボタンがある
    • <input type="hidden">で内容を保持するのは禁止(改竄対策などの為)

どう実装したか

tableXの情報を取得する

まずは以下のような感じに書いた。(ルーティングやミドルウェアについては省略)

App\User.php
// ...
    public function tableX(): HasOne
    {
        return $this->hasOne('App\TableX', 'userno')
    }
// ...
App\Http\Controllers\SomeformController.php
Class SomeformController extends Controller
{
    public function edit()
    {
        $tableX = Auth::user()->tableX();
        return view('someform.edit', ['tableX' => $tableX]);
    }
}
resources\views\someform\edit.blade.php
{{-- ... --}}
<form method="..." action="...">
  @csrf
  <div>
    <h4>項目A</h4>
    <input name="item_a" value="{{ $tableX->item_a }}" />
  </div>
  {{-- ... --}}
  <input type="submit" name="confirm" value="確認する" />
</form>
{{-- ... --}}

このように実装した結果……

ErrorException (E_ERROR)
Trying to get property 'item_a' of non-object

無事:innocent:
原因は上にも書いた…が、存在しないこともあるである。次のように書き換えて解消した。

resources\views\someform\edit.blade.php
  {{-- ... --}}
    <input name="item_a" value="{{ $tableX->item_a ?? '' }}"/>
  {{-- ... --}}

:innocent:な時$tableXnullで、通常の箇所で$tableX->item_aと書くとTrying to get property 'b' of non-objectと出てくるところだが、null合体演算子??の左辺に書いた場合はissetの時のように上手く処理してくれるようだった。

確認画面に入力値を表示させる

かなり苦戦した覚えがあるが、最終的に次のように書いたところ動作するようになった。

App\Http\Controllers\SomeformController.php
// ...
    public function confirm(Request $request)
    {
        $request->flash();
        return view('someform.confirm');
    }
// ...
resources\views\someform\confirm.blade.php
<div>
  <div>
    <h4>項目A</h4>
    <div>{{ old('item_a') }}</div>
  </div>
  {{-- ... --}}
  <form method="..." action="...">
    @csrf
    <input type="submit" name="submit" value="保存"/>
  </form>
  <form method="..." action="...">
    <input type="submit" name="back" value="戻る">
  </form>
</div>

old()$request->flash()の使い方を何となく理解できたようなできていないような……。

確認画面から戻った時に入力値がクリアされないようにする

ここまでのコードだと、戻るボタンを押した時に入力値がリセットされてしまう:innocent:
ここも最適解を見つけるまでに結構な時間が掛かったような覚えがあるが、最終的に次のように書くことで解決できた。

resources\views\someform\edit.blade.php
{{-- ... --}}
    <input name="item_a" value="{{ old('item_a', $tableX->item_a ?? '') }}"/>
{{-- ... --}}

保存処理を実装する

ここまで書いてしまえばあとは保存処理を実装するだけ、楽勝!

App\Http\Controllers\SomeformController.php
// ...
    public function submit()
    {
        $tableX = Auth::user()->tableX;
        $tableX->fill(old())->save();
        return redirect('...');
    }
// ...
Symfony \ Component \ Debug \ Exception \ FatalThrowableError (E_ERROR)
Call to a member function fill() on null

:innocent: :innocent: :innocent:

原因は最初にも出てきた…が、存在しないこともあるだった。
最終的に次のように書いて解決した。

App\Http\Controllers\SomeformController.php
// ...
    public function submit()
    {
        $tableX = TableX::firstOrNew(['userno' => Auth::user()->id]);
        $tableX->fill(old())->save();
        return redirect('...');
    }
// ...

TableXテーブルとUserテーブルは関連付いているからUserモデルのオブジェクトからTableXオブジェクトを……」という考えに固執していると無意味に複雑なコードになってしまう。(実際なってしまっていた。)

おわりに

本文中にもある通り、完成までにかなり苦戦しました。完成に至るまでに無意味に複雑で長いコードになって、「Laravelは厄介だ」と思うこともありました。しかし記事に起こしていると、firstOrNew関数など、煩雑な記述を解消する為のツールがLaravelによって既に用意されていることも分かってきました。そういったツールを使いこなせるよう、これからも勉強していきたい次第です。

ここまでお読みくださり、ありがとうございました。

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

Laravel & Guzzle httpdで500 Internal Server Errorの解決に手間取った

Laravelで実装している際、某社のAPIへリクエストして500エラーが発生し、解決に時間がかかったことがありました。
最終的に、API開発元に確認しHeaderにAcceptがないのが原因だと判明したのですが、なぜこれが原因だったのか、いまだに謎です。

下記のようなコードで送っています。

        $paramater = [
            'http_errors' => true, // 40X・50X系のエラーをExceptionにしない
            'headers' => [
                'Accept' => 'application/json',
                'Authorization' =>
                    'Bearer ' . \Config('authorization'),
            ],
        ];

        $client = new Client();
        $response = $client->request('GET', 'https://xxxxx', $paramater);

同社の別APIに対してはAcceptなしで送れて、このAPIだけエラーなのでハマりました。
レスポンスする時にAcceptなしをブロックしているなら500にならないだろうし、必須のヘッダーなら他のリクエストでも同様のエラーが出るはずだし・・・:rolling_eyes:

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

Laravel6.x移行でハマった話

Illuminate\Support\Facades\LangのLang::getFromJsonが6.x移行で使えなくなってて

local.ERROR: Method Illuminate\Translation\Translator::getFromJson does not exist.
などというエラーに見舞われてしまった
ググっても

php artisan view:clear
php artisan view:cache

しろとしかヒットせず
自作のメールテンプレートで多言語処理を実装していた場合、これでは解決できない

Laravel5とLaravel6にて
Illuminate\Translation\Translator.phpを見比べてみると確かにgetFromJsonメソッドがなくなっており
getFromJsonを利用する側のIlluminate/Auth/Notifications/VerifyEmail.phpのtoMail()メソッド内の処理を見比べてみると

-Lang::getFromJson()
+Lang::get()

と置き換わっており
このメソッド書き換えを行うと6.x(6.2環境で確認)で正しく動作した

関連
Laravelで多言語対応いろいろ
https://qiita.com/shikigamix/items/d92ec72b4418f31c356d

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

Laravel Mixでwebpackを簡単に利用する」ってどういうこと?

はじめに

業務上活用しているLaravel Mixにてコンパイル関連で色々と模索していおり、
今更ですがLaravel Mixについて整理したことを備忘録としてまとめました。

まず、Laravel Mix公式を見てみると

以下公式サイトhttps://readouble.com/laravel/5.5/ja/mix.html より引用

Laravel Mixは多くの一般的なCSSとJavaScriptのプリプロセッサを使用し、Laravelアプリケーションために、構築過程をWebpackでスラスラと定義できるAPIを提供しています。シンプルなメソッドチェーンを使用しているため、アセットパイプラインを流暢に定義できます。例を見てください。

mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');

Webpackやアセットのコンパイルを始めようとして、混乱と圧倒を感じているならLaravel Mixを気に入ってもらえるでしょう。しかし、アプリケーションの開発時に必要だというわけではありません。どんなアセットパイプラインツールを使用してもかまいませんし、使わなくても良いのです。

なるほど、わからん。:grin:カタカナ語が使いたい年頃の人が書いた文書のようにになっていますが、ざっくり言うと「webpackというモジュールバンドラー(モジュールの束)を簡単に使えるようにしたラッパー」ということだと後になって理解しました。

以下でもうちょっとだけ詳しく見ていきます。

概要

①Laravel Mixとは、フロントエンドのアセット(JS,SASS等)をコンパイル、バンドルしてくれるツール
②webpack設定ファイルをより分かりやすく簡単に書けるように設定ファイルをラップしている
③lessやsass、babelなどよく使われるローダーが最初から用意されていて、デフォルトで利用することができる
⑤Laravelを使っていないアプリでも、コンパイル・バインディングのツールとして利用できる

使い方

Laravelをインストールした段階で、package.jsonとwebpack.mix.jsが用意されています。

・package.json
⇒Laravel Mix本体やその他必要なパッケージが記述済み。
package.jsonのscriptsにはwebpackを実行するためのスクリプトも記述されている。

・webpack.mix.js
⇒webpack設定ファイル(webpack.config.js)のラッパー。
ここにコンパイル対象ファイルやバンドル対象ファイルなどの設定を記述していく。

※LaravelではないアプリでLaravel Mixを使う場合でも、
package.jsonとwebpack.mix.jsの2ファイルを用意するだけであとは同じです。
Laravel Mix公式サイトのStand-Alone Projectを参考に。↓
https://laravel-mix.com/docs/5.0/installation

設定ファイルの記述

webpack.mix.jsに、設定を記述する。

sassファイルをコンパイルしたい場合

mix.sass('resources/assets/sass/app.scss', 'public/css');

cssファイルをバンドルしたい場合

mix.styles([
    'public/css/vendor/normalize.css',
    'public/css/vendor/videojs.css'
], 'public/css/all.css');

jsをコンパイルしたい場合

mix.js('resources/assets/js/app.js', 'public/js');

jsファイルをバンドルしたい場合

mix.scripts([
    'public/js/admin.js',
    'public/js/dashboard.js'
], 'public/js/all.js');

他にもいろいろ設定できることはある。

Laravel Mixの実行
npm run dev を実行すると、package.jsonに書いてあるスクリプトが実行され、
設定ファイルに記述したコンパイル、バンドルが実行される。

npm run production の場合は圧縮されたファイルが出力される。

知っておくと良い機能抜粋

もう少し具体的な機能について、知っておいたほうがいい主な機能を抜粋。

jsのコンパイル
上記の通り、jsファイルをコンパイルするには

mix.js('resources/assets/js/app.js', 'public/js');
と書く。

この処理では
・ES2015記法
・モジュール
・.vueファイルのコンパイル
・開発環境向けに圧縮
のコンパイルが実行される。

①babel実行
jsファイルをバンドルする設定として、mix.scripts()を紹介したが、
この代わりにmix.babel()を使うことができる。

これをすると、バンドルされたファイルはES5記法に変換された状態になる。

IEはES5までしか対応していないため、もしIEでも動作させる必要があるシステムの場合は
このmix.babel()機能を使ってES5記法に変換するほうが良い。

ES5, CoffeeScript, ES6の記法の違い

②キャッシュバスティング

mix.js('resources/assets/js/app.js', 'public/js')
   .version();

このように記述すると、
ファイル名の末尾に一意のハッシュ値が付与される。

これによって、コンパイルのたびにファイル名が変更されるため、CSSやJSなどがブラウザキャッシュに残って変更が反映されないことを防止できる。
このファイルを読み込むbladeファイル側では、

のようにmix()関数を使うことで、ハッシュ値のついたファイル名でも取得することができる。

おわりに

Laravel Mixにはオプションの指定で挙動を簡単に変えられるという特徴もあります。
そのあたりのカスタマイズについてのメモがたまってきたのでまた、整理していこうと思います。

とりあえず、CSS側のコンパイルが極端に遅い方は
"processCssUrls: false"を試してみましょう。
(画像PASSの書き換えオプションをOFFにする。デフォルトはON。)

例)

mix.sass('src/app.scss', 'dist/')
   .options({
      processCssUrls: false
   });

詳細はまた後ほどほどまとめます!

参考
https://readouble.com/laravel/5.5/ja/mix.html
https://laravel-mix.com/docs/5.0/quick-webpack-configuration

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

Laravel モデルファイル作成

マイグレーションファイルの作成後に、モデルファイルを作成する手順。

Modelとは

Laravelにおいて、データベースを操作する為の実行ファイル。データベースに関連づけて作成する。

artisanコマンドでモデルを作成

$ php artisan make:model モデル名

Laravelには命名規則があり、テーブル名(複数系)とモデル名(単数系)とする必要がある。

項目 名前
テーブル名 users
モデル名 User

このルールを守る事で、自動的にデータベースとモデル(実行ファイル)が紐づいてくれる。

実行結果

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class モデル名 extends Model
{
    //
}

モデルが作成される場所

ディレクトリ名/app

appディレクトリ内に作成されるので確認

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

Laravel で他のフィールド名を受け取って、配列にも対応したカスタムバリデーションルールを作る

経緯

Laravel のバリデーションルールの same 等は他のフィールド名を受け取る事ができ、配列にも対応している。
このようなカスタムバリデーションルールを作ろうと思ったがなかなか情報が見つからずに苦労したのでまとめる。

あるデータ内の2つの値の合計が41になる事を検証したい事がよくあると思うので1本記事では例として実装する。

配列に対応していない版

AppServiceProvider::boot 内で Validator ファサードの extend メソッドを使ってルールを登録する。

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Validator;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Validator::extend('sum41', function ($attribute, $value, $parameters, $validator) {
            return $value + ($validator->getData())[$parameters[0]] === 41;
        });
    }
}

rule:param1[,...] のようにルールに渡した引数は、extend メソッドのクロージャの第3引数で配列として受け取れる。

第4引数は \Illuminate\Validation\Validator のインスタンスで、getData メソッドを使えば全体のデータが取得できる。

よって param1 に他のフィールドのキーを指定しているなら

($validator->getData())[$parameters[0]]

でそのフィールドの値が取得できる。

実際にルールを使ってみる。

>>> $data = ['data1' => 10, 'data2' => 31];
>>> $rules = ['data1' => 'required', 'data2' => 'sum41:data1'];
>>> Validator::make($data, $rules)->validate()
=> [
     "data1" => 10,
     "data2" => 31,
   ]
>>> $data2 = ['data1' => 10, 'data2' => 30];
>>> Validator::make($data2, $rules)->validate()
Illuminate/Validation/ValidationException with message 'The given data was invalid.'

データが一件の時は問題なく動いているように見えるが、配列に対して使おうとすると *.data1 をそのままキーとして認識するため Notice: Undefined index が発生する。

>>> $data = [['data1' => 10, 'data2' => 31], ['data1' => 20, 'data2' => 21]];
>>> $rules = ['*.data1' => 'required', '*.data2' => 'sum41:*.data1'];
>>> Validator::make($data, $rules)->validate()
PHP Notice:  Undefined index: *.data1 in /***/app/Providers/AppServiceProvider.php on line 14

配列に対応

調べた所、パラメータの * を配列のインデックスに変換するためには extend の代わりに extendDependent メソッドを使ってルールを登録すれば良い事が分かった。(この情報がなかなか見つからなかった)

これだけで $parameters 配列に自動的に 0.data1 のような値が入ってくれる。

また、 $validator->getData()

array:2 [
  0 => array:2 [
    "data1" => 10
    "data2" => 31
  ]
  1 => array:2 [
    "data1" => 20
    "data2" => 21
  ]
]

このような構造になっているので

Arr::get($validator->getData(), $parameters[0])

で目的の値が取得できる。

最終的に以下のようになった。

<?php

namespace App\Providers;

use Arr;
use Illuminate\Support\ServiceProvider;
use Validator;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Validator::extendDependent('sum41', function ($attribute, $value, $parameters, $validator) {
            return $value + Arr::get($validator->getData(), $parameters[0]) === 41;
        });
    }
}
>>> $data = [['data1' => 10, 'data2' => 31], ['data1' => 20, 'data2' => 21]];
>>> $rules = ['*.data1' => 'required', '*.data2' => 'sum41:*.data1'];
>>> Validator::make($data, $rules)->validate()
=> [
     [
       "data1" => 10,
       "data2" => 31,
     ],
     [
       "data1" => 20,
       "data2" => 21,
     ],
   ]

別の方法

一度しか使わないルールを検証時にその場で記述したい場合は、\Illuminate\Validation\Validator::addDependentExtension でもルールの登録ができる。

$validator = Validator::make($data, $rules);
$validator->addDependentExtension('sum41', function($attribute, $value, $parameters, $validator) {
    return $value + Arr::get($validator->getData(), $parameters[0]) === 41;
});
$validator->validate();

  1. ない 

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

ComposerでLaravelの開発環境を構築しよう

Laravelとは

Laravelは、MVCのWebアプリケーション開発用の無料・オープンソースのPHPで書かれたWebアプリケーションフレームワークです。webフレームワークを使うことでwebサービスの開発スピードが上がったり、セキュリティの安全性を担保できます。日本では、様々な企業で使われています。
引用: ウィキペディア
公式ページ: http://laravel.jp/

スクリーンショット 2019-11-29 0.13.57.png

Composerとは

composerはPHPのパッケージ管理システムです。
https://getcomposer.org/
スクリーンショット 2019-11-29 0.15.05.png

環境構築の手順

  1. Homebrewをインストール
  2. MySQLをインストール
  3. Composerをインストール
  4. Laravelのプロジェクトディレクトリを作成

1. Homebrewをインストール

HomebrewとはmacOS用パッケージマネージャーで、composerとmysqlをインストールをするために必要なのでインストールします。
Homebrew公式ページ: https://brew.sh/index_ja

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

2. MySQLをインストール

laravelはデータベースシステムを使うので、MySQLをインストールします。laravelはMySQL以外にもSQLiteやPostgreSQLなどを使うこともできます。

brewコマンドを使ってmysqlをインストールします。

brew install mysql

mysqlのサーバーを起動させます。

mysql.server start

Starting MySQL
. SUCCESS! 

mysqlサーバーに接続します。

mysql -u root

3. Composerをインストール

brewコマンドでcomposerでインストールします。

brew install composer

4. Laravelのプロジェクトディレクトリを作成

composerコマンドでlaravelのプロジェクトディレクトリを作成します。

composer create-project laravel/laravel your-project-name --prefer-dist

作成したプロジェクトディレクトリに移動

cd your-project-name

laravelサーバーを起動

php artisan serve

起動ができたら、 http://localhost:8000/ にアクセスしましょう。

スクリーンショット 2019-11-29 0.28.36.png

参考

Mac へ MySQL を Homebrew でインストールする手順

Mac に Homebrew で composer をインストールする (201808)

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