Notice: Undefined index: tag_id in /home/users/0/moo.jp-logic/web/www.pgmemo.tokyo/memo.php on line 242
PHPフレームワークLaravel|%E3%83%A2%E3%83%87%E3%83%AB|プログラムメモ
人気のPHP WEBアプリケーションフレームワークLaravelのTips。 (Laravelアプリの初期化)composer create-project laravel/laravel my-app :タグ「%E3%83%A2%E3%83%87%E3%83%AB」での検索

Laravel 10 へのアップグレード

古いLaravelアプリを 最新 Laravelへアップグレードする場合、おすすめの方法は

● 1. Laravel アプリを初期化

composer create-project laravel/laravel my_app

● 2. composer.json で使用しているパッケージをcomposerコマンドを使ってインストール

自動的に最新バージョンがインストールされます。
また、最新の環境でインストールできないものも教えてくれます。

composer require <パッケージ名>
# ● インストールNG パッケージ
chumper/zipper

# ● 廃止パッケージ
fideloper/proxy

# ● Laravel11で廃止されるパッケージ
laravelcollective/html → spatie/laravel-html

● 3. フレームワーク以外のファイルを手動で移動させる

・モデル app/Models
・コントローラー app/Http/Controllers/Admin/ConvertController.php
・ビュー app/resources
・DBマイグレーションファイル app/databases
No.2482
03/06 21:08

edit

削除されたヘルパー関数 Call to undefined function str_limit() を修正する

以下のような str_limit() 関数で「Call to undefined function str_limit()」エラーになることがあります

str_limit($publication->name_zh, 50, '...')

パッケージ laravel/helpers をインストールします

composer require laravel/helpers
No.2481
03/06 16:13

edit

Laravel 10 から削除された Model の $dates を $casts で書き換える

● Laravel 10 から削除された Model の $dates を $casts で書き換える

protected $dates = ['posted_date'];

 ↓

protected $casts = [
    'posted_date' => 'immutable_datetime'
];
No.2479
03/06 13:04

edit

nginx で Laravel でのアクセスが 503 になるので調査した

● KUSANAGI 9 の設定を確認する

kusanagi ratelimit status <プロファイル名>
kusanagi ratelimit off <プロファイル名>

● nginx の設定を確認する

nginx.conf

こちらを適宜変更します。

limit_req_zone $remote_addr$http_x_forwarded_for zone=one:10m rate=100r/s;

nginx 再起動

nginx -s reload

● php-fpmの設定を変更する

sudo vi /etc/opt/kusanagi/php-fpm.d/www.conf

こちらを適宜変更します。

pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15

php-fpm再起動

systemctl status php-fpm.service
systemctl restart php-fpm.service
No.2446
01/16 18:33

edit

LaravelでFirebaseのjwt(accessToken)を認証する。認証ミドルウェアを作成する(kreait / laravel-firebase)

● kreait / laravel-firebaseを使ってLaravelでFirebaseのjwt(accessToken)を認証する

1. kreait / laravel-firebase のインストール

composer require kreait/firebase-php
composer require kreait/laravel-firebase

2. ファイル config/firebase.php の自動生成

php artisan vendor:publish --provider="Kreait\Laravel\Firebase\ServiceProvider" --tag=config
vi tests/Unit/LaravelFirebaseTest.php

3. config/app.phpの「Class Aliases」の配列に以下を追加

'ServiceProvider' => Kreait\Laravel\Firebase\ServiceProvider::class,

4. .envへ以下の設定項目を追加。

# Firebase設定
FIREBASE_CREDENTIALS=firebase.json

5. Firebase コンソールへログインし、「プロジェクトの設定」→「新しい秘密鍵を生成」から jsonファイルをダウンロードする。

ダウンロードしたファイルを firebase.json としてプロジェクトトップへアップロードします。

6. ユニットテストファイルを作成する

vi tests/Unit/LaravelFirebaseTest.php
<?php

namespace Tests\Unit;

use Tests\TestCase;
use Kreait\Firebase\Exception\Auth\RevokedIdToken;
use InvalidArgumentException;

class LaravelFirebaseTest extends TestCase
{
    /**
     * @testdox JWTトークン取得
     */
    public function test_verify_firebase_auth_jwt()
    {
        // テストしたいトークン
        $token = "ey....................................";

        // @see vendor/kreait/laravel-firebase/src/ServiceProvider.php
        $auth = app('firebase.auth');
        try {
            // @see https://github.com/kreait/firebase-php/blob/7.x/src/Firebase/Contract/Auth.php
            $parsedToken = $auth->verifyIdToken($token, $checkIfRevoked = true);
        } catch (InvalidArgumentException $e) {
            return $e->getMessage();
        } catch (RevokedIdToken $e) {
            return $e->getMessage();
        }

        dump($parsedToken->claims());

        $this->assertSame($parsedToken->claims()->get('name'), "<Firebaseに登録しているユーザー名>");
    }
}

7. ユニットテストの実行

php artisan test --testdox tests/Unit/LaravelFirebaseTest.php

・Auth.php のメソッドはこちらを参照

https://github.com/kreait/firebase-php/blob/7.x/src/Firebase/Contract/Auth.php

● Laravel用のFirebase認証ミドルウェアを作成する

1. ミドルウェア firebase を 作成する

vi app/Http/Middleware/Firebase.php

app/Http/Middleware/Firebase.php

<?php

namespace App\Http\Middleware;

use Kreait\Firebase\Contract\Auth;
use Kreait\Firebase\Exception\Auth\FailedToVerifyToken;

class Firebase
{
  private Auth $auth;
  public function __construct(Auth $auth)
  {
    $this->auth = $auth;
  }

  public function handle($request, \Closure $next)
  {
    $idTokenString = $request->headers->get('authorization');
    $token = trim(str_replace('Bearer', '', $idTokenString));

    try {
      $verifiedIdToken = $this->auth->verifyIdToken($token);
    } catch (FailedToVerifyToken $e) {
      echo 'The token is invalid: ' . $e->getMessage();
    }

    $user_id = $verifiedIdToken->claims()->get('user_id');
    $email = $verifiedIdToken->claims()->get('email');

    $request->attributes->set('firebase_data', [
      'user_id' => $user_id,
      'email' => $email,
    ]);

    return $next($request);
  }
}

2. 作成したミドルウェアを Kernel に登録

vi app/Http/Kernel.php

app/Http/Kernel.php

    protected $middlewareAliases = [
        ........
        // 次の行を追加
        'firebase' => \App\Http\Middleware\Firebase::class,
    ];

3. /api/ping エンドポイントを追加(認証なし)

vi routes/api.php
Route::get('/ping', function () {
    return 'ping ok!!!';
});

まずはトークンなしでアクセスします(ブラウザからでok) http://YOUR-SERVER/api/ping

4. /api/ping エンドポイントを追加(認証あり)

vi routes/api.php

先程のルートにミドルウェアを追加します

Route::group(['middleware' => 'firebase'], function () {
    Route::get('/ping', function () {
        return 'ping ok!!!';
    });
});

PostmanやVS Code Thunder Client などでアクセスします。

添付ファイル1
No.2400
09/25 12:27

edit

添付ファイル

PHP , Laravel で vscode Intelephense のローカル変数の型エラーに対応する

vscode の Intelephense で以下のようなエラーが出る時があります。

        $disk = Storage::disk($disk_driver);
        $disk->path("tmp"); // ここで path というメソッドがないと怒られる

そのような時は以下のようにしてローカル変数の方を明示しておきましょう

        /** @var Storage::disk */
        $disk = Storage::disk($disk_driver);
        $disk->path("tmp");  // エラーでない
No.2366
06/30 15:13

edit

Laravel で アスペクト志向 Laravel-Aspect

● Laravel-Aspect

https://github.com/ytake/Laravel-Aspect

https://www.slideshare.net/KenjiroKubota/laravel-aspect

● 登場する用語

引用 : https://camp.trainocate.co.jp/magazine/whats-aop-programming/

①アドバイス(Advice) 処理内容を記述したものを「アドバイス」と呼びます。
②ジョインポイント(JoinPoint) アドバイスを注入する場所を「ジョインポイント」と呼びます。
アドバイスを結合(ジョイン)する場所(ポイント)と覚えましょう。
③ポイントカット(PointCut) アドバイスをジョインするポイントにおいて、処理を実行する条件などを指定したい場合もあるでしょう。
その指定方法を「ポイントカット」と呼びます。
アドバイスとジョンポイント、ポイントカットの覚え方
アドバイスとジョインポイント、ポイントカットはセットで覚える必要があるので注意しましょう。
覚え方のコツとしては、【ポイントカット】の場合、【アドバイス】を【ジョインポイント】へ注入するというように、ひとつの文章として覚えておくとよいでしょう。
④アドバイザ(Advisor) 実際にAOPを実装する際には、アドバイスとポイントカットはセットで記述することになります。
アドバイスとポイントカットの両方をもつモジュールクラスを作成する場合、そのクラスを「アドバイザ」と呼びます。
⑤インターセプタ(Interceptor) Interceptは「割り込み」の意味をもちます。
インターセプタは、本来はAOPの用語ではないため注意しましょう。
ただし、インターセプタの目的は「特定の処理に対して共通処理を注入する」であり、AOPにおけるアドバイザと同じような意味合いで利用されます。
ちなみに、AOPに非対応でもインターセプタをサポートしているフレームワークも多く存在します。
それらにあわせて、アドバイザのクラス名を「~Interceptor」とする場合もありますので、インターセプタの存在自体は覚えておいて損はないでしょう。
⑥プロキシとターゲット(Proxy , Target) プロキシは「アドバイスをもつオブジェクト」、ターゲットは「プロキシされる(アドバイスが注入される)オブジェクト」を指します。 プロキシとターゲットは対になる単語ですので、こちらもセットで覚えておきましょう。
No.2304
03/27 10:18

edit

Laravel で inertia を使用するときの cdn での css ファイルの設置方法

resources/views/app.blade.php に記述します

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title inertia>{{ config('app.name', 'Laravel') }}</title>

    <!-- ここに記述します -->
    <link href="/assets/css/my.css" rel="stylesheet">

    <!-- Fonts -->
    <link rel="stylesheet" href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap">

    <!-- Scripts -->
    @routes
    @vite(['resources/js/app.js', "resources/js/Pages/{$page['component']}.vue"])
    @inertiaHead
</head>
<body class="font-sans antialiased">
    @inertia
</body>
</html>

No.2285
02/16 21:37

edit

● Laravel10 + Vue3 + inertia

● Laravelのインストール

composer create-project --prefer-dist laravel/laravel laravel10-app dev-master
cd laravel10-app
php artisan -V
cat package.json

● Vue3のインストール

npm install
npm update
npm i @vitejs/plugin-vue
cat package.json 

● vite.config.js を変更

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue'; // ← この行を追加


export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
        vue(), // ← この行を追加
    ],
});

● vueの表示を確認

resources/js/app.js を以下のようにします

import './bootstrap';

// 追加
document.getElementById('app').textContent = 'Hello Vite !';

resources/views/welcome.blade.php をごっそり以下のように書き換えます

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Laravel</title>
  @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
  <div id="app"></div>
</body>
</html>

サーバーを2つ起動します。

1. vue3のdev serverを起動

npm run dev

2. laravel serverを起動

mamp などのサーバーを起動するか、 laravel sailを起動します。

(laravel sail を起動する場合は以下の sail インストールを実行後に次のコマンドを実行します)

php artisan serv

● sailインストールする場合は以下を実行します。

composer require laravel/sail --dev
php artisan sail:install

● inertia のインストール

laravel側 ( composer )

composer require inertiajs/inertia-laravel

フロントエンド側 ( node.js/npm )

npm install @inertiajs/inertia @inertiajs/inertia-vue3 --save-dev

● inertia の表示テスト ( http://localhost/InertiaSample で inertiaで接続された サンプルページを表示させる )

routes/web.php に以下を追加

// inertiテスト
Route::get('inertia-sample', function () {
    return Inertia::render('InertiaSample', ['message' => 'hello Inertia !!!',]);
});

resources/js/Pages/InertiaSample.vue

<template>
    <h1>InertiaSample</h1>
    <div>{{ props.message }}</div>
</template>

<script lang="ts" setup>
type Props = {
    message: string;
};
const props = defineProps<Props>();
console.log("● props");
console.log(props);
</script>

フロントアプリをビルドするため次のコマンドを実行します

npm run dev

(一度起動すると常駐してファイル変更のたびにビルド(開発用ビルド)します。)

これでアクセスしてみます。
http://localhost/InertiaSample

● inertia Link を使用する

a タグの代わりに Link コンポーネントを使用します。

<script lang="ts" setup>
import { Link } from "@inertiajs/vue3";
</script>
<template>
    <h1>navi</h1>
    <Link href="/home">ホームへ移動</Link>
</template>

● PRIME Vue を使用する

npm install primevue@^3 --save
npm install primeicons --save
No.2284
02/14 17:37

edit

LaravelでFirebaseのjwt(accessToken)を認証する

PHP用のいろいろなパッケージがありますが、firebase / php-jwt を使用してみます

● firebase / php-jwt

https://github.com/firebase/php-jwt

インストール

composer require firebase/php-jwt
composer require guzzlehttp/guzzle

 

vi tests/Unit/VerifyFirebaseTokenTest.php

tests/Unit/VerifyFirebaseTokenTest.php

<?php

namespace Tests\Unit;

use Tests\TestCase;
use Firebase\JWT\JWT as FirebaseJWT;
use Firebase\JWT\Key;
use Illuminate\Support\Facades\Http;

class VerifyFirebaseTokenTest extends TestCase
{
    /**
     * @testdox JWTトークンが正しい場合 verifyJWT() メソッドは true を返す
     */
    public function test_verify_firebase_auth_jwt()
    {
        $verify_result = $this->verifyJWT("<検証したいjwtをここに貼り付ける>");
        $this->assertTrue($verify_result);
    }

    /**
     * Firebase Auth の jwtを検証する(公開鍵はgoogleサーバから取得する)
     * @param   string    $accessToken 
     * @returns boolean
     */
    public function verifyJWT($accessToken)
    {
        $kid = $this->getKidFromJwtHeader($accessToken);
        $publicKey = $this->getPublicKeyFromFirebase('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com', $kid);
        $key = new Key($publicKey, 'RS256');

        $decoded = null;
        try {
            $decoded = FirebaseJWT::decode($accessToken, $key);
            return ($decoded) ? true : false;
        } catch (\Throwable $th) {
            // throw new Error('error 発生');
            return false;
        }
    }


    /**
     * jwtからヘッダの中のkidを取得
     * @param   string    $token 
     * @returns string
     */
    public function getKidFromJwtHeader($token)
    {
        $jwtHeader = $this->getJwtHeader($token);
        $obj = json_decode($jwtHeader, true);
        return $obj['kid'];
    }

    /**
     * jwtからヘッダを取得
     * @param   string $token 
     * @returns string
     */
    public function getJwtHeader($token)
    {
        $tokenHeader = preg_split('/\./', $token)[0];
        $jwtHeader = base64_decode($tokenHeader);
        return $jwtHeader;
    }

    /**
     * Firebaseのjwt検証用公開鍵を取得する
     * @param   string      $url 
     * @param   string      $kid 
     * @returns string
     */
    public function getPublicKeyFromFirebase($url, $kid)
    {
        $response = Http::get($url);
        $body = $response->body();
        $publicKeys = json_decode($body, true);
        $key = $publicKeys[$kid];
        return $key;
    }
}

● テストの実行

php artisan test --testdox  tests/Unit/VerifyFirebaseTokenTest.php

● 認証後に Laravelユーザーと紐付ける

公式の Userモデルを参考に Userモデルにカラム「uid_google」を追加する

php artisan make:migration change_userss_table_add_uid_google  --table=users

マイグレーションファイルを以下の内容で保存します

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('uid_google',128)->nullable()->after('id')->comment('UID(Firebase)');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('uid_google');
        });
    }
};
php artisan migrate
No.2223
09/24 14:37

edit

Laravel の イベント/リスナー を同時、または個別に作成する

● Laravel の イベント/リスナー を同時に作成する

1. app/Providers/EventServiceProvider.php にイベントとリスナーのセットを記述する

    protected $listen = [
    	// .....

    	// 追記する
        // 追記
        \App\Events\TenantDatabaseCreate::class => [
            \App\Listeners\TenantDatabaseCreateListner::class,
        ],

ネームスペースは 先頭の バックスラッシュから記述します ( \App... )

2. artisanコマンドで イベント/リスナー を同時に作成する

php artisan event:generate

これで次の2ファイルが自動生成されます。

app/Events/TenantDatabaseCreate.php

● イベントのみを作成する

php artisan make:event TenantDatabaseCreate

● リスナーのみを作成する

php artisan make:listener TenantDatabaseCreateListner --event=TenantDatabaseCreate
No.2220
10/04 12:59

edit

php composer で ローカルパッケージをインストールする

● php composer で ローカルパッケージをインストールする

以下のような名前のローカルパッケージを使用したいとします

myname/mypackage

1. ローカルパッケージを以下のディレクトリに設置する

localPackage/myname/mypackage

2. パッケージを使う側(つまり、アプリ側)の composer.jsonに以下を追記する

    "repositories": [
        {
            "type": "path",
            "url": "./localPackage/myname/mypackage",
            "options": {
                "symlink": true
            }
        }
    ],
    "require": {
        "myname/mypackage": "@dev",
    }

composer install コマンドからインストールする

composer install
No.2200
08/05 11:16

edit

Laravel Sail で開発環境を整える for Mac + VS Code

● Docker のインストール

こちらのサイトから Docker Desktop ダウンロードしてインストールします
https://www.docker.com/get-started/

● Laravel Sail のインストール

インストールしたいディレクトリに移動してターミナルから以下のコマンドを実行します

アプリ名 : my-app でインストールします

curl -s "https://laravel.build/my-app" | bash

このコマンドでは

mysql
mailhog
meilisearch
redis
selenium

が自動でインストールされます。

インストールしたいアプリケーションを限定する場合は以下のように明示的に示します

アプリ名 : my-app 、インストールアプリ : mysqlでインストールします

curl -s "https://laravel.build/my-app?with=mysql" | bash

指定できるオプションは以下の通りです

mysql, pgsql, mariadb, redis, memcached, meilisearch, minio, selenium

インストールが完了したら次のコマンドで起動します

cd my-app
./vendor/bin/sail up

● 毎回 ./vendor/bin/sail と入力するのが面倒だ

.bash_profile

# sail
alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail'

● ポート番号を変更する

トップディレクトリ直下に .env があるのでそれの1番下に以下のように設定します

.env

# ポート変更
APP_PORT=20080           # http://localhost:20080/
FORWARD_DB_PORT=23306   # MySQLのポート
FORWARD_REDIS_PORT=26379    # Redisのポート

ポート番号を変えた場合は、アプリケーションのURLのポート番号も変更します

.env

APP_URL=http://localhost:20080

● php artisan コマンドを実行する

./vendor/bin/sail artisan

● composer コマンドを実行する

./vendor/bin/sail composer

● docker コンテナ一覧を表示させる

docker ps

NAMES のところを見てコンテナ名を取得します

● dockerコンテナのシェルの中に入る

docker exec -i -t コンテナ名 bash

● VS Code で簡単に使えるようにする

./vendor/bin/sail artisan sail:install --devcontainer

以下のように聞かれますので使用しているアプリケーションをカンマで区切って入力します

 Which services would you like to install? [mysql]:
  [0] mysql
  [1] pgsql
  [2] mariadb
  [3] redis
  [4] memcached
  [5] meilisearch
  [6] minio
  [7] mailhog
  [8] selenium
 > 0,3

mysqalとredisを使用する場合

実行すると ディレクトリ .devcontainer にファイルができます。

一度マシンを再起動して、次はいきなりフォルダーを vscode で開いてみましょう。

● Laravel Sail に vim エディタをインストールする

apt update; apt -y upgrade
apt-get install vim -y

● Laravel Sail で ssl 接続できるようにする

● ryoluo/sail-ssl

https://github.com/ryoluo/sail-ssl

1. コンテナに入って状態から以下を実行

composer require ryoluo/sail-ssl --dev
php artisan sail-ssl:install
php artisan sail-ssl:publish

2. vscodeを再起動してdevcontainerで動かす

3. https://localhost/ にアクセスする

4. ssl証明書エラーとなる場合は、次のURLへアクセスして設定を変更する

chrome://flags/#allow-insecure-localhost

● PHP 7.4 で Laravel Sail を使用する

次の方法で最初からインストールします。

1. ・ (PHP7.4)Laravel Sail インストーラーファイルの作成

curl -s "https://laravel.build/myapp?with=mysql" > laravel_sail_installer.sh

2. ・ (PHP7.4)インストーラーファイルの書き換え

    laravelsail/php81-composer:latest \

      ↓

    laravelsail/php74-composer:latest \

3. ・ (PHP7.4)アプリ名の変更

myapp となっているところを全て作成したいアプリ名に変更します

4. ・ (PHP7.4)インストールの実行

sh laravel_sail_installer.sh 

cd <アプリ名>
./vendor/bin/sail up
添付ファイル1
No.2199
02/24 13:53

edit

添付ファイル

Laravel で Collection の Invalid argument supplied for foreach() を回避

下記のコードは $post_collection が null の時にエラーとなります。

foreach ($post_collection as $post) {
    ....
}

こちらのエラーが出る

Invalid argument supplied for foreach()

・修正方法01 optionalを使用する

foreach ( optional($post_collection) as $post) {
    ....
}

・修正方法02 null 判別

if ($post_collection !== null) {
    foreach ($post_collection as $post) {
        ....
    }
}
No.2178
06/17 10:55

edit

Laravel Eloquent でモデル取得後に再取得する

Laravel Eloquent でDBからデータ取得後に再取得するには次の2つのメソッドがあります。

● 新しいインスタンスを返す fresh()

$newPost = $post->fresh();

● 既存インスタンスを更新する refresh()

$post->refresh();

エラーになってしまう場合は手動で再取得するといいでしょう

$newPost = \App\Post::find($post->id);
No.2177
06/17 09:44

edit

Laravel の /api で csrf を導入する

● フロント側

withCredentials: true を有効にして post します。

  const axiosPost = axios.create({
    withCredentials: true
  });
      axiosPost.put(`http://localhost:8000/api/posts/${params.id}`, data
      ).then(res => {});

● Laravelバックエンド側

app/Http/Kernel.php

        'api' => [
            \App\Http\Middleware\EncryptCookies::class, // 追加
            \Illuminate\Session\Middleware\StartSession::class, // 追加
            \App\Http\Middleware\VerifyCsrfToken::class, // 追加

            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

.env

# ● COR設定
SESSION_DOMAIN=localhost

config/cors.php

    'allowed_origins_patterns' => ['/(localhost):?[0-9]*/'],  // ● localhost:3000 追加
    'supports_credentials' => true,     // ● trueに変更

config/session.php

    'secure' => false,

● 1. まず、CSRFトークンがない状態でデータの削除が失敗することを確認する

ターミナルから以下のコマンドを実行して失敗することを確認する

(URL は適宜書き換えてください。以下の例では postsテーブルの ID =2のデータを削除しに行きます)

curl -X DELETE http://localhost:8000/api/posts/2
 <title>Page Expired</title>

が返ってくる場合 csrf プロテクションが有効です

● 2. 次にフロントエンドから削除なり修正なりを実行してみます

わざと csrf エラーを出したい場合は

  const axiosPost = axios.create({
    withCredentials: true
  });

を使わないようにするとエラーが出ます

No.2146
01/27 16:32

edit

LaravelでAWSのロードバランサーにおいてhttpsがhttpになってしまう問題に対応する

● LaravelでAWSのロードバランサーにおいてhttpsがhttpになってしまう問題に対応する

app/Providers/AppServiceProvider.php

    public function boot()
    {
        // AWSロードバランサー対応
        if (request()->isSecure()) {
            \URL::forceScheme('https');
        }
    }

app/Http/Middleware/TrustProxies.php

protected $proxies = '*';
No.2113
12/13 12:48

edit

Laravel Sanctum によるSPA認証とLaravelサーバとフロントエンドのローカルサーバーを別IPでのテスト

● Laravel アプリの初期化と Sanctum のインストール

前提条件

フロントエンドとサーバーサイドで別サーバーを立てる場合は同じトップレベルドメインにつ所属している必要があります。
つまりフロントエンドだけ http://127.0.0.1 といった環境では sanctum の認証はうまく動作しません。
token error (The MAC is invalid) となります。

うまくいく例:
サーバーサイド : api.test.com
フロントエンド : local.test.com

Laravel Sanctum には 「1.APIトークン認証」「2. SPA認証(セッション+クッキー)」の2つの認証機能があります。
今回は 2. SPA認証を実装してテストしてみます

composer  create-project laravel/laravel my_app
cd my_app
# 以下 sanctum のインストール
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate


● apiミドルウェアに認証チェックを追加

これにより /api/xxxxx のすべてのURLに認証チェックが入ることになります。

app/Http/Kernel.php:42

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,  // ● Laravel Sanctum 追加
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],


● CORの設定

config/cors.php 次の設定を変更します

    'paths' => ['api/*', 'sanctum/csrf-cookie', 'api-register', 'api-login', 'api-logout'], // ● 3つのエンドポイントを追加
    'allowed_origins_patterns' => ['/localhost:?[0-9]*/'],  // ● localhost:3000 追加
    'supports_credentials' => true,     // ● trueに変更


● セッションCookieの設定

config/session.php:158 次の設定を確認します

    'domain' => env('SESSION_DOMAIN', null),

.envファイルの SESSION_DOMAIN の値を読み取る設定となっているので、 .env の設定を変更します。

サブドメインに対応するために先頭を . にします
例: dev.myhost.com の場合

.env

# config/session.php の SESSION 設定
SESSION_DOMAIN=".myhost.com"


● sanctumの設定

config/sanctum.php:16 の次の設定を確認します

    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
    ))),

これは次のホスト名をファーストパーティーとして認識させます

localhost
localhost:3000
127.0.0.1
127.0.0.1:8000
::1
env('APP_URL')

開発や実際に動作させるサーバー名がこちらのリスト↑ にない場合は追加するか、 .env の5行目の APP_URL を変更します


● 「ユーザー登録API」「ログインAPI」の作成

app/Http/Controllers/ApiAuthController.php を以下の内容で作成

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Models\User;
use \Symfony\Component\HttpFoundation\Response;

class ApiAuthController extends Controller
{
    public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name'        => 'required',
            'email'       => 'required|email',
            'password'    => 'required'
        ]);

        if ($validator->fails()) {
            return response()->json('validation error', Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        User::create([
            'name' =>  $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        return response()->json('User registration completed', Response::HTTP_OK);
    }

    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => 'required|email',
            'password' => 'required'
        ]);

        // ● cookie
        if (Auth::attempt($credentials)) {
            $request->session()->regenerate();
            return new JsonResponse(['message' => 'ログインしました']);
        }
        if ( env('APP_ENV') === 'local' ){
            $email = $request->get('email');
            $password = $request->get('password');
            return response()->json("User Not Found or password don't match. (email:{$email})(password:{$password}) ", Response::HTTP_INTERNAL_SERVER_ERROR);
        }
        else {
            return response()->json("User Not Found or password don't match.", Response::HTTP_INTERNAL_SERVER_ERROR);
        }

        // ● token
        // if (Auth::attempt($credentials)) {
        //     $user = User::whereEmail($request->email)->first();

        //     $user->tokens()->delete();
        //     $token = $user->createToken("login:user{$user->id}")->plainTextToken;

        //     return response()->json(['token' => $token], Response::HTTP_OK);
        // }
        // return response()->json("User Not Found or password don't match.", Response::HTTP_INTERNAL_SERVER_ERROR);
    }
}

routes/web.php に以下を追加

// (SPAクッキー認証)ユーザー登録 / ログイン / ログアウト
Route::post('/api-register', [\App\Http\Controllers\ApiAuthController::class, 'register']);
Route::post('/api-login'   , [\App\Http\Controllers\ApiAuthController::class, 'login']);
Route::post('/api-logout'   , [\App\Http\Controllers\ApiAuthController::class, 'logout']);


● 検証用の Vue.js (cli)ファイルを作成

公開フォルダーに検証用のhtml , js ファイルを置きます。 Vue.js と axios を使用して検証します。

public/test/test-login.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <mycomponent></mycomponent>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>
    <script src="./test-api.js"></script>
    <script>
        new Vue({
            el: '#app'
        });
    </script>
</body>
</html>

public/test/test-api.js

Vue.component('mycomponent', {
    data: function () {
        return {
            user: {},
            userState: 'ログインチェック前'
        }
    },
    mounted: function () {
        var self = this;
        // ログインチェック実行
        self.getUser();
    },
    methods: {
        doLogin: function (event) {
            var self = this;
            const instance = axios.create({
                withCredentials: true
            })

            const formdata = {};
            formdata.email = event.target.elements.email.value;
            formdata.password = event.target.elements.password.value;

            instance.get('https://YOUR-SERVER.COM/sanctum/csrf-cookie/')
                .then(function (response) {

                    instance.post('https://YOUR-SERVER.COM/api-login', formdata)
                        .then(function (response) {
                            console.log('● api-login result');
                            console.log(response);
                            alert('ログインしました');
                            // ログインチェック実行
                            self.getUser();
                        })
                        .catch(function (error) {
                            alert('api-login エラー');
                        });
                });
        },
        getUser: function () {
            var self = this;
            this.userState = '問い合わせ中 ...............';

            const instance = axios.create({
                withCredentials: true
            })

            setTimeout(() => {
                instance.get('https://YOUR-SERVER.COM/api/user/')
                    .then(function (response) {
                        console.log('● ログイン中のユーザー情報');
                        console.log(response.data);
                        self.user = response.data;
                        self.userState = `ログイン中 ( ${response.data.name} / ${response.data.email} )`;
                    })
                    .catch(function (error) {
                        self.userState = '未ログイン';
                        self.user = {};
                    });
            }, 500);
        },
        doLogout: function (event) {
            var self = this;
            const instance = axios.create({
                withCredentials: true
            })
            const formdata = {};
            instance.post('https://YOUR-SERVER.COM/api-logout', formdata)
                .then(function (response) {
                    alert('ログアウトしました');
                    // ログインチェック実行
                    self.getUser();
                })
                .catch(function (error) {
                    alert('api-logout エラー');
                });
        }
    },

    template: `
    <div>
    <form action="/api-login/" @submit.prevent="doLogin">
        <h5>ログイン</h5>
        <input type="text" name="email" value="">
        <input type="text" name="password" value="">
        <button type="submit">Vue.jsによるログイン実行</button>

        <hr>

        <h5>ログインユーザーの取得</h5>
        <button type="button" @click="getUser">ユーザー情報取得</button>
        <div style="display:inline-block"> → {{userState}}</div>

        <div v-if="user.id">
        <h5>ログインユーザーのログアウト</h5>
        <button type="button" @click="doLogout">ログアウト</button>
        </div>
    </form>
    </div>
    `
});

● テスト用ユーザーの作成

tinker を起動して以下のコードでユーザを作成します。

php artisan tinker
\DB::table("users")->insert([
    'name'                => 'テストユーザー' ,
    'email'               => 'test@user.com' ,
    'password'            => \Hash::make('1234') ,
]);

これで以下の情報でログインすることができます

ID : test@user.com
PASSWORD : 1234

● WEBブラウザからテストの実行

こちらのURLにウェブブラウザでアクセスします

https://YOUR-SERVER.COM/test/test-login.html

Vue.js を通して axios から以下のエンドポイントへxhrを投げます

/sanctum/csrf-cookie/ (getメソッド)(チェック無し) CSRF-TOKEN を暗号化した XSRF-TOKEN を取得します。

/api-login  (postメソッド)(1.csrfチェック)ログインの実行
/api-logout (postメソッド)(1.csrfチェック)ログインの実行

/api/user (getメソッド)(1.csrfチェック無し 2.sanctumログインチェック)ログインの実行

/api/ で始まるURLのみ「2.sanctumログインチェック」が入ります。
/api/ 以外のURLのpostメソッドのみ「1.csrfチェック」が入ります。

● SSL環境でのローカルマシン( https://:local.myhost.com:3000 )からテストの実行

testディレクトリをローカルマシンの適当なところにダウンロードしてきてそこにExpressサーバーを起動するserver.jsを作成します

server.js ( local.myhost.com )は適宜読み替えてください。

'use strict';
const express = require('express');
const serveIndex = require('serve-index');
const fs = require('fs');

// ==============================サーバ名とポートをセット
const host = 'local.myhost.com';
const port = 3000;
// ==============================サーバ名とポートをセット

const app = express();
const server = require('https').createServer({
    key: fs.readFileSync('./privatekey.pem'),
    cert: fs.readFileSync('./cert.pem'),
}, app)
app.use(express.static('.'));
app.use(serveIndex('.', {icons: true}));
// app.listen(port, host);

server.listen(port, host, () => console.log(`Server Started    https://${host}:${port}`))
openssl req -x509 -newkey rsa:2048 -keyout privatekey.pem -out cert.pem -nodes -days 365
npm init -y
npm i -S express serve-index
node server.js

でローカルサーバーを起動します
こちらからも同様にアクセスができるかどうか検証しましょう。

httpsサーバについてはこちらも参考にしてください
Macのローカルマシンの Express で https:// なサーバを立ち上げて Google Chromeからアクセスする|プログラムメモ

● /etc/hosts の書き換え

0.0.0.0          local.myhost.com

と書き換えて、ローカルマシンを騙します。 これで https://local.myhost.com:3000 ローカルのマシンにアクセスできます

● ローカルサーバーでCORエラーとなる場合

config/cors.php を確認しましょう

    'paths' => ['api/*', 'sanctum/csrf-cookie', 'api-register', 'api-login', 'api-logout'], // ● 3つのエンドポイントを追加
    'allowed_origins_patterns' => ['/localhost:?[0-9]*/'],  // ● localhost:3000 追加
    'supports_credentials' => true,     // ● trueに変更

● ローカルサーバーでCSRF token mismatch.エラーとなる場合

.env を本番環境とlocalhost では切り替える必要があります。

ローカルからアクセスする場合の .env

( myhost.com は適宜読み替えてください)

# config/session.php の SESSION 設定 (サブドメインを除いたドット始まりで記述する。  api.myhost.com の場合 .myhost.com と記述する)
SESSION_DOMAIN=".myhost.com"

# (●local) フロントエンドをローカルマシンにする場合は必ず設定。ドメインとポート番号を記述すること。
# フロントエンドのマシンが https://front.myhost.com:3000  の場合  front.myhost.com:3000 と記述すること
SANCTUM_STATEFUL_DOMAINS=local.myhost.com:3000


# (●local)  SESSION_SAME_SITE は次のうちから選択  ("lax", "strict", "none", null ) デフォルトは "lax"
SESSION_SAME_SITE=none


# (●local)  http:// なサイトから  xhr で送受信するときにCookieをやり取りしたい場合は false をセットしてアンセキュアにする デフォルトは true
# local,本番いずれも 特に変更しなくてデフォルトの true のままで良い
SESSION_SECURE_COOKIE=true
添付ファイル1
No.2072
01/27 15:51

edit

添付ファイル

SPA認証に使用する Laravel Sanctum / Laravel Fortify の違いを確認する

Laravelのヘッドレスな認証の仕組みとして(Laravel Sanctum / Laravel Fortify)があります。
違いをざっくりと認識しておきましょう。

● Laravel Sanctum

https://readouble.com/laravel/8.x/ja/sanctum.html

Laravel Sanctum(サンクタム、聖所)は、SPA(シングルページアプリケーション)、モバイルアプリケーション、およびシンプルなトークンベースのAPIに軽い認証システムを提供します。
 ・APIトークン
 1つ目にSanctumは、OAuthの複雑さなしに、ユーザーにAPIトークンを発行するために使用できるシンプルなパッケージです。この機能は、「パーソナルアクセストークン」を発行するGitHubやその他のアプリケーションに触発されています

 ・SPA認証
 SanctumはLaravelの組み込みのクッキーベースのセッション認証サービスを使用します。通常、SanctumはLaravelの「web」認証ガードを利用してこれを実現します。

Laravel Sanctumのインストールとルートの確認

composer  create-project laravel/laravel sanctum_app
cd sanctum_app
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan route:list

Laravel Sanctumのルート


● Laravel Fortify

https://readouble.com/laravel/8.x/ja/fortify.html

Laravel Fortifyは、Laravelのフロントエンドにとらわれない認証バックエンドの実装です。Fortifyは、ログイン、ユーザー登録、パスワードのリセット、メールの検証など、Laravelの認証機能をすべて実装するために必要なルートとコントローラを登録します。
基本的にLaravel Fortifyは、Laravel Breezeのルートとコントローラを持っており、ユーザーインターフェイスを含まないパッケージとして提供しています。

Laravel Fortifyのインストールとルートの確認

composer  create-project laravel/laravel fortify_app
cd fortify_app
composer require laravel/fortify
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"
php artisan route:list

Laravel Fortifyのルート

Laravel Fortifyの機能の概要

引用 : https://bit.ly/30aQ3bJ

基本となるログイン(Rate Limmiting機能付き)の仕組みにプラスして、
以下のFeatureがON/OFFできるようになっています。(config/fortify.php)
---------------------------
登録機能(registration)
パスワードリセット機能(resetPasswords)
メール認証機能(emailVerification)
プロフィール情報の更新機能(updateProfileInformation)
パスワードの更新機能(updatePasswords)
2段階認証機能(twoFactorAuthentication)
---------------------------
添付ファイル1
添付ファイル2
No.2071
10/14 09:09

edit

添付ファイル

Laravel で apiResource を使用する

● Laravel で apiResource を使用する

・A. ルートを限定しないで使用する

Route::apiResource('news', App\Http\Controllers\NewsController::class);

作成されるルーティング

| GET|HEAD  | api/news        | news.index   | App\Http\Controllers\NewsController@index    | api |
| POST      | api/news        | news.store   | App\Http\Controllers\NewsController@store    | api |
| GET|HEAD  | api/news/{news} | news.show    | App\Http\Controllers\NewsController@show     | api |
| PUT|PATCH | api/news/{news} | news.update  | App\Http\Controllers\NewsController@update   | api |
| DELETE    | api/news/{news} | news.destroy | App\Http\Controllers\NewsController@destroy  | api |
| GET|HEAD  | api/user        |              | Closure                                      | api |


・B. ルートを限定して使用する

Route::apiResource('news', App\Http\Controllers\NewsController::class)->only(['index','show']);

作成されるルーティング

| GET|HEAD  | api/news        | news.index   | App\Http\Controllers\NewsController@index    | api |
| GET|HEAD  | api/news/{news} | news.show    | App\Http\Controllers\NewsController@show     | api |
No.2061
10/05 16:25

edit

Laravel で モデルのプライマリキーをordered UUID (ULID) に変更する

● A. Laravel で rorecek/laravel-ulid を使ってモデルのプライマリキーをordered UUID (ULID) に変更する

1. rorecek/laravel-ulidのインストール

composer require rorecek/laravel-ulid

2. テーブルの型の変更をする

$table->bigIncrements('id');

   ↓

$table->char('id', 26)->primary();

3. モデルに以下を追加する

    use HasUlid;

    public $incrementing = false;
    
    protected $keyType = 'string';

以上です。

引用 : https://qiita.com/mitashun/items/90891c4f9e95bfabfe17

● B. Laravel で モデルのプライマリキーをordered UUID (ULID) に変更する

(例として News.php モデルのプライマリキーを変更してみます)

・1. モデルのプライマリキーの型を変更します

以下のプロパティーを追加します

app/Models/News.php

    // uuidなのでインクリメントOFF
    public $incrementing = false;

    // uuidなので string型
    protected $keyType = 'string';    

・2. モデルの新規作成時に自動的にorderedUuid をIDリセットするようにフックを追加する

同じくモデルファイルに以下を追加します

app/Models/News.php

    protected static function boot()
    {
        parent::boot();

        self::creating(function(News $model) {
            $model->id = \Str::orderedUuid();
        });
    }

・3. マイグレーションファイルの設定を変更して再度データベースを作成します

database/migrations/xxxxxxxx_create_news_table.php

// $table->increments('id');     // コメントアウト
$table->uuid('id')->primary(); // uuidに変更

マイグレーションファイルをいちどロールバックして再実行します

php artisan migrate:rollback
php artisan migrate

● その他にも ULID を生成するライブラリ symfony/uid というのがあります

● PHP7.3の場合は次のバージョンをインストールします

composer require symfony/uid:5.4.21

● PHP8.1以上の場合は最新バージョンをインストールします

use Symfony\Component\Uid\Ulid;

$ulid = new Ulid();  // e.g. 01AN4Z07BY79KA1307SR9X4MV3
No.2060
07/04 11:21

edit

Laravelでエラー画面からワンクリックでソースコードのエラー箇所に移動する

● Laravelでエラー画面からワンクリックでソースコードのエラー箇所に移動する

1. .env でエディタを設定する

.env にエディタ VS Code を設定する

IGNITION_EDITOR="vscode"

他にも以下のエディターが選択可能です

phpstorm
vscode
vscode-insiders
vscodium
textmate
emacs
sublime
atom
nova
macvim
idea
netbeans
xdebug

2. エラー画面で鉛筆のアイコンをクリックする

ここをクリックするだけで自分の好きなエディターでエラー箇所へ移動することができます。

3. ddの代わりに ddd を使用する

ダンプメソッドddの代わりに ddd を使用することもできます。 dddを使用した場合は同じような画面が表示されます。

dd( $test );
 ↓
ddd( $test );

4. 設定ファイルを生成する

php artisan vendor:publish

選択画面になるので次のクラスを選択します

Facade\Ignition\IgnitionServiceProvider

5. ssh remote development

で使用する https://github.com/facade/ignition/issues/314

添付ファイル1
vscode.png ( 62.7 KBytes ) ダウンロード
No.2051
10/07 17:09

edit

添付ファイル

laravel-ide-helper を使用する

● laravel-ide-helper を使用する

次の2ステップの作業のみでVS CodeやPhpStormでコード補完(インテリセンス)が効くようになります。

● 1. laravel-ide-helperのインストール

https://github.com/barryvdh/laravel-ide-helper

composer require --dev barryvdh/laravel-ide-helper

● 2. 設定ファイルを出力する

Facade のコードの補完をできるようにする

こちらのコマンドを実行すると _ide_helper.php ファイルを生成します

php artisan ide-helper:generate

Model のプロパティ補完をできるようにする

こちらのコマンドを実行すると _ide_helper_models.php ファイルを生成します
モデルファイルの格納ディレクトリ を --dir で指定します

php artisan ide-helper:models -N --dir="app/Models"

以上でOKです。

● VS Code を使用している場合は、設定を変更する

1. PHP Intelephense を インストールする

2. PHPの基本言語サポートをオフにする

拡張機能を「@builtin php」で検索して「PHPの基本言語サポート」をオフにします

● .gitignore に追加しておく

生成される2つのファイルは開発のファイルなので gitから除外しておきましょう

.gitignore

# ide-helperのファイルは除外する
/_ide_helper.php
/_ide_helper_models.php

参考 : https://qiita.com/PruneMazui/items/74034913bcd4af7a4eaf

● composer でエラーが出る場合

composer で何かしらのエラーが出る場合はファイル _ide_helper.php _ide_helper_models.php を削除してから Composer コマンドを実行します。 実行後に再度生成します。

No.2050
02/24 18:22

edit

Laravel8 で 独自のバリデーション ルールを作成する

● Laravel8 で 独自のバリデーション ルールを作成する

大きく分けると次の2つの方法があります

● 1. サービスプロバイダーを使用する方法 (おすすめです)
● 2. バリデーションルールを使用する方法

● 1-1. (サービスプロバイダ) バリデーションサービスプロバイダーの自動作成

php artisan make:provider ValidatorServiceProvider

app/Providers/ValidatorServiceProvider.php が自動生成されます

● 1-2. (サービスプロバイダ) ValidatorServiceProvider.php の boot メソッドにルールを記述

    public function boot()
    {
        \Validator::extend(
            'mytext',
            function ($attribute, $value, $parameters, $validator) {
                return preg_match('/^[0-9]{3}-?[0-9]{4}$/', $value);
            }
        );
    }

● 1-2. (サービスプロバイダ) config/app.php の 'providers' => [] に 作成した ValidatorServiceProvider.php を登録

config/app.php

/*
* Application Service Providers...
*/
App\Providers\ValidatorServiceProvider::class,

これだけでOKです!



● 2-1. (バリデーションルール) カスタムバリデーションルールファイルの自動作成

 php artisan make:rule Hankaku  

app/Rules/Hankaku.php が自動作成されます

● 2-2. (バリデーションルール) passes() メソッドにバリデーションが成功となる条件を記述する

バリデーションがOKの時に true を返すように記述します

    public function passes($attribute, $value)
    {
        return preg_match('/^[a-zA-Z0-9]+$/', $value);
    }

● 2-3. (バリデーションルール) message() メソッドにエラーメッセージを記述する

  public function message()
  {
    return ':attribute は半角英数字で入力してください';
  }

● 2-4. (バリデーションルール) 使用する

formRequest.php

public function rules(): array
    {
        return [
            'zipcode' => ['required', new Hankaku],
        ];
    }

配列で記述します。パイプ記法では使用できないみたい?

No.2042
09/17 14:16

edit

Laravel の オブジェクトが Carbon かどうかを判別する

● $model の プロパティ $k が Carbon オブジェクトかどうかを判別する

is_a($model->{$k}, 'Illuminate\Support\Carbon')
No.2041
09/17 09:53

edit

Laravel8でデフォルトのページネーション(pagination)をtailwindからbootstrapに変更する

● pagination のテンプレートファイル一式をコピーする

pagination ファイルを resources/views/vendor/pagination/ フォルダーにコピーする

php artisan vendor:publish --tag=laravel-pagination

これでデフォルトの pagination の bladeテンプレートファイルが resources/views/vendor/pagination/tailwind.blade.php になります。

● デフォルトの pagination のテンプレートファイルをtailwindからbootstrapに変更する

app/Providers/AppServiceProvider.php に以下のコードを追加する

    public function register()
    {
        // pagination を bootstrapに変更
        \Illuminate\Pagination\Paginator::useBootstrap();
    }
No.2040
09/16 10:22

edit

Laravel 8 で Bootstrap の laravel/ui を利用する

● Laravel 8 で Bootstrap の laravel/ui を利用する

composer  create-project laravel/laravel my_app
cd my_app
composer require laravel/ui
php artisan ui bootstrap --auth
npm install && npm run dev
app/Providers/AppServiceProvider.php を修正
.env を修正してDB設定を書き込む
php artisan migrate

これでエラーが表示されることがあるがその場合は以下のようにする

1. change "sass": "^1.33.0", to "sass": "1.32.13", in package.json.
2. package-lock.json を削除
3. node_modules フォルダを削除
4. run npm install


Tailwind CSS を利用したい場合はこちら
Laravel Breeze で シンプルな認証 ( with Tailwind CSS )|プログラムメモ

No.2039
10/25 17:45

edit

Laravelでモデルファイルで「接続するデータベース」「テーブル名」「プライマリキー」「ガードするカラム」を変更する

● Laravelでモデルファイルで「接続するデータベース」「テーブル名」「プライマリキー」「ガードするカラム」を変更する

    // ● データベースを変更する (config/database.php の connections.mysql_system_b を参照する)
    protected $connection= 'mysql_system_b';


    // ● テーブル名を変更する
    protected $table = 'item_dt';


    // ● プライマリキーを変更する
    protected $primaryKey = 'data_id';


    // ● ガードするカラムを変更する
    protected $guarded = ['data_id', 'modified_date'];
No.2035
09/03 16:10

edit

Laravel Breeze で シンプルな認証 ( with Tailwind CSS )

● laravel/breeze のインストール

composer require laravel/breeze

● artisanコマンドで breezeのインストール

php artisan breeze:install
npm install && npm run dev

これで画面の右上の「Login」「Register」画面が登場します。


Tailwind CSS ではなく Bootstrapが良い場合はこちら
Laravel 8 で Bootstrap の laravel/ui を利用する|プログラムメモ

No.2034
10/25 17:44

edit

Laravel Eloquentで件数( count )の取得

● Laravel Eloquentで件数( count )の取得

$counts = \App\Estimate::where('project_status_id', '=', 30)->count();

実際に実行されるSQL文は次のようになります

   "query" => "select count(*) as aggregate from `estimates` where `project_status_id` = ?"
    "bindings" => [
      0 => 30
    ]
No.2031
08/27 09:13

edit

Laravel のモデルにタグを追加するパッケージ rtconner/laravel-tagging を使用する

● Laravel のモデルにタグを追加するパッケージ rtconner/laravel-tagging を使用する

https://packagist.org/packages/rtconner/laravel-tagging


# ● モデルに以下を追加

例: /app/Post.php にトレイトを追加

    use \Conner\Tagging\Taggable;           // tag


● モデル取得時にタグのリレーションも同時に取得する

eager loadingされます

$post = \App\Post::with('tagged')->first();


● モデルに「タグを付ける」「タグを外す」「タグを更新する」

$post->tag('Gardening'); // attach the tag
$post->tag('Gardening, Floral'); // attach the tag
$post->tag(['Gardening', 'Floral']); // attach the tag

$post->untag('Cooking'); // remove Cooking tag
$post->untag(); // remove all tags

$post->retag(['Fruit', 'Fish']); // delete current tags and save new tags
$post->retag('Fruit', 'Fish');
$post->retag('Fruit, Fish');
No.2029
08/12 08:53

edit

Laravel で タグを簡単に取り扱う spatie/laravel-tags

● spatie/laravel-tags の インストール

PHP 8 & Laravel 8 以上の場合

composer require spatie/laravel-tags

PHP 7.4 & Laravel 8 以上の場合

composer require spatie/laravel-tags:3.1.0

PHP 7.2 & Laravel 5.8 以上の場合

composer require spatie/laravel-tags:2.7.2

● spatie/laravel-tags 設定ファイルの publish

php artisan vendor:publish

一覧が表示されるのでそこから「Provider: Spatie\Tags\TagsServiceProvider」の横に表示されている番号を入力します

Copied File [/vendor/spatie/laravel-tags/database/migrations/create_tag_tables.php.stub] To [/database/migrations/2021_08_02_160308_create_tag_tables.php]
Copied File [/vendor/spatie/laravel-tags/config/tags.php] To [/config/tags.php]

DBテーブルをマイグレーションする

php artisan migrate

● モデルでタグを取り扱う

// ============= Trait
use \Spatie\Tags\HasTags;
// ============= Trait
dump( $post->tagsTranslated );
No.2027
08/06 16:51

edit

Laravel で inertia.js を使用する

● jetstremのインストール

・1.A ・Laravelアプリインストール時に jetstrem 付きでインストールする

laravel new project-name --jet

・1.B ・Laravelアプリインストール後に 後から jetstrem をインストールする

composer require laravel/jetstream

・2.B ・チーム機能を使わない場合

php artisan jetstream:install inertia

・2.B ・チーム機能を使う場合

php artisan jetstream:install inertia --teams

・3.・フロントアプリのビルド

npm install && npm run dev

・4 ・データベースマイグレーション

php artisan migrate

● サンプル画面を表示させる

routes/web.php

Route::get('/test', function () {
    return Inertia::render('Test');  // Test.vue を表示させる
});

resources/js/Pages/Test.vue

<template>
    <h1>Hello world!</h1>
    <inertia-link href="/" class="text-sm text-blue-700 underline">back to HOME</inertia-link>
</template>

● ユーザー登録 / ログイン画面を表示する

http://localhost/register

http://localhost/login

No.2024
02/13 11:47

edit

Laravel 8 で LiveWire を使用する

● Laravel 8 の Livewire とは

Laravel8では
・inertia.js を選択すればvue.jsで作成されたスキャフォールディングを使用する。
・Livewire を選択すれば PHP + Blade + ajax で作成されたスキャフォールディングを使用する。
・Laravel Breeze をインストールすればPHP + Bladeのみで作成されたスキャフォールディングを使用する。

● livewireのインストール

1. パッケージのインストール

composer require livewire/livewire

2. assets と config の出力

php artisan livewire:publish


● blade.php で LiveWire を読み込む

以下のタグで livewire を読み込みます

    @livewireStyles
</head>
<body>
    ...

    @livewireScripts
</body>
</html>

● laravel-livewire-tables をインストールして使用する

1. パッケージのインストール

composer require rappasoft/laravel-livewire-tables

2. make:livewire コマンドから bladeとClassを作る

php artisan make:livewire posts-table

2. Post モデルの一覧表示の livewire クラスを作成する

touch app/Http/Livewire/PostsTable.php

以下の内容で作成します


welcome.blade.php

@extends('layout')

@section('content')

    @yield('content__header')
    @yield('content__search')

    <div class="row">
        <div class="col-md-12">
            @livewire('posts-table')
        </div>
    </div>
@endsection

resources/views/livewire/posts-table.blade.php

<div>
    <div class="row">
        <div class="col">
            <input wire:model="search" class="form-control" type="text" placeholder="Search books...">
        </div>
    </div>

    <div class="row">
        <table class="table table-hover table-condensed table-striped table-bordered table_sm">
            <thead>
            <tr>
                <th class="sortable">ID</th>
                <th class="sortable">
                    <a wire:click.prevent="sortBy('title')" role="button" href="#">
                        Title
                    </a>
                </th>
                <th class="sortable">
                    <a wire:click.prevent="sortBy('author_id')" role="button" href="#">
                    Author
                    </a>
                </th>
            </tr>
            </thead>
            <tbody>
            @foreach ($posts as $post)
                <tr>
                    <td>{{ $post->id }}</td>
                    <td>{{ $post->name }}</td>
                    <td>{{ $post->content_name }}</td>
                </tr>
            @endforeach
            </tbody>
        </table>
    </div>
</div>

app/Http/Livewire/PostsTable.php

<?php

namespace App\Http\Livewire;

use Livewire\Component;

class PostsTable extends Component
{

    use \Livewire\WithPagination;

    public $perPage = 10;
    public $search = '';
    public $sortField = 'id';
    public $sortAsc = true;

    public function render()
    {
        $posts = \App\Post::search($this->search)
            ->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc')
            ->paginate($this->perPage);
        return view('livewire.posts-table', ['posts' => $posts]);
    }
}


● 既存の livewire コンポーネントをインストールして使用する

https://github.com/imliam/awesome-livewire

● livewire と vue.js の違い

livewire では vue.js や react と異なり、ajaxでhtmlコードを返します。
検索はサーバーサイド( DBによる再select )を使用します。
No.2021
09/03 11:11

edit

Fast-Excel で Laravelでエクセルファイル、CSVファイルをダウンロードさせる

● インストール

composer require rap2hpoutre/fast-excel

● Excelファイル( .xlsx )のダウンロード

コントローラーから次のように呼び出します

use Rap2hpoutre\FastExcel\FastExcel;

return (new FastExcel(User::all()))->download('file.xlsx');

● CSVファイル( .csv )のダウンロード

コントローラーから次のように呼び出します。 (拡張子をCSVにするとCSVでダウンロードされます)

use Rap2hpoutre\FastExcel\FastExcel;

return (new FastExcel(User::all()))->download('file.csv');

↑ このコードでは

・\App\User モデルの全データが出力
・\App\User モデルの全カラムが出力
・文字コード 「UTF-8 With BOM」
・CSVのダブルクォーテーション ““ での 囲みは自動(値にクォーテーションを含むなど必要な時だけクォーテーションがつく。)

でダウンロードされます。

● ダウンロードさせるCSVに出力するカラムを指定する

		return (new FastExcel(User::all()))->download('file.csv', function ($user) {
			return [
				'メールアドレス'         => $user->email,
				'姓'     => strtoupper($user->lastname),
				'名'    => $user->firstname,
			];
		});

このようにすると3つのカラムのみ出力します。

● CSVの区切り文字とUTF8のBOMの設定をしてダウンロードさせる.

use Rap2hpoutre\FastExcel\FastExcel;
return (new FastExcel(User::all()))->configureCsv(',', '"', 'UTF-8', false)->download('file.csv');

パラメーターは次の通りです

configureCsv($delimiter = ',', $enclosure = '"', $encoding = 'UTF-8', $bom = false)
No.2017
07/05 09:57

edit

Laravel で エラー(Exception)発生時に管理者へメール通知する

● Laravel で エラー(Exception)発生時に管理者へメール通知する

・LaravelアプリケーションでエクセプションがThrowされたときに、Adminモデルの管理者宛にメールを通知してみます

● 1. Adminモデルに Notifiable トレイトを追加

/app/Admin.php

class Admin extends Authenticatable
{
    use \Illuminate\Notifications\Notifiable;

● 2. Notification ExceptionEmail を作成する

ExceptionEmail の名前は適宜変更可能です

php artisan make:notification ExceptionEmail

app/Notifications/ExceptionEmail.php が自動生成されます

エラー内容 Exception を受け取れるようにメンバ変数を設定します。
コンストラクタのタイミングでエラー内容を受け取ります

    private $ex;

    public function __construct( \Exception $ex )
    {
        $this->ex = $ex;
    }

また toEmail() メソッドを次のように修正します

    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->greeting('ERRORが発生しています')
            ->line( \Request::fullUrl() )
            ->line( 'Exceptionクラス名 (' . get_class($this->ex) .')' )
            ->line( "{$this->ex->getFile()}:{$this->ex->getLine()}" )
            ->line( "{$this->ex->getMessage()}");
    }

● 3. Exception発生時にフックをかけて通知処理を間に挟む

app/Exceptions/Handler.php

    public function report(Throwable $exception)
    {
        parent::report($exception);
    }

  ↓ このメソッドに管理者へメール通知する命令を追記します。(この例ではIDが1の管理者へメールを送信しています)

    public function report(Throwable $exception)
    {
        // 管理者にメール通知する
        $admin = \App\Admin::where('id','=',1)->first();
        // AuthenticationException|AuthorizationException|HttpException|ModelNotFoundException|ValidationException 以外の場合は 通知する
        if ( ! preg_match("/(AuthenticationException|AuthorizationException|HttpException|ModelNotFoundException|ValidationException)/", get_class($exception)) ){
            $admin->notify(new \App\Notifications\ExceptionEmail($exception));
        }
        // 管理者にメール通知する

        parent::report($exception);
    }

正規表現で次の Exception クラスの場合はメールを送信しないようにしています

\Illuminate\Auth\AuthenticationException
\Illuminate\Auth\Access\AuthorizationException
\Symfony\Component\HttpKernel\Exception\HttpException
\Illuminate\Database\Eloquent\ModelNotFoundException
\Illuminate\Validation\ValidationException

以上です。
とても簡単ですね。
これでエラーが発生すると管理者宛にこのようなメールが到着します

添付ファイル1
No.2016
10/29 10:12

edit

添付ファイル

Laravel で x日経過したファイルを削除する

● Laravel で x日経過したファイルを削除する

tmp/my_folder 内で 1日以上経過したテンポラリファイルを削除する

// 1日経過したテンポラリファイルを削除
collect(\Storage::disk('local')->listContents('tmp/my_folder', true))
->each(function($file) {
    if ($file['type'] == 'file' && $file['timestamp'] < now()->subDays(1)->getTimestamp()) {
        // dd( "{$file['path']} を削除する" , \Storage::disk('local')->path($file['path']) );
        \Storage::disk('local')->delete($file['path']);
    }
});
No.2012
06/21 13:28

edit

Laravel で JPEG画像の最適化(ファイルサイズ減少)コマンド jpegoptim を使用する

● jpegoptim の インストール

sudo yum install -y jpegoptim

または

sudo dnf install -y jpegoptim

インストールの確認

jpegoptim --version

● jpegoptim の コマンド

・余計な情報を取り除く

jpegoptim --strip-all sample.jpg

・圧縮率80%で圧縮する

jpegoptim --max=80 sample.jpg

・サブディレクトリの複数ファイルに対して実行する

jpegoptim --max=80 **/*.jpg

実行例 (40.82% サイズが小さくなりました)

sample_02.jpg 1400x1194 24bit N JFIF  [OK] 369121 --> 218432 bytes (40.82%), optimized.

・圧縮率75%で圧縮する

jpegoptim --max=75 sample.jpg

実行例 (46.37% サイズが小さくなりました)

sample_03.jpg 1400x1194 24bit N JFIF  [OK] 369121 --> 197954 bytes (46.37%), optimized.

圧縮率 75% 〜 85% あたりがおすすめです。

・ファイルサイズを半分にする

jpegoptim --size=50% sample.jpg

実行例 (50.48% サイズが小さくなりました)

sample_04.jpg 1400x1194 24bit N JFIF  [OK] 369121 --> 182787 bytes (50.48%), optimized.

● spatie / laravel-image-optimizer のインストール

・1. インストール

composer require spatie/laravel-image-optimizer

・2. 設定ファイルを自動生成する

php artisan vendor:publish --provider="Spatie\LaravelImageOptimizer\ImageOptimizerServiceProvider"

・3-A. 自動で画像最適化を行うルートを通るようにする

// app/Http/Kernel.php
protected $routeMiddleware = [
   ...
   'optimizeImages' => \Spatie\LaravelImageOptimizer\Middlewares\OptimizeImages::class,
];

・3-B. 手動で任意のタイミングで画像最適化を行う

use ImageOptimizer;

// the image will be replaced with an optimized version which should be smaller
ImageOptimizer::optimize($pathToImage);

// if you use a second parameter the package will not modify the original
ImageOptimizer::optimize($pathToImage, $pathToOptimizedImage);
No.2010
04/15 13:39

edit

Laravel で シングルクォートのみエスケープをやめる

● Laravel で シングルクォートのみエスケープをやめる

<meta name="description" content="{!! addslashes($description) !!}" />

  ↓

<meta name="description" content="{!! preg_replace("{\\\'}", "'", addslashes($description) ) !!}" />
No.1999
05/21 12:54

edit

Laravel の モデルでカテゴリの様な階層化データを扱う

● Laravel の モデルでカテゴリの様な階層化データを扱う

● lazychaser / laravel-nestedset

https://github.com/lazychaser/laravel-nestedset

● etrepat / baum

https://github.com/etrepat/baum

No.1989
04/26 09:10

edit

Laravel で 404ページを表示する代わりに任意のURLにリダイレクトする

● Laravel で 404ページを表示する代わりに任意のURLにリダイレクトする

routes/web.php

必ずページの1番下に 次のように記述します

// =============== fallback 404 ===============
Route::fallback(function () {
	return redirect('/my_error.php');
});

● findOrFail() などの時も任意のURLにリダイレクトする場合

上記の方法を実行しても、findOrFail() メソッドなどを実行したときのリダイレクト先は並べる独自の404ページになってしまいます そこで 404ページを作成してリダイレクトさせます。(強引ですが。)

/resources/views/errors/404.blade.php にファイル下記の内容で作成します。

  <script>window.location = "/my_error.php";</script>
No.1971
03/15 18:19

edit

Laravelで特定のルートだけCSRFミドルウェアをオフにする

app/Http/Middleware/VerifyCsrfToken.php

class VerifyCsrfToken extends Middleware
{
    protected $except = [];
}

  ↓

class VerifyCsrfToken extends Middleware
{
    protected $except = [ '/front_ajax/*' ];
}

No.1962
03/12 19:09

edit

Laravel で ラジオボラン

● Laravel で ラジオボラン

@php
    $k = 'vaccination_flag';
    $class_name = 'form-controlx';
    $style = 'width:70px;';
    $input_values_array = ['1' => '有り' , '0' => '無し'];
    $_form_value = old('vaccination_flag',@$user[$k]);
@endphp
@foreach ( $input_values_array as $input_k => $input_v )
  <label class="{{$class_name}}" style="{{$style}}"><input name="{{$k}}" type="radio" value="$input_v" @if( $_form_value===$input_v ) checked @endif> {{$input_v}}</label>
@endforeach
No.1956
02/26 22:14

edit

● Laravel で バリデーションを使用する

● Laravel で バリデーションを使用する

一番シンプルなやり方はコントローラーに次のように記述します

● 方法1. Laravel で シンプルなバリデーションをコントローラーに使用する

public function confirm( Request $request )
{
        $validation_rule = [
            "name_sei"           => 'required' ,
        ];
        // バリデーション(エラーがある場合は前の画面に戻ります)
        $this->validate( $request, $validation_rule );
}

● Laravel のバリデーションの記述方法には2種類あります

1. パイプでつなぐ記法

'item_id' => 'required|integer',

2. 配列記法 (正規表現で | を使用したい時はこちら。)

'item_id' => ['required', 'integer'] ,
'name'    => ['required','regex:{^[^/]+$}'] ,

● よく使う Laravel のバリデーション記述

'hogehoge_date'   => 'nullable|date',            		 // 日付( null を許す )
'hogehoge_time'   => 'date_format:H:i',            		 // 時間( 12:40 や 07:05 などのフォーマット)
// regex を使用する時は array() を使用しましょう。 | が正規表現の文字として使えるようになります。
'hogehoge_time2'   => array( 'regex:{((0?[0-9])|(1[0-9])|(2[0-9])|(3[0-9])|(4[0-9])|(5[0-9]))}' ) ,  // 分( 00 〜 59 または 0 〜 59  )
'hogehoge_id'     => 'nullable|integer',          		 // 数字( null を許す )
'hogehoge_tax_no' => 'required_if:withtax_flg,0', 		 // 条件( withtax_flg が 0 の時は hogehoge_tax_no 必須 )
'price_initial_no' => 'required_unless:mt_payment_id,21', // 条件( mt_payment_id が 21以外 の時は price_initial_no 必須 )
'grade_no'      => 'required_with:school_id',  // 条件( school_id に値がある時 grade_no 必須。条件となるカラムはカンマ区切りで複数指定可 )
'send_flg'         => 'integer|in:0,1' , //  0 または 1 の数値
'year_month_name'          => ['required','regex:{^[0-9]+/[0-9]+$}'] ,  // 正規表現 2019/12 のような表記にマッチするか?
'email' => 'required|email|confirmed',        // 必須 , メールアドレス , 「email_confirmation」にも同じ値が入っているかチェック
'password' => 'nullable|confirmed',         // nullを許す, 「password_confirmation」にも同じ値が入っているかチェック
'name'    => ['required','regex:{^[^/]+$}'] , // 入力文字列にスラッシュを含めない
'password'      => 'required|min:4',        // 4文字以上

● 全ての checkbox にチェックが入っていることを確認する

// rule
$validation_rule['confirmations'] =  [ 'required', function($attribute, $value, $fail) use ($confirmations_count) {
    if ( $confirmations_count !== count($value) ){
        return $fail('確認事項全てにチェックをつけてください');
    }
  }
];

// message
$validation_message = [
    'confirmations.required' => '確認事項にチェックをつけてください',
];  

チェックが一つも入っていない時は required のエラー「確認事項にチェックをつけてください」が表示されます
全てにチェックが入っていない時は クロージャー のエラー「確認事項全てにチェックをつけてください」が表示されます

● すでにDBに登録ずみのメールアドレスを除外(ユニーク)

・1-1. ユニーク

テーブル users の中に同じ email で登録がある場合はバリデーションエラーとしたい、場合は次のように記述します

'email' => 'unique:users'

・1-2. ユニーク( update時)

↑ 上の記述は不完全で、データ更新(update)の時に自分自身のメールアドレスもエラーとしてしまいます。
そこで、自分自身は除外するように次のように記述します。

		// update時の validation
		$this->validation_column['email'] = [ 'required',
       	            \Illuminate\Validation\Rule::unique('users')->ignore($id),
		];

・2. 条件付きユニーク( 同じ shop_id と email を持つものをバリデーションエラーとしたい )

// 同じ shop_id の中で email はユニーク
$this->validation_column['email'] = [ 'required', \Illuminate\Validation\Rule::unique('users')->where(function ($query) use ($q) {
	return $query->where('shop_id', $q['shop_id']);
}) ];
// バリデーション実行
$this->validate($request, $this->validation_column);

● すでにDBに登録ずみのメールアドレスを除外を insert,update 時のvalidationをまとめて記述する

次のように記述すると新規登録時と更新時の 登録ずみのメールアドレス除外 バリデーションをまとめて登録することができます。

// 事前にモデルの $id を取得しておきます。
$id = xxxxxxxxxxxxxxxxxx;

$this->validation_column = [
	'email'    => 'required|email|unique:users,email' . ($id > 0 ? ",{$id}" : ''),
	'password' => ($id > 0 ? '' : 'required|') . 'min:4|confirmed',
];

● 方法2. Laravel で FormRequest を使用してバリデーションルールを記述する

独自のバリデーションルールを適用したい時バリデーションルールを動的に変更したいときなどは FormRequest を使用すると、バリデーション部分が外に出るのでコントローラーがすっきりします。

UsersRequest という フォームリクエストを作成します

php artisan make:request UsersRequest

app/Http/Requests/UsersRequest.php が自動作成されます。

例として shop_id と email をチェックしてユニークかどうかを判定するバリデーションを記述してみます

app/Http/Requests/UsersRequest.php

<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UsersRequest extends FormRequest
{
    /**
     * 認証のロジックを記述する場合はここに記述する。
     * それ以外は常に true を返すように記述しておかないとバリデーションが動作しないので return true; とする。
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * バリデーションルールを記述
     *
     * @return array
     */
    public function rules()
    {
        return [
            "shop_id" => 'required' ,
            "email" => [ 'required', 'email', 'confirmed',
                        \Illuminate\Validation\Rule::unique('users')->ignore($this->input('id'))->where(function($query) {
                            // 入力されたshop_idの値と同じ値を持つレコードでのみ検証する
                            $query->where('shop_id', $this->input('shop_id'));
                        }),
            ] ,
        ];

    }
}

・コントローラーを次のように変更する

public function confirm( Request $request )
{
        $validation_rule = [
            "name_sei"           => 'required' ,
        ];
            // バリデーション(エラーがある場合は前の画面に戻ります)
            $this->validate( $request, $validation_rule );
}

  ↓

    /**
     * ユーザー登録確認
     *
     * \App\Http\Requests\UsersRequest による自動バリデーションが行われる(エラーの場合は前の画面に戻される)
     *
     */
    public function confirm( \App\Http\Requests\UsersRequest $request )
    {

これでOKです。コントローラーにバリデーションの記述が一切なくなりました。 コメントはどこかに記述しておくとよいでしょう。


● Blade内でバリデーションエラーを確認する

1. Bladeテンプレート内で確認する

@php
    dump( $errors );
@endphp


● Blade内でバリデーションエラーを表示する

emailパラメーターのエラーを表示させるには

@include('partial.error_message', ['param_name' => 'email'])

Bootstrapの場合

@if($errors->has($param_name))
    <div class="text-danger small" style="flex-wrap: wrap;">{{ $errors->first($param_name) }}</div>
@endif

Tailwind CSSの場合

@error('email')
    <div class="text-red-500 text-xs absolute mt-1">{{ $message }}</div>
@enderror

● Bladeでバリデーションエラーで戻ってきた時に、入力済みの値をフォームに入れて表示する

old を使用します。

<input type="text" placeholder="" name="email" required value="{{ old('email') }}">

https://twitter.com/juandmegon/status/1274134701800919040?s=21

● 全角変換などバリデーションまえの処理を行う

use Illuminate\Support\Str;

/**
 * バリーデーションのためにデータを準備
 *
 * @return void
 */
protected function prepareForValidation()
{
    $this->merge([
        'slug' => Str::slug($this->slug),
    ]);
}

● バリデーションのエラーメッセージを日本語化する

https://pgmemo.tokyo/data/archives/1952.html

● バリデーションのカスタムエラーメッセージを表示する

$validation_rule = [
    'last_name'       => 'required',
];

$validation_message = [
    'last_name.required' => '名前は必ず入力してください',
];

$this->validate( $request, $validation_rule, $validation_message );
No.1485
04/01 17:11

edit

● Laravel で 多対多 のリレーションを扱う

● Laravel全リレーション

1対1
1対多
多対多
Has Many Through
1対1(ポリモーフィック)
1対多(ポリモーフィック)
多対多(ポリモーフィック)

これらのうち 多対多 リレーションを操作してみます。

● Laravel の 多対多リレーションのDB構造

とても簡単です。2つのテーブルをつなぐピボットテーブルを作成すればOKです。

・users
・practitioners

というテーブルを多対多でつなぐ時は
テーブル ↓

・user_practitioner

を作成します。( practitioner_user でも良い。 アルファベット順に並べると規則がはっきりして良いですね。単数形 をアンダースコアでつなげます。)
中身は

テーブル「practitioner_user」の構造

id                  (primary key)
user_id             (integer,unsigned)
practitioners_id    (integer,unsigned)

とします。

マイグレーションファイルの作成

php artisan make:migration create_practitioner_user_table

マイグレーションファイルの up() メソッドは次のようになります。

    public function up()
    {
        Schema::create('practitioner_user', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('practitioner_id');
            $table->unsignedInteger('user_id');
            $table->timestamps();
        });
    }

● Laravel の 多対多リレーション(belongsToMany)をモデルに設定する

多対多リレーションある意味シンプルです。
これを両方のテーブルに(メソッド名は適宜変更して)定義します。
例)Userモデルに設定

    /**
     * ● 多対多 リレーション : ->practitioners でXXXXXを取得する
     * ピボットテーブル「practitioner_user」
     *
     * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
     */
    public function practitioners()
    {
        return $this->belongsToMany('App\Practitioner','practitioner_user');
    }

メンバ名を ->practitioners から ->mypractitioners に にしたい場合は、そのまま

    public function my_practitioners()

とします

● 「検索条件」」「ソート順」を設定して Laravel の 多対多リレーション(belongsToMany)をモデルに設定する

    /**
     * ● 多対多 リレーション
     * ピボットテーブル : practitioner_user
     * ソート順        : id DESC
     *
     * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
     */
    public function practitioners()
    {
        return $this->belongsToMany('App\Practitioner','practitioner_user')->where('is_active', '=', 1)->orderBy('id','DESC');
    }

●多対多リレーションのIDリストを取得する

$model = User::findOrFail($id);
dump( $model->practitioners()->allRelatedIds() );   // (注意)Laravel 5.4以前は getRelatedIds() という名前でした

●多対多リレーションの項目名( カラム名 = practitioner_name )をカンマ区切りで取得する

ユーザ1 , ユーザ2 , ユーザ3 の様な文字列を取得します

$model = User::findOrFail($id);
dump( $model->practitioners()->implode('practitioner_name' , ' , ' ) );

bladeテンプレートに記述するときは以下のようにします。

{!! $consentform->patient->managers->pluck('name')->implode('<br>') !!}

このようにも記述できます。( ↑ と同じです。)

{!! $consentform->patient->managers->implode('name','<br>') !!}

●多対多リレーションの項目名へのリンクを作成する

@php
	$consentform->patient->managers->each(function ($item) {
		$route_manager = route('admin.managers.show', $item->id);
		print <<< DOC_END
			<a href="{$route_manager}" target="_blank">{$item->name}</a><br>
		DOC_END;
	});
@endphp

●多対多リレーションに新たに紐づけする(attach)

$model = User::findOrFail($id);
$model->practitioners()->attach([5,6,7]);    // ピボットテーブルに(すでにあるデータは消さずに)データを追加

●多対多リレーション紐づけを解除する(detach)

$model->practitioners()->detach(1) // id=1の紐付けを解除する

●多対多リレーションをまとめての紐づけと紐づけの解除を行う(sync)

$model = User::findOrFail($id);
$model->practitioners()->sync([11,22,33]);    // ピボットテーブルを指定した配列のデータで更新(すでにあるデータを書き換え)

リレーションを一気に削除する場合は

->sync();

● データコンバートの時にリレーションを追加する(すでにある場合は何もしない)

// 既に持っているリレーションデータをすべて取得
$tmp_all_list = $c->practitioners()->pluck('practitioners.id')->toArray(); // [1,2,3,4,5,6]
if (! in_array($row[0],$tmp_all_list) ){
    array_push($tmp_all_list,$row[0]);
    $c->practitioners()->sync($tmp_all_list);    // ピボットテーブルを指定した配列のデータで更新(すでにあるデータを書き換え)
}

参考: pivot table に他のカラムデータを保存させる https://stackoverflow.com/questions/48323971/laravel-saving-to-pivot-table-in-many-to-many-relationship-with-extra-column

参考 : php - Laravel sync Relation with optional parameters - Stack Overflow

No.1452
05/07 13:57

edit

● Laravel の 1対1リレーション

● Laravel全リレーション

1対1
1対多
多対多
Has Many Through
1対1(ポリモーフィック)
1対多(ポリモーフィック)
多対多(ポリモーフィック)

これらのうち 1対1 リレーションを操作してみます。

● Laravel の 1対1リレーション(hasOne)

呼び出す側に記述します。

    /**
     *  ● 1対1リレーション
     * 対象モデル「App\Doctor」
     *
     */
    public function doctor()
    {
        return $this->hasOne('App\Doctor','id','doctor_id')->withDefault();
    }
    /**
     * 1対1リレーション(hasOne) : ->created_user で作成したユーザーの情報を取得できるようにする
     *
     * @return \Illuminate\Database\Eloquent\Relations\hasOne
     */
    public function created_user()
    {
        return $this->hasOne('App\User', 'id', 'created_user_id')->withDefault();
    }

● Laravel の 1対1リレーション(belongsTo)

呼び出される側に記述します。

    /**
     * ● 1対1リレーション(belongsTo) : ->mt_branch で営業支店の情報を取得できるようにする
     * 対象モデル「App\MtBranch」
     *
     */
    public function mt_branch()
    {
        return $this->belongsTo('App\MtBranch','branch_id')->withDefault();
    }
No.1450
02/20 21:33

edit

● phpの便利な日付、時刻オブジェクト Carbon を使用する。平成、令和などの和暦を表示させる

● 文字列から Carbon オブジェクトを作成する

次のどちらもokです。

$carbon_obj = new Carbon('2019-03-11');
$carbon_obj = new Carbon('2019/03/11');

● UNIX Timestamp から Carbon オブジェクトを作成する

$carbon_obj = \Carbon\Carbon::createFromTimestamp( $v->getMTime() );

● UNIXタイムスタンプを出力する

dd(Carbon::now()->timestamp);

● 現在のタイムゾーンを取得する

echo $dt->timezone->getName();

( Asia/Tokyo )( +00:00 )のような形で返ってきます。

● 現在の Asia/Tokyo の時刻を DBで使用する文字列で取得する

echo Carbon\Carbon::now('Asia/Tokyo')->toDateTimeString()

● 年月日などを出力する

echo $dt->year;
echo $dt->month;
echo $dt->day;
echo $dt->hour;
echo $dt->minute;
echo $dt->second;
2019
5
1
10
42
28

● LaravelのBlade内で直接 Carbon を呼び出して日付を表示させる

bladeテンプレートで Carbon を呼び出すには次のように記述します。

{{ \Carbon\Carbon::now() }}
{{ \Carbon\Carbon::now()->format("Y年m月d日") }}    // 2019年03月05日
{{ \Carbon\Carbon::now()->format("Y年n月j日") }}    // 2019年3月5日
{{ \Carbon\Carbon::now()->format("Y年m月d日 H:i:s") }} // 2019年03月05日 12:30:59
{{ \Carbon\Carbon::now()->format("Y年n月j日 H:i") }} // 2019年3月5日 12:30
{{ \Carbon\Carbon::now()->format("Ymd_His_v") }} // ( v はミリ秒です )20190305_123059_699

format メソッドの引数は PHP の date と同じです。
http://php.net/manual/ja/function.date.php

● Carbonのリファレンス

https://carbon.nesbot.com/docs/

● Carbon で時刻 00:00:00 を指定する

$dt = new \Carbon\Carbon();
$dt->setTime(0, 0, 0)
dd($dt);

● 日付の指定

$dt->setDate($dt->year, $dt->month, 15);

● 日付の指定や加算で注意すること

$dt = new \Carbon\Carbon('2019-05-31');
dump( $dt->copy()->addMonth(1) );
dump( $dt->copy()->addMonthNoOverflow(1) );

 ↓ 結果

date: 2019-07-01 00:00:00.0 Asia/Tokyo (+09:00)
date: 2019-06-30 00:00:00.0 Asia/Tokyo (+09:00)

となります。addMonth() メソッドでは 6月31日がないので、7月1日になるのです。
安全のため addMonth () を 使うのを今すぐやめて addMonthNoOverflow() を使用しましょう
同じく
安全のため addYear () を 使うのを今すぐやめて addYearNoOverflow() を使用しましょう

● addMonthsNoOverflow() と addMonthNoOverflow() ( s ありと s なし)

addMonthNoOverflow は addMonthsNoOverflow を呼び出しているので、ほぼラッパーみたいな感じですが、デフォルトで引数1がセットされるので、1ヶ月だけ追加する場合はaddMonthNoOverflow() だけでもokです。

    public function addMonthsNoOverflow($value)
    public function addMonthNoOverflow($value = 1)

● 日付の加算、減算、指定

// 日付の加算(addition)
$dt->addDay();
$dt->addWeek();
$dt->addMonthNoOverflow();
$dt->addYearNoOverflow();
$dt->addHour();
$dt->addMinute();
$dt->addSecond();

// 日付の減算(subtraction)
$dt->subDay();
$dt->subWeek();
$dt->subMonthNoOverflow();
$dt->subYearNoOverflow();
$dt->subHour();
$dt->subMinute();
$dt->subSecond();

// 昨日
$dt->yesterday();

// 月初
$dt->startOfMonth();  
// 月末 の(00:00:00)  
$dt-> lastOfMonth();  
// 月末 の(23:59:59)  
$dt->endOfMonth();  

// その日の 00:00:00
$dt->startOfDay();

// その日の 23:59:59.999999
$dt->endOfDay();

// parseによる指定(来月末)
$dt->parse('last day of next month');

● Carbon で日本語の曜日を表示させる

setlocale(LC_TIME,'ja_JP.utf8');
echo $dt->formatLocalized('%Y/%m/%d(%a)');
echo $dt->formatLocalized('%Y/%m/%d(%a) %H:%M:%S')

出力例

2020/08/28(金)
2020/08/28(金) 15:41:45

● Carbonで和暦(昭和、平成、令和)を表示させる

こちら https://qiita.com/chiyoyo/items/da32649b0e04957856c1
の DatetimeUtility.php を使わせてもらいましょう

date_default_timezone_set('Asia/Tokyo');

require_once('DatetimeUtility.php');
$dt = Carbon::parse('2019-05-01');

echo DatetimeUtility::date('JK年n月j日 H:i:s', $dt->timestamp)."\n";
echo DatetimeUtility::date('Jk年n月j日 H:i:s', $dt->timestamp)."\n";

$dt->subSecond(1);
echo DatetimeUtility::date('JK年n月j日 H:i:s', $dt->timestamp)."\n";
echo DatetimeUtility::date('Jk年n月j日 H:i:s', $dt->timestamp)."\n";
令和元年5月1日 00:00:00
令和1年5月1日 00:00:00
平成31年4月30日 23:59:59
平成31年4月30日 23:59:59

● 年齢を表示させる

$dt = new Carbon('2000-01-01');
echo $dt->age . '才です';

● マシンの時計を変更する代わりに、Carbonで取得する「今日・現在」の日時を変更する

テストや障害の発生確認などの時に有用です。

Carbon::setTestNow(new Carbon('2019-11-22 11:59:59'));
No.1429
06/25 15:17

edit

Laravel の 多対多リレーションで 中間テーブル(Pivot Table)に値を保持する

● Laravel の 多対多リレーションで 中間テーブル(Pivot Table)に値を保持する

->withPivot('reservation_no') とすると、pivot table に持たせた値を取得することができます。

    /**
     * ● 多対多 リレーション(with 追加カラム reservation_no) : ->programdays であるユーザーの予約日を取得する
     * ピボットテーブル「program_day_user」
     *
     * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
     */
    public function program_days()
    {
        return $this->belongsToMany('App\ProgramDay','program_day_user')->withPivot('reservation_no');
    }

● 中間テーブルの値の取得の仕方

pivot で取得できます

$model->program_days[0]->pivot;
No.1954
02/19 10:33

edit

● Laravel の 1対多 リレーション

● Laravel全リレーション

1対1
1対多
多対多
Has Many Through
1対1(ポリモーフィック)
1対多(ポリモーフィック)
多対多(ポリモーフィック)

これらのうち 1対多 リレーションを操作してみます。

● Laravel の 1対多リレーション(hasMany)

親から子を呼び出すイメージが hasMany です。

    /**
     * ● 1対多リレーション : ->items でショップ内の商品を取得します
     *
     * ソート順 : sort_no , ASC
     *
     */
    public function items()
    {
        return $this->hasMany('App\Item','shop_id')->orderBy('sort_no','ASC');
    }

● 使用方法

->items で取得できます

dump( $user->items );

● Laravel の 1対多リレーション(hasMany)に検索条件( where )をつける

後ろの where() メソッドを追加することで条件をつけることができます。

    /**
     * ● 1対多リレーション : ->items_active でショップ内のアクティブな商品を取得します
     *
     * ソート順 : sort_no , DESC
     *
     */
    public function items_active()
    {
        return $this->hasMany('App\Item','shop_id')->where('is_active','=',1)->orderBy('sort_no','ASC');
    }

● Laravel の 1対多リレーション(belongsTo)

逆に子から親を呼び出すイメージが belongsTo です。

    /**
     * ● 1対多リレーション(belongsTo): ->shop で商品が所属するショップを取得します
     *
     * 1件のみ取得のためソートなし
     *
     */
    public function shop()
    {
        return $this->belongsTo('App\Shop');
    }

● リレーション先の id = 10 を持つデータを検索する場合

whereHas を使用します。(スロークエリとなるので注意) こちらで回避します。
https://qiita.com/mpyw/items/0761a5e44836c9bebcd5

                $model->whereHas('actcategories', function($query) use ($q){
                    $query->where('actcategories.id', '=', $q['actcategory']);
                });
No.1484
06/21 16:00

edit

Laravelで生年月日フォーム

● Laravelで生年月日フォーム

Bootstrap使用

@php
    $dt = new \Carbon\Carbon();
@endphp
<div class="mr-3">
@php
    $k = 'birth_date__year';
    $class_name = 'form-control';
    $style = '';
    $input_values_array = [ "" => "年" ];
    for ($i = 0; $i <= 100; $i++){
      $input_values_array[$dt->format("Y")] = $dt->format("Y");
      $dt->subYearNoOverflow();
    }
    $_form_value = old($k);
@endphp
{{ Form::select($k, @$input_values_array, $_form_value, ['class' => $class_name, 'style' => $style]) }}
@error('birth_date__year')
    <div class="text-danger small">{{ $message }}</div>
@enderror
</div>

<div class="mr-3">
@php
    $k = 'birth_date__month';
    $class_name = 'form-control';
    $style = '';
    $input_values_array = [ "" => "月" ];
    for ($i = 1; $i <= 12; $i++){
      $input_values_array[$i] = $i;
    }
    $_form_value = old($k);
@endphp
{{ Form::select($k, @$input_values_array, $_form_value, ['class' => $class_name, 'style' => $style]) }}
@error('birth_date__month')
    <div class="text-danger small">{{ $message }}</div>
@enderror
</div>


<div class="mr-3">
@php
    $k = 'birth_date__day';
    $class_name = 'form-control';
    $style = '';
    $input_values_array = [ "" => "日" ];
    for ($i = 1; $i <= 31; $i++){
      $input_values_array[$i] = $i;
    }
    $_form_value = old($k);
@endphp
{{ Form::select($k, @$input_values_array, $_form_value, ['class' => $class_name, 'style' => $style]) }}
@error('birth_date__day')
    <div class="text-danger small">{{ $message }}</div>
@enderror
</div>

こんな感じのドロップダウンリストになります

添付ファイル1
No.1953
02/11 11:23

edit

添付ファイル

Laravel バリデーションメッセージを日本語化する

● 1. 日本語ファイルをダウンロードする

https://github.com/Laravel-Lang/lang/tree/master/src/ja

● 2. 項目名を日本語化する

resources/lang/ja/validation.php

に日本語の項目名を追加していく

    'attributes' => [
        'last_name' => '姓' ,
    ] ,

No.1952
02/10 21:22

edit

添付ファイル

LaravelでPHPStanを使用する

PHPStanについてはこちらを読むと理解が深まります
PHPの静的解析 Phan/Psalm/PHPStan の違い - ログ日記

● nunomaduro / larastan のインストール

https://github.com/nunomaduro/larastan

composer require --dev nunomaduro/larastan

● 設定ファイルの作成

vi phpstan.neon

次の内容で保存

includes:
    - ./vendor/nunomaduro/larastan/extension.neon
parameters:
    paths:
        - app
    level: 5

● nunomaduro / larastan ( PHPStan ) の実行

./vendor/bin/phpstan analyse

● VSCodeから使用する

https://github.com/nunomaduro/larastan/issues/139 

No.1949
06/24 09:54

edit

Laravel-Excel で一覧リストをエクセル形式やCSV形式のファイルでダウンロードさせる

● Laravel Excel で一覧リストをエクセルでダウンロードさせる

1. Laravel-Excelのインストール

composer require "maatwebsite/excel"

続けてコンフィグファイルを生成します

php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider"

config/excel.phpが生成されます。

次を変更します

        'csv'                    => [
            'enclosure'              => '',
            'line_ending'            => "\r\n",    // 改行を Windows形式にする
            'use_bom'                => true,      // utf8 With BOM 形式にする
        ],

enclosure は CSVの各項目を囲む文字を設定します。 通常 “ ですが、いったん何もなしで設定しておいて blade 側で“を設定したいから無理だけセットするようにします。

2. Exportクラスの作成

ExportExcel クラスを生成します。

php artisan make:export ExportExcel

ファイル app/Exports/ExportExcel.php が自動生成されます。

3. app/Exports/ExportExcel.php の編集

次のように変更します。

<?php

namespace App\Exports;

use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use Maatwebsite\Excel\Concerns\WithCustomValueBinder;
use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder;


class ExportExcel extends DefaultValueBinder implements FromView, WithCustomValueBinder
{
    private $view;

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

    /**
     * @return View
     */
    public function view(): View
    {
        return $this->view;
    }

    /**
     * 文字列型をセット
     */
    public function bindValue(Cell $cell, $value)
    {
        // 全てを文字列型で返す
        $cell->setValueExplicit($value, DataType::TYPE_STRING);
        return true;
    }
}

$cell->setValueExplicit($value, DataType::TYPE_STRING); メソッドですべてを文字列型に設定しています。
これを設定しないと「0001112222」は「1112222」として出力されてしまいます。

4. コントローラーにダウンロード命令を記述

ファイル名 download.xlsx でダウンロードさせます。

$view = \view('estimates.excel_index', compact('estimates_rows'));
return \Excel::download(new \App\Exports\ExportExcel($view), 'download.xlsx');

5. blade ファイルの作成

<table>
    <thead>
        <tr>
            <th>管理ID</th>
            <th>プログラム名</th>
        </tr>
    </thead>
    <tbody>
        @forelse ( $programs as $program )
            <tr>
                <td>{{ $program->id }}</td>
                <td>"{{ str_replace('"','""',$program->name) }}"</td>
            </tr>
        @empty
            データがありません
        @endforelse
    </tbody>
</table>

1カラム目は クォーテーション 無し
2カラム目は クォーテーション あり
としています。

5. CSVファイルにてダウンロードさせる

ファイル名を download.csv のように拡張子をCSVにするだけでokです

$view = \view('estimates.excel_index', compact('estimates_rows'));
return \Excel::download(new \App\Exports\ExportExcel($view), 'download.csv');

6. サーバー上で保存する

\Excel::download(new \App\Exports\ExportExcel($view), 'download.csv');

とすると、storage/ ディレクトリ直下にファイルが保存されます。

7. CSVファイルの文字コードをsjisにしてダウンロードさせる

一旦 utf-8 のCSVファイルを tmp/myfile.csv 保存し sjis に変換してダウンロードさせます (メモリ上で utf-8 sjis 変換を行なっているのでファイルサイズが大きい場合はメモリエラーになります。 ファイルを1行ずつ処理するように適宜コードを書き換えてください)

// いったんutf8で保存
$file_path = 'tmp/myfile.csv';
$filename = pathinfo($file_path, PATHINFO_BASENAME);
\Excel::store(new \App\Exports\ExportExcel($view), $file_path);

// sjisダウンロード
$disk = \Storage::disk('local');
$content = $disk->get($file_path);

// remove BOM
$bom = hex2bin('EFBBBF');
$content = preg_replace("/^{$bom}/", '', $content);

// utf-8 to sjis
$content = mb_convert_encoding($content, 'sjis', 'UTF-8');

$headers = [
'Content-Type'        => 'text/csv',
'Content-Disposition' => 'attachment; filename="' . $filename . '"'
];
return \Response::make($content, 200, $headers);
No.1938
07/09 13:12

edit

Laravel で intervention/image を使って画像変換する

● Laravel で intervention/image を使って画像変換する

(準備) ImageMagickがインストール済みかどうかを確認する

convert -version

Version: ImageMagick 6.7.8-9 2019-02-01 Q16 http://www.imagemagick.org のようにバージョンが表示されればOKです

(準備) intervention/image のインストール

composer require intervention/image

(準備) /config/image.php の自動生成

php artisan vendor:publish --provider="Intervention\Image\ImageServiceProviderLaravelRecent"

(準備) /config/image.php の設定 (ドライバをimagickに変更 )

    // 'driver' => 'gd'
    'driver' => 'imagick'
“imagick” is much faster than “gd” , even 3 times more faster!

https://bit.ly/3gEtY9N

1. 画像の読み込み

パスから読み込み

$interv = \Image::make( storage_path('app/images/test.png') );

URLから読み込み

$interv = \Image::make( 'https://www.xxx.com/xxx/test.jpg' );

base64データから読み込み

$data = file_get_contents($path);
$data_url = 'data:image/png;base64,'. base64_encode($data);
$interv = \Image::make($data_url);

2. 画像フォーマットの変更

jpgフォーマットへ変換

$jpg = $interv->encode('jpg');

3. 画像を保存

$interv->save($save_path);

4. 画像を Amazon AWS S3 へ保存

S3へ保存するときは文字列変換をかませます

 $data = $interv->__toString();
 \Storage::disk($file_store_disk)->put("{$file_store_dir}/{$image_name}", $data);

● 画像サイズ、画像情報を取得する

$interv->width(); // 幅
$interv->height(); // 高さ
$interv->filesize(); // ファイルサイズ
$interv->mime(); // mimeタイプ
$interv->exif(); // exif情報

● 画像サイズを変更する

横幅 800px
縦幅 自動
でリサイズする

$interv->resize(800, null, function($constraint){
    $constraint->aspectRatio();
});

● 画像を最適化する

https://github.com/spatie/laravel-image-optimizer
https://ariteku.hatenablog.com/entry/2020/08/02/082201

No.1932
05/04 10:10

edit

Laravel で ログインの記録ログを取得する

● Laravel で ログインの記録ログを取得する

1. yadahan / laravel-authentication-log のインストールと準備

パッケージのインストール

composer require yadahan/laravel-authentication-log

設定ファイルの自動作成

php artisan vendor:publish --provider="Yadahan\AuthenticationLog\AuthenticationLogServiceProvider"

DBテーブル「authentication_log」の自動作成

php artisan migrate

2. /app/user.php へトレイトの追加

次のトレイトを追加します

    use Notifiable;                                         // 通知
    use \Yadahan\AuthenticationLog\AuthenticationLogable;   // ログ

これでokです。

ログイン後にDBを確認すると、テーブル「authentication_log」へログイン ID とユーザーエージェントが記録されているのが確認できると思います。

3. 最終ログインの IP と時間を表示させる

resources/views/home.blade.php

                    最終ログイン
                    {{ @Auth::user()->lastLoginAt() }}

                    最終ログインIP
                    {{ @Auth::user()->lastLoginIp() }}
No.1926
12/07 09:54

edit

Laravelで使用するMySQLのエンコーディングを utf8mb4 → utf-8 に変更する

● Laravelで使用するMySQLのエンコーディングを utf8mb4 → utf-8 に変更する

.env

DB_CHARSET=utf8
DB_COLLATION=utf8_general_ci

config/database.php

'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
No.1898
11/08 17:14

edit

Laravel で データベースの定期バックアップとローテーションを行う

● 1. spatie/laravel-backup のインストール

composer require spatie/laravel-backup

● 2. 設定ファイル ( config/backup.php ) の自動生成

php artisan vendor:publish --provider="Spatie\Backup\BackupServiceProvider"

結果

Copied File [/vendor/spatie/laravel-backup/config/backup.php] To [/config/backup.php]
Copied Directory [/vendor/spatie/laravel-backup/resources/lang] To [/resources/lang/vendor/backup]

● 3. 設定ファイルの変更

config/backup.php を変更します

        'mail' => [
            'to' => 'your@example.com',

 ↓

        'mail' => [
            'to' => array_map('trim', explode(',', env('BACKUP_MAIL_TO'))),

( .env から読み込むようにします。)

.env にメール送信先を追加

# DB Backup
BACKUP_MAIL_TO=yourname@your.dcomain.com

引用: https://bit.ly/34a2TXt

● DBの手動バックアップの実行

バックアップの実行 (データベースのみ)

php artisan backup:run --only-db

バックアップの実行 (データベースのみ, 通知機能なし)

php artisan backup:run  --only-db  --disable-notifications

古いバックアップファイルの削除

php artisan backup:clean

設定ファイルにどの期間まで保持するかを設定できるのでそこを好きな設定に書き換えて変更しておきます。

バックアップ先は

storage/app/<アプリ名>/2020-12-31-10-15-00.zip

のようにローカルディスクの中のアプリ名の中に日付のファイル名で保存されます。 変更する場合は設定ファイルを変更します。

● 定期バックアップの設定と実行

app/Console/Kernel.php に次のように記述します

通知が不要な場合は --disable-notifications を後ろにつけます。

        // DBの定期バックアップ
        $schedule->command('backup:clean')->daily();
        $schedule->command('backup:run --only-db')->daily();

->daily() をつけることで、デイリーで実行しています。 外すと 1分ごとにバックアップが実行されます。(Laravelの設定によりますが。)

● バックアップされたファイルの確認

php artisan backup:list
+---------+-------+-----------+---------+--------------+---------------+--------------+
| Name    | Disk  | Reachable | Healthy | # of backups | Newest backup | Used storage |
+---------+-------+-----------+---------+--------------+---------------+--------------+
| my-app | local | ✅         | ✅       |            8 | 0.00 (2分前)  |   1021.23 KB |
+---------+-------+-----------+---------+--------------+---------------+--------------+

というふうに表示されます。

No.1883
10/26 11:59

edit

Laravel で BrowsershotでPDF表示

● BrowsershotでPDF表示

● spatie/browsershot

https://github.com/spatie/browsershot

1. puppeteer のインストール

npm install puppeteer

こちらのコマンドでうまくいかないときは以下のコマンドでインストールします

npm install puppeteer --global

2. browsershot のインストール

composer require spatie/browsershot


3-A. 表示のテスト(Tinker使用)

php artisan tinker
use Spatie\Browsershot\Browsershot;
$timestamp = \Carbon\Carbon::now()->format("Ymd_His_v");
$file_path = "C:/Users/hogehoge/test_puppeteer_{$timestamp}.png";
Browsershot::url('https://www.google.com/?hl=ja')->setOption('args', ['--no-sandbox','--disable-web-security'])->save($file_path);

testpuppeteer<実行日時>.png が作成されれば成功です

3-B. 表示のテスト(ウェブサイトをpdfにして表示する場合)

use Spatie\Browsershot\Browsershot;
$timestamp = \Carbon\Carbon::now()->format("Ymd_His_v");
$file_path = "C:/Users/hogehoge/test_puppeteer_{$timestamp}.png";

Browsershot::url('https://www.google.com/?hl=ja')
    ->setOption('args', ['--no-sandbox','--disable-web-security'])
    ->save( $file_path );

$file = file_get_contents( public_path( $file_path ) );

return response($file, 200)
    ->header('Content-Type', 'application/pdf')
    ->header('Content-Disposition', 'inline; filename="' . $file_path . '"');

● node が見つからないと言うエラーになる場合は明示的に指定します

nodenv を使用している場合は ~/.anyenv/envs/nodenv/versions/ 以下のパスを直接指定します。

/home/kusanagi/.anyenv/envs/nodenv/versions/18.13.0/bin

の場合

->setNodeBinary('/home/kusanagi/.anyenv/envs/nodenv/versions/18.13.0/bin/node')
->setNpmBinary('/home/kusanagi/.anyenv/envs/nodenv/versions/18.13.0/bin/npm')

setNodeBinary でnodeのパスを明示的に指定します

Browsershot::url('https://www.google.com/?hl=ja')
            ->setNodeBinary('/home/kusanagi/.anyenv/envs/nodenv/versions/18.13.0/bin/node')

3-C. 表示のテスト(ローカルのhtmlをpdfにして表示する場合)

use Spatie\Browsershot\Browsershot;
        $html = "<h1>TEST</h1>";
        Browsershot::html( $html )
            ->setOption('args', ['--no-sandbox','--disable-web-security'])
            ->save('./test.pdf');

        $file = file_get_contents( public_path('./test.pdf') );

        return response($file, 200)
            ->header('Content-Type', 'application/pdf')
            ->header('Content-Disposition', 'inline; filename="' . './test.pdf' . '"');

error while loading shared libraries: libatk-1.0.so.0: cannot open shared object file エラーになる場合

puppetter の 共有ライブラリが不足しています。

・足りない共有ライブラリを調べる

cd /YOUR-PATH-TO-PUPPETEER/puppeteer/.local-chromium/linux-818858/chrome-linux
ldd chrome

これで not found と言われている .so をインストールする必要があります。

yumコマンドでインストールしましょう。

● LaravelでPDF作成時に外部cssが読み込まれない不具合の対応

  <link rel="stylesheet" href="{{ url('/assets/css_pdf/pdfprint.css') }}">

 ↓ 次のようにして絶対パスに書き換えます

  <link rel="stylesheet" href="{{ public_path('/assets/css_pdf/pdfprint.css') }}">

● puppetter の LD_LIBRARY_PATH を手動で追加する

vendor/spatie/browsershot/Browsershot.php

    protected function callBrowser(array $command)
    {
        $fullCommand = $this->getFullCommand($command);
        $process = Process::fromShellCommandline($fullCommand)->setTimeout($this->timeout);

        // ● この行を追加 ↓
        $process->setEnv(array('LD_LIBRARY_PATH' => "/PATH/TO/YOUR/puppeteer_lib64" ));

        $process->run();

● verumconsilium/laravel-browsershot を使用する

こちらのパッケージを利用するともっと簡単にPDF出力することができます

composer require verumconsilium/laravel-browsershot
	return \VerumConsilium\Browsershot\Facades\PDF::loadHtml('<h1>TEST印刷</h1>')
			->setNodeBinary('/Users/hogehoge/.anyenv/envs/nodenv/shims/node')
			->setNpmBinary('/Users/hogehoge/.anyenv/envs/nodenv/shims/npm')		
			->inline();
No.1880
08/14 12:51

edit

Laravel Snappy で PDFを生成する

● Snappyのインストール

1. wkhtmltopdf を composer からインストール

composer require h4cc/wkhtmltopdf-amd64 0.12.x

2. snappy パッケージを composer からインストール

composer require barryvdh/laravel-snappy

3. configファイルの自動生成

php artisan vendor:publish --provider="Barryvdh\Snappy\ServiceProvider"

config/snappy.php が生成されます

4. config/snappy.phpの変更

binary を以下のように変更します。

    'pdf' => [
        'enabled' => true,
        'binary' => base_path('vendor/h4cc/wkhtmltopdf-amd64/bin/wkhtmltopdf-amd64'),
        'timeout' => false,
        'options' => [],
        'env'     => [],
    ],

5. 動作確認

web.php

Route::get('/hello', function () {
        $pdf = \PDF::loadHTML('<h1>日本語の表示テストです</h1><style>h1{color:red;}</style>');
        return $pdf->setOption('encoding', 'utf-8')->inline();
});

/hello でアクセスします。

引用: https://bit.ly/3jLIixU

No.1879
10/12 14:06

edit

Laravel の ログインの SESSION_LIFETIME を変更する

● Laravel の ログインの SESSION_LIFETIME を変更する

これがどういう値かと言うと

「ログインを記憶する」にチェックをつけずにログインをした時にログインが有効な時間

と言い換えることができます。

変更箇所

.env

# デフォルトでは2時間
SESSION_LIFETIME=120

  ↓

# 4時間に変更する
SESSION_LIFETIME=240
No.1876
10/09 11:54

edit

Laravelでメール認証をログインさせずに行う

● Laravelでメール認証をログインさせずに行う

Laravelではメール認証を行う時、すでにログインをされた状態 である必要があります

ログインしていなくてもメール認証のみ実行するように変えてみましょう

1. 現在のルーティングを確認する

php artisan route:list
Method URI Name Action Middleware
GET|HEAD email/verify/{id}/{hash} verification.verify App\Http\Controllers\Auth\VerificationController@verify web
auth
signed
throttle:6,1

ミドルウェアに auth が入っています。これがあると、ログインしていない場合は、ログイン画面へ飛ばされます。


2. VerificationController.phpを変更する

auth ミドルウェアを外します。

app/Http/Controllers/Auth/VerificationController.php

    public function __construct()
    {
        $this->middleware('auth');
        $this->middleware('signed')->only('verify');
        $this->middleware('throttle:6,1')->only('verify', 'resend');
    }

  ↓ auth ミドルウェアをコメントアウト

    public function __construct()
    {
        // $this->middleware('auth');
        $this->middleware('signed')->only('verify');
        $this->middleware('throttle:6,1')->only('verify', 'resend');
    }


3. VerificationController.phpに verify メソッドを追加する

app/Http/Controllers/Auth/VerificationController.php

    /**
     * 
     * メールアドレス確認(メソッドのオーバーライド)
     * 
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     *
     */
    public function verify(Request $request)
    {
        $user = \App\User::find($request->route('id'));
    
        if (!hash_equals((string) $request->route('hash'), sha1($user->getEmailForVerification()))) {
            throw new AuthorizationException;
        }
    
        if ($user->markEmailAsVerified())
            event(new \Illuminate\Auth\Events\Verified($user));
    
        return redirect($this->redirectPath())->with('verified', true);
    }

これでokです。

No.1872
10/05 15:10

edit

No.1869
09/29 15:41

edit

Laravel で サブクエリ ANY を使用する

例えば次のようなWHERE句に ANY を使ったサブクエリ は

SELECT 
	* 
FROM `mail_table`
WHERE
	'2020-01-15 23:59:59' > ANY ( select update_date from `sub_table` WHERE main_id = main_table.id )

↓ 

Laravelでは 次のように記述します。

// サブクエリ
$queryBuilder = $queryBuilder->where(function($query) {
	$query->whereRaw("'2020-01-15 23:59:59' > ANY ( select update_date from `sub_table` WHERE main_id = main_table.id )");
});
No.1860
09/18 11:32

edit

Laravelのパスワード再発行メールを日本語化する

● Laravelのパスワード再発行メールを日本語化する

1. /app/Notifications/ResetPasswordNotification.php を次の内容で作成する

<?php

namespace App\Notifications;

use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;

class ResetPasswordNotification extends Notification
{
    public $token;

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

    public function via($notifiable)
    {
        return ['mail'];
    }

    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->subject('パスワードの再発行')
            ->line("パスワード再発行のリクエストを受け付けました。こちらのボタンを押してパスワードを再設定してください。")
            ->action('パスワードを再発行する', route('password.reset', $this->token))
            ->line('もしこのメールに心当たりがない場合はこのメールを破棄してください。');
    }
}

2. モデルファイル /app/User.php に設定する

/app/User.php

    /**
     * オリジナルのパスワード再発行メールを送信する
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new \App\Notifications\ResetPasswordNotification($token));
    }

以上です。簡単ですね。

No.1848
08/28 12:18

edit

● LaravelのマイグレーションでDBにカラムを追加する

● LaravelのマイグレーションでDBにカラムを追加する

Laravelのマイグレーションでカラムのを追加するには、
今既にあるマイグレーションファイルは 変更せずに置いておいて、変更を記述したマイグレーションファイルを新規に作成します。

テーブル名 カラム名
catalogs start_date datetime

 ↓ (例)こちらの2カラムを追加するとします。

テーブル名 カラム名
catalogs start_date datetime
catalogs start_dat_2 datetime
catalogs start_dat_3 datetime

●1. 変更用マイグレーションファイルの新規作成

マイグレーションファイル名はなんでもいいですが、クラス名(1番目の引数)が被ってしまうとエラーになるので、注意して命名してください。

php artisan make:migration change_catalogs_table_add_2columns  --table=catalogs

クラス名(class ChangeConsentformsDelBiko extends Migration)でファイルが作成されます。

成功すると 次のようなファイルが生成されます

2019_07_08_180737_change_catalogs_table_add_2columns

次のように記述します。
(複数カラムを追加する時は追加する順番に注意しましょう)
after('start_date') で start_date カラムの後ろに追加しています。
before() メソッドはうまく動作しないことがあるので、after を使いましょう。

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class ChangeCatalogsTableAdd2columns extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('catalogs', function (Blueprint $table) {
            $table->datetime('start_date_3')->nullable()->after('start_date')->comment('開始時間_3(開始日時_3)');
        });

        Schema::table('catalogs', function (Blueprint $table) {
            $table->datetime('start_date_2')->nullable()->after('start_date')->comment('開始時間_2(開始日時_2)');
        });

    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('catalogs', function (Blueprint $table) {
            $table->dropColumn('start_date_3');
        });
        Schema::table('catalogs', function (Blueprint $table) {
            $table->dropColumn('start_date_2');
        });
    }
}

・カラムの型

コマンド 説明
$table->bigIncrements('id'); 符号なしBIGINTを使用した自動増分ID(主キー)
$table->bigInteger('votes'); BIGINTカラム
$table->binary('data'); BLOBカラム
$table->boolean('confirmed'); BOOLEANカラム
$table->char('name', 100); オプションの文字長を指定するCHARカラム
$table->date('created_at'); DATEカラム
$table->dateTime('created_at'); DATETIMEカラム
$table->dateTimeTz('created_at'); タイムゾーン付きDATETIMEカラム
$table->decimal('amount', 8, 2); 有効(全体桁数)/小数点以下桁数指定のDECIMALカラム
$table->double('amount', 8, 2); 有効(全体桁数)/小数点以下桁数指定のDOUBLEカラム
$table->enum('level', ['easy', 'hard']); ENUMカラム
$table->float('amount', 8, 2); 有効(全体桁数)/小数点以下桁数指定のFLOATカラム
$table->geometry('positions'); GEOMETRYカラム
$table->geometryCollection('positions'); GEOMETRYCOLLECTIONカラム
$table->increments('id'); 符号なしINTを使用した自動増分ID(主キー)
$table->integer('votes'); INTEGERカラム
$table->ipAddress('visitor'); IPアドレスカラム
$table->json('options'); JSONフィールド
$table->jsonb('options'); JSONBフィールド
$table->lineString('positions'); LINESTRINGカラム
$table->longText('description'); LONGTEXTカラム
$table->macAddress('device'); MACアドレスカラム
$table->mediumIncrements('id'); 符号なしMEDIUMINTを使用した自動増分ID(主キー)
$table->mediumInteger('votes'); MEDIUMINTカラム
$table->mediumText('description'); MEDIUMTEXTカラム
$table->morphs('taggable'); 符号なしINTERGERのtaggable_idと文字列のtaggable_typeを追加
$table->multiLineString('positions'); MULTILINESTRINGカラム
$table->multiPoint('positions'); MULTIPOINTカラム
$table->multiPolygon('positions'); MULTIPOLYGONカラム
$table->nullableMorphs('taggable'); NULL値可能なmorphs()カラム
$table->nullableTimestamps(); timestamps()メソッドの別名
$table->point('position'); POINTカラム
$table->polygon('positions'); POLYGONカラム
$table->rememberToken(); VARCHAR(100)でNULL値可能なremember_tokenを追加
$table->smallIncrements('id'); 符号なしSMALLINTを使用した自動増分ID(主キー)
$table->smallInteger('votes'); SMALLINTカラム
$table->softDeletes(); ソフトデリートのためにNULL値可能なdeleted_at TIMESTAMPカラム追加
$table->softDeletesTz(); ソフトデリートのためにNULL値可能なdeleted_atタイムゾーン付きTIMESTAMPカラム追加
$table->string('name', 100); オプションの文字長を指定したVARCHARカラム
$table->text('description'); TEXTカラム
$table->time('sunrise'); TIMEカラム
$table->timeTz('sunrise'); タイムゾーン付きTIMEカラム
$table->timestamp('added_on'); TIMESTAMPカラム
$table->timestampTz('added_on'); タイムゾーン付きTIMESTAMPカラム
$table->timestamps(); NULL値可能なcreated_atupdated_atカラム追加
$table->timestampsTz(); タイムゾーン付きのNULL値可能なcreated_atupdated_atカラム追加
$table->tinyIncrements('id'); 符号なしTINYINTを使用した自動増分ID(主キー)
$table->tinyInteger('votes'); TINYINTカラム
$table->unsignedBigInteger('votes'); 符号なしBIGINTカラム
$table->unsignedDecimal('amount', 8, 2); 有効(全体桁数)/小数点以下桁数指定の符号なしDECIMALカラム
$table->unsignedInteger('votes'); 符号なしINTカラム
$table->unsignedMediumInteger('votes'); 符号なしMEDIUMINTカラム
$table->unsignedSmallInteger('votes'); 符号なしSMALLINTカラム
$table->unsignedTinyInteger('votes'); 符号なしTINYINTカラム
$table->uuid('id'); UUIDカラム
$table->year('birth_year'); YEARカラム

●3. 変更用マイグレーションファイルの実行

php artisan migrate

●4. 変更をやっぱり戻す(ロールバック)

php artisan migrate:rollback

● ロールバックする時にテーブルのカラムが存在するかどうかをチェックする

ロールバックする時にDBカラムが存在しないとエラーとなります。
そこで存在チェックを入れましょう

$table->dropColumn('text_name');

   ↓ ( newsテーブルに text_name カラムが存在するなら削除する)

if (Schema::hasColumn('news', 'text_name')){
    $table->dropColumn('text_name');
}
No.1552
08/25 08:18

edit

Laravel バックグランドの非同期処理を行う

● Laravel でキューを使ってバックグランドの非同期処理を行う

1. テーブル作成

php artisan queue:table
php artisan queue:failed-table
php artisan migrate

2 .envファイルに追加

QUEUE_DRIVER=database # キュードライバを DB にする

3 .ジョブ(実際に行いたい処理)を作成

PDF ファイルに透かしを入れるジョブを作成してみます

php artisan make:job CreatePdfWaterMark

/app/Jobs/CreatePdfWaterMark.php に実際のロジックを記述します

4.Jobの起動方法

1. キューワーカの実行

php artisan queue:work

常駐キューワーカの実行

nohup php artisan queue:work --daemon &

または

nohup php artisan queue:work --daemon > /dev/null 2>&1 &

nohup php artisan queue:work --daemon > ./storage/logs/laravel.log &

常駐キューワーカーの削除

ps -ef |grep artisan

次のようにプロセス番号が分かるのでプロセス番号からkillします

username 20423 17965  0 12:01 pts/0    00:00:00 php artisan queue:work --daemon
kill 20423
No.1836
08/14 12:05

edit

LaravelでSendgrid API 使ってメールを送信する

● LaravelでSendgrid API 使ってメールを送信する

パッケージ laravel-sendgrid-driver をインストールすることで、
通常のSMTPサーバーを使ったメール送信からSendGridAPIを使ったメール送信に簡単に切り替えたりまた戻すことができます。

・1. s-ichikawa/laravel-sendgrid-driver のインストール

composer require s-ichikawa/laravel-sendgrid-driver

・2. 設定ファイルへ記述

config/mail.php へ以下を追加

    'mailers' => [
        // ● 追加
        'sendgrid' => [
            'transport' => 'sendgrid',
        ],

config/services.php へ以下を追加

    // ● 追加
    'sendgrid' => [
        'api_key' => env('SENDGRID_API_KEY'),
    ],

.env へ以下を追加

# ● SendGrid設定
MAIL_DRIVER=sendgrid
MAIL_MAILER=sendgrid 
SENDGRID_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

.env の以下をコメントアウト

# MAIL_MAILER=smtp

これだけで ok です。

● 3. Laravelはどこを見てメール送信のドライバを決定しているのか?

vendor/laravel/framework/src/Illuminate/Mail/MailManager.php:404

    protected function getConfig(string $name)
    {
        // Here we will check if the "driver" key exists and if it does we will use
        // the entire mail configuration file as the "driver" config in order to
        // provide "BC" for any Laravel <= 6.x style mail configuration files.

        return $this->app['config']['mail.driver']
            ? $this->app['config']['mail']
            : $this->app['config']["mail.mailers.{$name}"];
    }

つまり .env の MAIL_DRIVER を見ているので、切り替えたい場合はここを変更しましょう。

SendGridを使用する場合

MAIL_DRIVER=sendgrid

SMTPサーバを使用する場合

MAIL_DRIVER=smtp

● SendGridを使って同時に複数のメールアドレスへ同時にメール配信する

addTo()addTos() メソッドを使用するとあるユーザに送ったメールに他のユーザのメールアドレスも見えてしまいます。
Personalizationを使用しましょう

        // To (全員のアドレスが見えてしまう)
        // foreach ($to_emails as $to) {
        //     $email->addTo( $to );    
        // }

        // Personalization (全員のアドレスを隠す)
        foreach ( $to_emails as $email_address ) { 
            $personalization = new Personalization();
            $personalization->addTo( new To( $email_address ) );
            $email->addPersonalization( $personalization );
        }        
No.1831
03/10 20:59

edit

Laravel で グローバルスコープを外してデータを取得する

● Laravel で グローバルスコープを外してデータを取得する

// グローバルスコープを外す(クラスの場合)
User::withoutGlobalScope(AgeScope::class)->get();

// グローバルスコープを外す(クロージャで指定したスコープの場合)
User::withoutGlobalScope('myscope')->get();

// 全てのグローバルスコープを外す
User::withoutGlobalScopes()->get();

// 複数のグローバルスコープを外す
User::withoutGlobalScopes([
    FirstScope::class, SecondScope::class
])->get();
No.1804
06/29 15:55

edit

Laravel の 通知(Notification)を使用する

● Laravel の 通知(Notification)を使用する

1. モデルファイルに Notifiableトレイトを追加する

/app/User.php

class User extends Authenticatable
{
    use \Illuminate\Notifications\Notifiable;

(実はデフォルトでこのトレイトは記述されています。何もしなくても OK です)

2. Notification を作成する

php artisan make:notification <クラス名>

例 (掲示板にデータがポストされた通知「BoardsPosted」を作成してみます )

php artisan make:notification BoardsPosted

/app/Notifications/BoardsPosted.php が自動作成されます。

コンストラクタに通知に使用するデータ受け取りを記述します。 (後でここで受け取ったデータを加工して使用します。)

    private $board;
    public function __construct( $board )
    {
        $this->board = $board;
    }

3. 通知する

テスト的にメール送信で通知してみます

例としてログインするたびに自分自身にメールを送信するようにしてみます。

/app/Http/Controllers/HomeController.php

    public function index()
    {
        // ログインユーザー自身に通知する 
        $user = \Auth::user();
        $user->notify(new \App\Notifications\BoardsPosted($user));

        return view('home');
    }

なお、全ユーザーに通知するには次のようにします。

        // 全ユーザーに通知する 
        $users = \App\User::get();
        \Notification::send($users, new \App\Notifications\BoardsPosted(null));

.env に smtp の設定も記述します

MAIL_MAILER=smtp
MAIL_HOST=xxx.you-server.com
MAIL_PORT=587
MAIL_USERNAME=your@host.com
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=your@host.com

これだけでログインしてホーム画面に行くだけで自分自身にメールが送信されます。 とても簡単ですね

4. DBテーブルへ通知する

php artisan notifications:table
php artisan migrate

このようなテーブルが作成されます

5. 通知チャンネルに DatabaseChannel を追加する

(DatabaseChannelを追加します) app/Notifications/BoardsPosted.php

    public function via($notifiable)
    {
        return ['mail',\Illuminate\Notifications\Channels\DatabaseChannel::class];
    }

チャンネルはデフォルトでは3つ「BroadcastChannel」「DatabaseChannel」「MailChannel」があります。

DBに通知するときに好きなデータを保存できます。

    public function toArray($notifiable)
    {
        return [
            'invoice_id' => 111,
            'amount' => 123456,
        ];
    }

とすると、notificationsテーブルの「data」カラムに

{"invoice_id":111,"amount":123456}

のような json が保存されます。

notificationsテーブルは次のようなレイアウトです。

morphtoMany が設定されていますね。 これは

vendor/laravel/framework/src/Illuminate/Notifications/Notifiable.php

trait Notifiable
{
    use HasDatabaseNotifications, RoutesNotifications;
}

vendor/laravel/framework/src/Illuminate/Notifications/HasDatabaseNotifications.php

    public function notifications()
    {
        return $this->morphMany(DatabaseNotification::class, 'notifiable')->orderBy('created_at', 'desc');
    }

で設定されているようです。 全ての通知を notifications テーブルに集約する事ができます。 とてもとても簡単ですね。

参考 : https://laravel.com/docs/7.x/notifications#database-prerequisites

6. DBに保存された自分宛の未読の通知を表示する

// 自分宛の通知を表示する
$user = \Auth::user();
foreach ($user->unreadNotifications as $notification) {
    dump( $notification );
}

あるユーザー宛の全ての通知 と 未読の通知

$user = \Auth::user();
$notifications = $user->notifications;           // あるユーザー宛の全ての通知
$notifications = $user->unreadNotifications;     // あるユーザー宛の未読の通知

1件のデータに紐づいている全ての通知

$notifications = $model->notifications;           // 1件のデータに紐づいている全ての通知
$notification->markAsRead();    // 既読にする

未読の通知を既読にするには次のようにしてもOKです。

if ( $notification->read_at === null ){
	$notification->update(['read_at' => now()]);    // 既読日時をセット
}
else {
	dump( '既に既読です' );
}

bladeで通知数を表示する

{{ @Auth::user()->unreadNotifications->count() }} 件のお知らせがあります。

Blade だと次のように記述します。

<h3>{{ count($notifications) }}件の通知があります</h3>

@foreach ($notifications as $notifications)
    {{$notifications->id}} : {{$notifications->type}} <br>
@endforeach

7. notificationsテーブルを拡張する

引用 : https://bit.ly/3ewBWzT
にあるように、notificationsテーブルは拡張しておきましょう。

php artisan make:migration change_notifications_table_add_2columns  --table=notifications

マイグレーションファイルの編集 database/migrations/2020_xx_xx_xxxxx_change_notifications_table_add_2columns.php

    public function up()
    {
        Schema::table('notifications', function (Blueprint $table) {
            $table->unsignedBigInteger('resource_id')->after('type');
            $table->string('resource_type')->after('type');
        });
    }

    public function down()
    {
        Schema::table('notifications', function (Blueprint $table) {
            $table->dropColumn('resource_id');
            $table->dropColumn('resource_type');
        });
    }

app/Notifications/MyDatabaseChannel.php

DatabaseChannel を拡張してオリジナルの MyDatabaseChannel を作成します。

<?php 

namespace App\Notifications;

use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Channels\DatabaseChannel;

class MyDatabaseChannel extends DatabaseChannel
{
    protected function buildPayload($notifiable, Notification $notification)
    {
        return [
            'id' => $notification->id,
            'type' => get_class($notification),
            'data' => $this->getData($notifiable, $notification),
            'resource_type' => $notification->resource->getMorphClass() ?? null,
            'resource_id' => $notification->resource->getKey() ?? null,
            'read_at' => null,
        ];
    }    
}

app/Notifications/BoardsPosted.php も変更します。

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class BoardsPosted extends Notification
{
    use Queueable;

    public $resource;

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

    public function via($notifiable)
    {
        return [\App\Notifications\MyDatabaseChannel::class];
    }

    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->line('システムからのお知らせです。')
                    ->action('Notification Action', url('/'))
                    ->line('それではどうぞよろしくお願いいたします');
    }

    public function toArray($notifiable)
    {
        return [];
    }
    
}

● 通知対象のモデル側から、ある書き込み(対象モデル)をユーザーに対して通知したかどうかを調べる

データに対して
「unread_notifications」で未読の通知が、
「notifications」で全ての通知が
取得できます。

app/Notification.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Notification extends Model
{
    protected $table = 'notifications';

    protected $guarded = ['id', 'created_at', 'updated_at'];

    protected static function boot()
    {
        parent::boot();
    }
}

通知対象のモデル側 app/Board.php に「フック」と「リレーション」と「メソッド」を追加

    protected static function boot()
    {
        parent::boot();

        /**
         * ● フック : deleted() 削除時にモデルに紐づく全ての「通知」を削除する
         */
        static::deleted(function ($model) {
            foreach ($model->notifications as $notification) {
                $notification->delete();
            }
        });
    }

    /**
     * ● (Notification) 1対多 ポリモーフィックリレーション : ->unread_notifications で書き込みに紐づく「現在ログイン中ユーザーが未読の通知」を取得します。
     *
     * ソート順 : sort_no , DESC
     */
    public function unread_notifications()
    {
        return $this->morphMany('App\Notification', 'resource')->where('read_at','=',null)->where('notifiable_type','=','App\User')->where('notifiable_id','=',optional(\Auth::user())->id)->orderBy('created_at','ASC');
    }

    /**
     * ● (Notification) 1対多 ポリモーフィックリレーション : ->notifications で書き込みに紐づく全ての通知を取得します
     * ソート順 : sort_no , DESC
     */
    public function notifications()
    {
        return $this->morphMany('App\Notification', 'resource')->orderBy('created_at','ASC');
    }

    /**
     * ● メソッド : ->is_notified('App\User',1) id=1 のユーザーに対して通知を完了したかどうかを返します。
     */
    public function is_notified( $class_name, $id )
    {
        foreach ($this->notifications as $k => $v) {
            if ( $v->notifiable_type === $class_name && $v->notifiable_id === $id ){
                return true;
            }
        }        
        return false;
    }

これでコントローラーから次のようにして「ある書き込み」が「あるユーザーに対して」通知済みかどうかを知る事ができます。
またモデルのデータを削除したときにそれに紐づく全てのDB通知も削除されます。

        // id = 26 の board
        $board = \App\Board::find(26);
        if ( $board->is_notified('App\User',1) ){
            dump( '1 のユーザーには通知済み' );
        }
        if ( $board->is_notified('App\User',2) ){
            dump( '2 のユーザーには通知済み' );
        }

● [laravel]Notificationを使ってメール送るときにMailableを使う

https://bit.ly/2BIlFKc

添付ファイル1
添付ファイル2
No.1800
01/15 10:07

edit

添付ファイル

自作クラスをSingletonとしてLaravelに登録して使い回す

● 自作クラスを作成する

作成する場所はお好きなところどこでもOKです。

Config.php

<?php
namespace Mysettings;
class Config
{
	public $name            = "アイウエオ";
}

● Composerで自動読み出しできるようにする

composer.json へ パス "./" を追加します

	"autoload": {
		"classmap": [
			"./"
		],

設定ファイルの再出力

composer dump-autoload

● シングルトン結合を行う。

シングルトンとして登録するだけです。

app/Providers/AppServiceProvider.php

	public function register()
	{
		$this->app->bind(
			'Illuminate\Contracts\Auth\Registrar',
			'App\Services\Registrar'
		);

		// この辺りに追記
        $this->app->singleton('\Mysettings\Config', function()
        {
            return new \Mysettings\Config();
        });

	}

● 呼び出して使う

    // 設定情報を取得
    $config = app()->make('Mysettings\Config');
    dump( $config );
    $config->clinic_name = '001';
    dump( $config );

    // シングルトンであることの確認
    $config2 = app()->make('Mysettings\Config');
    dd( $config2 );

以上です。 Laravelだと簡単ですね!

No.1779
09/14 17:13

edit

Laravel で 日付の範囲を指定してデータを取得する

● Laravel で 日付の範囲を指定してデータを取得する

● Laravel で今月のデータを取得する

		$dt_from = new \Carbon\Carbon();
		$dt_from->startOfMonth();

		$dt_to = new \Carbon\Carbon();
		$dt_to->endOfMonth();

		$reports = Report::whereBetween('report_date', [$dt_from, $dt_to])->get();

● Laravel で 今から 10日以上前より新しいデータを取得

$queryBuilder->whereRaw( "last_checkup_date >= DATE_SUB( CURDATE(),INTERVAL 10 DAY )" );
No.1778
07/23 13:59

edit

Laravelでマルチテナント

●tenancy/tenancy ( ★ 971 + 2296 )

( hyn/multi-tenant ( ★ 2296 )の後継 )

https://packagist.org/packages/tenancy/tenancy

●stancl/tenancy ( ★ 1 990 )

https://packagist.org/packages/stancl/tenancy

✔️ No model traits to change database connection
✔️ No replacing of Laravel classes (Cache, Storage, ...) with tenancy-aware classes
✔️ Built-in tenant identification based on hostname (including second level domains)

●spatie/laravel-multitenancy ( ★ 221 )

https://packagist.org/packages/spatie/laravel-multitenancy

No.1766
10/25 17:18

edit

Laravel で post で リダイレクトする

● Laravel で post で リダイレクトする

ルート名でリダイレクトします。

/reports/999/edit のようなURLへリダイレクトします。

return redirect()->route('reports.edit', ['report'=>$report->id, '_back_uri'=>$q['_back_url']]);
No.1764
05/25 18:25

edit

Laravelのリレーション取得 with() でカラムを指定する

● Laravelのリレーション取得 with() でカラムを指定する

Group::with('users')->get();

  ↓

Group::with('users:id,name')->get();

idは必ず含める必要があります

● Laravelのリレーション取得 with() でカラムを指定し別名で取得する

Group::with('users:id,name as user__name')->get();
No.1760
05/22 10:16

edit

Laravel で mailto リンクを難読化する

● Laravel で mailto リンクを難読化する

@php
    $email = "test@user.com";
    $email2 = preg_replace_callback('/./', function($m) {
           return '&#'.ord($m[0]).';';
    }, $email );
@endphp
<a href="mailto:{!! $email2 !!}" target="_blank">{{ $email }}</a>
No.1758
05/15 17:55

edit

Laravel でユーザーがログインしたログを取得する

● saeed/laravel-login-log

(あまりインストールやSTARが多くないパッケージなので慎重に。)

composer require saeed/laravel-login-log
php artisan vendor:publish --tag=Login-log
php artisan migrate

テーブル「LoginLog 」が作成されます。

No.1756
05/15 11:33

edit

LaravelでSQLのログとスロークエリログを取得する

● mnabialek/laravel-sql-logger

https://packagist.org/packages/mnabialek/laravel-sql-logger

1. インストール

laravel-sql-loggerのインストール

composer require mnabialek/laravel-sql-logger

laravel-sql-loggerのインストール(開発環境のみインストールする場合は後ろに --dev をつけます)

composer require mnabialek/laravel-sql-logger --dev

2. .envファイルに設定を記述

SQL_LOGGER_DIRECTORY="logs/sql"
SQL_LOGGER_USE_SECONDS=false
SQL_LOGGER_CONSOLE_SUFFIX=
SQL_LOGGER_LOG_EXTENSION=".sql"
SQL_LOGGER_ALL_QUERIES_ENABLED=true
SQL_LOGGER_ALL_QUERIES_OVERRIDE=false
SQL_LOGGER_ALL_QUERIES_PATTERN="#.*#i"
SQL_LOGGER_ALL_QUERIES_FILE_NAME="[Y-m-d]-log"
SQL_LOGGER_SLOW_QUERIES_ENABLED=true
SQL_LOGGER_SLOW_QUERIES_MIN_EXEC_TIME=100
SQL_LOGGER_SLOW_QUERIES_PATTERN="#.*#i"
SQL_LOGGER_SLOW_QUERIES_FILE_NAME="[Y-m-d]-slow-log"
SQL_LOGGER_FORMAT_NEW_LINES_TO_SPACES=false
SQL_LOGGER_FORMAT_ENTRY_FORMAT="/* [origin]\\n   Query [query_nr] - [datetime] [[query_time]] */\\n[query]\\n[separator]\\n"
No.1755
06/18 08:33

edit

Laravel Echo Server のインストール

● Laravel Echo Server のインストール

* 1. laravel-echo のインストール

npm install --save laravel-echo socket.io-client

* 2. laravel-echo-server のインストール

npm install -g laravel-echo-server

SSL鍵ファイルの場所を調べておく(nginx の場合)

cat /etc/nginx/conf.d/[YOUR-DOMAIN-NAME]_ssl.conf | grep cert

初期化 (Laravelのプロジェクトトップに移動してから初期化します)

cd [Laravelのプロジェクトトップ]
laravel-echo-server init

次のように質問に答えます( YOUR-SERVER.COM は適宜読み替えてください。 )

? Do you want to run this server in development mode? Yes
? Which port would you like to serve from? 6001
? Which database would you like to use to store presence channel members? redis
? Enter the host of your Laravel authentication server. https://YOUR-SERVER.COM/
? Will you be serving on http or https? https
? Enter the path to your SSL cert file. /etc/letsencrypt/live/YOUR-SERVER.COM/fullchain.pem
? Enter the path to your SSL key file. /etc/letsencrypt/live/YOUR-SERVER.COM/privkey.pem
? Do you want to generate a client ID/Key for HTTP API? No
? Do you want to setup cross domain access to the API? No
? What do you want this config to be saved as? laravel-echo-server.json

完了すると laravel-echo-server.json が作成されます。

* 3. 6001 ポートを開ける

サーバーの6001ポートのファイアーウォールを空けておきます。

* 4. redis のインストール

sudo yum install -y epel-release
sudo yum install -y redis

redisのバージョン確認

redis-server --version

redisの起動

redis-server

● そのまま起動すると ssl証明書のエラーが出るので次のコマンドを実行します

/opt/eff.org/certbot/venv/bin/letsencrypt certonly -a webroot --webroot-path=[site document root] --agree-tos --email [your email] --config-dir="/home/[user]/letsencrypt/etc" --domains [site domain]

https://komelin.com/articles/realtime-apps-laravel-echo-tips-and-tricks/#laravel-echo-server-through-https

● redisサーバーの起動

redis-server

● laravel-echo-server サーバーの起動

laravel-echo-server init
laravel-echo-server start

● Laravelアプリの設定

1. config/app.php の providers のコメントアウトをやめる

App\Providers\BroadcastServiceProvider::class,      // ON

2. .env の変更

BROADCAST_DRIVER=log

 ↓ 変更

BROADCAST_DRIVER=redis

● Laravelアプリでイベントの作成

1. イベントの作成

php artisan make:event PublicEvent

app/Events/PublicEvent.php が自動生成されますので以下の内容で保存します。

app/Events/PublicEvent.php

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class PublicEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct()
    {
    }

    public function broadcastOn()
    {
        return new Channel('public-event');
    }

    public function broadcastWith()
    {
        return [
            'message' => 'PUBLIC',
        ];
    }
}

2. ルーティングの追加

routes/web.php

Route::get('/test-public-event', function(){
    broadcast(new \App\Events\PublicEvent);
    return 'test-public-event';
});

3. resources/js/bootstrap.js に記述

import Echo from "laravel-echo"
window.io = require('socket.io-client')
window.Echo = new Echo({
    broadcaster: 'socket.io',
    host: 'http://' + window.location.hostname + ':6001'
})
//購読するチャネルの設定
window.Echo.channel('public-event')
            .listen('PublicEvent', (e) => {
                console.log(e);
            });

4. npm のビルド

npm install
npm run dev
No.1744
04/26 23:27

edit

laravel-auditing で DBモデルの更新履歴を自動でとる

● owen-it/laravel-auditing のインストール

1. composer でインストールする

composer require owen-it/laravel-auditing

2. config/app.php の providers に追加する

config/app.php

'providers' => [
    // ...
    OwenIt\Auditing\AuditingServiceProvider::class,    // laravel-auditing
],

3. config/audit.php を自動生成する

php artisan vendor:publish --provider "OwenIt\Auditing\AuditingServiceProvider" --tag="config"

4. マイグレーションファイルを を自動生成し、マイグレーションを実行する

php artisan vendor:publish --provider "OwenIt\Auditing\AuditingServiceProvider" --tag="migrations"
php artisan migrate

● owen-it/laravel-auditing の使い方

1. モデルファイルを変更する

class Memo extends Model

  ↓

use OwenIt\Auditing\Contracts\Auditable;
class Memo extends Model implements Auditable
{
    // ========== Trait ==========
    use \OwenIt\Auditing\Auditable;
    // ========== Trait ==========

implements Auditable を追加し use \OwenIt\Auditing\Auditable でトレイトを追加します、 これだけで自動的に更新履歴がテーブル audits に保存されます。

2. 履歴を取得する

// Get first available Article
$memo = Article::first();

// Get all associated Audits
$all = $memo->audits;

// Get first Audit
$first = $memo->audits()->first();

// Get last Audit
$last = $memo->audits()->latest()->first();

// Get Audit by id
$audit = $memo->audits()->find(4);
No.1743
06/27 21:39

edit

Laravel で HTMLをminifyして高速化する Laravel HTMLMin

Laravel で HTMLをminifyして高速化する Laravel HTMLMinを使用する

● Laravel HTMLMinのインストール

1. composer からパッケージをインストール

composer require htmlmin/htmlmin

2. aliases に追加

config/app.php の aliases に 以下を追加

        'HTMLMin' => HTMLMin\HTMLMin\Facades\HTMLMin::class, // Laravel HTMLMin

3. config/htmlmin.php の生成

php artisan vendor:publish

リストが表示されるので HTMLMin の番号を押す(以下の例の場合は6をタイプします。)

  [6 ] Provider: HTMLMin\HTMLMin\HTMLMinServiceProvider

config/htmlmin.php が生成されていればOKです。

4. config/htmlmin.php と .env の書き換え

.env に以下を追加する

HTMLMIN_ENABLED=true	# HTMLMinを(有効/無効)にする

config/htmlmin.php を以下のように書き換える

    'blade' => false,

 ↓

    'blade' => env('HTMLMIN_ENABLED'),

HTMLMinはキャッシュファイルを作成しますので、設定を変えたときはキャッシュをクリアする必要があります。

5. view のキャッシュをクリアする

php artisan view:clear;
No.1742
05/19 21:47

edit

Laravel の php artisan コマンドをコントローラーから実行する

call メソッドから呼ぶことができます。

● hoge:fuga コマンドを実行する場合(引数なし)

\Illuminate\Support\Facades\Artisan::call('hoge:fuga');

● hoge:fuga コマンドを実行する場合(引数あり)

\Illuminate\Support\Facades\Artisan::call('hoge:fuga',['param1'=> 'value1','param2'=>'value2']);
No.1724
03/25 16:54

edit

Laravel で ajax post 時に csrfトークンを送信する

● Laravel で ajax post 時に csrfトークンを送信する

● 1. Laravel Bladeファイルに次を追加

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

● 2. axios で post送信時にcsrfトークンを送信する

postメソッドを次のように書き換えます

axios.post(url, postdata)

 ↓

axios.post(url, postdata,{
        headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }
})
No.1720
03/20 06:48

edit

\Auth::user() が Model の Boot メソッドで null になるのに対処する

app/Http/Kernel.php

$middleware に以下を追加します

    protected $middleware = [
        \App\Http\Middleware\TrustProxies::class,
        ........
        \App\Http\Middleware\EncryptCookies::class,           // \Auth::user() を取得するために追加
        \Illuminate\Session\Middleware\StartSession::class,   // \Auth::user() を取得するために追加
    ];
No.1719
03/20 09:15

edit

Laravel で 複数のモデルに対応した多対多のポリモーフィック・リレーションを扱う

● Laravel全リレーション

1対1
1対多
多対多
Has Many Through
1対1(ポリモーフィック)
1対多(ポリモーフィック)
多対多(ポリモーフィック)

これらのうち 多対多ポリモーフィック・リレーションを操作してみます。

● Laravel の 多対多リレーションのDB構造

「チームデータ」を複数のテーブルにつける。のをイメージするととても簡単です。 まずは2つのテーブルをつなぐピボットテーブルを作成すればOKです。 さらに、ポリモーフィック・リレーションの場合はテーブル名(テーブルのクラス名)も保存します。 (これにより「チームデータ」は色々なテーブルと多対多リレーションさせることができます。)

・posts(テキスト投稿データ)
    id - integer
    name - string

・videos(ビデオ投稿データ)
    id - integer
    name - string

・teams(チームデータ)
    id - integer
    name - string

というテーブルを多対多ポリモーフィック・リレーションでつなぐ時は
テーブル ↓

・teamaccessible
    team_id - integer
    teamaccessible_id - integer
    model_name - string

を作成します。

マイグレーションファイルの作成

php artisan make:migration create_teamaccessible_table

マイグレーションファイルの up() メソッドは次のようになります。

    public function up()
    {
        Schema::create('teamaccessibles', function (Blueprint $table) {
            $table->integer('team_id');
            $table->integer('teamaccessible_id');
            $table->string('teamaccessible_type');
        });
    }

● Laravel の 多対多ポリモーフィック・リレーション(morphToMany)をモデルに設定する

多対多リレーションの時は belongsToMany でしたが、 多対多ポリモーフィック・リレーションの場合は morphToMany を設定します。

これを トレイトに 定義して、複数のテーブルから使用します。

例) app/TeamAccessibleTrait.php に設定

    /**
     * ● 多対多ポリモーフィック・リレーション
     *
     * ->teams でモデルに紐づけられたチーム一覧を取得します。
     *
     * ピボットテーブル「teamaccessible」
     *
     * @return \Illuminate\Database\Eloquent\Relations\morphToMany
     */
    public function teams()
    {
        return $this->morphToMany('App\Team', 'teamaccessible');
    }

モデルでトレイトを呼び出します

    use \App\TeamAccessibleTrait;       // チームでアクセス可能なデータに限定する
No.1718
06/21 16:15

edit

Laravel7 で 「Trait 'Illuminate\Console\DetectsApplicationNamespace' not found」エラーが出る場合の対応

● Laravel7 で 「DetectsApplicationNamespace' not found」エラーが出る場合の対応

1.

use Illuminate\Console\DetectsApplicationNamespace;

  ↓ 削除する

// use Illuminate\Console\DetectsApplicationNamespace; // コメントアウトまたは削除

2.

	// use DetectsApplicationNamespace;

  ↓ 削除する

	// use DetectsApplicationNamespace; // コメントアウトまたは削除

3.

$this->getAppNamespace()

  ↓ 次のように置換

\App::getNamespace()
No.1716
03/09 11:57

edit

No.1712
03/03 21:37

edit

Laravel で バックエンド REST API を作成する

● APIコントローラーの作成

php artisan make:controller Api/CategoryController --api

app/Http/Controllers/API/CategoryController.php が自動生成されます。

● ルーティングを設定する

routes/api.php

// API Category
Route::group(['middleware' => ['api']], function () {
    Route::resource('articles', 'Api\CategoryController');
});

● 設定したルートを確認する

php artisan route:list
| GET|HEAD  | api/categories                  | categories.index   
| POST      | api/categories                  | categories.store   
| GET|HEAD  | api/categories/create           | categories.create  
| GET|HEAD  | api/categories/{category}       | categories.show    
| PUT|PATCH | api/categories/{category}       | categories.update  
| DELETE    | api/categories/{category}       | categories.destroy 
| GET|HEAD  | api/categories/{category}/edit  | categories.edit       

● コントローラーのメソッドを記述していく

app/Http/Controllers/API/CategoryController.php

index()

    public function index()
    {
        $categories = \App\Category::all();
        return $categories;
    }

return $categories; のところは次のようにしてもいいでしょう

        return $categories->toJson(JSON_UNESCAPED_UNICODE);

store()

    public function store(Request $request)
    {
        $category = new \App\Category;
        $category->fill( $request->all() )->save();
        return $category;
        // return response('store OK', 200);
    }

どのような値を返すのかはフロントエンドの実装によります。 上の例では作成したモデルをjsonで返しています。

show()

    public function show($id)
    {
        $category = \App\RakutenCategory::find($id);
        return $category;
    }

update()

    public function update(Request $request, $id)
    {
        $category = \App\RakutenCategory::findorFail($id);
        $category->fill( $request->all() )->save();
        return $category;
        // return response('update OK', 200);
    }

destroy()

    public function destroy($id)
    {
        $category = \App\RakutenCategory::findorFail($id);
        $category->delete();
        return $category;
        // return response('delete OK', 200);
    }

これで完成です。 あとはフロントエンド( Angular / React / Vue.js )からガリガリ操作してください。

No.1700
10/12 15:43

edit

TCPDF での レイアウト調整方法

● スペース調整によく使用する tableタグ

    <table border="0"><tr><td style="width:200px; height:200px;"></td></tr></table>

● テーブル内のセルに対して一括で padding を指定することができる

    <table style="padding:10px;">

とすると、テーブル内のセルに対して一括で padding を指定することができます。
逆に言うとこれ以外の方法では指定ができないようです。

No.1696
06/09 11:33

edit

今更だけど PHP 5.6 + Laravel 5.4 環境のアプリを作成する

● PHP 5.6 + Laravel 5.4 環境のアプリを作成する

composer create-project --prefer-dist laravel/laravel my_app 5.4

● バージョンを確認する

cd my_app
php artisan -V
No.1690
02/05 14:04

edit

php artisan config:cache すると エラーが出るときの対処法

● php artisan config:cache すると エラーが出るときの対処法

/bootstrap/cache/config.php を削除する

php artisan config:cache を行うと キャッシュファイル /bootstrap/cache/config.php が生成され .envファイルを読み込まなくなります

なので、これを削除する事でエラーは回避できます。

● 根本的な解決法

env()はconfigファイル内でしか使わない事

コントローラーや Console コマンドでは使わないようにしましょう。

No.1684
02/05 20:24

edit

Laravel で CORS に対応して開発マシンから axios などでリクエストを送信できるようにする

● laravel-cors のインストール

A: Laravel 7 以上の場合

laravel-cors はLaravel のバージョン7からデフォルトでインストールされるようになりましたのでわざわざインストールしなくても OK です

B: Laravel 6 以下の場合

barryvdh/laravel-cors のインストール

composer require barryvdh/laravel-cors

● Laravelのミドルウェアに追加

app/Http/Kernel.php の $middleware に以下を追加

api のみ適用したい!と思いますが、 middleware に入れてしまって、後から設定で URL を絞り込みます。

protected $middleware = [
    // ...
    \Fruitcake\Cors\HandleCors::class,
];

namespace が なぜか \Fruitcake\Cors\HandleCors ですが、これで okです。

● 設定ファイル生成

設定ファイルを以下のコマンドで自動作成する。

php artisan vendor:publish --tag="cors"

● 設定ファイルへ設定を記述

config/cors.php

    'paths' => ['api/*'],
    'allowed_origins_patterns' => ['/localhost:?[0-9]*/'],

allowed_origins_patterns は preg_match に渡す、正規表現用文字列です。 上記の設定では localhost や localhost:8080 などを通すようにしています。

これで /api/ 以下のみ クロスドメインアクセス が有効になりました。

● エラーが返ってくる場合にチェックすること

1. テスト用のルートを作成してそれに対してリクエストを送信してみます。

config/cors.php

    // cor-test を追加
    'paths' => ['cor-test', 'api/*'],

routes/web.php

// COR-TEST
Route::get('/cor-test', function () {
    return response()->json([
        'message' => 'cor-test-ok'
    ]);
});

これを設定して、 axios などからアクセスします。

    axios.get('/cor-test', { withCredentials: true })
      .then(response => {
        console.log('cor-test ok:', response.data);
      })
      .catch(error => {
        console.log('cor-test error:', error);
      });
No.1669
10/14 09:16

edit

Laravel Passport をインストールする

● Laravel6 の Auth をインストールする

Laravel6 で ユーザー認証(Auth)機能を作成する|プログラムメモ

● Laravel Passport をインストールする

composer require laravel/passport

● .env にDB設定の記述

● DBテーブルの作成

php artisan migrate

こちらのテーブルが作成されます。

● キーの作成

php artisan passport:install

次のような結果が返ってきます

Encryption keys generated successfully.
Personal access client created successfully.
Client ID: 1
Client secret: S37VP14Txxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Password grant client created successfully.
Client ID: 2
Client secret: MCAW4tmxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
「client_id: 1」が、「5. Personal Access Client」
「client_id: 2」が、「2. Password Grant Client」

です。

● 認証の種類について

Laravel Passportの認証には次の種類があるようです。

・1. OAuth2 with authorization codes (Auth Code)→SNS認証でよく使うやつ(確認画面あり・Code発行あり)
・2. Password Grant Token(確認画面無し。Codeなし。ID, PWでTokenを発行)
・3. Implicit Grant Token(いきなりTokenを発行。安全性に問題?)
・4. Client Credentials Grant Token(マシン間通信向け。個人の認証無し)
・5. Personal Access Token(その名の通り、個人・内部利用向け)

引用 : http://bit.ly/2G9or9H
http://bit.ly/2Gb9WSJ

● app/User.php に HasApiTokens トレイトを追加

app/User.php

    // ===== Trait =====
    use HasApiTokens, Notifiable;
    // ===== Trait =====

● app/Providers/AuthServiceProvider.php の boot メソッドに追加

        // Laravel Passport 追加
        \Laravel\Passport\Passport::routes();
        // Laravel Passport 追加

● config/auth.php を変更

config/auth.php

        'api' => [
            // Laravel Passport へ変更 token → passport
            'driver' => 'passport',
            'provider' => 'users',
            'hash' => false,
        ],

● artisan の キャッシュをクリア

php artisan config:cache

● ユーザーを作成する

php artisan tinker
App\User::create(['name' => 'test', 'email' => 'test@test.com', 'password' => bcrypt('test')]);

●(トークン作成方法 1)作成したユーザーのトークンを tinker で作成する

トークン作成を管理者が行う場合はこのようにバックエンドで作成するだけでOKです。

php artisan tinker

(id=1 の User のトークンを作成します。)

echo \App\User::find(1)->createToken('my_token')->accessToken;

トークンが表示されるのでコピーします。 なお、一番最後に改行コードがついてくるので、改行コードはコピーしないよう 注意してください。

● (トークン作成方法 2)作成したユーザーのトークンを ユーザー自身が作成する

Postmanでは以下のようにjsonを送信します。

成功すると json でトークンが帰ってきます

● 接続をテストする

これらのクライアントを使用するといいでしょう。

*Postman

https://www.getpostman.com/

*Talend API Tester

https://chrome.google.com/webstore/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm

● トークンを使って /api/user へ GET アクセスする

Headers を次のようにして GET でアクセスします

KEY VALUE
Accept application/json
Authorization Bearer <取得したトークン>

Bearer の後にスペースをつけて トークンを入力して GET でアクセスします。

成功すると次のようなjsonが取得できます

{
    "id": 1,
    "name": "test",
    "email": "test@test.com",
    "email_verified_at": null,
    "created_at": "2020-01-20 21:18:21",
    "updated_at": "2020-01-20 21:18:21"
}

● ユーザーにトークン作成を行わせる

*1. app/Http/Controllers/Api/AuthController.php を作成する

<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class AuthController extends Controller
{
    public function login(Request $request) {
        $credentials = $request->only('email', 'password');
        if(auth()->attempt($credentials)) {
            $user = auth()->user();
            $token = $user->createToken('my_token')->accessToken;
            return ['access_token' => $token];
        }
        return response([
            'message' => 'Unauthenticated.'
        ], 401);
    }
}

*2. routes/api.php の一番後ろに追加

// 最後に追加
Route::post('/login', 'Api\AuthController@login');

*3. /api/login へ POSTアクセスする

Params を次のようにして POST でアクセスします

KEY VALUE
emai test@test.com
password test

成功すると、トークンが帰ってきます。

● クライアントを追加する

php artisan passport:client --personal

実行すると以下を聞かれます

1. クライアント名
 What should we name the personal access client? [Laravel Personal Access Client]:
 > myclient

Personal access client created successfully.
Client ID: 3
Client secret: mI5Q25xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
php artisan passport:client --password
添付ファイル1
添付ファイル2
No.1663
01/21 10:16

edit

添付ファイル

Laravel で 日本語ファイル名でファイルをダウンロードさせる

● Laravel で 日本語ファイル名でファイルをダウンロードさせる

Windows IE11 でも文字化けしないようにutf-8 エンコードします。

*Laravel で日本語ファイル名のPDFをダウンロードさせる

$content に データを入れておきます。mime_type も取得しておきます。

$disk = \Storage::disk('local');
$file_path = 'tmp/myfile.pdf';
$content = $disk->get( $file_path );
$mime_type = $disk->mimeType( $file_path );

日本語ファイル名をセットしてダウンロードさせます。

$filename = '日本語pdfファイル名.pdf';
$headers = [
    'Content-Type'        => 'application/pdf',
    'Content-Disposition' => 'attachment; filename*=UTF-8\'\''.rawurlencode($filename)
];
return \Response::make($content, 200, $headers);

引用 : https://bit.ly/3jmpg0S



*Laravel で日本語ファイル名のテキストファイルをダウンロードさせる

$content に データを入れます

$content = <<< DOC_END
==================================
テキスト内容
==================================
DOC_END;

$filename = 'サンプルテキスト.txt';
$headers = [
	'Content-type'        => 'text/plain', 
	'Content-Disposition' => sprintf('attachment; filename="%s"', $filename),
];
return \Response::make($content, 200, $headers);
No.1646
06/04 10:03

edit

Laravel で DBテーブルとLaravelモデルに uuidを追加する

● webpatser/laravel-uuid のインストール

composer require webpatser/laravel-uuid

● モデルに uuid カラムを追加

マイグレーションファイルに次のように uuid 型のカラムを追加します
( 実際には CHAR(36)として作成されます )

ユニーク制約をつける場合

$table->uuid('uuid')->after('id')->unique()->comment('uuid');

nullを許可する場合

$table->uuid('uuid')->after('id')->nullable()->comment('uuid');

● uuidを自動的にセットするトレイトを作成する

/app/UuidTrait.php

<?php
namespace App;
use Webpatser\Uuid\Uuid;
trait UuidTrait
{
    public static function bootUuidTrait()
    {
       static::creating(function ($model) {
            $model->uuid = Uuid::generate()->string;
        });
    }
}

● モデルにトレイトを追加

モデルファイルに以下を追加

    use \App\UuidTrait;

● routes/web.php に以下を追加

Route::get("posts/share/{uuid}", "MemoController@share")->name('posts.share')->where([ 'id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' ]);

/posts/share/05eface0-edc911eb-bfda-2f6c633e3f11 というようなURLでアクセスすることができます

No.1644
07/26 13:33

edit

Laravel の バリデーションに独自のルールを追加する

public function rules()
{
    $ignore_email_array = [ 'aaa.com', 'bbb.com', 'ccc.com' ];

    return [
        'email' => [
            'required', 'email', 'confirmed',
            // ===== 独自ルール(指定したドメインは除外する)
            function ($attribute, $value, $fail) use ($ignore_email_array) {
                foreach ($ignore_email_array as $ignore_domain) {
                    $pattern = '/' . preg_quote($ignore_domain, '/') . '/';
                    if (preg_match($pattern, $value) !== 0) {
                        return $fail("{$ignore_domain} ドメインのメールアドレスはご登録いただけません");
                    }
                }
            } ,
            // ===== 独自ルール(指定したドメインは除外する)
        ],
    ];
}
No.1641
12/12 22:01

edit

Laravel で Mysql の タイムゾーンを変更する

● Laravel で Mysql の タイムゾーンを変更する

// DBのTimezoneを変更
\DB::select("SET time_zone = '+09:00'");

● Laravel で 「 アプリの起動ごとに 毎回必ず」Mysql の タイムゾーンを変更する

app/Providers/AppServiceProvider.php に 記述します。

    public function boot()
    {
        // DBのTimezoneを変更
        \DB::select("SET time_zone = '+09:00'");
    }
No.1634
12/02 10:39

edit

Laravel で DBのデータを 「表示させるタイマー時刻内に限定する」スコープを作成する

● Laravel で DBのデータを 「表示させるタイマー時刻内に限定する」スコープを作成する

開始タイマーと終了タイマー二つを作成します

( DBのカラム view_start_date が 現在時刻を超えたデータのみに限定する )

    /**
     * ● ローカルスコープ : ->withinStartTime() で 「表示タイマー開始」を現在時刻が過ぎた slide に限定する
     *
     * @param    \Illuminate\Database\Eloquent\Builder    $query
     * @return   \Illuminate\Database\Eloquent\Builder
     */
     public function scopeWithinStartTime( \Illuminate\Database\Eloquent\Builder $query )
     {
         return   $query->whereNull('view_start_date')
                        ->orWhere(function($query) {
                            $query->whereNotNull('view_start_date')
                                  ->where('view_start_date','<=', \DB::raw('NOW()') );
                        });
     }

( DBのカラム view_end_date が 現在時刻を超えていないデータのみに限定する )

    /**
     * ● ローカルスコープ : ->withinEndTime() で 「表示タイマー終了」を現在時刻が過ぎていない slide に限定する
     *
     * @param    \Illuminate\Database\Eloquent\Builder    $query
     * @return   \Illuminate\Database\Eloquent\Builder
     */
     public function scopeWithinEndTime( \Illuminate\Database\Eloquent\Builder $query )
     {
         return   $query->whereNull('view_end_date')
                        ->orWhere(function($query) {
                            $query->whereNotNull('view_end_date')
                                  ->where('view_end_date','>',\DB::raw('NOW()') );
                        });
     }

表示タイマー内のデータに限定する

$data_loop = $model->withinStartTime()->withinEndTime()->get();
No.1632
11/30 14:23

edit

Laravel の便利な配列処理 Collection を使う

● 重複データを除いて Collection へ登録する

$collection = collect();

$collection_hash = [
    'id'   => 1 ,
    'name' => 'テスト太郎' ,
];

// Collectionになければ追加
if ( ! $collection->contains( $collection_hash ) ){
    $collection->push( $collection_hash );
}

● 配列を順に処理する map を実行する。PHPのarray_map()

$collection->map(function ($d) {
	dump($d->name);
});

● Laravel 以外でも Collection は使えます

composer require tightenco/collect
No.1626
05/27 11:46

edit

Laravel 6 7 で ユーザー認証(Auth)機能を作成する

● Laravel 6 や 7 では、昔ながらの php artisan make:auth コマンドが実行できなくなりました

代わりに php artisan ui vue --auth コマンドが追加されました。  

● Laravel 7 で ユーザー認証(Auth)機能を作成する(bootstrapを使用する場合)

composer require laravel/ui
php artisan ui bootstrap --auth
npm install && npm run dev

● Laravel 7 で ユーザー認証(Auth)機能を作成する(Vue.jsを使用する場合)

composer require laravel/ui
npm install
php artisan ui vue --auth
npm install && npm run dev

● 初期設定を行う

Laravel で最初にやっておいた方が良い初期設定|プログラムメモ

● Auth用DBテーブルを生成する

php artisan migrate

● ログイン画面にアクセスする

https://YOUR-SERVER.TLD/login


● メール認証機能 1.(登録ユーザーのメールアドレス確認を追加する)

app/User.php

class User extends Authenticatable
{

 ↓

class User extends Authenticatable implements MustVerifyEmail
{

routes/web.php

Auth::routes();

  ↓

Auth::routes([
    'verify'   => true, // メール確認機能(※5.7系以上のみ)
    'register' => true, // デフォルトの登録機能ON
    'reset'    => true,  // メールリマインダー機能ON
]);

● メール認証機能 2.(ミドルウェアの認証を verified に変更する)

auth だと本登録しなくてもログインできてしまうので authの後に verified を追加します

Route::group(['middleware' => ['auth']], function () {
    Route::get("file/downloadlocalfile/", "FrontFileController@downloadlocalfile")->name("file.downloadlocalfile");
});

   ↓ auth verified に変更する

Route::group(['middleware' => ['auth', 'verified']], function () {
    Route::get("file/downloadlocalfile/", "FrontFileController@downloadlocalfile")->name("file.downloadlocalfile");
});

● メール認証機能 2.(ログイン後にメール認証がまだのユーザーの場合は認証メール再送信画面へ強制的に遷移させる)

app/Http/Controllers/Auth/LoginController.php

    /**
     * ユーザーが認証された後の処理
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  mixed  $user
     * @return mixed
     */
    protected function authenticated(\Illuminate\Http\Request $request, $user)
    {
        if( $user->email_verified_at === null ){
            return redirect()->route('verification.notice', ['message'=>'メールアドレス認証が完了していません']);
        }
    }

● メール認証機能 3.(確認メール再送信画面をカスタマイズする)

app/Http/Controllers/Auth/VerificationController.php

    public function resend( \Illuminate\Http\Request $request)
    {
        if ($request->user()->hasVerifiedEmail()) {
            return $request->wantsJson()
                        ? new Response('', 204)
                        : redirect($this->redirectPath());
        }

        $request->user()->sendEmailVerificationNotification();

        return $request->wantsJson()
                    ? new Response('', 202)
                    : back()->with('resent', true);
    }

また、確認メールを送信したかどうかは ビューファイル resources/views/auth/verify.blade.php の中で

                        @if (session('resent') == true )
                            確認メールを送信しました。(再送信する場合はクリック)
                        @else
                            確認メール再送信
                        @endif

のようにして取得できます。

No.1615
10/12 10:46

edit

git が 使用できないサーバへ Laravelアプリを zip圧縮/解凍してデプロイする

● テストサーバから本番サーバへ手動でzipを使ってデプロイする

1. cms_app ディレクトリを cms_app.zip に圧縮します。

 zip -r cms_app.zip   cms_app  --exclude=*/.git/*  --exclude=*/___bak/*  --exclude=*/___*  --exclude=cms_app/storage/debugbar/*  --exclude=cms_app/storage/app/public/* --exclude=cms_app/storage/app/*

2. unzip.php をサーバ上に置いて展開します。

● 展開方法

unzip.php を使用する

<?php

ini_set("max_execution_time", 300);
ini_set("max_input_time", 300);

$zip_file = 'cms_app.zip'; // 展開するzipファイルを指定

$zip = new ZipArchive;
if ($zip->open("$zip_file") === true) {
    $zip->extractTo('./');
    $zip->close();
    echo 'zip解凍に成功しました。';
} else {
    echo 'zip解凍に失敗しました。';
}


if ( is_file($zip_file) ){
	unlink($zip_file) or die(" / ファイル {$zip_file} を削除できませんでした。");
	echo " / ファイル {$zip_file} を削除しました。";
}

No.1610
10/25 18:12

edit

Laravel で ホスト名によって .env ファイルを変える

● Laravel で ホスト名によって .env ファイルを変える

https://qiita.com/takaday/items/b992c7d8cd69343b6626

こちらの方法がとても良いです。

bootstrap/app.php

switch ($_SERVER['SERVER_NAME'] ?? 'localhost') {
    case 'development.co.jp':
        $app->loadEnvironmentFrom('.env.dev');
        break;
    case 'staging.co.jp':
        $app->loadEnvironmentFrom('.env.stg');
        break;
    case 'production.co.jp':
        $app->loadEnvironmentFrom('.env.prod');
        break;
}
No.1608
10/24 17:28

edit

Laravel で DBで取得したデータを年ごとにまとめる

● Laravel で DBで取得したデータを年ごとにまとめる(その1)

news_date が format() メソッドを使えるように、モデルファイルに

    protected $dates = ['news_date'];

を記述してから、

// 方法 1.
$model = \App\News::inActive();
$all_loop = $model->get();
$all_loop->map(function ($v) {
    $v['year'] = optional($v->news_date)->format("Y");
});        
$group_news_loop = $all_loop->groupBy('year');

● Laravel で DBで取得したデータを年ごとにまとめる(その2)

最下層のオブジェクトは Eloquentモデルではなく配列になります。
なのでこの方法はあまりお勧めしません。

$all_loop = \DB::table('news')
                    ->select(\DB::raw("*,DATE_FORMAT( news_date ,'%Y') as year"))
                    ->get();
$group_news_loop = $all_loop->groupBy('year');
dd( $group_news_loop );
No.1607
10/24 17:06

edit

Laravel Blade ファイル内でランダムな文字列を表示する

● Laravel Blade ファイル内でランダムな文字列を表示する

Laravel6 の 場合は helpers をインストールする必要があります。

composer require laravel/helpers
public.html?{{ str_random(8) }}

public.html?yX4R50rl
No.1606
11/06 10:14

edit

Laravel で 自動的に作成されるキャッシュファイルの権限を 0777 0666 にする

● Laravel で 自動的に作成されるキャッシュファイルの権限を 0777 0666 にする

とある事情によりキャッシュファイルの権限を 0777 にする必要があった時の変更方法

app/Providers/AppServiceProvider.php

    public function boot()
    {
        // ↓ この行を追加
        umask(0);
    }

です。 少し力技ですが。。。

No.1605
10/16 16:11

edit

Laravelで現在のルート名またはURLを取得する / ルート名でリンクする

● Laravelで現在のルート名を取得する

\Route::currentRouteName();

blade内では次のように確認します。

 @php
     $now_route = \Route::currentRouteName();
     dump( $now_route );
 @endphp

● Laravel Bladeで現在のURLを取得する

@php
     $current_url = Request::url();
     dump( $current_url );
@endphp

http://localhost/hoge/fuga?id=12345 の時 http://localhost/hoge/fuga が返ります(クエリパラメータは帰ってきません)

● Laravelでルート名でリンクする

routes/web.php に info.show という名前をつける

    // お知らせ
    Route::get("info/{id}", "FrontInfoController@show")->name("info.show");
// $info = \App\Info::findOrFail(999);
<a href="{{ route('info.show', $info->id) }}" >お知らせのタイトル</a>
No.1596
06/24 10:00

edit

● LaravelのマイグレーションでDBカラムにインデックスを追加する

● LaravelのマイグレーションでDBカラムにインデックスを追加する

マイグレーションファイル名はなんでもいいですが、クラス名(1番目の引数)が被ってしまうとエラーになるので、注意して命名してください。

php artisan make:migration change_catalogs_add_title_index  --table=catalogs

クラス名(class ChangeConsentformsDelBiko extends Migration)でファイルが作成されます。

成功すると 次のようなファイルが生成されます

2019_09_28_180737_change_catalogs_add_title_index

このファイルを次のように記述します。

    public function up()
    {
        Schema::table('consentforms', function (Blueprint $table) {
            $table->index('title'); // この行を追加
        });
    }

    public function down()
    {
        Schema::table('consentforms', function (Blueprint $table) {
            $table->dropIndex(['title']); // この行を追加(配列で渡す)
        });
    }

マイグレーションの実行

php artisan migrate

実行するとインデックス catalogs_title_index が作成されます。

● Laravelのマイグレーションで MySQLの TEXT型のカラムにインデックスを作成する

MySQLのTEXT型の場合はインデックスのサイズを指定する必要があります。(最大255 bytes)

テーブル : artists
カラム : yomi_name
の場合

        Schema::table('artists', function (Blueprint $table) {
            DB::statement('CREATE INDEX artists_yomi_name_index ON artists (yomi_name(100));');
        });

とします。

No.1590
05/15 10:29

edit

Laravel で iPhone や iPad や Windows , Mac などの機種を判別する

composer require jenssegers/agent

config/app.php

Jenssegers\Agent\AgentServiceProvider::class, // providerに追加

'Agent' => Jenssegers\Agent\Facades\Agent::class, // aliasに追加
$agent->is('Windows');
$agent->is('Firefox');
$agent->is('iPhone');
$agent->is('OS X');
$agent->isAndroidOS();
$agent->isNexus();
$agent->isSafari();
$agent->isMobile();
$agent->isTablet();
$agent->match('regexp');
No.1588
09/18 09:09

edit

Laravel の ユーザーのパスワード(のハッシュ)を Tinkerで生成する

● Laravel の ユーザーのパスワード(のハッシュ)を Tinkerで生成する

ユーザー(User)モデルなどのパスワードを変えたいけれど、まだインターフェースがない場合は、Tinkerからパスワードのハッシュを生成します。

php artisan tinker

ある、ユーザーのパスワードを hogehoge にしたい場合は次のように入力します

echo Hash::make('hogehoge');

これで ハッシュ化されたパスワードが表示されますのでこれを直接データベースに登録します。

No.1577
08/23 16:38

edit

Laravel の CSRFチェックをあるルートではオフにする

● Laravel の CSRFチェックをあるルートではオフにする

app/Http/Middleware/VerifyCsrfToken.php に追加します。
( admin/apidebug/* ) のルートでは CSRF チェックをオフにします。

<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
    protected $addHttpCookie = true;

    protected $except = [
        'stripe/*',
        'admin/apidebug/*',     // ここに追加
    ];
}
No.1576
08/22 22:36

edit

Laravel で モデルを json にして渡すとき削除(unset)したいカラムを設定する。日本語をUnicode化せずそのままjson化する

● Laravel で モデルを json にして渡すとき削除(unset)したいカラムを設定する

JSON で API データを渡す時などにパスワードなどを含めたくないということが多々あります。
Laravelでは一撃で書くことができます。

$json = $model->toJson();

  ↓ makeHiddenメソッドを挟みます。

$json = $model->makeHidden(['password','remember_token'])->toJson();

引用 : http://bit.ly/2z5fqLk

● Laravel で モデルを json にして渡すとき日本語をUnicode化せずそのまま渡す

$json = $model->toJson();

  ↓ toJsonメソッドにオプションを渡します。

$json = $model->toJson(JSON_UNESCAPED_UNICODE);

簡単ですね!

No.1574
08/19 19:15

edit

Laravel で データベース更新時にフックをかけて自動的に処理を行う。

● Laravel で データベース更新時にフックをかけて自動的に処理を行う。

Laravelではフックというとても便利な機能があります。
これはデータベースのデータに更新や削除などデータ操作があった際に好きな 処理を挟み込むことができます。
またトレイトも使用できますのでコントローラーから分離することもできます。

● 公式マニュアル

https://laravel.com/docs/5.8/eloquent#events

● 日本語公式マニュアル

(イベント)の項目を参照
https://readouble.com/laravel/5.8/ja/eloquent.html

Eloquentモデルは多くのイベントを発行します。creating、created、updating、updated、saving、saved、deleting、deleted、restoring、restored、retrievedのメソッドを利用し、モデルのライフサイクルの様々な時点をフックすることができます

● Traitを使ったフックの作成

一番簡単な Trait を使ったフックを紹介します。

・1. まずトレイトを作成します

/app/WebApiTrait.php

<?php
namespace App;
trait WebApiTrait
{
    public static function bootWebApiTrait()
    {
       static::created(function ($model) {
            dd( 'created hook', $model );
        });

       static::updated(function ($model) {
            dd( 'updated hook', $model );
        });

       static::deleted(function ($model) {
            dd( 'deleted hook', $model );
        });
    }
}

・2. フックをかけたいモデルから呼び出します。

Userモデルに追加してみましょう

<?php
namespace App;
use App\Notifications\UserResetPassword;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
    // ===== Trait フック(この行を追加) =====
    use WebApiTrait;
    // ===== / Trait フック(この行を追加) =====

これだけです。
これだけでDBデータに「新規登録」「更新(実際にデータ更新があった時)」「削除」
の時に dd(); が動いて、ダンプして停止します。
実際のロジックはdd() を書き換えればいいでしょう。

● クロージャを使ったフックの作成

モデルの boot()メソッド内 に次のように追加します。

        // Hook
        static::created(function ($model) {
            dd( 'created hook', $model );
        });
        static::updated(function ($model) {
            dd( 'updated hook', $model );
        });

● イベントを使ったフックの作成

既に \App\Events などにイベントが作成してある場合は

    // events
    protected $dispatchesEvents = [
        'updated' => \App\Events\MyEvent::class,
    ];

でディスパッチするだけです。簡単ですね。

● モデルに変更があったかどうかと変更があった内容取得する

updatingのフックで使用 ( $model->save() メソッド実行前)

// 変更があったかどうかの検知
$model->isDirty();    // true , false

// 変更内容
$d = $model->getDirty();    // 配列

updatedのフックで使用 ( $model->save() メソッド実行後)

// 変更があったかどうかの検知
$model->wasChanged();    // true , false

// 変更内容
$d = $model->getChanges();    // 配列
No.1573
10/04 09:52

edit

Laravel Blade に ディスクの残り容量を表示させる

● Bladeファイルで残り容量を表示させる

resources/views/admin/inc/disk_info.blade.php

@php
	$d =  `df -hT`;
	$d_array = preg_split("/\n/",$d);

	$disk_info = $d_array[0];
	$disk_info = preg_replace("/Mounted on/","MountedOn",$disk_info);
	$disk_info = preg_replace("/\s+/"," ",$disk_info);
	$disk_headers = preg_split("/\s/",$disk_info);

	$disk_data = [];
	foreach ($d_array as $k => $v) {
	    if ( preg_match("{/$}",$v) ) {
	        $v = preg_replace("/\s+/"," ",$v);
	        $v = preg_replace("{ /$}","",$v);
	        $disk_data_array = preg_split("/\s/",$v);
	        array_push($disk_data,$disk_data_array);
	    }
	}
// dd( $disk_data );
	echo "<table class='table table-striped table-bordered table-condensed' style='max-width:400px;'>\n";

	// 1. ヘッダ
	echo "<tr>\n";
	foreach ($disk_headers as $v) {
	    if ( strcmp($v, 'MountedOn') == 0 ){ continue; }
	    if ( strcmp($v, 'Avail') == 0 ){ $v = "残り容量"; }
	    echo "<th>{$v}</th>";
	}
	echo "</tr>\n";

	// 2. データ
	echo "<tr>\n";
	foreach ($disk_data as $row) {
	    foreach ($row as $v) {
	        echo "<td>{$v}</td>";
	    }
	}
	echo "</tr>\n";
	echo "</table>\n";
@endphp

他のBladeから呼び出します

@include('admin.inc.disk_info')
添付ファイル1
No.1568
08/17 10:25

edit

添付ファイル

Laravel Collection で 重複するデータを削除する

● Laravel Collection で モデルのコレクションの重複するデータを削除する

同じ user_id を持つデータを1つ(後ろにある方を有効)にする

$reports_diet = $reports->keyBy('user_id');

注意 : コレクションの中身が(単純な数値ではなく)モデルなどのクラスの場合次の方法ではうまくいきません。

$unique = $collection->unique();

● Laravel Collection で 指定した主キーを持つモデルを削除

https://readouble.com/laravel/7.x/ja/eloquent-collections.html#method-contains

exceptメソッドは、指定した主キーを持たないモデルをすべて返します。
$users = $users->except([1, 2, 3]);
No.1560
06/03 21:10

edit

Laravelで別のコントローラーのメソッドを実行する

● Laravelで別のコントローラーのメソッドを実行する

わざわざ別のクラスを作成したりトレイトに外出ししなくてもLaravel では簡単に別のコントローラーのパブリックメソッド実行することができます

App\Http\Controllers\User\MypageControllercreateParam メソッドを実行してみます
なお呼び出されるメソッドは public である必要があります

$mypage_controller = app()->make('App\Http\Controllers\User\MypageController');
$user_param = $mypage_controller->createParam( $hogehoge );

参考 : https://bit.ly/3Mta7KN

No.1559
10/07 16:18

edit

Laravel の Eloquent で 日付が今日に近い順

->orderBy(\DB::raw('abs(datediff(CURDATE(), start_date))'),"ASC")
No.1551
07/12 19:54

edit

Laravelのマイグレーションでカラムの名前と型を変更する

● Laravelのマイグレーションでカラムの名前と型を変更する

● Laravelのバージョンが10より低い場合はカラムの変更にはパッケージのインストールが必要です。

composer require doctrine/dbal

Laravelのマイグレーションでカラムの名前と型を変更するには、
今既にあるマイグレーションファイルは 変更せずに置いておいて、変更を記述したマイグレーションファイルを新規に作成します。

テーブル名 カラム名
artists year_birth_no smallint

 ↓ (例)こちらに変更するとします。

テーブル名 カラム名
artists year_birth_no_name string

●1. 変更用マイグレーションファイルの新規作成

マイグレーションファイル名はなんでもいいです。

php artisan make:migration change_artists_table_column_year_birth_no  --table=artists

成功すると 次のようなファイルが生成されます

2019_07_08_180737_change_artists_table_column_year_birth_no

●2. 変更用マイグレーションファイルの編集

以下のように変更用の命令と戻し用の命令を記述しておきます。

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class ChangeArtistsTableColumnYearBirthNo extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        // カラム名を変更
        Schema::table('artists', function (Blueprint $table) {
            $table->renameColumn('year_birth_no', 'year_birth_no_name');
        });

        // 型を変更
        Schema::table('artists', function (Blueprint $table) {
            $table->string('year_birth_no_name')->default(NULL)->change();
        });

        // カラム「fax_name」を「string型」「nullを許可」に変更
        $table->string('fax_name')->nullable()->change();
    }


    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        // 型を戻す
        Schema::table('artists', function (Blueprint $table) {
            $table->smallInteger('year_birth_no_name')->change();
        });

        // カラム名を戻す
        Schema::table('artists', function (Blueprint $table) {
            $table->renameColumn('year_birth_no_name','year_birth_no');
        });

        // カラム「fax_name」を「string型」「nullを許可しない」に変更
        $table->string('fax_name')->nullable(false)->change();

    }

}

(注意)tinyIntegerでは実行できません。 ↓ を参照

●3. 変更用追加パッケージをインストールする

composer require doctrine/dbal

●4. 変更用マイグレーションファイルの実行

php artisan migrate

●5. 変更をやっぱり戻す(ロールバック)

php artisan migrate:rollback

● エラー Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found となる場合

 composer require "doctrine/dbal:2.*"

● tinyInteger の場合はSQL文を直接流します

        Schema::table('applications', function (Blueprint $table) {
            // nullを許可に変更
            DB::statement('ALTER TABLE users MODIFY COLUMN is_active tinyint COMMENT \'フラグ\'');
        });
No.1545
09/25 15:30

edit

artisan

Laravel で CLI から起動したのかWEBから起動したのかを検知する

● Laravel で CLI から起動

if (strpos(php_sapi_name(), 'cli') !== false) {
    // Run from command
}

● Laravel で WEB から起動

if (strpos(php_sapi_name(), 'cli') === false) {
    // Run from web
}

引用 : http://bit.ly/2Ji9Efi

No.1544
07/06 23:27

edit

Laravel の laravel-responsecache で表示を10倍(?) 高速化する

● spatie/laravel-responsecache

laravel-responsecacheは ララベルのミドルウェアとして動作するキャッシュクラスです。 Laravelのルーティングを通る時に動作する( テンプレートファイルの更新すら確認しない)ので とても高速に動作します

1. インストール

composer require spatie/laravel-responsecache

2. 設定ファイルの生成

php artisan vendor:publish --provider="Spatie\ResponseCache\ResponseCacheServiceProvider"

↑ このコマンドを実行すると
/config/responsecache.php ファイルが生成されます。

3. ミドルウェアにセット

キャッシュをミドルウェア(アクセスのたびに自動的に呼び出されるクラス)にセットします。

既存の $routeMiddleware の一番最後に追加します。

app/Http/Kernel.php

protected $routeMiddleware = [
   ...
   'cacheResponse'      => \Spatie\ResponseCache\Middlewares\CacheResponse::class, // 追加
   'doNotCacheResponse' => \Spatie\ResponseCache\Middlewares\DoNotCacheResponse::class, // 追加
];

次に routes/web.php のキャッシュを適応したいルートをこちらのミドルウェアで囲みます。

	Route::group(['middleware' => 'cacheResponse'], function () {
		Route::get("/", function (Request $request) {
                   // 何かしらの処理
		});
	});

以上でキャッシュは実行されています。
確認してみましょう。

● spatie/laravel-responsecache の動作を確認する。

1. .env の APP_DEBUG=true を確認する

.env の以下の記述があるか確認しましょう

APP_DEBUG=true

2. Laravel の GETメソッドでアクセスできるページへ2回アクセスしてGoogle Chrome でヘッダを見る。

● 管理画面でデータが変更されたときに自動的にキャッシュをクリアする

app/ResponsecacheTrait.php を以下の内容で新規作成します

<?php
namespace App;
use Spatie\ResponseCache\Facades\ResponseCache;

trait ResponsecacheTrait
{
    public static function bootClearsResponseCache()
    {
        self::created(function () {
            ResponseCache::clear();
        });

        self::updated(function () {
            ResponseCache::clear();
        });

        self::deleted(function () {
            ResponseCache::clear();
        });
    }
}

Laravelアプリで使用する全てのモデルファイルに以下のトレイトを追加します

例 : app/news.php に追加します

class News extends Model
{
    // ===== Trait =====
    use \App\ResponsecacheTrait;  // ● 追加
    // ===== Trait =====

これで DBにデータを「新規登録」「変更」「削除」したときにキャッシュがクリアされます。

● spatie/laravel-responsecache の効果をベンチマーク

こちらのコマンドで実際にWEBアプリのベンチマークを3回ずつとってみたところ

ab -n 100 -c 100 https://TEST-SITE.TLD/
Requests per second:    9.19 [#/sec] (mean)
Requests per second:    7.54 [#/sec] (mean)
Requests per second:    6.50 [#/sec] (mean)

 ↓

Requests per second:    80.19 [#/sec] (mean)
Requests per second:    73.79 [#/sec] (mean)
Requests per second:    76.89 [#/sec] (mean)

約9.9倍 速くなりました !!

● 設定を .env にセットする

開発時にこのミドルウェアが入っていると 一時的にキャッシュをオンにしたりオフにしたり する必要が出てきますそこで設定を .env ファイルに設定しておき そこで簡単に切り替えられるようにします

.env

RESPONSE_CACHE_FLAG=true

routes/web.php

// キャッシュミドルウェア( .env の RESPONSE_CACHE_FLAG が true の場合 キャッシュ on)
$cache_middleware = [];
if ( env('RESPONSE_CACHE_FLAG',false) == true ){
	$cache_middleware = ['middleware' => 'cacheResponse'];
}

Route::group($cache_middleware, function () {
	Route::get("/", function (Request $request) {
        ........... 何かしらの処理
	});
});
添付ファイル1
No.1543
07/27 09:42

edit

添付ファイル

Laravel で オブジェクトの存在確認を省略するヘルパー optionalヘルパー

● Laravel optionalヘルパー

https://laravel.com/docs/5.8/helpers

if ( $user->touroku_date ){
    echo $user->touroku_date->format("Y年n月j日");
}

↓ このように記述できます。

echo $accountId = optional($user->touroku_date)->format("Y年n月j日");

オブジェクトが存在しない場合は null が返ります。
便利ですね

引用 : http://bit.ly/31QDPkW

No.1540
06/28 14:37

edit

Laravel で リレーション先のさらに先のリレーションの件数を取得する。検索条件にする。

● Laravel で リレーション先のさらに先のリレーションの件数を取得する

受診データ (hasOne) -> 患者 (hasMany)-> 担当医(複数)

こちらの例のように、リレーション先の患者にさらにリレーションで担当医が複数いる場合の担当医の数を取得します。

・普通に「受診データ」 + 「患者データリレーション」のみ取得する場合

$data_loop = $model->with('patient')->get();

 ↓ このように追加します

・普通に「受診データ」 +「患者データリレーション」+「担当医リレーション(データ)」を取得する場合

$data_loop = $model->with(['patient' => function($query){
    $query->with('doctors');
}])->get();

・普通に「受診データ」 +「患者データリレーション」+「担当医リレーション(の件数)」を取得する場合

$data_loop = $model->with(['patient' => function($query){
    $query->withCount('doctors');
}])->get();

● Laravel で リレーション先のさらに先のリレーションの件数を検索条件にする。

「doctors を2人以上持つ」「patient」のデータを取得する

$data_loop = $model->with(['patient' => function($query){
    $query->with('doctors');
}])->has('patient.doctors','>',1)get();
No.1539
06/27 23:24

edit

タスクランナー Laravel Envoy と git を使って本番環境へのデプロイを自動化する

● タスクランナー Laravel Envoy と git を使って本番環境へのデプロイを自動化する

本番環境にデプロイするときに複数のリポジトリからデータを取ってくる必要があったりだとか
何かシェルコマンドを実行する必要があったりだとか処理が多い場合はタスクランナーを是非使いましょう。
Envoyは シンプルでとても使いやすいタスクランナーです。
(Laravel がインストールされていなくても使用することができます)

● Laravel Envoy 公式ドキュメント

https://readouble.com/laravel/5.8/ja/envoy.html
https://laravel.com/docs/5.8/envoy

● Laravel Envoy のインストール

composer global require laravel/envoy

パスを通します。

cd
vi .bash_profile

次の内容を追加

# laravel / envoy
export PATH="~/.composer/vendor/bin:$PATH"

● コマンド実行の確認

envoy -v

次のようにバージョンが帰って来ればインストール成功です

Laravel Envoy 1.5.0

Usage:
  command [options] [arguments]

● Eovoy コマンドの作成

(ディレクトリはどこでもokです。自分で好きなディレクトリを作成し、そのディレクトリ内で Envoy コマンドを作成して実行できます。)

ディレクトリの作成

mkdir test
cd test

envoyコマンドファイルの作成 (ローカルで作業する場合)

envoy init 127.0.0.1

envoyコマンドファイルの作成 ( 他のサーバに対して作業する場合)

envoy init [user]@[host]

実行するとカレントディレクトリに Envoy.blade.php が生成されます。
少し編集して以下のようにします。

@servers(['web' => '127.0.0.1'])

@task('deploy')
    cd /path/to/site
    git pull origin master
@endtask


@task('hoge')
    ls -la
@endtask

上の例ではタスク deployhoge が登録されています。

● Envoy タスクの実行

envoy run hoge

実行結果

[127.0.0.1]: total 8
[127.0.0.1]: drwxr-xr-x   3 akimitsu  staff    96  6 26 22:44 .
[127.0.0.1]: drwxr-xr-x+ 45 akimitsu  staff  1440  6 26 22:43 ..
[127.0.0.1]: -rw-r--r--@  1 akimitsu  staff   144  6 26 22:44 Envoy.blade.php

このようにタスクを登録して使用します。

● 登録されている Envoyタスクを確認する

envoy tasks

● Envoy 複数タスク(ストーリー)の実行

複数のタスクをまとめてストーリーとして登録しておいてそのストーリーを 実行することができます。

ストーリーの登録 

Envoy.blade.php ファイル内に以下を記述

@story('story_deploy_all__dev')
    task_deploydev_cms
    task_deploydev_html
@endstory

ストーリーの実行 

envoy run story_deploy_all__dev
No.1537
06/28 18:35

edit

Laravel で 自動リダイレクトを使用せずに Validate(バリデーション)を行う

● Laravel で 自動リダイレクトを使用せずに Validate(バリデーション)を行う

        // validation(自動リダイレクト)
        $request->validate([
            $this->username() => 'required|string',
            'password'        => 'required|string',
        ]);

 ↓ このように書き換えます。

        // validation(チェックのみ)
        $validator = Validator::make($request->all(), [
            $this->username() => 'required|string',
            'password'        => 'required|string',
        ]);

        // validation エラーがある場合、エラーメッセージをダンプする
        if ($validator->fails()) {
            dd( $validator->messages()->toArray() );
        }

これで、とりあえず バリデーションエラーがあるときはエラーメッセージが表示されます。 好きな動作を ダンプメソッド dd( $validator->messages()->toArray() ); のところに書き込めば自由にカスタマイズできます。

No.1534
06/24 18:14

edit

Laravel の 複数形を事前に調べる

DBマイグレーションする前にLaravelのモデルの複数形のチェックは行いましょう。

php artisan tinker

として tinker を立ち上げて次のコマンドで調べます。

echo str_plural('複数形を調べたい単語');

次のように記述をしてもオッケーです

echo echo Str::plural('複数形を調べたい単語');

● 例: book → books

( Laravel 6 以降 )

echo Str::plural('book');

( Laravel 5 )

echo str_plural('book');

 → 結果 : books

● 例: blog → blogs

echo str_plural('blog');

 → 結果 : blogs

● 例: information → information

echo str_plural('information');

 → 結果 : information

● 例: news → news

echo str_plural('news');

 → 結果 : news

No.1533
06/14 13:46

edit

Laravel で 404 を返す

● Laravel で 404 を返す

return \App::abort(404);

● Laravel で 独自ページの404 を返す

$data['title'] = '404';
$data['name'] = 'Page not found';
return response()->view('errors.404',$data,404);
No.1531
06/17 18:00

edit

Laravel アプリ の 高速化

● Laravel アプリ の 高速化

● 1. composer の高速化

次のうちどちらかを行いコンポーザのオートローダーを高速化しておきましょう。

● composer を updateする時に パスを絶対パスに書き換えて高速化する

composer --optimize-autoloader update

● composer の autoloader の パスを絶対パスに書き換えて高速化する

composer dumpautoload -o

● 2. Laravel の クラスマップの最適化

次のコマンドを実行してLaravelのクラスマップを最適化し高速化しておきましょう

php artisan optimize

↑ 上記コマンドで php artisan config:cache , php artisan route:cache が実行されます。

No.1528
07/09 10:22

edit

Laravel の Blade で テンプレートファイルの代わりに文字列からレンダリングする

● Laravel の Blade で テンプレートファイルの代わりに文字列からレンダリングする

    private function renderString($string, $data)
    {
        $php = \Illuminate\Support\Facades\Blade::compileString($string);

        $obLevel = ob_get_level();
        ob_start();
        extract($data, EXTR_SKIP);

        try {
            eval('?' . '>' . $php);
        } catch (Exception $e) {
            while (ob_get_level() > $obLevel) ob_end_clean();
            throw $e;
        } catch (Throwable $e) {
            while (ob_get_level() > $obLevel) ob_end_clean();
            throw new FatalThrowableError($e);
        }

        return ob_get_clean();
    }

● 使い方

$user = \App\User::first();
$text = "こんにちは {{$user->name}} さん";
$html = $this->renderString($text, compact('user'));
dump( $html );

● 注意点

{{$user->name}}

としてください。

{{ $user->name }}

ではエラーになる場合があります。

No.1526
08/28 15:11

edit

Laravel で 動的に locale を変更し、フォームバリデーションを多言語対応する

● Laravel で 動的に locale を変更する

コントローラーのコンストラクタで変更しても、フォームのエラーメッセージには適用されないので、ミドルウェアで locale を設定します

・1. ミドルウェアを作成

php artisan make:middleware Language

app/Http/Middleware/Language.php が自動で 作成されるので以下のように書き換えます。

<?php
namespace App\Http\Middleware;
use Closure;
class Language
{
    public function handle($request, Closure $next)
    {
        $now_url = request()->fullUrl();

        $pattern_zh = '/' . preg_quote( env('BASE_URL_ZH') , '/') . '/';
        $pattern_ja = '/' . preg_quote( env('BASE_URL_JA') , '/') . '/';
        $pattern_en = '/' . preg_quote( env('BASE_URL_EN') , '/') . '/';

        if ( preg_match($pattern_zh, $now_url) ){
            \Illuminate\Support\Facades\App::setLocale( 'zh-TW' );
        }
        elseif ( preg_match($pattern_ja, $now_url) ){
            \Illuminate\Support\Facades\App::setLocale( 'ja' );
        }
        elseif ( preg_match($pattern_en, $now_url) ){
            \Illuminate\Support\Facades\App::setLocale( 'en' );
        }
        return $next($request);
    }
}

・2. ミドルウェアを app/Http/Kernel.php に登録

app/Http/Kernel.php

$middlewareGroups のリストに追加します。
これでWEBアクセスの時には必ず読み込まれます。

    protected $middlewareGroups = [
        'web' => [
            ......
            ......
            \App\Http\Middleware\Language::class,   // ● この行を追加
        ],

・3. 多言語を設定する方法を決める

いろいろやり方はあるかと思いますが今回は .env ファイルに 他言語のサイトのベース URL を設定して、 その「ベースURLにマッチする言語を現在の言語」と判別させてみます

.env

BASE_URL_JA=https://ja.YOUR-SITE.TLD
BASE_URL_ZH=https://zh.YOUR-SITE.TLD
BASE_URL_EN=https://en.YOUR-SITE.TLD

これでURLが「https://zh.YOUR-SITE.TLD」の場合は自動的にロケール「zh-TW」がセットされるようになりました。

・4. 多言語リソースを入手する

https://github.com/caouecs/Laravel-lang

ここから太陽市大言語のリソースファイルを入手して resources/lang に コピーすれば多言語対応は完了です。

引用 : http://bit.ly/2I0bWiA

No.1525
06/07 09:55

edit

Laravel で PHPの notice エラーを無視する

● Laravel で PHPの notice エラーを無視する

app/Providers/AppServiceProvider.php に以下を追加します

    public function boot()
    {
        // 767bytes問題の対応
        Schema::defaultStringLength(191);

        // notice を解除 ● ↓ これを追加 ●
        error_reporting(E_ALL ^ E_NOTICE);
    }
No.1523
06/04 11:23

edit

laravel のモデルの検索結果 collection をさらに検索する

● laravel のモデルの検索結果 collection をさらに検索する

 
https://readouble.com/laravel/9.x/ja/collections.html
 

laravel のモデルの検索結果 collection をさらに検索する時に便利なメソッドを紹介します。

・sortBy()

sortByメソッドは指定したキーでコレクションをソートします。
$sorted = $collection->sortBy('price');

・sortByDesc()

このメソッドの使い方はsortByと同じで、コレクションを逆順にソートします。

・first()

firstメソッドは指定された真偽テストをパスしたコレクションの最初の要素を返します。
No.1522
05/04 14:41

edit

PHP のCarbon2 で 多言語(日本語・英語・中国語)で日付を表示させる

● Carbonのバージョンが2以上かどうかチェック

composer show | grep carbon
nesbot/carbon                           2.17.1   A simple API extension for DateTime.

(バージョン2以上である事を確認します。)

● 多言語での日付表示

・1. 日本語での日付表示

\Carbon\Carbon::setLocale('ja_JP');
$dt = new \Carbon\Carbon();
return $dt->isoFormat('YYYY.M.D (dddd)');

結果例

2019.5.30 (木) 木曜日

・2. 英語での日付表示

\Carbon\Carbon::setLocale('en-US');
$dt = new \Carbon\Carbon();
return $dt->isoFormat('YYYY.M.D (dddd)');

結果例

2019.5.30 (Thursday)

・3. 中国語での日付表示

\Carbon\Carbon::setLocale('zh-TW');
$dt = new \Carbon\Carbon();
return $dt->isoFormat('YYYY.M.D (dddd)');

結果例

2019.5.30 (星期四)

● Carbon の isoFormat メソッドの書式

$dt->isoFormat("YYYY年MM月DD日 HH:mm:ss dddd");  // 2019年05月10日 12:34:56 水曜日

moment.js と互換があるそうなので、こちらを参考にするといいです。

http://momentjs.com/docs/#/parsing/string-format/

No.1520
06/07 16:46

edit

Laravel の データベースシーダー実行時に sql文 のダンプファイルを実行する

● Laravel の データベースシーダー実行時に sql文 のダンプファイルを実行する

Laravel シーダー実行時に、sql文(mysqlumpなどのダンプファイル)を実行したい。
という要件は結構あったりします。
Laravelならとても簡単にできます。

通常のデータベースシーダー ( database/seeds/MyTableSeeder.php )

<?php
use Illuminate\Database\Seeder;
class LangdicTableSeeder extends Seeder {
    public function run()
    {
        \DB::table("mytable")->insert([
			'id'      => 1 ,
			'name'    => "ichitaro suzuki" ,
        ]);
    }
}

↓ このように書き換えます

SQL文を実行するデータベースシーダー ( database/seeds/MyTableSeeder.php )

<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class LangdicTableSeeder extends Seeder
{
    public function run()
    {
        $path = 'database/sql/mytable_2019-05-20.sql';
        \DB::unprepared(file_get_contents($path));
    }
}

sqlファイルをここにおきます。 database/sql/mytable_2019-05-20.sql'

シーダーを実行します

php artisan migrate:fresh --seed

以上です。簡単ですね。

No.1518
03/31 19:54

edit

Laravel で DBのER図を表示させる

● A. graphviz を使ったやり方

● A1. graphviz のインストール

yum install -y graphviz

● A2. beyondcode/laravel-er-diagram-generator のインストール

composer require beyondcode/laravel-er-diagram-generator --dev

● A3. ER図の生成

php artisan generate:erd

graph.png が生成されるので、これを見てみましょう。

● B. schema spy を使ったやり方

https://qiita.com/mya-zaki@github/items/e4f0514b320460ae64c5

No.1517
07/14 18:04

edit

php(Laravel)でzipファイルを解凍する

● chumper/zipper はもう使えなくなったので、ZipArchiveを使ってzipファイルを解凍する

$zip = new \ZipArchive;
if ($zip->open($disk->path("zip/{$save_file_basename}.zip")) === TRUE) {
    $zip->extractTo($disk->path("zip/{$save_file_basename}"));
    $zip->close();
} else {
    die('次のzipファイルが開けません:' . $disk->path("zip/{$save_file_basename}.zip"));
}
No.1516
03/06 17:19

edit

laravel blade 独自ヘルパーを作成する

php artisan make:provider HelperServiceProvider

app/Providers/HelperServiceProvider.php が自動生成されます

    public function register()
    {
        // ここから追加
        foreach (glob(app_path().'/Helpers/*.php') as $filename){
            require_once($filename);
        }
        // ここまで追加
    }

● config/app.php の providers へ追加

'providers' => [
    ...
    ...
    // ここを追加
    App\Providers\HelperServiceProvider::class ,
],

● bootstrap/cache フォルダに書き込み権限を与える

(ディレクトリ権限エラーが出る場合は実行してください。)

chmod 0777 bootstrap/cache

● ヘルパーファイルを作成する

作成場所は app/Helpers/myhelper.php です。 myhelper は 好きな名前に変更してください。

if (!function_exists('myhelper')) {
    /**
     * 自作ヘルパー関数
     */
	function myhelper( $arg1=null, $arg2=null, $arg3=null, $arg4=null, $arg5=null )
	{
		return 'test';		
	}
 }

No.1514
03/06 12:04

edit

Laravel の パスワードリセット(再設定)時のユーザー検索カラムを増やす

● Laravel の パスワード再設定時のユーザー検索カラムを増やす

Laravelデフォルトでは、 パスワード再設定時にメールアドレスが同じユーザーの最初に検索されたユーザーに対して
パスワード変更が行われます。
これのユーザー検索するためのDB検索カラムを増やして見ましょう。

例として shop_id と email が同じユーザーで検索するように変更して見ましょう。 ( shop_id を追加します)

● shop_id を検索時に追加する

app/Http/Controllers/UserAuth/ResetPasswordController.php を変更します。
( Laravel デフォルトの authの場合は app/Http/Controllers/Auth/ )

    /**
     * オーバーライド用に  (vendor/laravel/framework/src/Illuminate/Foundation/Auth/ResetsPasswords.php) からコピー
     */
    protected function credentials(Request $request)
    {
        // ● shop_id をプラスする
        return $request->only(
            'shop_id', 'email', 'password', 'password_confirmation', 'token'
        );
    }
    /**
     * オーバーライド用に  (vendor/laravel/framework/src/Illuminate/Foundation/Auth/ResetsPasswords.php) からコピー
     * $shop_id は ルーターから受け取る
     */
    public function reset(Request $request, int $shop_id )
    {

        // ● request に shop_id をプラスする
        $request->request->add(['shop_id' => $shop_id]);

        $request->validate($this->rules(), $this->validationErrorMessages());

        $user = \App\User::where("shop_id","=",$shop_id)->where("email","=",$request->email)->first();

        // Here we will attempt to reset the user's password. If it is successful we
        // will update the password on an actual user model and persist it to the
        // database. Otherwise we will parse the error and return the response.
        $response = $this->broker()->reset(
            $this->credentials($request), function ($user, $password) {
                $this->resetPassword($user, $password);
            }
        );

        // If the password was successfully reset, we will redirect the user back to
        // the application's home authenticated view. If there is an error we can
        // redirect them back to where they came from with their error message.
        return $response == Password::PASSWORD_RESET
                    ? $this->sendResetResponse($request, $response)
                    : $this->sendResetFailedResponse($request, $response);
    }

以上です。
簡単ですね。

No.1512
06/19 15:35

edit

laravelのデータベースのidを文字列型(varchar)(Laravelで言う所の string )にする

● 1. マイグレーションファイル

	public function up()
	{
		Schema::create('dics', function(Blueprint $table) {
            $table->string('id')->primary(); //  ● id を string にする
            $table->text('name')->nullable()->comment('テキスト');
            $table->integer('sort_no')->unsigned()->comment('ソート順');
            $table->timestamps();

			$table->primary('id');            
        });
	}

● 2. モデルファイル

class dics extends Model
{
public $incrementing = false; 
}
No.1511
05/30 10:53

edit

Laravel で フォーム画面を開いたまま放置した時のCSRFトークンタイムアウトを防ぐ

● laravel-caffeine

フォームをかなりの時間スクリーン上に置いておいた後に送信するときに、フォームがタイムアウトしないようにします。 (Laravelのデフォルトは120分ですが、これは設定可能であり、サイトごとに異なる可能性があります。)

https://github.com/GeneaLabs/laravel-caffeine

No.1509
05/13 10:11

edit

Laravel で DBの数値型カラムが文字列型で返ってくる場合の対処法

● Laravel で DBの数値型カラムが文字列型で返ってくる場合の対処法

Laravelはもちろん(Laravel に限らず PHP で PDO を使っている場合)DBの数値型カラムが文字列型で返ってくる事があります。
なぜこのような自動型変換が起こるかというと、PHPのMySQL PDOドライバの仕様だそうです。

ただし、次の条件の時に

1. mysqlのドライバーが 「mysqlnd(MySQLNaitiveDriver)」である。
2. PDO::ATTR_EMULATE_PREPARES属性が false である。

自動型変換を回避することが可能です。

● 1. phpinfo() を使って mysqlnd ドライバーが使用されているかどうかチェックする

phpinfo() の出力から確認しましょう。

または次のコマンドで mysqlnd が表示されれば使用されています

php -m | grep mysqlnd

● 2. LaravelアプリのPHPのPDOオプションをチェックする

        $dbh = \DB::connection()->getPdo();

        $attributes = [
            "ATTR_CLIENT_VERSION",
            "ATTR_EMULATE_PREPARES",
            "ATTR_STRINGIFY_FETCHES",
            "ATTR_AUTOCOMMIT",
            "ATTR_ERRMODE",
            "ATTR_CASE",
            "ATTR_ORACLE_NULLS",
            "ATTR_PERSISTENT",
            "ATTR_PREFETCH",
            "ATTR_SERVER_INFO",
            "ATTR_SERVER_VERSION",
            "ATTR_TIMEOUT",
        ];

        foreach ($attributes as $val) {
            echo "PDO::{$val}: ";
            try {
                echo $dbh->getAttribute(constant("PDO::{$val}")) . "<br>\n";
            } catch (\Exception $e) {
                echo "error not supported !!! <br>\n";
            }
        }

結果例

PDO::ATTR_CLIENT_VERSION: mysqlnd 5.0.12-dev - 20150407 - $Id: 38fea24f2847fa7519001be390c98ae0acafe387 $
PDO::ATTR_EMULATE_PREPARES: 0

↑ この例の場合は(mysqlnd有り)(PDO::ATTR_EMULATE_PREPARES: 0)なので、数値型カラムの値は数値型で返ってきます。

レンタルサーバで(おそらく昔の)XSERVERとかは mysqlnd が入ってないので、自動型変換は必ず起きると思われます。

● mysqlnd が入っていないサーバーで MySQLの自動型変換をさせないようにする

Laravel の モデルには $casts プロパティがあり、これに型をセットすると、Eloquentが結果セットを返すときに、明示的にその方にキャストしてくれます。

    /**
     * 明示的なdb型変換
     *
     */
    protected $casts = [
        'id'                => 'int' ,
        'shop_id'           => 'int' ,
        'is_active'         => 'int' ,
        'price_no'          => 'int' ,
    ];

● いちいちデータベースの定義を見て、キャストを作成するのがめんどくさい

次のコードを実行してください。自動で生成します。( int のみ。) コントローラーから次のようなメソッドを実行します

    /**
     * Laravelのモデルの Casts を生成する
     *
     * [int, integer, real, float, double, string, bool, boolean, object, array, json, collection, date, datetime]
     *
     *
     */
    public function getModelCasts(string $table_name = '')
    {
        print "<hr>\n";
        echo "<strong>{$table_name}</strong>";
        print "<hr>\n";
        echo "<pre style='margin: 20px; border: solid #eee 1px;'>";

print <<< 'DOC_END'
/**
 * カラムの明示的なdb型変換
 *
 */
protected $casts = [

DOC_END;

        $query = "SHOW COLUMNS FROM {$table_name}";
        foreach (\DB::select($query) as $column) {
            if     (preg_match("{^int}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^tinyint}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^smallint}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^mediumint}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^bigint}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^(text|char|varchar)}", $column->Type)) {} 
            elseif (preg_match("{^}", $column->Type)) {} 
            else {
                dump($column);
            }
        }

print <<< 'DOC_END'
];
DOC_END;

        echo "</pre>";
        print "<hr>\n";
    }
No.1508
05/10 10:33

edit

Laravel で Gmail アカウント(Gmailのsmtpサーバ)を使ってメールを送信する

.env

(YOUR-NAME)(YOUR-PASSWORD)を適宜書き換えてください。

MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=YOUR-NAME@gmail.com
MAIL_PASSWORD=【gmail2段階認証のアプリパスワード】
MAIL_ENCRYPTION=tls

MAIL_FROM_ADDRESS=YOUR-NAME@gmail.com
MAIL_FROM_NAME=デフォルトサイト名

gmail2段階認証のアプリパスワード は通常のログインパスワードとは別に作成する必要があります。
https://myaccount.google.com/security

コントローラー

// テキストメール送信
$mail_subject = "メールのタイトルテスト";
$mail_content = "メールの本文です\nテスト";
$to_email = "customer@user.com";

\Mail::send([], [], function($message) use ($from_email, $from_name, $mail_subject, $mail_content, $to_email ) {
    $message->to( $to_email );
    $message->subject( $mail_subject );
    $message->setBody($mail_content);
});
No.1507
05/16 15:36

edit

Laravel で IDの auto_increment の値をセットする

● Laravel で IDの auto_increment の値をセットする

users テーブルのIDを5000番から始める場合は次のように記述します。

            // SET auto-increment start value
            DB::statement("ALTER TABLE users AUTO_INCREMENT = 5000;");
No.1506
05/08 15:27

edit

Laravel で DBに INSERT した時に ID(last inserted id )を取得する

● Laravel で insert() した時に id を取得する

$inserted_flag = $model->insert($insert_data);

  ↓  メソッド insertGetId() を使用します

$inserted_id = $model->insertGetId($insert_data);
insertGetId()
テーブルが自動増分IDを持っている場合、insertGetIdメソッドを使うとレコードを挿入し、そのレコードのIDを返してくれます。

その他 : INSERT時に使用するメソッド一覧 http://bit.ly/2PGUl1h

No.1502
05/01 10:19

edit

Laravel で「テキストメール」「htmlメール」を送信する

● Laravel で 「テキストメール」をシンプルに送信する

// テキストメール送信
$from_email = "shop@test.server.com";
$from_name = "ショップ名";
$mail_subject = "ホームページからお問い合わせがありました。";
$mail_content = "メールの本文です\nテスト";
$to_email = "customer@user.com";
\Mail::send([], [], function($message) use ($from_email, $from_name, $mail_subject, $mail_content, $to_email ) {
    $message->from( $from_email, $from_name );
    $message->to( $to_email );
    $message->subject( $mail_subject );
    $message->setBody($mail_content);
});

● Laravel で 「htmlメール」をシンプルに送信する

// htmlメール送信
$from_email = "shop@test.server.com";
$from_name = "ショップ名";
$mail_subject = "ホームページからお問い合わせがありました。";
$mail_content = "<h1>メールの本文です</h1>";
$to_email = "customer@user.com";
\Mail::send([], [], function($message) use ($from_email, $from_name, $mail_subject, $mail_content, $to_email ) {
    $message->from( $from_email, $from_name );
    $message->to( $to_email );
    $message->subject( $mail_subject );
    $message->setBody($mail_content, 'text/html');
});

メール送信方式は .env の値を参照しに行きます sendmail を使って送信する場合の設定

MAIL_FROM_ADDRESS=test@user.com
MAIL_FROM_NAME=サイト名
MAIL_DRIVER=sendmail
MAIL_HOST=localhost

● Laravel で メールの設定を動的に変更する

.env の メール設定を取得するには config() を使用します

$mailconfig = config('mail');
dump( $mailconfig );

メール設定を動的に変更する

$mailconfig = [];
$mailconfig['driver']     = 1;
$mailconfig['host']       = 2;
$mailconfig['port']       = 3;
$mailconfig['username']   = 4;
$mailconfig['password']   = 5;
$mailconfig['encryption'] = null;

// メール設定の変更
$mailconfig = config(['mail' => $mailconfig ]);

● htmlメール送信のテストを行う

https://putsmail.com/tests/new

こちらからhtmlメール送信が行えます。

● htmlメールのサンプル

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta name="robots" content="noindex">
  <title>htmlメールのタイトル</title>
</head>
<body>

<h1>見出し1</h1>
<h2>見出し2</h2>
<h3>見出し3</h3>
<h4>見出し4</h4>
<h5>見出し5</h5>

<a href="https://www.google.co.jp/" target="_blank">GoogleのHP</a>

</body>
</html>
No.1496
05/21 10:37

edit

Laravelで一時的にメンテナンスモードに入る

● Laravelで一時的にメンテナンスモードに入る

127.0.0.1
123.123.123.0/24

からのアクセスのみ許す場合

php artisan down --allow=127.0.0.1 --allow=123.123.123.0/24  --message="現在メンテナンス中です"

● Laravelのメンテナンスモードから戻る

php artisan up
No.1494
06/04 08:10

edit

LaravelのBladeで パス, URL を表示する

● LaravelのBladeで public のパス , publicのURL を表示する

Laravel アプリ内に複数の index.php が存在する場合は

{{ env('APP_URL') }}

をお勧めします。
.env にURLを記述します。

明示的に public ディレクトリを指定するときには asset('/') をおすすめします。

asset('/') はスラッシュ終わりの場合に最後にスラッシュがつきます。
url('/') はスラッシュ終わりの場合に最後にスラッシュがつきません。

{{ asset('/') }}
{{ url('/') }}

 ↓

https://YOUR-SERVER.TLD/
https://YOUR-SERVER.TLD

また YOUR-SERVER.TLD のところは、実際に駆動させているサーバ名 が入ります。( .env の APP_URL ではありません )

ファイル名まで指定する場合はどちらも同じです。

{{ asset('/assets/js/jquery-1.7.2.min.js') }}
{{ url('/assets/js/jquery-1.7.2.min.js') }}

 ↓

https://YOUR-SERVER.TLD/assets/js/jquery-1.7.2.min.js
https://YOUR-SERVER.TLD/assets/js/jquery-1.7.2.min.js

● その他のパス表示

{!! app_path() !!}
{!! base_path() !!}
{!! config_path() !!}
{!! database_path() !!}
{!! public_path() !!}
{!! resource_path() !!}
{!! storage_path() !!}
/PATH/TO/LARAVEL_APP/app
/PATH/TO/LARAVEL_APP
/PATH/TO/LARAVEL_APP/config
/PATH/TO/LARAVEL_APP/database
/PATH/TO/LARAVEL_APP/public
/PATH/TO/LARAVEL_APP/resources
/PATH/TO/LARAVEL_APP/storage
No.1347
10/23 18:59

edit

Laravel の Auth で ログイン後 / ログアウト後 / ログイン済 の遷移先を変更する

● Laravel の Auth で ログイン後の遷移先を変更する

1. メンバ変数で指定する場合

LoginController.php

    public $redirectTo = '/myhome/';    // ここを変更する

2. メソッドで指定する場合

LoginController.php

    public function redirectPath()
    {
        return '任意のurl';
    }

● Laravel の Auth で ログイン後の遷移先を元いたページから判別する

元にいたページは次のようにして取得できます。

        $path = \Session::pull('url.intended');

LoginController.php

    public function redirectPath()
    {
        $path = \Session::pull('url.intended');
        return $path;
    }

● Laravel の Auth で ログアウト後の遷移先を変更する

LoginController.php

次の行を追加する。( /mylogin にリダイレクトする )

    /**
     * ログアウトしたときの画面遷移先
     */
    protected function loggedOut(\Illuminate\Http\Request $request)
    {
        return redirect('/mylogin');
    }

● Laravel の Auth で ログインしようとした時すでにログイン済みだった時の遷移先を変更する

app/Http/Middleware/RedirectIfAuthenticated.php

    public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check()) {
            return redirect('/myhome');  // ここを変更する
        }
No.1483
02/22 13:46

edit

Laravelコントローラでバリデーションエラーがあるかどうか判別する

● Laravelコントローラでバリデーションエラーがあるかどうか判別する

        // バリデーションエラーがない場合の処理
        if ( \Session::get('errors') == null ){
                // ...
                // ...
                // ...
        }
No.1482
04/12 09:30

edit

Stripeでプランを作成する

https://stackoverflow.com/questions/30817249/create-plan-on-stripe-through-laravel

https://stripe.com/docs/api/plans/create

use \Stripe\Plan;

Plan::create(array(
  "amount" => 2000,
  "interval" => "month",
  "name" => "Amazing Gold Plan",
  "currency" => "usd",
  "id" => "gold")
);
No.1481
04/11 22:22

edit

Laravel のルーティングで GET,POST を受け付けるルートを作成する

● Laravel のルーティングで GET,POST を受け付けるルートを作成する

POSTメソッドのみのルーティング

Route::post("order/input", "OrderController@input")->name('order.input');

 ↓

GET , POSTメソッドを受け付けるルーティング

Route::match(['get', 'post'],"order/input", "OrderController@input")->name('order.input');

● コントローラーでメソッドを判別する $request->isMethod()

例)POSTメソッドの時だけバリデーションするようにします

if ( $request->isMethod('post') ){
    // バリデーション
    $this->validate( $request, $this->customer_info );
}
No.1479
12/12 21:41

edit

Laravel Bladeテンプレート で nl2br する

● Laravel Bladeテンプレート で nl2br する

htmlタグをエスケープする場合
(通常こちらを使用します)

{!! nl2br(e($shop->my_text)) !!}

エスケープしない場合

{!! nl2br($shop->my_text) !!}
No.1478
05/04 18:49

edit

Laravel の mysql の sql_mode を 調査し変更する

● Laravel の mysql の sql_mode を 調査する

Laravelアプリケーションではmysql の sql_mode はどうなっているのでしょうか?

$sql_mode = DB::select( 'SHOW VARIABLES LIKE "%sql_mode%"' );
dump($sql_mode);

戻り値()

ONLY_FULL_GROUP_BY,
STRICT_TRANS_TABLES,
NO_ZERO_IN_DATE,
NO_ZERO_DATE,
ERROR_FOR_DIVISION_BY_ZERO,
NO_AUTO_CREATE_USER,
NO_ENGINE_SUBSTITUTION

● Laravel の mysql の デフォルトの sql_mode を変更する

app/config/database.php

        'mysql' => [
            'driver' => 'mysql',
            .........
            // これをコメントアウトOFF  'strict' => true,
        ],

この状態で sql_mode を調べると

$sql_mode = DB::select( 'SHOW VARIABLES LIKE "%sql_mode%"' );
dump($sql_mode);

戻り値()

NO_AUTO_CREATE_USER,
NO_ENGINE_SUBSTITUTION

となります。
sql_mode を追加するには modes に記述してあげます。

● Laravel で groupBy() を使用した時のエラー「Syntax error or access violation: 1055」に対応する

app/config/database.php を以下のように変更します

            // 'strict' => true,    // OFF
            'modes' => [
                //'ONLY_FULL_GROUP_BY', // OFF
                'STRICT_TRANS_TABLES',
                'NO_ZERO_IN_DATE',
                'NO_ZERO_DATE',
                'ERROR_FOR_DIVISION_BY_ZERO',
                'NO_AUTO_CREATE_USER',
                'NO_ENGINE_SUBSTITUTION'
            ],
No.1477
04/08 09:18

edit

Laravel の Auth で ログイン認証に使用するカラムを追加(変更)する

app/Http/Controllers/Auth/LoginController.php に以下のメソッドを追加します

    /**
     * ログイン認証カラムを増やす
     *
     */
    protected function credentials(Request $request)
    {
        // ログインに必要なすべてのパラメーターが渡っているかチェックする
        if (! $request->has(['email', 'shop_id', 'password']) ) {
            throw new \Exception("ログインに必要なパラメーターが渡されていません");
        }
        return $request->only('email', 'shop_id', 'password');
    }

ログインに必要なすべてのパラメーターが渡っているかチェックしないと非常に危険です。
必ずチェックしてください。

No.1476
04/17 15:04

edit

Laravel で 長い文字列を切り詰める(mb_truncate)

● Laravel で 長い文字列を切り詰める

Smartyで言う所のmb_truncateは

echo str_limit($report->report_honnin_name, $limit = 80, $end = '...');

という風に記述します。

htmlタグをエスケープする場合は

echo htmlspecialchars( str_limit($report->report_honnin_name, $limit = 80, $end = '...') );

● Laravel の helper

https://laravel.com/docs/5.8/helpers

No.1475
08/07 14:50

edit

Laravel で Session(セッション)を使用する

● Laravelでセッションへ値を保存する

$request を使う場合

$request->session()->put('contact', $request->contact);

Session を使う場合

use Illuminate\Support\Facades\Session;

Session::put('contact', 'hogehoge');

ヘルパーを使う場合

session(['contact' => 'hogehoge' ]);

● Laravelでセッションから指定の変数を読み込む

$request を使う場合

$request->session()->get('contact');

Session を使う場合

\Session::get('contact', 'xxx'); // 取得できない場合 xxx を返す

ヘルパーを使う場合

session('contact');

● Laravelでセッション値の存在確認をする has()

// $request を使う場合
if ( ! $request->session()->has('_old_input') ){ ..... }

// \Session を使う場合
if ( ! \Session::has('_old_input') ){ ..... }

// session() を使う場合
if ( ! session()->has('_old_input'); ){ ..... }

● Laravelでセッションから指定の変数を削除

// $request を使う場合
$request->session()->forget('contact');

// \Session を使う場合
\Session::forget('contact');

// session() を使う場合
session()->forget('contact');

● Laravelでセッションから全データを削除

$request->session()->flush();
\Session::flush();

● Laravelで全てのセッションを読み込む

$session__all = \Session::all();
No.1474
04/12 15:50

edit

Laravel で最初にやっておいた方が良い初期設定

・varcharのデフォルト文字数を191文字にする

変更ファイル : app\Providers\AppServiceProvider.php:26

public function boot()
{
    // ↓ この行を追加
    \Illuminate\Support\Facades\Schema::defaultStringLength(191);
}

・timezoneの設定を変更する

変更ファイル : config/app.php:70

'timezone' => 'UTC',

 ↓

'timezone' => 'Asia/Tokyo',

・ロケールの設定を変更する

変更ファイル : config/app.php:83

'locale' => 'en',

  ↓

'locale' => 'ja',

・ディスク設定にパーミッションを追加する

変更ファイル : config/filesystems.php

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
          // ===== permissionsを追加 =====
            'permissions' => [
                'dir' => [
                    'public'  => 0777 ,
                    'private' => 0777 ,
                ],
                'file' => [
                    'public'  => 0666 ,
                    'private' => 0666,
                ],
            ],
            // ===== / permissionsを追加 =====

        ],

変更ファイルconfig/logging.php

        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
            'days' => 7,
            // ===== permissionsを追加 =====
            'permission' => 0666,
            // ===== / permissionsを追加 =====
        ],

変更ファイル: config/cache.php

        'file' => [
            'driver' => 'file',
            'path' => storage_path('framework/cache/data'),
            // ===== permissionsを追加 =====
            'permission' => 0666,
            // ===== / permissionsを追加 =====
        ],

umaskも設定する必要がある場合は次のようにします
設定ファイル : app/Providers/AppServiceProvider.php

    public function boot()
    {
        // umask設定
        umask(0);
    }

● .env の内容を変更する

APP_URL=https://your.site.com

合わせてエディタも設定しておきます。(エラー時に鉛筆のアイコンをクリックすると設定したエディターで開くことができます)

IGNITION_EDITOR="vscode"

後 DB 関連も書き換えておきましょう

● Laravelのログを設定する

Laravelのログを日別でローテーションさせる( +自動削除 )|プログラムメモ
Laravel でユーザーがログインしたログを取得する|プログラムメモ
LaravelでSQLのログとスロークエリログを取得する|プログラムメモ
Laravelで任意のログチャンネルを追加してログを出力する|プログラムメモ

● 必要なパッケージを追加する

必要であれば以下のようなパッケージをインストールしておきます

# renatomarinho/laravel-page-speed (最終的に生成されるhtmlをminifyする)
composer require renatomarinho/laravel-page-speed

# helper
composer require laravel/helpers

# form ヘルパー(Laravel Collective)
composer require laravelcollective/html

# yaml
composer require symfony/yaml

# Debug Bar
composer require barryvdh/laravel-debugbar

# DB Backup
composer require spatie/laravel-db-snapshots

# Laravel 6 7 で Auth を使う場合
composer require laravel/ui

Laravel で DB のバックアップを簡単にとる (laravel-db-snapshots)|プログラムメモ

● 不要なデフォルトのミドルウェアを停止する

TrimStringsはフォーム入力の無駄な前後スペースを取り除きます。

不要ならコメントアウトしましょう app/Http/Kernel.php

    protected $middleware = [
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
// OFF        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];

● Laravelでエラー画面からワンクリックでソースコードのエラー箇所に移動する

Laravelでエラー画面からワンクリックでソースコードのエラー箇所に移動する|プログラムメモ

● VS CodeやPhpStormでコード補完(インテリセンス)が効くようにする

laravel-ide-helper を使用する|プログラムメモ

No.1473
12/14 15:13

edit

blade テンプレートのひな型

● 継承元テンプレート

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
    <title>{{$page_title or ''}}</title>
</head>
<body>
    @yield('content')
</body>
</html>

● 各ページテンプレート

{{-- 継承元 --}}
@extends('user.layout_user')


{{-- ページタイトル --}}
@php
    $page_title = '記事詳細'
@endphp


{{-- コンテンツ --}}
@section('content')
    <div>ページ内容</div>
@endsection
No.1472
04/03 17:46

edit

コントローラーの作成

● artisan コマンドによるコントローラーのひな形作成

(先頭は大文字です)

php artisan make:controller InfoController

app/Http/Controllers/InfoController.php が自動で作成されます。

No.1471
04/03 18:15

edit

TCPDF で 外字使用してPHPからPDF出力をする

● 外字フォントの用意

1. ファイルのリネーム( tte を ttf にリネームする)

例: 外字ファイルが eudc.tte の場合

「eudc.tte」→「eudc.ttf」

2. 任意のフォルダで tcpdfをダウンロード(git clone)する

git clone https://github.com/tecnickcom/tcpdf

3. フォントファイル(eudc.ttf)を( tcpdf/tools )へコピーする

4. コンバートの実行

cd tcpdf/tools
php ./tcpdf_addfont.php -b -t eudc -f 32 -i eudc.ttf

これでフォルダ tcpdf/fonts にファイル

eudc.ctg.z
eudc.php
eudc.z

が生成されます。

これらの3つのファイルをtcpdfを動作させるサーバの好きなディレクトリにコピーします。

● TCPDFで使用する

$pdf = new Fpdi\TcpdfFpdi();
$font_2 = $pdf->AddFont( "eudc", "", "/YOUR/SERVER/PATH/fonts/eudc.php", true );

これで

<span style="font-family:eudc">◆◆◆</span>

(◆ が外字 )

で表示されます。 外字文字のところだけ、このCSSをあててあげます。

● CSSを自動で当てる(Laravel)

Laravel の モデルにアクセサを登録します。

/**
 * アクセサー : PDF生成時に外字文字のみCSSを追加します。
 */
public function getPdfGaijiNameAttribute()
{
    if ( $this->attributes['name'] != null ){
        $text = $this->attributes['name'];
        $encoding = "UTF-8";
        $textLength = mb_strlen($text, $encoding);
        for($i = 0; $i < $textLength; $i++) {
            $m = mb_substr($text, $i, 1, $encoding);
            if (preg_match('/^(\xEE[\x80-\xBF])|(\xEF[\x80-\xA3])|(\xF3[\xB0-\xBF])|(\xF4[\x80-\x8F])/', $m)){
                echo '<span style="font-family:eudc">' . $m ."</span>";
            }
            else {
                echo $m;
            }

        }
    } else {
        return 'ERROR: getPdfGaijiNameAttribute()';
    }
}

使い方

{{$user->name}}

 ↓ の代わりに次のように記述します。

{{$user->pdf_gaiji_name}}

引用:https://goo.gl/AkQXfT

No.1468
03/20 18:56

edit

Laravel で プライマリキー(ID)を bigint にする

● Laravel で プライマリキー(ID)を bigint にする

            $table->bigIncrements('id');

これだけで unsigned, auto increment , primary key 設定になります。

No.1466
03/20 11:08

edit

Laravelでフォームパラメーター($request)の自動トリムをoffにする

● フォームパラメーター($request)の自動トリムをoff

1. app/Http/Kernel.php** の ミドルウェアを削除する

app/Http/Kernel.php
「TrimStrings」( フォーム入力値の前後の空白を自動的にトリムする)
「ConvertEmptyStringsToNull」( フォーム入力値の空文字をnullに自動的に変換する)
を削除します。

    protected $middleware = [
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        // コメントアウト \App\Http\Middleware\TrimStrings::class,
        // コメントアウト \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];
No.1464
03/19 13:36

edit

Laravel Mix を使用する

● Laravel Mix を使用する

Laravel Mix とは 何のことはない、webpack をラッパーしたものです。
webpackは何かというと、javascript や css を圧縮したり1つのファイルにまとめたり、古いブラウザでも使えるように変換するものです。
どういうときに使うかというと、複数のcssやjsを1つにまとめる , cssやjsファイルの圧縮 などを行いたい時です。

1. Laravel アプリを作成する

composer create-project "laravel/laravel" my_app
cd my_app

2. Laravel Mix(Webpack)をインストールする

npm install

これで準備はOKです。

3. Laravel Mix で操作するファイルを(webpack.mix.js)内で設定する

ファイル webpack.mix.js をエディタで開き編集します。

● public/assets/js/mylib.js を圧縮して public/assets/js_dist/mylib.js ファイルを生成する

mix.js( 'public/assets/js/mylib.js', 'public/assets/js_dist');

● aa.css と bb.css をくっつけて all.css を生成する

mix.styles([
    'public/css/aa.css',
    'public/css/bb.css'
], 'public/css/all.css');

● style.scss をコンパイルして、public/css/style.css を生成する

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

4. Laravel Mix を実行する

テスト環境(ソースを圧縮せず)で実行する

npm run dev

本番環境環境(ソースを圧縮して)で実行する

npm run prod

5. ファイル更新のたびに自動で実行するようにする( watch )

npm run watch

解凍ファイルが更新されるたび自動でLaravel mixが実行されます

No.1462
06/16 13:55

edit

Laravel で DB のバックアップを簡単にとる (laravel-db-snapshots)

laravel-db-snapshotsを使用すると、Laravel で DBの構造とデータをまとめてバックアップ / リストア することができます。 (データのみの取得はできないようです。)

● spatie/laravel-db-snapshots

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

composer require spatie/laravel-db-snapshots

● インストールの確認

インストールを確認します。

php artisan
 snapshot
  snapshot:create      Create a new snapshot.
  snapshot:delete      Delete a snapshot.
  snapshot:list        List all the snapshots.
  snapshot:load        Load up a snapshot.

● バックアップを取るディスクを設定する

config/filesystems.php:56

    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

        // ここから追加
        'snapshots' => [
            'driver' => 'local',
            'root' => database_path('snapshots'),
        ],
        // ここまで追加

(実際のディレクトリはバックアップを取るときに自動作成されます)

● バックアップを取る

テストで database/snapshots/my-first-dump.sql を作成します

php artisan snapshot:create my-first-dump

ファイル名を日付にしてもいいと思います。

php artisan snapshot:create 2019_03_12

何度もとるなら時刻も入れておくといいと思います。

php artisan snapshot:create 2019_04_16__03_19_00

なお 同名のファイルの場合は上書き されますので注意してください。

● ファイル名自動でバックアップを取る

php artisan snapshot:create
Creating new snapshot...
Snapshot `2019-06-04_08-06-49` created (size: 1.37 MB)

2019-06-04_08-06-49 というフォーマットで自動的にファイル名を命名してバックアップを取ってくれます。

●バックアップファイルからリストア(データを流し込む)する

バックアップファイル一覧の確認

php artisan snapshot:list

一覧の Name を指定してリストアを実行します。

リストアの実行

php artisan snapshot:load <バックアップName>

● リストアエラー「 The "force" option does not exist. 」が出る場合

本番環境 (.env に APP_ENV=production が設定されている場合)ではリストアはエラーとなります。

一時的に戻します

.env

APP_ENV=production

 ↓

APP_ENV=local

これで実行できます。 .env ファイルの中身は戻しておきましょう

参考: FTPにバックアップを取る場合は
http://bit.ly/35EXQwg
http://bit.ly/34BWLUT

● docker環境では動作しない

mysqldumpコマンドを使ってバックアップを作成するのでこちらのコマンドを実行できるようにすればdocker環境でも使用することができます

No.1459
12/16 10:16

edit

Laravel で back URL を持ち回る

● Laravel で back URL を持ち回る

管理画面の編集完了後にリダイレクトするページを複数にしたい時、 戻る URL を次のようにして 持ちまわると便利です。

● _back_url を(1画面だけ有効な)セッションに保存

use Illuminate\Support\Facades\Session;
\Session::flash('_back_url', $request->fullUrl());

● _back_url を経過するコントローラーで(1画面だけ有効なセッションを)保持

use Illuminate\Support\Facades\Session;
if (\Session::has('_back_url')) { \Session::keep('_back_url'); }

● データをUPDATEするコントローラーでもセッションを保持しつつ、リダイレクト

セッションがある時とない時で処理を分けます。

use Illuminate\Support\Facades\Session;
if (\Session::has('_back_url')) { \Session::keep('_back_url'); }
if ( \Session::get('_back_url') ){
    return redirect( \Session::get('_back_url') )->with([ 'message' => 'success !' ]);
}
else {
    return redirect()->route("admin.data.index")->with([ 'message' => 'success !' ]);
}

● 全てのコントローラーに適用したい場合は

httpミドルウェアを使用しましょう。
https://readouble.com/laravel/5.8/ja/middleware.html

No.1455
04/16 10:54

edit

Laravel の バージョン( Ver )をソースコード、コマンド、Bladeテンプレート、コントローラーから調べる

● Laravel の バージョンをソースコードから調べる

vendor/laravel/framework/src/Illuminate/Foundation/Application.php を調べます

	/**
	 * The Laravel framework version.
	 *
	 * @var string
	 */
	const VERSION = '5.0.31';

● Laravel の バージョンをコマンドから調べる

php artisan -V

● Laravel の バージョンを Blade テンプレートから調べる

{{ App::VERSION() }}

● Laravel の バージョンを コントローラーで取得する

$laravel_ver = app()->version();

文字列で返ります

5.4.36

● Laravel の 先頭のメジャーバージョンを コントローラーで取得する

$laravel_ver = preg_replace("{([0-9]+)\.([0-9]+)\.([0-9]+)}","$1", app()->version() );

文字列で返ります

5.4.36
No.1454
07/12 12:51

edit

Blade
artisan

Laravel の ユーザー認証の ハッシュ方式を任意のものに変更する

● Laravel の ユーザー認証の ハッシュ方式を任意のものに変更する

例) として aes256(キー固定)に変更してみます。

● 1.CustomHasher.php の作成

app/Libs/CustomHash/CustomHasher.php を以下の内容で作成する

<?php
 namespace App\Libs\CustomHash;
 
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
 
class CustomHasher implements HasherContract {

    public function info($hashedValue)
    {
        return $this->driver()->info($hashedValue);
    }
 
    public function make($value, array $options = array()) {
        $key = env('CUSTOM_HASHER_AES256_KEY', false);
	    return openssl_encrypt($value,'aes-256-ecb',$key);
    }

    public function unmake($value, array $options = array()) {
	    $key = env('CUSTOM_HASHER_AES256_KEY', false);
	    return openssl_decrypt($value,'aes-256-ecb',$key);
    }
 
    public function check($value, $hashedValue, array $options = array()) {
        return $this->make($value) === $hashedValue;
    }
 
    public function needsRehash($hashedValue, array $options = array()) {
        return false;
    }
 
}

● 2. CustomHashServiceProvider.php の作成

app/Providers/CustomHashServiceProvider.php を以下の内容で作成する

<?php
namespace App\Providers;
 
use Illuminate\Hashing\HashServiceProvider;
use App\Libs\CustomHash\CustomHasher as CustomHasher;
 
class CustomHashServiceProvider extends HashServiceProvider
{
    public function register()
    {
        $this->app->singleton('hash', function () {
            return new CustomHasher;
        });
    }
}

● 3.config/app.php の変更

config/app.php の変更

    'providers' => [
		・・・・・
        App\Providers\CustomHashServiceProvider::class ,    // 追加
        // Illuminate\Hashing\HashServiceProvider::class,    // コメントアウト
		・・・・・

● 4. .env に追加

CUSTOM_HASHER_AES256_KEY=my_aes256key

● 5. bcrypt で記述しているシーダーがあれば修正

            'password'            => bcrypt('my-pass-word') ,

  ↓

            'password'            => \Illuminate\Support\Facades\Hash::make('my-pass-word') ,
No.1453
03/07 14:35

edit

bcrypt
Hasher
Hashing

Laravel で fill を通さない DBデータINSERT

(管理画面で初期マスタデータをクライアントに登録してもらう時などに使用します)

● 通常の INSERT(その1)

$insert_data = [
    'name'    => '代入 太郎' ,
];
$practitioner = \App\Practicioner::create($insert_data);

● 通常の INSERT(その2)(その1と同じです)

$insert_data = [
    'name'    => '代入 太郎' ,
];
$practitioner = \App\Practicioner::fill($insert_data)->save();

● fill を通さない INSERT

$insert_data = [
    'id'      => 123456 ,
    'name'    => '代入 太郎' ,
];
$practitioner = \App\Practitioner::insert($insert_data);
No.1449
03/05 11:46

edit

Laravelで id またはメールアドレスでログインする

● Laravelで id またはメールアドレスでログインする

1. コントローラーを修正

(パラメータ名「email_or_id」を受けて、メールアドレスの場合はメールアドレスとして認証。それ以外の場合はidとして認証に行きます。)

app/Http/Controllers/Auth/LoginController.php に以下を追加します

    function username()
    {
        return 'email_or_id';
    }

    function attemptLogin(\Illuminate\Http\Request $request)
    {
        $email_or_id = $request->input($this->username());
        $password = $request->input('password');

        if (filter_var($email_or_id, \FILTER_VALIDATE_EMAIL)) {
            $credentials = ['email' => $email_or_id, 'password' => $password];
        } else {
            $credentials = ['id' => $email_or_id, 'password' => $password];
        }
        return $this->guard()->attempt($credentials, $request->filled('remember'));
    }

2. ビューを修正

パラメータ名を「email」 →「email_or_id」に変更します resources/views/auth/login.blade.php を以下のように変更します

<input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" autofocus>

<input id="email" type="text" class="form-control" name="email_or_id" value="{{ old('email_or_id') }}" autofocus>

以上で id またはメールアドレスでログインする事ができます

引用元 : https://goo.gl/Wodfh1

No.1448
02/22 09:21

edit

Laravel アプリで WordPress 自動整形機能(wpautop) を使用する

● wptrait

https://github.com/akat03/wptrait

● 準備 ( Model )

class Mymodel extends Model
{
    use WpTrait;
}

● 使い方 ( Blade テンプレート)

{!! $mymodel->wpautop($mymodel->contents)  !!}

● 使い方 ( Voyager BREAD )

カラム「text_name」にのみ適用します

@if ( $row->field == 'text_name' )
    @php
        echo $dataTypeContent->wpautop($dataTypeContent->text_name);
    @endphp
@else
   <p>{!! $dataTypeContent->{$row->field} !!}</p>
@endif
No.1445
02/15 11:47

edit

Laravel 「No application encryption key has been specified.」エラーの対処法

● Laravel 「No application encryption key has been specified.」エラー

.env.example からリネームして .env を作成した時に主に出現します。
「application encryption key」がないですよ、と言う事ですので作成しましょう。

php artisan key:generate
No.1441
02/12 10:38

edit

Laravel で lang ファイルを使って多言語化する

● 言語ファイル格納場所とファイル名

英語の場合は「resources/lang/en」ディレクトリ内に言語ファイルを格納します(元からあるはずです) 日本語の場合は「resources/lang/en」ディレクトリを作成します。

ファイル名の例(myapp)

resources/lang/ja/myapp.php

● 言語ファイルの書式

return [
    'list'        => '一覧',
    'edit'        => '編集',
    'delete'      => '削除',
    'print'       => '印刷',
];

● Bladeビューでの使い方

こちらの記法がオススメです

@lang('myapp.list')

(一覧 と表示されます。)

こういう書き方もできます。

{{ __('messages.welcome') }}

● パラメーターを渡す方法

    'delete_confirm'                     => ':nameを削除します。よろしいですか?',

と定義している時、次のようにしてパラメーターを渡します。

@lang('excrud.delete_confirm',['name' => 'データ100番'])

「データ100番を削除します。よろしいですか?」になる

● Bladeビュー @php ブロック内での使い方

@php
    $page_title = \Lang::get('myapp.edit');
@endphp
No.1439
06/18 13:10

edit

Laravel Voyager を使用する

● Voyager

オリジナルのCMSや管理画面を作りたい時に、面倒な色々の準備をやってくれるのが Voyager です。
必要最低限の機能がかなりのクォリティで用意されています。

https://github.com/the-control-group/voyager

● Voyager のインストラクションビデオ

( 必ず見るようにしましょう。 簡潔に説明してくれています。)

https://laravelvoyager.com/academy/configurations/

● Voyager のインストール

Laravel コマンドでアプリを作成した後に次のコマンドを入力します

・1. laravelコマンドでアプリを作成

composer create-project "laravel/laravel=5.7.*" my_app
cd my_app

・2. composer で Voyagerをインストール

composer require tcg/voyager

・2-B. インストールした Laravel , Voyager のバージョンを確認

composer show

laravel と voyager だけ表示させる

composer show | grep -e laravel/framework -e tcg/voyager

リストの中の次のバージョンを確認する(何か問題が発生した時はバージョンを考慮して検索しましょう)

laravel/framework                     v5.7.28 
tcg/voyager                           v1.1.12

・3-A. .env に DB設定を書き込む

適宜変更します

https://YOUR-SERVER-NET
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

・3-B. Laravel の varcharのデフォルト文字数を191文字にして(191*4 = 764バイト) 767バイトより小さくする

app\Providers\AppServiceProvider.php

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

    public function boot()
    {
        // ↓ この行を追加
        Schema::defaultStringLength(191);
    }

・4. php artisan voyager コマンドからインストールする

・ダミーデータも一緒にインストールする場合
php artisan voyager:install --with-dummy

(↑ このコマンドでDBデータも自動作成されます)

・管理ユーザーを設定してインストールする場合
php artisan voyager:install
php artisan voyager:admin your@email.com --create

・5. Voyagerにアクセスします

https://<your-site>/admin

admin@admin.com
password

● nginx で Voyager を動作させる

Voyager 1.2 以降から

{{ voyager_asset('lib/css/responsive.dataTables.min.css') }}

の様な形で voyager_asset が追加されました。 「今まで public ディレクトリ以下にファイルをコピーしていた代わりに内部ファイルを返す」 といったものです。これが標準の nginx では動作しない可能性があります。

・nginx設定ファイルに以下を追加

location ~* .(js|css|jpg|png|gif|svg|woff|ttf)$ {
	try_files $uri $uri/ /index.php?$args;
}
nginx -s reload

これで読み込まれます。

 

● 管理画面の見た目のカスタマイズ

・1. 管理画面を日本語にローカライズする

右上のメニューから「Profile」 →「Edit My Profile」→「Locale を ja にセットする」

・2. 管理画面のローディングアイコンを変更する

「Settings」→「Admin」→「Admin Loader」に画像をアップロードする

・3. 管理画面のログイン画面のメイン画像を変更する

こちらの画像ファイルを変更します。

/public/vendor/tcg/voyager/assets/images/bg.jpg

デフォルトでは画像サイズ「1800ox * 1200px 」となっています。

・4. 管理画面のBREDのテンプレートビューを変更する

管理画面のBREDのテンプレートビューを変更するには次の位置に Bladeテンプレートファイルを作成します。

(詳細表示 : browse)を変更する場合

/resources/views/vendor/voyager/<テーブル名>/browse.blade.php

元の管理画面を参考に変更したい場合は次のファイルの内容をコピーします。

vendor/tcg/voyager/resources/views/bread/browse.blade.php

解説: https://laravelvoyager.com/academy/views/

・5. 管理画面のBREDのレイアウトテンプレートを変更する

vendor/tcg/voyager/resources/master.blade.php

 ↓ コピーする

resources/views/vendor/voyager/master.blade.php

・4. Voyager 管理画面のBREDのテンプレートビュー(Blade)内で権限により処理を変える

例: BREADのリスト表示で
「admin の role を持つ場合は表示させる」
「user の role の場合は Policy(別途作成が必要)により TCG\Voyager\Actions\EditAction が可能な場合は表示させる」
を Bladeテンプレートに記述します。

@if (Auth::user()->hasRole('admin'))
    <!-- admin -->
@else
    @php
        $edit_action = new TCG\Voyager\Actions\EditAction($dataType, $data);
    @endphp
    @can($edit_action->getPolicy(), $data)
        @php echo '<!-- user (edit可能です)-->'; @endphp
    @else
        @php echo '<!-- user (edit ×不可能です)-->'; @endphp
        @continue
    @endcan
@endif

・6. 管理画面のBREDのコントローラを変更する

管理画面のBREDのコントローラーを変更するには次の位置に コントローラーファイルを作成します。

例: (EstimateController)を変更する場合

app/Http/Controllers/EstimateController.php

app/Http/Controllers/EstimateController.php を次のようにします。( VoyagerBaseController を継承して中身が空のコントローラを作成 )

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use TCG\Voyager\Database\Schema\SchemaManager;
use TCG\Voyager\Events\BreadDataAdded;
use TCG\Voyager\Events\BreadDataDeleted;
use TCG\Voyager\Events\BreadDataUpdated;
use TCG\Voyager\Events\BreadImagesDeleted;
use TCG\Voyager\Facades\Voyager;
use TCG\Voyager\Http\Controllers\Traits\BreadRelationshipParser;

class  EstimateController extends \TCG\Voyager\Http\Controllers\VoyagerBaseController
{
}

 
元の管理画面を参考に変更したい場合は次のコントローラーファイルの内容をコピーします。

vendor/tcg/voyager/src/Http/Controllers/VoyagerBaseController.php

最後にVoyager の BREAD から 対象となるDBテーブルの「コントローラー名」に「\App\Http\Controllers\EstimateController」を指定します。 (必須です) これで作成したコントローラが使用されます。

● Voyager に 独自cssを追加する

1. /config/voyager.php の以下を変更します。

    'additional_css' => [
        'storage/css/voyager_custom.css',
    ],

storage/app/public/css/voyager_custom.css にcssファイルを置きます

2. css を設置します

<My-APP>/public/css/voyager_custom.css

● Voyager チートシート

https://voyager-cheatsheet.ulties.com/

● Google Analytics API を使用する

https://goo.gl/JrtvkG

● 設定ファイル

/config/voyager-hooks.php
/config/voyager.php

● WordPressデータをインポートしたい

https://github.com/thedevdojo/wordpress-import

● 辞書ファイルの場所

辞書ファイル(日本語)はこちらにあります。

/vendor/tcg/voyager/publishable/lang/ja

● Voyager の ユーザーモデルを変更する

app/User.php

<?php
namespace App\Models;
use TCG\Voyager\Models\User as VoyagerUser;
class User extends VoyagerUser {
    // add custom mutators and other code in here
}

app/config/voyager.php

'user' => [
    'add_default_role_on_register' => true,
    'default_role'                 => 'user',
    'admin_permission'             => 'browse_admin',
    'namespace'                    => App\User::class,
],

● BREADの options を使用する

BREADのオプションに任意のパラメータを設定した場合は次のように $row->details から参照できます。

例)編集画面にコメントを表示させる
resources/views/vendor/voyager/<BREAD名>/edit-add.blade.php

{{-- コメント表示 --}}
@php 
if ( @$row->details->comment ){
    echo '<small>';
    echo $row->details->comment;
    echo '</small>';
}
@endphp
{{-- / コメント表示 --}}

なお、編集時に項目の見出しをつけるには

{
    "legend": {
        "text": "○○編集エリア"
    }
}

のように指定すると見出しが表示されます。

引用 : https://goo.gl/ydJEd6

● BREAD画面でリレーションにリンクをつける

https://github.com/the-control-group/voyager/issues/2008

● Select Dropdown を使った BREAD のリレーション設定方法

・1. 入力タイプを「Select Dropdown」を選択する

・2. オプション詳細を次のように記述する

{
    "relationship": {
        "key": "id",
        "label": "name"
    }
}
No.1438
04/10 11:38

edit

Laravel の モデルで リレーション先のカラムで親データをソートする

● Laravel の モデルで リレーション先のカラムで親データをソートする

少しややこしい表現になりますが、
リレーション先の複数のカラムをソートする のではなく、
リレーション先のカラムを使って親データをソート します

\App\Shirt::with('size')
    ->select('shirts.*', \DB::raw('(SELECT sort FROM sizes WHERE shirts.size_id = sizes.id limit 1 ) as sort'))
    ->orderBy('sort', 'ASC')
    ->get();  // または paginate( $limit );

注意

limit 1 をつけないと、複数行ある時に 「Subquery returns more than 1 row」エラーが返ります

● Laravelのサブクエリ用メソッド

Laravel 5.6.19 から は DB::raw を使用する代わりに joinSub()、leftJoinSub()、rightJoinSub()が追加されたようです。

DB::table('table')->joinSub('select * from "subtable"', 'sub', ...);
DB::table('table')->leftJoinSub(function ($q) { $q->from('subtable'); }, 'sub', ...);
DB::table('table')->rightJoinSub(DB::table('subtable')->where('foo', 'bar'), 'sub', ...);

引用元 : https://goo.gl/2UPi1E

No.1436
10/11 17:28

edit

Eloquent
DB

Laravel で ファイルをアップロードする

● Blade テンプレートに以下を記述してフォームタグを生成

{{ Form::file('myfile', ['class' => $class_name]) }}

( Form::text の場合は 2番目の引数に値をセットしますが、Form::file はセットしません。 )

● ファイル名を「お任せ」で保存する

コントローラーに以下を記述

$file_store_dir = 'img/';
$file_store_disk = 'local';  // local または public または s3
$file_name = $request->myfile->store($file_store_dir, $file_store_disk );

● ファイル名を指定して保存する

コントローラーに以下を記述

$ext = pathinfo($request->myfile->hashName(), PATHINFO_EXTENSION);	// ファイル名から拡張子を取得
$file_store_dir = 'img/';
$file_store_file_name = $id.".{$ext}"; // IDに拡張子をつけたものを保存ファイル名とする
$file_store_disk = 'local';  // local または public または s3
$file_name = $request->myfile->storeAs($file_store_dir, $file_store_file_name, $file_store_disk );  	// ディレクトリ, ファイル名, ディスク
No.1434
02/01 17:39

edit

Laravel の モデル で 生年月日から 和暦(昭和45年6月27日)を返すアクセサーを定義する

    /**
     * アクセサー : 「->patient_birth_date_wareki」 で 値「昭和45年6月27日」のフォーマットで誕生日を返す
     */
    public function getPatientBirthDateWarekiAttribute()
    {
        if ( $this->attributes['patient_birth_date'] != null ){
            $c = new \Carbon\Carbon($this->attributes['patient_birth_date']);
            setlocale(LC_TIME, 'ja_JP.utf8');
            $format = '%EC%Ey年%-m月%-d日';
            return $c->formatLocalized($format);
        } else {
            return 'ERROR: getPatientBirthDateWarekiNengoAttribute()';
        }
    }


    /**
     * アクセサー : 「->patient_birth_date_wareki_nengo」 で 値「昭和」を返す
     */
    public function getPatientBirthDateWarekiNengoAttribute()
    {
        if ( $this->attributes['patient_birth_date'] != null ){
            $c = new \Carbon\Carbon($this->attributes['patient_birth_date']);
            setlocale(LC_TIME, 'ja_JP.utf8');
            $format = '%EC';
            return $c->formatLocalized($format);
        } else {
            return 'ERROR: getPatientBirthDateWarekiNengoAttribute()';
        }
    }


    /**
     * アクセサー : 「->patient_birth_date_wareki_nengo」 で 年号以降の和暦の誕生日を返す
     */
    public function getPatientBirthDateWarekiNengappiAttribute()
    {
        if ( $this->attributes['patient_birth_date'] != null ){
            $c = new \Carbon\Carbon($this->attributes['patient_birth_date']);
            setlocale(LC_TIME, 'ja_JP.utf8');
            $format = '%Ey年%-m月%-d日';
            return $c->formatLocalized($format);
        } else {
            return 'ERROR: getPatientBirthDateWarekiNengoAttribute()';
        }
    }

・使い方

dump($model->patient_birth_date); // DB に入っている誕生日
dump($model->patient_birth_date_wareki);
dump($model->patient_birth_date_wareki_nengo);
dd($model->patient_birth_date_wareki_nengappi);

・結果

Carbon @1548926428 {#558 ▼
  date: 2019-01-31 18:20:28.0 Asia/Tokyo (+09:00)
}
"平成31年1月31日"
"平成"
"31年1月31日"
No.1433
01/31 18:22

edit

Laravelでリレーション先のデータを少なくとも1つ以上持つデータを取得する

https://readouble.com/laravel/5.7/ja/eloquent-relationships.html

// 最低1つのコメントを持つ全ポストの取得…
$posts = App\Post::has('comments')->get();
// 3つ以上のコメントを持つ全ポストの取得
$posts = App\Post::has('comments', '>=', 3)->get();
// 最低1つのコメントと、それに対する評価を持つポストの取得
$posts = App\Post::has('comments.votes')->get();
No.1431
01/30 17:36

edit

LaravelでDBのトランザクションを使用する

● LaravelでDBのトランザクションを使用する

・ A. クロージャ(無名関数)で LaravelでDBのトランザクションを記述する

// ========== トランザクション ==========
DB::transaction(function () use ($params) {

    // ...
    // ...
    // 何かしらの処理を記述

});
// ========== / トランザクション ==========

クロージャ(無名関数)が正常に実行されると、トランザクションは自動的にコミットされます


・ B. try / catch で LaravelでDBのトランザクションを記述する

// ===== トランザクション =====
try {
    DB::beginTransaction();

    // ...
    // ...
    // 何かしらの処理を記述

    DB::commit();
} catch (\Exception $e) {
    DB::rollBack();
}
// ===== /トランザクション =====

● LaravelでDBのトランザクション内で作成したデータを返す

// ========== トランザクション ==========
$data = DB::transaction(function () use ($params) {

    // DB 処理
    .......

    return $data;
});
// ========== / トランザクション ==========
No.1428
11/04 09:47

edit

Laravelのモデルで明示的にIDの値を指定して INSERT する

$insert_data = [
    'id'         => 1001 ,
    'user_name'  => 'テスト太郎' 
];

IDがガードされている場合、次のようにすると user_name のみ INSERTするSQL文が実行されます。

・ id に値をセットしないで INSERT

$model = new MyModel();
$model->fill($insert_data)->save();

 ↓ 明示的に ID にも値を入れて INSERT する場合はこのようにします。

・ id に値をセットして INSERT

$model = new MyModel();
$model->insert($insert_data);

ただし この方法では自動的に created_at にインサート時刻が入りません。 そこで明示的に現在時刻を渡してあげます。

$insert_data['created_at'] = new \Carbon\Carbon();
No.1427
02/01 14:45

edit

Eloquent
DB

Laravel の ディスク・ファイルシステム Storage , Filesystem を使用する

● ディスクの種類

local : ローカルディスク(一般公開しないファイル)
public : ローカルディスク(一般公開するファイル。コマンド aritizan storage:link を実行すると公開することができる。 )
s3 : Amazon S3
ftp :FTP (別途パッケージのインストールが必要)

など。

● publicディスクへのシンボリックリンクを作成する

publicディスクを使用するときは次のコマンドを事前に実行しておきましょう。

php artisan storage:link

● ディスクの設定記述場所

config/filesystems.php

    'disks' => [
        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],
        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],
        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
        ],
    ],

● ディスク設定にパーミッションを設定する

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
            // ===== permissions =====
            'permissions' => [
                'dir' => [
                    'public'  => 0775 ,
                ],
                'file' => [
                    'public' => 0664 ,
                ],
            ],
            // ===== / permissions =====
        ],

● ディスク(Local )への移動の例

次のようなファイル移動を行うとします。

<アプリのトップディレクトリ>/storage/app/tmp/test.csv
 ↓
<アプリのトップディレクトリ>/storage/app/csv/123456789.csv

注意: Diskをまたいだコピーや移動は move()メソッドだけではできません
参考 : http://bit.ly/2Lk5Qw7

Laravel Storage を使って次の様に記述します。

$disk = \Storage::disk('local');
$disk->move("tmp/test.csv", "csv/12345679.csv" );

簡単ですね。 おすすめです。

● Laravel Storage を使ってファイルのパスを表示させる

dump( $disk->path(false) );    // パスのみの表示
dump( $disk->path("csv/test.csv") );    // ファイル名をフルパスで表示

 ↓ 表示例

/var/www/laravel/my_app/storage/app/
/var/www/laravel/my_app/storage/app/test.csv

● public のディスクの保存したファイルにアクセスする

url(\Storage::disk('public')->url($image_name))

blade

<img src="{{ url(\Storage::disk('public')->url($image_name)) }}">

.env を次のように修正しておきましょう

APP_URL=http://localhost

 ↓

APP_URL=https://YOUR-SITE.TLD/

● Laravel Disk を使ってローカルの pdf や画像を表示する

例えばコントローラに次のように記述するとローカルファイルのデータを表示できます。

        $pdf_file_path = 'pdf/125_01.pdf';
        $disk = \Storage::disk('local');
        $mimetype = $disk->mimeType($pdf_file_path);
        return response($disk->get($pdf_file_path))->header('Content-Type', $mimetype);

● Storageのメソッド

// ファイルの絶対パスを取得
$file_path = \Storage::disk('local')->path($path);

//  ファイル内容の取得
$contents = Storage::get('file.jpg');

//  ファイルの存在
$exists = Storage::disk('s3')->exists('file.jpg');

// ファイルをダウンロードさせる
return Storage::download('file.jpg');
return Storage::download('file.jpg', $name, $headers);

// ファイル情報
$url = Storage::url('file.jpg');
$size = Storage::size('file.jpg');
$time = Storage::lastModified('file.jpg');

// ファイル操作
Storage::put('file.jpg', $contents);
Storage::copy('old/file.jpg', 'new/file.jpg');
Storage::move('old/file.jpg', 'new/file.jpg');

// ディスク外のディレクトリにあるファイルをコピー
$tmp_file_fullpath_name = "/tmp/test.jpg";
$d = file_get_contents( $tmp_file_fullpath_name );
\Storage::disk($file_store_disk)->put("{$file_store_dir}/{$image_name}", $d);


// 公開状態かどうか
Storage::setVisibility('file.jpg', 'public')

// ファイルの削除
Storage::delete('file.jpg');
Storage::delete(['file.jpg', 'file2.jpg']);
Storage::disk('s3')->delete('folder_path/file_name.jpg');

// ファイル/ディレクトリ一覧の取得
$files = Storage::files($directory);
$files = Storage::allFiles($directory);
$directories = Storage::directories($directory);
$directories = Storage::allDirectories($directory);

// ディレクトリ操作
Storage::makeDirectory($directory);
Storage::deleteDirectory($directory);

// ディレクトリ操作(ディスク指定)
Storage::disk('local')->makeDirectory($directory);

● Filesystemのメソッド

// ディレクトリ内のファイルやディレクトリを削除して 「ディレクトリの中身を空」にする
\File::cleanDirectory( storage_path().'/app/zip' );

// ディレクトリを パーミッション 0777 で作成する
umask( 0 );
\File::makeDirectory($path, '0777');

// ディレクトリ内のファイル一覧を取得(SplFileInfo クラスが返ります)
$files_list = \File::files( $folder );

https://www.php.net/manual/de/class.splfileinfo.php

● 戻り値などはソースを確認しましょう

https://github.com/illuminate/filesystem/blob/master/Filesystem.php

No.1426
06/25 15:08

edit

Laravel の ルーティングで Route::resource() で生成されるルート名の先頭に 文字列を加える

Route::group(['prefix' => 'admin', 'middleware' => 'admin', 'namespace' => 'Admin'], function () {
    Route::resource("consentforms","ConsentformController");
});

とするとルート名は

| GET|HEAD  | admin/consentforms/create              | consentforms.create  | 
| GET|HEAD  | admin/consentforms/{consentform}       | consentforms.show    | 
| PUT|PATCH | admin/consentforms/{consentform}       | consentforms.update  | 
  ... (以下続く)

と、なりますが
次の様に 'as' => 'admin.' を使うと 名前の先頭に admin. が追加されます

・'as' => 'admin. を追加

Route::group(['prefix' => 'admin', 'middleware' => 'admin', 'namespace' => 'Admin', 'as' => 'admin.'], function () {
    Route::resource("consentforms","ConsentformController");
});

 ↓ この様なルート名になります

| GET|HEAD  | admin/consentforms/create              | admin.consentforms.create  | 
| GET|HEAD  | admin/consentforms/{consentform}       | admin.consentforms.show    | 
| PUT|PATCH | admin/consentforms/{consentform}       | admin.consentforms.update  | 
  ... (以下続く)

No.1425
01/25 17:35

edit

ルーティング

Laravel の Blade でテンプレートファイルの格納ディレクトリを変更(追加する)

● Laravel の Blade でテンプレートファイルの格納ディレクトリを変更(追加する)

1. app/resources/pdf を追加し、 Pdf:: としてアクセスできるようにします

use View;
View::addNamespace('Pdf', base_path().'/resources/pdf' );

2. app/resources/pdf/my_template.blade.php を表示する

return View::make('Pdf::my_template');
No.1420
01/18 11:15

edit

Laravelで Bladeテンプレートのレンダリング結果を文字列として取得する

● Laravelで Bladeテンプレートのレンダリング結果を文字列として取得する

レンダリングするファイルが Views 以下にある場合はこちらでOKです。

$html = view('welcome', compact('user'))->render();
dd( $html );

Views 以外のディレクトリにあるテンプレートファイルをレンダリングして変数に代入するには

    /**
     * views 以外にあるbladeテンプレート( *.blade.php )を描画して変数とし受け取る
     *
     * @param   string      $template_full_path      テンプレートhtmlファイル名(フルパス)
     * @param   string      $compact                 compact() で受け取るテンプレートパラメーター
     *
     * @return  string      htmlテンプレート描画内容
     */
    private function getViewRender( string $template_full_path ,  array $compact )
    {
        $template_path     = pathinfo($template_full_path, PATHINFO_DIRNAME);
        $templatefile_name = pathinfo($template_full_path, PATHINFO_FILENAME);

        $app = app();
        $paths = [$template_path];
 
        // もとの設定を取得
        $originalFinder = \Illuminate\Support\Facades\View::getFinder();
         $finder = new \Illuminate\View\FileViewFinder($app['files'], $paths);
        \Illuminate\Support\Facades\View::setFinder($finder);
 
            $html = view($templatefile_name, $compact )->render();
            dd($html);

        // もとの設定に戻す
        \Illuminate\Support\Facades\View::setFinder($originalFinder);
 
        return $html;
    }

使い方

        $html = $this->getViewRender( $template_html, compact('param') );

引用: https://goo.gl/nTfMmR

No.1419
01/31 15:20

edit

artisan migrate コマンドを本番サーバで安易に実行しないようにする

● artisan migrate コマンドを本番サーバで安易に実行しないようにする

.env を 以下のように設定します。

APP_ENV=production

APP_ENV=production の設定がある時に、migrate コマンドを実行すると

$ artisan migrate:fresh --seed

次のような確認入力が表示されます。
これで本番環境で安易なマイグレーションの実行を抑止することができます。

**************************************
*     Application In Production!     *
**************************************
 Do you really wish to run this command? (yes/no) [no]:
No.1415
01/16 11:51

edit

artisan

Laravelでリレーション先のカラムを文字列で検索する

● Laravelでリレーション先のカラムを文字列で検索する

1対1のリレーションに有効です。
1対多のリレーションの場合は Slow Query になる可能性があるようです。必ず生成されるSQL文を確認しましょう。

● モデル

    /**
     * 1対1リレーション (従属の関係)
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function user() // 単数形
    {
        return $this->belongsTo('App\User');
    }

● コントローラー

(モデルの中に記述してもいいです。お好みで。)

whereHasメソッド または orWhereHasメソッド を使用します。

// ● リレーション先のユーザー名「太郎」でも検索
$q = '太郎';
$model->orWhereHas('user', function ($query) use ($q) {	// リレーション名 user を渡す
    $query->where('name', 'LIKE', "%{$q}%");
});

● 実行されるSQL

WHERE
exists(select * from users
    where articles.user_id = users.id
    and name LIKE '%太郎%')

exists を使ってサブクエリを呼び出します。

● 1対多の場合は 最適化されないクエリで呼ばれるようです。遅いです。

● Laravel WhereHas を早くする

パッケージのインストール

composer require mpyw/eloquent-has-by-join
composer require mpyw/eloquent-has-by-non-dependent-subquery
No.1397
06/07 16:10

edit

Laravel で 全ての View でモデルのカウント数を表示する

全ての View で 何かの件数を表示したい時、ミドルウェアを作ったり、コントローラーを継承した親コントローラーに 共通処理を書いてもいいのですが、直接モデルオブジェクトを呼ぶという方法もあります。

● 全ての Bladeビューでモデルのカウント数を表示させる

Blade の ビューファイル から直接呼び出します。

自分の記事数 : {{ \App\MyArticle::count() }}
自分のユーザー数 : {{ \App\MyUser::count() }}

● 全ての Bladeビューから変数を読み込む

同様に static なメソッドを作成すれば、どこからでも呼ぶことができます。

app/Http/Controllers/MyController.php

	public static function getMyCount() {
		// ここに何かの処理
		$result = 99999;
		return $result;
	}

ビューからはこのように呼び出せます。

{{ \App\Http\Controllers\MyController::getMyCount() }}
No.1394
12/15 10:30

edit

モデル

Laravel で pdfテンプレートとhtmlと日本語フォントを使ってpdfを出力する

● Laravel で pdfテンプレートとhtmlを使ってpdfを出力する

pdfを出力する時に、全てをhtmlで書いてもいいのですが、ロゴなどはpdfテンプレートで使い回すという方法も便利なので紹介します。

1. composer パッケージ「fpdi」「tcpdf」「html-compress 」のインストール

composer require setasign/fpdi
composer require tecnickcom/tcpdf
composer require wyrihaximus/html-compress 

2. pdfテンプレートとhtmlをディレクトリに保存

格納場所 resources/pdf を作成します。

mkdir resources/pdf

ここに pdf と html をコピーします。以下のファイル名とします。

template_01.pdf
template_01.pdf.html

3. 日本語フォントファイル「源真ゴシック」をこちらからダウンロードする

http://jikasei.me/font/genshin/

明朝フォントはこちらの 源様明朝 がお勧めです。
https://github.com/ButTaiwan/genyo-font

格納場所 resources/fonts を作成します。

mkdir resources/fonts

ここに 拡張子が .ttf のフォントファイルをコピーします。

4. pdfテンプレートとhtmlを読み込んで出力

Laravelのコントローラーからpdf 出力

use setasign\Fpdi;
use TCPDF_FONTS;
	public function tcpdf()
	{
        $pdf = new Fpdi\TcpdfFpdi();
		// $pdf = new \TCPDF("L", "mm", "A4", true, "UTF-8" );	// pdf テンプレートを使わない場合はこちら

		$pdf->setPrintHeader(false);
		$pdf->setPrintFooter(false);
		$pdf->AddPage();

		// テンプレートPDFファイル読み込み
		$pdf->setSourceFile(resource_path('pdf/template_01.pdf'));
		$page = $pdf->importPage(1);
		$pdf->useTemplate($page);

		// フォント
		$font = new TCPDF_FONTS();

		// フォント:源真ゴシック
		// $font_1 = $font->addTTFfont( resource_path('fonts/ipag.ttf') );
		$font_1 = $font->addTTFfont( resource_path('fonts/GenShinGothic-Medium.ttf') );
		$pdf->SetFont($font_1 , '', 10,'',true);

		// テンプレートhtmlファイル読み込み
		$html = \File::get( resource_path('pdf/template_01.pdf.html') );
		$pdf->writeHTML( $html, $ln=false, $fill=0, $reseth=false, $cell=false, $align="L" );

	 	$pdf->Output("output.pdf", "I");
	 }

5. tcpdf について調べるなら

このサイトがとても便利です

http://tcpdf.penlabo.net/

6. tcpdf の 改行や空白スペースがスペースとして認識されてずれる問題

htmlをminifyしましょう

composer require wyrihaximus/html-compress 
$parser = \WyriHaximus\HtmlCompress\Factory::construct();
$html = $parser->compress($html);

7. TCPDF で使用できるCSSプロパティ

以下のcssプロパティが使用できます。スタイルを思った通りにするのにはかなり足りないと思います。

font-family
font-size
font-weight
font-style
color
background-color
text-decoration
width
height
text-align

ではレイアウトはどうすれば良いのか?

「line-height を使う」か「いにしえのテーブルレイアウトを使う」のがいいでしょう。

例: line-height を使う

<table border="0">
    <tr>
        <td style="font-size:18px;line-height:57px;">お名前 太郎</td>
    </tr>
</table>                

例: いにしえのテーブルレイアウトを使う

<html><body>
<table border="0">
	<tr>
		<td style="width:30px;"></td>
		<td style="width:350px; height:75px;"></td>
		<td></td>
	</tr>
	<tr>
		<td></td>
		<td></td>
		<td>平成31年1月25日</td>
	</tr>
</table>
</body></html>

● pdfフォームの例 (ただし、日本語NGです。。。使えない。)

https://tcpdf.org/examples/example_014/

No.1393
05/14 10:07

edit

Laravelの Blade でドロップダウンリスト(selectボックス)の任意の項目だけ選択不可能にする

● 1. Laravelの Blade でドロップダウンリスト(selectボックス)の全体を選択不可能にする

LaravelCollective の Form::select の 4番目の引数に配列を渡す事で実現できます。

{{ Form::select($name, $select_loop, $selected_value, ['class' => 'my_css_class', 'disabled' => 'disabled']) }}
{{ Form::select('size', ['L' => 'Large', 'S' => 'Small'], 'S', ['disabled' => 'disabled']) }}

引数4番目の配列に 'disabled' => 'disabled' を指定するだけでOKです。
これだけで、ドロップダウンリスト全体が disabled になります。

● 2. Laravelの Blade でドロップダウンリスト(selectボックス)の任意の項目だけ選択不可能にする

全体じゃなくて特定の <option>項目だけ選択できないようにしたいという時がたまにあります。 その場合は以下のようにフォームマクロを作成すると簡単にできます。

2-1. FormMacroServiceProvider の作成

次のコマンドを実行します

php artisan make:provider FormMacroServiceProvider

コマンドが実行されると app/Providers/FormMacroServiceProvider.php が自動生成されます。

2-2. FormMacroServiceProvider.phpにファイルを追記する

boot() メソッド内に以下を追加します

    public function boot()
    {
        // この行を追記する ↓
        require base_path() . '/resources/macros/selectbox.php';
    }

2-3. config/app.php の providers に登録する

  'providers' => [
        // この行を追記する ↓
        App\Providers\FormMacroServiceProvider::class,
  ]

2-4. macros ディレクトリを作成する

mkdir ./resources/macros

2-5. ファイル /resources/macros/selectbox.php を以下の内容で保存する

<?php
Form::macro('mySelectBox', function ($name, $list = [], $selected = null, array $selectAttributes = [], array $optionsAttributes = []) {
    $html = '<select name="' . $name . '"';
    foreach ($selectAttributes as $k => $v) {
        $html .= ' ' . $k . '="' . $v . '"';
    }
    $html .= ">\n";

    foreach ($list as $value => $text) {
        $html .= '<option value="' . $value . '"';
        if (strcmp($value, $selected) == 0) {
            $html .= ' selected="selected"';
        }
        if (isset($optionsAttributes[$value])) {
            $html .= ' ' . $optionsAttributes[$value];
        }
        $html .= '>' . $text . "</option>\n";
    }
    $html .= '</select>';
    return $html;
});

2-6. Bladeテンプレートに記述する

今まではこのように記述してドロップダウンリストを生成していたと思いますが次のように書き換えます

{{Form::select('size', ['L' => 'Large', 'M' => 'Medium', 'S' => 'Small'], 'S')}}

   ↓

{!! Form::mySelectBox('size', ['L' => 'Large', 'M' => 'Medium', 'S' => 'Small'], 'S', [], ['L' => 'disabled']) !!}

このようなドロップダウンリストが生成されます。 (Largeだけ選択出来ないようになりました)

添付ファイル1
No.1392
12/13 15:02

edit

添付ファイル

Laravel artisan の makeコマンド

● Laravel artisan の makeコマンド

コントローラーなどの各モジュールを作成するときにもちろん既存のファイルのコピペでもいいのですが、 artisan の make コマンドを使うと、エラーのない中身が空のひな形ファイルが作成されます。

● Laravel artisan でコントローラーを作成する(make:controller)

0. コントローラー作成コマンドのヘルプを表示

php artisan make:controller -h

例 : コントローラー「HomeController」を作成する

php artisan make:controller HomeController

成功すると app/Http/Controllers/HomeController.php が作成されます。

● その他のLaravel artisan の make系コマンド

以下、同様に次の make コマンドがあります。

 make:auth             Scaffold basic login and registration views and routes
  make:channel          Create a new channel class
  make:command          Create a new Artisan command
  make:controller       Create a new controller class
  make:event            Create a new event class
  make:exception        Create a new custom exception class
  make:factory          Create a new model factory
  make:job              Create a new job class
  make:listener         Create a new event listener class
  make:mail             Create a new email class
  make:middleware       Create a new middleware class
  make:migration        Create a new migration file
  make:model            Create a new Eloquent model class
  make:notification     Create a new notification class
  make:observer         Create a new observer class
  make:policy           Create a new policy class
  make:provider         Create a new service provider class
  make:request          Create a new form request class
  make:resource         Create a new resource
  make:rule             Create a new validation rule
  make:scaffold         Create a scaffold with bootstrap 3
  make:seeder           Create a new seeder class
  make:test             Create a new test class

● よく使う make系コマンド

種類 ヘルプ表示コマンド
コントローラー php artisan make:controller -h
モデル(DBデータ操作) php artisan make:model -h
マイグレーション(DBデータ構造) php artisan make:migration -h
シーダー(DBデータ作成) php artisan make:seeder -h
No.1387
12/05 10:11

edit

Laravelのモデル(Eloquent)の結果セット(Collection)に任意のカラムを追加する。またアクセサーで書式を変更する

● Laravelのモデルの結果セットに「任意のカラム」を追加する(自動)

モデルファイルに $appends プロパティをセットします

    // SELECTされるデータセットに次の独自カラムを追加する
    protected $appends = ['_editable_flag' , '_email_aisatsu' ];

● Laravelのモデルの結果セットに「任意のカラム」を追加する(半自動)

任意のタイミングで appends に追加したいときにはメソッドを使用します。

return $user->append('is_admin')->toArray();

● Laravelのモデルの結果セットに「任意のカラム」を追加する(手動)

Laravelのモデル(Eloquent)の結果セット(Collection)に手動で任意のカラムを追加するには map() を使用します。

// コレクションのすべてのデータにurl = http://your.url/here を追加
$collection->map(function ($v) {
    $v['url'] = 'http://your.url/here';
    return $v;
});
// コレクションそれぞれに count=xxx (任意の値) を追加
$collection->map(function ($v) {
    $v['count'] = <計算ロジック>;
    return $v;
});

● Laravelのモデルの結果から「特定のカラム」を削除する(隠す)(手動)

user_code カラムを隠して結果セットをjsonで返します

$collection->setHidden(['user_code'])->toJson(JSON_UNESCAPED_UNICODE)

● Laravelのモデルの結果セットを含む Paginator に「任意のカラム」を追加する(手動)

getCollection() で コレクションを取り出してから行います

$paginator->getCollection()->map(function ($v) {
    $v['count'] = <計算ロジック>;
    return $v;
});

● Laravelのモデルの結果セットに書式を変える「Accessor /Mutator」を追加する

少し書式を変えたいときは Accessor /Mutator を使いましょう

例1: is_starred=1 の時に ☆ を表示するアクセサ

モデルファイルに以下を記述

    /**
     * ● アクセサー : ->_star_mark で is_starred=1 の時に ☆ を表示する
     *
     */
    public function getStarMarkAttribute()
    {
        if ($this->attributes['is_starred'] == 1){
            return '<div class="text-warning">★</div>';
        }
    }

呼び出し方

$model->star_mark

例2: 「dispatch_date」が存在する時にフォーマットして表示する。存在しない場合は未発送を返す アクセサ

    /**
     * アクセサー : 「->dispatch_date_ja」 で 値「dispatch_date」が存在する時にフォーマットして表示する。存在しない場合は未発送を返す。
     */
    public function getDispatchDateJaAttribute()
    {
        if ( $this->attributes['dispatch_date'] != null ){
            $c = new \Carbon\Carbon($this->attributes['dispatch_date']);
            return $c->format('m/d');
        } else {
            return '<span class="text-danger">未発送</span>';
        }
    }

呼び出し方

$model->dispatch_date_ja
No.1384
06/02 09:41

edit

モデル
Eloquent
DB

Laravel で Laravel Excel と LibreOfficeを使ってファイルを開いて pdf 出力する

● Laravel で Excel ファイルを開いてダウンロードする

Laravel-Excel ver 3 では まだ現時点では import が使えないようなので ver 2 を入れます。( 参考: https://goo.gl/wxHLE7

● Laravel Excelのインストール

composer で インストールします

composer require "maatwebsite/excel:~2.1.0"

● インストールされたモジュールのバージョン確認

インストールされたモジュールのうち excel 関連のバージョンを調べて置きます

composer show | grep excel
maatwebsite/excel                     2.1.30   Supercharged Excel exports in Laravel
phpoffice/phpexcel                    1.8.2    PHPExcel - OpenXML - Read, Create and Write Spreadsheet ...

Laravel Excel の 2.1.30phpexcel1.8.2 がインストールされています。
ググる時にはこれらのバージョンを参考にしましょう。

● Laravel Excel で テンプレートとなるエクセルファイルを読み込んで表示する

1. 元となるエクセルファイルを storage/excel/test.xls にアップロードする

( storage/excel/ )は存在しないので作成します。

2. routes/web.php に以下を追加する

Route::get('/test/excel', 'TestController@excel');

3. app/Http/Controllers/TestController.php を以下の内容で新規作成する

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;

class TestController extends Controller
{
	public function excel()
	{
		$excel_file = storage_path('excel/test.xls');
		Excel::load($excel_file, function($reader) {
			// 1番目のシートを選択
			$reader->sheet(0, function($sheet) {
				// セルA3に現在の日付を書き込み
				$sheet->cell('A3', function($cell) {
				    $cell->setValue( now()->format('Y/m/d') );
				});		    
			});		    
		})->export('xlsx');
	}
}

4. ブラウザでアクセスして エクセルファイルがダウンロードされることを確認する

https://YOUR-APP.COM/test/excel

にアクセスすると、エクセルファイルがダウンロードされます。
最初のシートの「A3」に今日の日付が書き込まれていることを確認します。
まずこれで Laravel Excel の 動作確認の完了です。

● エクセルファイルをpdfに変換する

PHPでするよりLibreOfficeを使用しましょう。再現性が上がります。(それでも80点ぐらいの再現度ですが。)

1. Libreofficeのインストール

yum -y install libreoffice libreoffice-langpack-ja

インストール後 バージョンを確認します。

libreoffice --version

もし libreoffice が見つからない場合は こちらから検索します。

find / -name soffice.bin

2. IPAフォントをインストール

wget https://ipafont.ipa.go.jp/old/ipafont/IPAfont00303.php -O IPAfont00303.zip
unzip IPAfont00303.zip
cd IPAfont00303
mv *.ttf /usr/share/fonts
cd ..
rm -rf IPAfont00303
rm -f IPAfont00303.zip

3. 源真ゴシックをインストール

mkdir genshingothic
cd genshingothic
wget https://osdn.jp/downloads/users/8/8637/genshingothic-20150607.zip genshingothic-20150607.zip
unzip genshingothic-20150607.zip
mv *.ttf /usr/share/fonts
cd ..
rm -rf genshingothic
rm genshingothic-20150607.zip

4. フォントがインストールされていることを確認

fc-list | grep IPA

5. libreoffice を使ってコンバート

コマンドラインから以下のコマンドを実行します。

libreoffice --headless --convert-to pdf --outdir /home/kusanagi/pdf test.xls

パスは絶対パスで指定しておくと確実です。

No.1383
12/02 22:46

edit

Laravel で DBモデル(データベース)のカラムを変更(追加)する

1. テーブル変更のパッケージを追加する

composer require doctrine/dbal

2. テーブル「articles」を変更するマイグレーションファイル「add_columns_articles_table」を生成する

add_columns_articles_table は任意の命名でOKですが「作業内容_テーブル名_table」としておくとテーブル作成時のファイルと命名が揃います

php artisan make:migration add_columns_articles_table  --table=articles

3. 生成されたファイル「2018_xx_xx_xxxxxx_add_columns_articles_table.php」にテーブル追加の命令を記述する

例:「articles」テーブルに以下のカラムを追加します

・「status_id」カラムの後ろにint型の「recruit_flg」を追加します
・「recruit_flg」カラムの後ろにint型の「recruit_date」を追加します
    public function up()
    {
        Schema::table('articles', function (Blueprint $table) {
            $table->integer('recruit_flg')->default(0)->after('status_id');    // この行を追加
            $table->dateTime('recruit_date')->nullable()->after('recruit_flg');    // この行を追加
        });
    }

注意:SQLiteでは任意の位置にカラム追加が出来ないようです
回避策 : https://goo.gl/a2atCx

4. マイグレーションを戻す処理を down()メソッドに記述します

    public function down()
    {
        Schema::table('articles', function (Blueprint $table) {
            $table->dropColumn(['recruit_date','recruit_flg']);
        });
    }

5. カラム追加のマイグレーション実行

php artisan migrate

6. 実行したマイグレーションを戻す

php artisan migrate:rollback

以上で、既存のデータベースのデータを削除することなく、カラムを追加できます。

No.1381
08/26 18:25

edit

モデル
artisan

LaravelでCSVファイルをインポートする

● LaravelでCSVファイルをインポートする

ガリガリ書いてもいいですが、 fast-excelを使ってやるとさっとできます

https://github.com/rap2hpoutre/fast-excel

https://github.com/rap2hpoutre/fast-excel

fast-excelのインストール

composer require rap2hpoutre/fast-excel
use Rap2hpoutre\FastExcel\FastExcel;
$csv = (new FastExcel)->import('data.csv');
dd($csv);

これだけで「data.csv」を読み込んで $csv にハッシュの配列形式で格納します。

● CSVインポート時にカラム名(項目名)を自動でつける

CSVの1行目にカラム名を記述しておくとその名前のハッシュに変換して取り込まれます。

● 日本語の文字コードが化ける問題への対応

文字コードは utf-8 以外の場合文字化けします。 csvファイルは sjis なことが多いので、事前に変換しておきましょう。

    //========================================== convertFileEncode
    protected function convertFileEncode($infname="", $incode='sjis-win', $outfname="", $outcode='UTF-8', $nl="\r\n") {
        if ( ! is_file($infname) ) {
            die("変換失敗:{$infname} が見つかりません.");
        }
        $tmp_filename = getmypid().'.tmp';
        $outfp = fopen($tmp_filename, 'wb');
        if ($outfp === FALSE) {
            die("変換失敗:{$tmp_filename} に書き込むことができません.");
        }
        $fp = fopen($infname,'r') or die("ファイル({$infname})のオープンに失敗しました");
        while ( ($line = fgets($fp,999999)) !== false ) {
            $outstr = mb_convert_encoding($line, $outcode, $incode);
            $outstr = preg_replace("/\r\n|\r|\n/", $nl, $outstr);
            fwrite($outfp, $outstr);
        }
        fclose($fp);
        fclose($outfp);
        rename($tmp_filename, $outfname);
        chmod($outfname, 0666);
        return true;
    }

エラー処理は適宜書き換えてください。(とりあえず die() していますが、例外を投げた方がいいでしょう。)

No.1379
10/13 14:01

edit

Laravelで try catch する

Laravel では以下のコードでは catch できません。

try {
    // 何かしらの処理
} catch (Exception $e) {
    dd($e);
}

 ↓ このように変更します

try {
    // 何かしらの処理
} catch (\Exception $e) {
    dd($e);
}

\Exception と表記します)

No.1373
11/22 22:05

edit

Laravelのモデルで複数のAND条件のクエリーを実行する

● クロージャを使ってLaravelのモデルで複数のAND条件のクエリーを実行する

例えば「日付が 2019/01/01」かつ「 report_name_1に何か入っている または report_name_2に何か入っている または report_name_3に何か入っている」
という条件はクロージャを使って以下のように記述できます。

$data_loop = \App\Report::where('kigen_date','=','2019-01-01')
            ->where(function($query) {
                $query->orWhereNotNull('report_name_1')
	                  ->orWhereNotNull('report_name_2')
	                  ->orWhereNotNull('report_name_3');
            })
->get();

● スコープを使ってLaravelのモデルで複数のAND条件のクエリーを実行する

ローカルスコープを使うのもおすすめです
スコープを使うと SQL文で言う所の 「AND ( 何かしらのSQL文 )」 を発行することができます。

例えば次のような単純なSQL文もスコープにしてしまうと簡単です。

AND ( feed_id = 100 )

モデルファイル( 例:User モデルの場合 ) app/User.php

    /**
     * ローカルスコープ:inFeed:フィードIDに限定するスコープ
     */
    public function scopeInFeed($query, $feed_id)
    {
        return $query->where('feed_id','=', $feed_id);
    }

コントローラファイルに記述

$model->inFeed( 100 );	// ローカルスコープ適用
No.1370
06/28 19:22

edit

Laravelのモデルでリレーションに条件をつける

次のようにリレーションがあった場合

    /**
     * 1対1リレーション
     */
    public function mt_status()
    {
        return $this->belongsTo('App\MtStatus');
    }

 ↓ 次のようにして条件をつけることができます ( is_only_admin = 0 )

    /**
     * 1対1リレーション
     */
    public function mt_status()
    {
        return $this->belongsTo('App\MtStatus')->where('is_only_admin', '=', 0);
    }
No.1365
11/16 14:47

edit

Laravelのモデルで自分で設定した日付型カラムで format メソッドを使用する

Laravelで自分で作成した

Laravelのモデルで自分で設定した日付型カラムで

$touroku_date->format('Y/m/d')

のようなformat メソッドを使用すると
以下のようなエラーとなります

Call to a member function format() on string 

このエラーを出さないようにするにはモデルファイルに登録します

app/モデル.php

    // dates(formatメソッドを使用できるようにする)
    protected $dates = [
        'created_at',
        'updated_at',
        'touroku_date' // 追加する
    ];
No.1364
11/27 14:58

edit

Laravelで指定したIPアドレスからしかアクセスできないように制限をかける

● Laravelで指定したIPアドレスからしかアクセスできないように制限をかける

https://goo.gl/mzAAiv

こちらの方法がとてもよかったので引用させていただきます。
変更点としては アクセスを許可するIP を .env ファイルに記述しています(後から見返したときに見通しをよくするため)

1. ミドルウェア(IpLimit.php)を作成する

app/Http/Middleware/IpLimit.php


<?php

/**
 * IpLimit.php
 *
 * クライアントのリアルIPがホワイトリストに存在するかチェックするためのミドルウェア
 *
 * @version  0.2		[fix] (int)に正しく型キャストするよう修正
 *
 */

namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Log;

class IpLimit {

	/**
	 * Handle an incoming request.
	 * @param  \Illuminate\Http\Request  $request
	 * @param  \Closure  $next
	 * @return mixed
	 */
	public function handle($request, Closure $next) {
		$config = \Config::get('ip_limit');
		if ($config['enable'] !== true) {
			return $next($request);
		}
		if ($config['isProxy'] === true) {
			$request->setTrustedProxies([$request->ip()]);
		}
		if ($this->isAllow($request->ip(), $config['allowIps']) === false) {
			$error_message = 'cannot access from : ' . $request->ip();
			Log::debug($error_message);
			abort(404, $error_message);
		}
		return $next($request);
	}

	private function isAllow(string $remoteIp, array $accepts) {
		foreach ($accepts as $accept) {
			if ($this->isIn($remoteIp, $accept)) {
				return true;
			}
		}
		return false;
	}

	private function isIn(string $remoteIp, string $accept) {
		if (strpos($accept, '/') === false) {
			return ($remoteIp === $accept);
		}
		list($acceptIp, $mask) = explode('/', $accept);
		$acceptLong = ip2long($acceptIp) >> (32 - (int) $mask);
		$remoteLong = ip2long($remoteIp) >> (32 - (int) $mask);
		return ($acceptLong == $remoteLong);
	}

}

2. Kernel に登録する

app/Http/Kernel.php に以下を追加

    protected $routeMiddleware = [
        'iplimit' => \App\Http\Middleware\IpLimit::class,    // この行を追加する

3. 設定ファイルを作成する

config/ip_limit.php

<?php
return [
    'enable'   => env('IP_LIMIT_ENABLE', false),
    'isProxy'  => env('IP_LIMIT_PROXY',  false),
    'allowIps' => [
        env('LOCALHOST_IP', '127.0.0.1'),
	    env('IP_LIMIT_ENABLE_ADDRESS_01', false) ,
	    env('IP_LIMIT_ENABLE_ADDRESS_02', false) ,
	    env('IP_LIMIT_ENABLE_ADDRESS_03', false) ,
	    env('IP_LIMIT_ENABLE_ADDRESS_04', false) ,
	    env('IP_LIMIT_ENABLE_ADDRESS_05', false) ,
    ],
];

4. .env にアクセスを許可する IP を記述する

.env

IP_LIMIT_ENABLE=true		
# IP_LIMIT_PROXY: Laravelがバランサー配下に設置されている場合にtrueにすると、Real IPを取得する
IP_LIMIT_PROXY=false
IP_LIMIT_ENABLE_ADDRESS_01=xxx.xxx.xxx.xxx/24
IP_LIMIT_ENABLE_ADDRESS_02=yyy.yyy.yyy.yyy
IP_LIMIT_ENABLE_ADDRESS_03=
IP_LIMIT_ENABLE_ADDRESS_04=
IP_LIMIT_ENABLE_ADDRESS_05=

5. .ルーターにミドルウェアを追加する

routes/web.php

ミドルウェアをかませます

Route::get('/register', 'RegisterController@showRegistrationForm')->name('register');

 ↓

Route::group(['middleware' => 'iplimit'], function () {
    Route::get('/register', 'RegisterController@showRegistrationForm')->name('register');
});

以上です。

No.1362
05/03 13:12

edit

Laravelで独自のエラーページ(404 , 403 , 500)をカスタマイズして作成する

● Laravelで独自のエラーページ(404 , 403 , 500)をカスタマイズして作成する

Laravelの標準のエラーページをカスタマイズするには

/resources/views/errors/404.blade.php にファイルを置くだけです。

1. ディレクトリ errors を作成する

2. ファイルを作成する

ファイル名は

403.blade.php
404.blade.php
500.blade.php

とすると、404エラーの時は 「404.blade.php」を見に行きます。

● Laravelでエラーページ(404 , 403 , 500)に独自メッセージを表示させる

エラー発生時にエラーメッセージを追加する

abort( 404, '独自のエラーメッセージ' );

Bladeテンプレートでエラーメッセージを表示

{{ $exception->getMessage() }}

これだけで、エラーページにメッセージが表示されます。

● Laravelの独自のエラーページをデフォルトのエラーページと同じようなデザインにする

デフォルトのデザイン ↑ にしたい場合は次のようなコードにします

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Page Not Found</title>
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet" type="text/css">
    <style>
    html,
    body {
        background-color: #fff;
        color: #636b6f;
        font-family: 'Nunito', sans-serif;
        font-weight: 100;
        height: 100vh;
        margin: 0;
    }

    .full-height {
        height: 100vh;
    }

    .flex-center {
        align-items: center;
        display: flex;
        justify-content: center;
    }

    .position-ref {
        position: relative;
    }

    .content {
        text-align: center;
    }

    .title {
        font-size: 36px;
        padding: 20px;
    }
    </style>
</head>

<body>
    <div class="flex-center position-ref full-height">
        <div class="content">
            <div class="title">
                Sorry, the page you are looking for could not be found.
            </div>
        </div>
    </div>
</body>
</html>

● エラーページにエラーメッセージを表示させてデバッグしやすくする

以下のようにすると .envAPP_DEBUG=1 の時だけエラーメッセージを表示させることができます。

            @if(env('APP_DEBUG') == 1)
                {{ $exception->getMessage() }}
            @endif

例:
Laravelエラーページにエラーメッセージを表示させる

添付ファイル1
添付ファイル2
No.1361
12/20 22:33

edit

添付ファイル

Laravel のデバッグを楽にする laravel-debugbar を使用する

Laravel のデバッグパッケージといえば laravel/telescope とlaravel-debugbar の2つが有名ですが、ここでは laravel-debugbar の使い方を紹介いたします。

● laravel-debugbarのインストール

composer require barryvdh/laravel-debugbar

● laravel-debugbarのインストール( Laravel 5 〜 Laravel 5.4 を使っている場合)

composer require barryvdh/laravel-debugbar:~2.4

● 使い方

.env の設定が以下になっていることを確認します。

APP_DEBUG=true

● laravel-debugbar の起動方法

Google Chrome で Laravelアプリを起動すると、

  1. 左下に Laravelの赤いアイコンが表示されていますのでそれをクリックします。
  2. 画面したの一番右から2番目のアイコンをクリックするとウィンドウ幅が上に大きくなって表示されます。

● laravel-debugbar の debug() メソッド

laravel-debugbar を インストールすると debug() メソッドが使用できるようになります。
これは dump() メソッドの出力を laravel-debugbarのウィンドウ内に表示するものです。

dump('test);        // 通常のダンプ
 ↓
debug('test);        // debug-bar に表示させるダンプ 

● (APP_DEBUG=true)にしたままlaravel-debugbarを無効にする

次のように設定すると laravel-debugbarのみ無効 にできます

APP_DEBUG=true
DEBUGBAR_ENABLED=false # laravel-debugbarを無効

● laravel-debugbar を一時的にオフにする

laravel-debugbar は 実行するSQL文が多いと重くなるので、ページごとに オンオフを切り替えたい時があります。
次のように オン/オフ します。

\Debugbar::enable();
\Debugbar::disable();
No.1360
08/12 10:45

edit

LaravelでFactoryとFakerとSeederを使ってダミーデータを作成する

● LaravelでFactoryとFakerとSeederを使ってダミーデータを作成する

PHPのFakerです。Faker Senpai ではないです。
Laravelだと標準でFakerがインストールされ、とても簡単に扱えるのでデータを作成する際には是非利用しましょう。
(Laravelアプリのインストール、モデルファイルはすでに用意してある前提で進めます。 )

Factoryを日本語で使用する

config/app.php の一番下に以下を追加

'faker_locale' => 'ja_JP',

先ずはダミーデータがどのようなデータになるか確認する

routes/web.php に以下を追加

Route::get('/faker', function () {
	$faker = Faker\Factory::create('ja_JP');
	$dummyData = [
		'random_no'                     => $faker->randomNumber(4) ,  // 最大4桁の数字
		'name'                     => $faker->name,
		'password'                 => $faker->password,
		'country'                  => $faker->country,
		'prefecture'               => $faker->prefecture,
		'city'                     => $faker->city,
		'postcode'                 => $faker->postcode,
		'address'                  => $faker->address,
		'streetAddress'            => $faker->streetAddress,
		'phoneNumber'              => $faker->phoneNumber,
		'email'                    => $faker->email,
		'safeEmail'                => $faker->safeEmail, // (実在しないアドレスのため処理とかで使っても安心)
		'company'                  => $faker->company,
		'iso8601'                  => $faker->iso8601($max = 'now'),
		'dateTimeBetween'          => $faker->dateTimeBetween($startDate = '-110 years', $endDate = 'now')->format('Y年m月d日'),
		'numberBetween'            => $faker->numberBetween($min = 100, $max = 200),
		'title'                    => $faker->title,
		'realText'                 => $faker->realText($maxNbChars = 50, $indexSize = 2),
		'randomNumber'             => $faker->randomNumber($nbDigits = NULL),
		'randomFloat'              => $faker->randomFloat($nbMaxDecimals = NULL, $min = 0, $max = NULL),
		'randomElement'            => $faker->randomElement($array = ['男性', '女性']),
		'lexify'                   => $faker->lexify($string = '??????'),
		'hexcolor'                 => $faker->hexcolor,
		'ipv4'                     => $faker->ipv4,
		'url'                      => $faker->url,
		'imageUrl'                 => $faker->imageUrl($width = 640, $height = 480, $category = 'cats', $randomize = true, $word = null),
		'userAgent'                => $faker->userAgent,
		'creditCardType'           => $faker->creditCardType,
		'creditCardNumber'         => $faker->creditCardNumber,
		'creditCardExpirationDate' => $faker->creditCardExpirationDate,
		'isbn13'                   => $faker->isbn13,
		'isbn10'                   => $faker->isbn10,
	];
	dump($dummyData);
	exit();
});

http://YOUR-APP.TLD/faker にアクセスして表示させます。 次のようなダミーデータが表示されます。

array:31 [▼
  "random_no" => 724
  "name" => "浜田 舞"
  "password" => "sPz?uT[rr"
  "country" => "ドミニカ国"
  "prefecture" => "埼玉県"
  "city" => "廣川市"
  "postcode" => "9926575"
  "address" => "1202087  大阪府加藤市西区原田町廣川1-8-9"
  "streetAddress" => "若松町山岸5-1-2"
  "phoneNumber" => "05-4265-9978"
  "email" => "mitsuru67@gmail.com"
  "safeEmail" => "chiyo.kanou@example.net"
  "company" => "株式会社 喜嶋"
  "iso8601" => "1984-04-25T21:15:23+0000"
  "dateTimeBetween" => "1974年08月07日"
  "numberBetween" => 125
  "title" => "Dr."
  "realText" => "るしていただおじぎを捕とりと歴史れきしになって、と思ってすうりんどんどん流ながら上着うわぎのりを川。"
  "randomNumber" => 55
  "randomFloat" => 6539745.6
  "randomElement" => "男性"
  "lexify" => "znkbep"
  "hexcolor" => "#fb573b"
  "ipv4" => "136.204.203.254"
  "url" => "http://wakamatsu.jp/delectus-aspernatur-unde-quae-explicabo-aut-nisi"
  "imageUrl" => "https://lorempixel.com/640/480/cats/?97142"
  "userAgent" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2) AppleWebKit/5330 (KHTML, like Gecko) Chrome/37.0.841.0 Mobile Safari/5330"
  "creditCardType" => "Visa"
  "creditCardNumber" => "4485419150999"
  "creditCardExpirationDate" => DateTime @1634115758 {#207 ▼
    date: 2021-10-13 09:02:38.0 UTC (+00:00)
  }
  "isbn13" => "9793648060130"
  "isbn10" => "7424451421"
]

引用: https://goo.gl/VmSXqV
参考: https://fwhy.github.io/faker-docs/ 参考: https://bit.ly/3roaudM

artisanコマンドから Factory を作成する

モデル「Client.php (DBテーブル名:client)」に対する「ClientFactory」を作成します。

php artisan make:factory ClientFactory

完了すると database/factories/ClientFactory.php が作成されています

デフォルトでは

$factory->define(Model::class, function (Faker $faker) {
    return [
        //
    ];
});

となっているのでここに設定を記述して行きます。

    return [
		'client_name'    =>  $faker->name ,
		'addr_name'      =>  $faker->address,
    ];

またクラス名も変更しておきます。

use App\Model;
use Faker\Generator as Faker;
$factory->define(Model::class, function (Faker $faker) {

  ↓

use App\Client;
use Faker\Generator as Faker;
$factory->define(App\Client::class, function (Faker $faker) {

該当クラス( Client )に書き換えた例。↑

これでOKです。

Seederファイルを作成する

以下の内容で database/seeds/ClientsSeeder.php を作成する

<?php

use Illuminate\Database\Seeder;

class ClientsSeeder extends Seeder {

    public function run()
    {
    	// 10件作成
      factory(App\Client::class, 10)->create();
    }

}

シーダー実行ファイルに記述してあげます

database/seeds/DatabaseSeeder.php

    public function run()
    {
        $this->call(ClientsSeeder::class);
    }

artisanからシーダーを実行する

まずキャッシュを削除する

php artisan cache:clear; php artisan config:clear; php artisan route:clear; php artisan view:clear; composer dump-autoload

データを全て作り直し、シーダーを実行します

php artisan migrate:fresh --seed
No.1359
02/05 16:10

edit

LaravelのBladeテンプレートでルーターのプレフィックスを使う

● LaravelのBladeテンプレートでルーターのプレフィックスを使う

ルーターで

Route::group(['prefix' => 'admin'], function () {

のように、プレフィクスをつけている場合に Bladeでその値を取得する方法。

・Bladeでプレフィクスを取得する

    @php
        $prefix = Request::route()->getPrefix()
    @endphp

としておいてから

{{URL::to($prefix.'/articles/search')}}

このようにも書けます

{{ url("$prefix/articles/search") }}

http://YOUR-SITE.TLD/admin/articles/search

もちろん、わざわざこんなことをせずに素直に

{{ route('articles.search') }}

と書くのがいいです。

No.1357
11/10 11:04

edit

Laravelのメッセージを日本語化する

● Auth関連のメッセージを日本語化する

resources/lang/ja.json を以下の内容で作成するだけでメッセージが日本語になります。

{
    "Login":"ログイン",
    "E-Mail Address":"メールアドレス",
    "Password":"パスワード",
    "Remember Me":"ログイン状態を保存する",
    "Forgot Your Password?":"パスワードをお忘れですか ?",
    "Register":"登録",
    "Name":"お名前",
    "Confirm Password":"パスワード(確認用)",
    "Reset Password":"パスワードリセット",
    "Send Password Reset Link":"パスワードリセットリンク送信",
    "Logout":"ログアウト",

    "Verify Your Email Address":"ユーザ登録を完了してください",
    "A fresh verification link has been sent to your email address.":"新しいリンクをあなたのメールアドレスに送信しました。",
    "Before proceeding, please check your email for a verification link.":"メールに記載されているリンクをクリックして登録手続きを完了してください。",
    "If you did not receive the email":"もしメールが届いていない場合は",
    "click here to request another":"こちらをクリックして確認メールを再送信してください",

    "Please click the link below to verify your email address.":"メールアドレスを確認するために下のリンクをクリックしてください。",
    "Verify Email Address":"メールアドレス確認",
    "If you did not create an account, no further action is required.":"心当たりがない場合は、本メッセージは破棄してください。",

    "Click button below and reset password.":"下のボタンをクリックしてパスワードを再設定してください。",
    "Reset password":"パスワードリセット",
    "If you did not request a password reset, no further action is required.":"心当たりがない場合は、本メッセージは破棄してください。"
}

https://qiita.com/nekyo/items/eba7f145a71a5d04b57d

https://github.com/caouecs/Laravel-lang/tree/master/src/ja

1. 多言語リソースファイルをダウンロードする

https://github.com/caouecs/Laravel-lang

2. /resources/lang/に「ja」 ディレクトリをアップロードする。

3. config/app.php の設定を変更する

    'locale' => 'en',

  ↓

    'locale' => 'ja',

4. timezone も合わせて変更しておく

'timezone' => 'UTC',

 ↓

'timezone' => 'Asia/Tokyo',

以上です。

● Laravelで送信される htmlメールのヘッダフッタを日本語に変更する

php artisan vendor:publish --tag=laravel-mail
php artisan vendor:publish --tag=laravel-notifications

コマンドを実行すると

resources/views/vendor/mail/(複数のテンプレートファイル)
resources/views/vendor/notifications/email.blade.php

ファイルが自動生成されます。 変更する箇所は次の画像の通りです。

↓ すべて日本語化すると次のようになります

● ログインせずにログイン認証が必要なページを表示させようとした時のリダイレクト先を変更する

app/Http/Middleware/RedirectIfNotUser.php

	public function handle($request, Closure $next, $guard = 'user')
	{
	    if (!Auth::guard($guard)->check()) {
	        return redirect('user/login');  // ここを書き換えます
	    }
No.1356
08/17 10:10

edit

Laravelでエラーログをファイルに保存しつつメールで送信する

● Laravelでエラーログをファイルに保存しつつメールで送信する

ログにはレベルがあります。( PSR-3 )
PSR-3: Logger Interface: https://www.php-fig.org/psr/psr-3/

(危険な順)

8. EMERGENCY
7. ALERT
6. CRITICAL
5. ERROR
4. WARNING
3. NOTICE
2. INFO
1. DEBUG

このログレベルを利用して

・ログレベルが ERROR 以上の時はログに保存しつつメールで送信
・ログレベルが WARNING 以下の時はログに保存

という運用ができるように設定してみます。

コントローラーのコンストラクタに以下を記述

protected $monolog;
public function __construct()
{
    // umask。適宜書き換えてください。
    umask(0000);

	$this->monolog = Log::getLogger();	// laravel 5.6以降

	// 1. monolog - mail
	$transport = new \Swift_SendmailTransport('/usr/sbin/sendmail -t');
	$mailer    = new \Swift_Mailer($transport);
	$message   = new \Swift_Message('monolog ERROR');
	$message->setFrom('メールアドレス');
	$message->setTo('メールアドレス');
	$this->monolog->pushHandler(new \Monolog\Handler\SwiftMailerHandler($mailer, $message, \Monolog\Logger::ERROR, false));

	// 2. monolog - file - rotation
	$formatter = new \Monolog\Formatter\LineFormatter(null, null, true);
	$handler = new \Monolog\Handler\RotatingFileHandler(storage_path('logs').DIRECTORY_SEPARATOR.'error.log', 10, \Monolog\Logger::ERROR );
	$handler->setFormatter($formatter);
	$this->monolog->pushHandler($handler);
}

次のメソッドでログに記録します。

ログファイルに記録

$this->monolog->debug('1. debug');
$this->monolog->info('2. info');
$this->monolog->notice('3. notice');
$this->monolog->warning('4. warning');

ログファイルに記録(ファイル名と行番号)

$this->monolog->info('ログメッセージ', ['file' => basename(__FILE__), 'line' => __LINE__]); // ファイル名(フルパス)
$this->monolog->info('ログメッセージ', ['file' => basename(__FILE__), 'line' => __LINE__]); // ファイル名のみ

ログファイルに記録 + メール送信

$this->monolog->error('5. error');
$this->monolog->critical('6. critical');
$this->monolog->alert('7. alert');
$this->monolog->emergency('8. emergency');
No.1355
03/27 10:59

edit

Laravelで任意のログチャンネルを追加してログを出力する

● Laravelで任意のログチャンネルを追加してログを出力する

cron実行時などは手動でログを出力して保存しておくと後からのデバッグが楽になります

・1. 設定ファイルを準備する

config/logging.php の 'channels に追加します。

    'channels' => [

        // 追加したチャンネル [apidebug]
        'apidebug' => [
            'driver'     => 'daily',
            'path'       => storage_path('logs/apidebug.log'),
            'level'      => 'info',
            'days'       => 30,
            'permission' => 0666,
        ],

・2. ログを出力する

// ロガー
$logger = \Log::channel('apidebug')->getLogger();
$logger->info( "TEST LOG", ['file' => basename(__FILE__), 'line' => __LINE__] );

・3. 出力例

storage/logs/apidebug-2019-xx-xx.log 次のようなログが 出力されます

[2019-08-26 11:34:06] local.INFO: TEST LOG {"file":"ApiDebugController.php","line":87} 

ログの出力レベル(下に行くほど深刻)

// DEBUG
$monolog->debug($message, $context);

// INFO
$monolog->info($message, $context);

// NOTICE
$monolog->notice($message, $context);

// WARNING
$monolog->warning($message, $context);

// ERROR
$monolog->error($message, $context);

// CRITICAL
$monolog->critical($message, $context);

// ALERT
$monolog->alert($message, $context);

// EMERGENCY
$monolog->emergency($message, $context);

● Laravelのログにファイル名やメソッド名を自動的に追加する

引用: https://goo.gl/NtbtSa

use Log;
$monolog = Log::getLogger();
$ip = new \Monolog\Processor\IntrospectionProcessor(
    \Monolog\Logger::DEBUG,
    [
        'Monolog\\',
        'Illuminate\\',
    ]
);
$monolog->pushProcessor($ip);

このようなログになります

[2018-11-09 01:52:35] local.WARNING: テーブルからデータを取得できません  {"file":"/home/app/Http/Controllers/TestController.php","line":231,"class":"App\\Http\\Controllers\\TestController","function":"postarticle"}

とても便利なので是非利用しましょう。

● ログの時刻がおかしい時の修正方法

config/app.php を以下のように変更します

    'timezone' => 'Asia/Tokyo',
No.1354
06/22 11:53

edit

Laravelの命名規約(または命名法則)

● Laravel命名規約

LaravelのコーディングスタイルはPSR-2に準拠。

PSR-2 スタイルの特徴

    PSR-1のコーディング規約には従わなければなりません。
    インデントには4つのスペースを利用しなければなりません。タブは使用してはいけません。
    1行の長さに制限はありませんが、120文字以下が望ましいです。できれば80文字以下にして下さい。
    クラス・メソッドの波括弧の前には改行を入れなければなりません。
    メソッドやプロパティの定義は最初にabstract/final、次にpublic/protected/private、最後にstaticを書かなければなりません。
    制御構文開始の波括弧の前には改行を入れません。
    制御構文の(の後、)の前にはスペースを入れません。

引用 : https://goo.gl/Fchriz

単数形複数形には注意する事。

http://www.infiniteloop.co.jp/docs/psr/psr-2-coding-style-guide.html

種類 単数 or 複数 ケース
モデル
(ファイル名)
単数形 パスカルケース(先頭大文字) Post.php
MsCountry.php
テーブル名 複数形 スネークケース(小文字) posts
ms_countries
コントローラ
(ファイル名)
どちらもOK パスカルケース(先頭大文字) PostController.php
MsCountryController.php
クラス どちらもOK パスカルケース(先頭大文字) PostController
MsCountryController
メソッド --- キャメルケース(先頭小文字) public function searchUser()
変数 --- スネークケース(小文字) $user_name

● Laravel DB命名規約

名前が決まっているカラム

カラム名 何を表すか?
id 主キー
created_at 登録日時
updated_at 更新日時
No.1353
12/05 11:56

edit

Laravelでフォーム送信時の先頭と最後のスペースや改行の自動削除を停止する

● Laravelでフォーム送信時の先頭と最後のスペースや改行の自動削除を停止する

Laravelではフォーム送信時に先頭と最後のスペース(改行も。)は自動削除されます。
これを停止するには Http/Kernel.php の 次の行をコメントアウトします。

protected $middleware = [
    \App\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    // \App\Http\Middleware\TrimStrings::class, // この行をコメントアウトする
No.1352
11/08 22:03

edit

Laravel + nginx をサブディレクトリで動作させる

● Laravel + nginx で動作しているアプリをサブディレクトリで動作させる

例: /my_app/ で Laravelアプリを動作させる

/etc/nginx/conf.d/YOUR-SITE.conf

KUSANAGI9 の場合は /etc/opt/kusanagi/nginx/conf.d/

	location /my_app/ {
		try_files $1 /my_app/index.php?$query_string;
	}

nginx -s reload で nginx をリスタートすればOKです。

my_app に シンボリックリンクを貼ってください。

● kusanagi を使用している場合

KUSANAGIを使っている場合は設定ファイルが2つに分かれている事があります YOUR-SITE_http.conf , YOUR-SITE_ssl.conf の2ファイルを編集しましょう

参考: https://goo.gl/fcmdT4

● うまくいかないときは Laravel 公式の 設定ファイルをチェックする

こちらに公式の設定ファイルがあるので、手元の設定ファイルと比較して揃えておきましょう https://laravel.com/docs/5.8/deployment#nginx

No.1349
09/21 17:55

edit

Laravel で admin / user のMulti-Auth を素早く作成する

管理者側テーブル「admin」を利用した認証とユーザー側テーブル「users」を利用した認証を作成する
「素早く」がテーマなので composer パッケージを利用します。

「Laravelアプリの作成」「DB接続設定」は設定完了しているものとします。

● Hesto/multi-auth

https://github.com/Hesto/multi-auth

・パッケージのインストール

composer require hesto/multi-auth

Laravel 6 , 7の場合は次のパッケージもインストールします

composer require laravel/helpers

Laravel 8 , 9 の場合は次のパッケージもインストールします

composer require laravel/ui

・認証ファイルのインストール(小文字、単数形で命名します)

php artisan multi-auth:install admin -f
php artisan multi-auth:install user -f

・管理者アカウントをシーダーに登録する

database/seeders/DatabaseSeeder.php

    public function run()
    {
        // この行を追加 ↓
        $this->call(AdminsSeeder::class);
    }

database/seeders/AdminsSeeder.php を新規作成

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class AdminsSeeder extends Seeder
{
  public function run()
  {
    DB::table("admins")->insert([
      'name'                => 'YOUR-NAME',
      'email'               => 'YOUR-EMAIL',
      'password'            => Hash::make('YOUR-PASSWORD'),
    ]);
  }
}

・DBの作成

composer dump-autoload
php artisan migrate
php artisan db:seed

・Laravel8 以降の場合は routes/web.php を書き換える

   Route::get('/login', 'UserAuth\LoginController@showLoginForm')->name('login');

              ↓

use Illuminate\Support\Facades\Route;

Route::get('/login', [App\Http\Controllers\UserAuth\LoginController::class, 'showLoginForm'])->name('login');

以上です。 (composer dump-autoload は必ず実行しましょう。クラスが not found になってしまう可能性があります。)

・早速使う

管理者ログイン

http://YOUR-SITE.TLD/admin/login

ユーザーログイン

http://YOUR-SITE.TLD/user/login

・Bladeでの認証記述方法

Bladeテンプレートで adminまたはuserでログインしているかどうかを判別する

@if ( Auth::guard('admin')->check() )
<h1>adminとしてログイン済みです</h1>
@endif
@if ( Auth::guest() )
<h1>管理者ログイン前のguestです</h1>
@endif

・Bladeテンプレートでログイン後のユーザー名を表示する

user の場合

{{ Auth::guard('user')->user()->name }}

admin の場合

{{ Auth::guard('admin')->user()->name }}

・コントローラーでログイン済ユーザを取得する

use \Illuminate\Support\Facades\Auth;

$loginUser = Auth::user();

・コントローラでログイン後のユーザー名を表示する

dump( \Illuminate\Support\Facades\Auth::guard('user')->user()->name );

・コントローラでユーザーを強制ログアウトさせる

// 強制ログアウト
Auth::guard('user')->logout();

・コントローラでユーザーがログインしているかどうかをリダイレクトさせずにチェックする

if ( Auth::guard('admin')->check() ){
    // ..................
}

・管理者から「ある特定のユーザー」をログインさせる(認証なしで)

// 特定のユーザーでログインさせる(後ろに true をつけると自動ログイン(ログインを記憶)となります。)
\Auth::login($user, true);

● コントローラで認証チェックを使用する

app/Http/Kernel.php には以下のような記述が増えています

    protected $routeMiddleware = [
        'user' => \App\Http\Middleware\RedirectIfNotUser::class,
        'user.guest' => \App\Http\Middleware\RedirectIfUser::class,
        'admin' => \App\Http\Middleware\RedirectIfNotAdmin::class,
        'admin.guest' => \App\Http\Middleware\RedirectIfAdmin::class,
........

なのでこれを使用しましょう。

コントローラのコンストラクタに以下のように記述します

	function __construct(){
		// admin ログイン認証
		$this->middleware('admin');
	}

● ルーターで認証チェックを使用する

routes/web.php に次のように 'middleware' => 'admin' ,'as' => 'admin.' を追加します

// ● admin ログインが必要なページ
Route::group(['prefix' => 'admin', 'middleware' => 'admin' ,'as' => 'admin.' ], function () {
            Route::get("articles/index", "ArticleController@index")->name("articles.index");
});

routes/admin.php に記述する場合は次のようにします。
合わせて app/Providers/RouteServiceProvider.php もチェックしておきます。

// ● admin ログインが必要なページ
// check app/Providers/RouteServiceProvider.php
// 
Route::group(['namespace' => 'Admin'], function () {
  Route::resource("posts","PostController");
});

● 管理者、ユーザー毎にログアウトした時のリダイレクト先を設定する

ログアウトした時のリダイレクト先はコントローラーで設定します
管理者用、ユーザー用のAuthコントローラーは
/app/Http/Controllers/AdminAuth/LoginController.php
/app/Http/Controllers/UserAuth/LoginController.php
にあるので、ここに記述します。
vendor/hesto/multi-auth/src/Traits/LogsoutGuard.php のトレイトをオーバーライドします )

    /**
     * 
     * ログアウト後のリダイレクト先を「/admin/login」に設定する
     *
     */
    public function logoutToPath() {
        return '/admin/login';
    }

● /user/home , /admin/home など homeアクションの動作をカスタマイズする

デフォルトでは routes/user.php

Route::get('/home', function () {
    $users[] = Auth::user();
    $users[] = Auth::guard()->user();
    $users[] = Auth::guard('admin')->user();
    return view('admin.home');
})->name('home');

という風にクロージャで /user/home へのルーティングが記述されています。 これらをコメントアウトで消してしまってroutes/web.php に記述していくと見通しがいいと思います。

● ログイン成功時にリクエストしたURLへジャンプするようにする

デフォルトではログイン成功時には /admin/home に全てリダイレクトされますが、次のようなケース

(ログインしていない状態で)
/admin/special(ログインが必要なページ)へアクセス
    ↓
/admin/login(ログイン画面へリダイレクト)
    ↓
ID, PASS 入力(ログイン成功)
    ↓
もともとアクセスしたページ(/admin/special)へリダイレクトしたい!

に対応するには次の箇所を書き換えます。

app/Http/Middleware/RedirectIfNotAdmin.php

	public function handle($request, Closure $next, $guard = 'admin')
	{
	    if (!Auth::guard($guard)->check()) {
		    \Session::put('RedirectIfNotAdmin_next_url', $request->fullUrl()); // ●追加● ログイン成功後の遷移先を保存
	        return redirect('admin/login');
	    }
	    return $next($request);
	}

app/Http/Controllers/AdminAuth/LoginController.php 以下の login()メソッドを丸々追加します。

    /**
     *
     * ログイン時のメソッド
     *
     */
    public function login(\Illuminate\Http\Request $request)
    {
        $this->validateLogin($request);

        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        if ($this->attemptLogin($request)) {
            // ● 追加 ↓
            $RedirectIfNotAdmin_next_url = \Session::get('RedirectIfNotAdmin_next_url', null );
            \Session::forget('RedirectIfNotAdmin_next_url');

            if ( $RedirectIfNotAdmin_next_url ){
                return redirect( $RedirectIfNotAdmin_next_url );
            }
            // ● 追加 ↑
            return $this->sendLoginResponse($request);
        }

        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

● パスワードリマインダーのメールテキストを日本語に変更する(方法1)

・1. 独自のテンプレートを使用するように変更する

app/Notifications/UserResetPassword.php を以下のように変更

        return (new MailMessage)
            ->line('You are receiving this email because we received a password reset request for your account.')
            ->action('Reset Password', url('user/password/reset', $this->token))
            ->line('If you did not request a password reset, no further action is required.');

↓ テンプレート (resources/views/email/password_reset.blade.php) を使ってメール送信するように変更する

        return (new MailMessage)
            ->from($email_address, $email_name)
            ->view( 'email.password_reset' , [
                    'shop'      => $shop ,
                    'reset_url' => url('user/password/reset', [$shop->id, $this->token]) ,
            ]);

メール送信者はデフォルトで .env でしたいしたアドレスになるので、明示的に変更したい場合は ->from() メソッドで書き換えます 独自のパタメーターは配列で渡します。( 上の例では $shop を渡している)
'reset_url は適宜書き換えてください。

テンプレート例 (resources/views/email/password_reset.blade.php に保存)

<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
    <body>
        <style>
        body{
            font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';
            box-sizing:border-box;
        }
        </style>

        <div style="background-color:#f8fafc;color:#74787e;height:100%;line-height:1.4;margin:0;width:100%!important;word-break:break-word">
        <table width="100%" cellpadding="0" cellspacing="0" style="background-color:#f8fafc;margin:0;padding:0;width:100%">
        <tbody>
        <tr>
        <td align="center">
        <table width="100%" cellpadding="0" cellspacing="0" style="margin:0;padding:0;width:100%">
        <tbody>
        <tr>
        <td style="padding:25px 0;text-align:center">
        <a href="{{ url('/user/login', $shop->id) }}" style="color:#bbbfc3;font-size:19px;font-weight:bold;text-decoration:none" target="_blank" >
        {{ $shop->name }}
        </a>
        </td>
        </tr>
        <tr>
        <td width="100%" cellpadding="0" cellspacing="0" style="background-color:#ffffff;border-bottom:1px solid #edeff2;border-top:1px solid #edeff2;margin:0;padding:0;width:100%">
        <table align="center" width="570" cellpadding="0" cellspacing="0" style="background-color:#ffffff;margin:0 auto;padding:0;width:570px">
        <tbody>
        <tr>
        <td style="padding:35px">
        <p style="color:#3d4852;font-size:16px;line-height:1.5em;margin-top:0;text-align:left">「パスワード再設定」<wbr>ボタンを押してパスワードを再設定してください。</p>
        <table align="center" width="100%" cellpadding="0" cellspacing="0" style="margin:30px auto;padding:0;text-align:center;width:100%">
        <tbody>
        <tr>
        <td align="center">
        <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tbody>
        <tr>
        <td align="center">
        <table border="0" cellpadding="0" cellspacing="0">
        <tbody>
        <tr>
        <td>
        <a href="{{ $reset_url }}" style="border-radius:3px;color:#fff;display:inline-block;text-decoration:none;background-color:#3490dc;border-top:10px solid #3490dc;border-right:18px solid #3490dc;border-bottom:10px solid #3490dc;border-left:18px solid #3490dc" target="_blank" >パスワード再設定</a>
        </td>
        </tr>
        </tbody>
        </table>
        </td>
        </tr>
        </tbody>
        </table>
        </td>
        </tr>
        </tbody>
        </table>
        <p style="color:#3d4852;font-size:16px;line-height:1.5em;margin-top:0;text-align:left">もしこのメッセージに心当たりがない場合は破棄してください。</p>
        <table width="100%" cellpadding="0" cellspacing="0" style="border-top:1px solid #edeff2;margin-top:25px;padding-top:25px">
            <tbody>
                <tr>
                    <td>
                        <p style="color:#3d4852;line-height:1.5em;margin-top:0;text-align:left;font-size:12px">もし上の "パスワード再設定" ボタンを押してもうまく動作しない時はこちらのURLをブラウザ<wbr>に貼り付けてアクセスしてください。
                            <a href="{{ $reset_url }}" style="color:#3869d4" target="_blank">
                                {{ $reset_url }}
                            </a>
                        </p>
                    </td>
                </tr>
            </tbody>
        </table>
        </td>
        </tr>
        </tbody>
        </table>
        </td>
        </tr>
        <tr>
        <td>
        <table align="center" width="570" cellpadding="0" cellspacing="0" style="margin:0 auto;padding:0;text-align:center;width:570px">
            <tbody>
                <tr>
                    <td align="center" style="padding:35px">
                        copyright (c)2019 YOUR-SITE.COM
                    </td>
                </tr>
            </tbody>
        </table>
        </td>
        </tr>
        </tbody>
        </table>
        </td>
        </tr>
        </tbody>
        </table>
        </div>
    </body>
</html>


● パスワードリマインダーのメールテキストを日本語に変更する(方法2)

・1. モデルファイルにパスワードリマインダーのメソッドをオーバーライドする

モデルが User の場合は /app/User.php に以下を追加

    /**
     * パスワード再設定メールの送信
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify( new \App\Notifications\UserResetPassword($token) );
    }

・2. メッセージを日本語化する

app/Notifications/UserResetPassword.php を以下のように変更

            // OFF  ->line('You are receiving this email because we received a password reset request for your account.')
            // OFF  ->action('Reset Password', url('user/password/reset', $this->token))
            // OFF  ->line('If you did not request a password reset, no further action is required.');
            ->line('「パスワード再設定」ボタンを押してパスワードを再設定してください。')
            ->action('パスワード再設定', url('user/password/reset', $this->token))
            ->line('もしこのメッセージに心当たりがない場合は破棄してください。');

・3. htmlメールのヘッダフッタを日本語に変更する

php artisan vendor:publish --tag=laravel-mail
php artisan vendor:publish --tag=laravel-notifications

コマンドを実行すると

resources/views/vendor/mail/(複数のテンプレートファイル)
resources/views/vendor/notifications/email.blade.php

ファイルが自動生成されます。 変更する箇所は次の画像の通りです。

↓ すべて日本語化すると次のようになります

● ログインせずにログイン認証が必要なページを表示させようとした時のリダイレクト先を変更する

app/Http/Middleware/RedirectIfNotUser.php

	public function handle($request, Closure $next, $guard = 'user')
	{
	    if (!Auth::guard($guard)->check()) {
	        return redirect('user/login');  // ここを書き換えます
	    }
添付ファイル1
添付ファイル2
添付ファイル3
No.1348
03/06 11:37

edit

添付ファイル

LaravelのValidatorで独自のチェックを行った後独自のエラーメッセージを表示させる

● LaravelのValidatorで独自のチェックを行った後独自のエラーメッセージを表示させる

Laravelでフォーム入力値のバリデーションを行った後、独自のチェックを行って、エラーが発生した場合 エラーメッセージと共に入力画面へ戻したい。といったことがあるかと思います。

次のようにして好きなメッセージでエラーメッセージを設定できます。

use Validator;
// 通常のバリデーション
$validator = Validator::make($request->all(), [
	'feed_url'  => 'required',
]);
$validator->validate();

// 追加で入力チェックを行う
if ( エラーがある場合 ) {
$validator->errors()->add('feed_url', 'このURLにはRSSフィードが含まれていません。');
return back()->withInput()->withErrors($validator);
}

エラーメッセージのセット方法は次のように記述してもOKです。

return back()->withInput()->withErrors(array('feed_url' => 'このURLにはRSSフィードが含まれていません。'));
No.1345
11/10 09:55

edit

LaravelでRSSフィードを読み込む

● awjudd/feed-reader

ざっと見たところ、「simplepie/simplepie」のLaravel5用ラッパーです。

● awjudd/feed-reader のインストール

composer require awjudd/feed-reader

config/app.php の編集

    'providers' => [
        .....
        Awjudd\FeedReader\FeedReaderServiceProvider::class, // この行を追加
    'aliases' => [
        .....
        'FeedReader' => Awjudd\FeedReader\Facade::class,, // この行を追加

● 設定ファイルの作成

php artisan vendor:publish

(  [1 ] Provider: Awjudd\FeedReader\FeedReaderServiceProvider )を選択します。

● Laravelコントローラーから使用する

use Awjudd\FeedReader\Facade as FeedReader;
$feed = FeedReader::read('https://RSS-FEED-SITE-URL');

if ( $feed->error() ) {
    echo $feed->error();
}

foreach ($feed->get_items() as $item) {
    $hash = [];
    $hash['site_title'] = $item->get_feed()->get_title();
    $hash['title'] = trim($item->get_title());
    $hash['permalink'] = trim($item->get_permalink());
    $hash['link'] = trim($item->get_link());
    $hash['date'] = $item->get_date('Y-m-d H:i:s');
    $hash['content'] = $item->get_content();
    dump($hash);
}

中身は「simplepie/simplepie」なので下記を参照してください。

https://github.com/simplepie/simplepie
https://goo.gl/ma6j9T

No.1344
11/08 17:43

edit

Laravelでsqliteを使用する / sqliteデータベースファイルを指定する

● Laravelでsqliteを使用する

Laravelでデータベース sqlite を使用します。データベースファイルは database/database.sqlite になります。

1. .env にsqlite設定を記述

mysqlの設定を # でコメントアウトして、sqlite設定を記述します。

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

DB_CONNECTION=sqlite

2. 中身がからのデータベースファイルを作成する

touch database/database.sqlite

3. マイグレーション実行

php artisan migrate

以上です。簡単ですね。

● Laravelでデータベースファイルを指定して sqlite を使用する

.envフルパスでDBファイルを指定します。

例: /PATH/TO/YOUR/APP/database/production.sqliteを指定する

DB_DATABASE=/PATH/TO/YOUR/APP/database/production.sqlite 
No.1342
11/07 13:35

edit

Laravelで最速でソースコードを色づけしながらMarkdownを使用する

● Laravelで最速でソースコードを色づけしながらMarkdownを使用する「gitdown」

● gitdown

https://github.com/calebporzio/gitdown

1. gitdown のインストール

composer require calebporzio/gitdown

2. Bladeの記述を変更する

{{ $memo->text_name }}

 ↓

{!! GitDown::parseAndCache($memo->text_name) !!}

これだけでマークダウンでパースされます。

3. ソースコードに色づけする

@gitdown

以上です。最速。

● laravelで markdown をライトに使用する「laravel-markdown」

1. laravel-markdown のインストール

composer require andreasindal/laravel-markdown

2. Bladeの記述を変更する

{{ $memo->text_name }}

 ↓

@markdown($memo->text_name)

これだけでマークダウンでパースされます。

No.1341
03/12 16:59

edit

Laravelで指定したGETデータ(またはPOSTデータ)を取得する

● Laravelで指定したGETデータ(またはPOSTデータ)を取得する

Laravelで httpの全てのGETデータやPOSTデータは

$request->all();

で取得しますが、この時、取得したい項目を指定することができます。

「user_name」「email_name」を取得

$request->all(['user_name','email_name']);

なので、EloquentモデルでデータのUPDATEを行う時

$user->fill($request->all())->save();

 ↓

$user->fill( $request->all(['user_name','email_name']) )->save();

と書くことができます。 便利ですね。

No.1338
10/31 18:30

edit

LaravelのモデルにDBに存在しない独自のカラム(プロパティ)を追加する

● LaravelのモデルにDBに存在しない独自のカラム(プロパティ)を追加する

例としてあるモデル(User)に独自カラム「_editable_flag」を値「normal」で追加する。

app/User.php に以下の記述を追加する

    // 独自カラム
    // protected $appends = array('editable_flag');
    protected $appends = array('_editable_flag'); // このようにアンダースコア始まりのカラムも作成できます。(メソッド名は変更せずでOK)

    // 独自カラムのアクセサ
    public function getEditableFlagAttribute()
    {
        return 'normal';
    }

No.1337
10/31 13:35

edit

LaravelのBladeビューでテンプレートファイル名を表示させる

デバッグ時にどのBladeビューファイルが使用されているかを確認したい時があります。 そんな時は

● LaravelのBladeビューでテンプレートファイル名を変数に代入する( .env の APP_DEBUG=true の時のみ )

それぞれのテンプレートファイル名に配置

@if(env('APP_DEBUG') == 1)
    @php($template_filename = 'show.blade.php')
    @php($compiled_template_filename = __FILE__)
@endif

( show.blade.php はファイルごとに適宜書き換えてください。 )

● LaravelのBladeビューで変数を表示する( .env の APP_DEBUG=true の時のみ )

@if(env('APP_DEBUG') == 1)
<!-- {{$template_filename or '' }} -->
<!-- {{$compiled_template_filename or '' }} -->
@endif

レイアウトファイルに記述しておきます。

No.1336
10/31 09:30

edit

Laravelのログを日別でローテーションさせる( +自動削除 )

● Laravelのログを日別でローテーションさせる

Laravelのログはデフォルトでは1つのファイルに追記を繰り返していきます。
ローテーションさせましょう。

.envファイルのデフォルト値「stack」を「daily」に変更します。

.env

LOG_CHANNEL=stack

 ↓

LOG_CHANNEL=daily

これだけでローテーションできるようになります。

● Laravelのログの最大保存日数を設定する

/config/logging.php の days 項目に設定できます。

        'daily' => [
            'days' => 7,
        ],

その他以下のような設定が可能です。

名前 説明
stack 「マルチチャンネル」チャンネルを作成するためのラッパー機能
single シングルファイル/パスベースのロガーチャンネル(StreamHandler
daily RotatingFileHandlerベースの毎日ファイルを切り替えるMonologドライバ
slack SlackWebhookHandlerベースのMonologドライバ
syslog SyslogHandlerベースのMonologドライバ
errorlog ErrorLogHandlerベースのMonologドライバ
monolog サポートしているMonologハンドラをどれでも使用できる、Monologファクトリドライバ
custom チャンネルを生成するため、指定したファクトリを呼び出すドライバ

monolog を選択して、深刻なエラーの場合はメールなり通知を送信するという運用が実用的かと思われます。

● cronを使っている場合に cron実行ユーザーの権限でログを作成されてしまうので permission denied エラーとなる場合の対処法

/config/logging.php に permission 項目を記述できます。 (Laravel version 5.6.10 以降)

        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
            'days' => 7,
            'permission' => 0666,
        ],
No.1332
06/22 11:56

edit

LaravelのEloquentに追加したいパッケージ

● Laravelで権限(Role)を管理するパッケージ

● zizaco/entrust(★6297)

https://packagist.org/packages/zizaco/entrust
https://itsolutionstuff.com/post/laravel-52-user-acl-roles-and-permissions-with-middleware-using-entrust-from-scratch-tutorialexample.html

● spatie/laravel-permission(★7234)

https://packagist.org/packages/spatie/laravel-permission

● LaravelでOauthするパッケージ

● laravel/socialite(★3507)

https://packagist.org/packages/laravel/socialite

● LaravelのEloquentでタグを管理するパッケージ

● rtconner/laravel-tagging(★679)

https://packagist.org/packages/rtconner/laravel-tagging

● cviebrock/eloquent-taggable(★266)

https://packagist.org/packages/cviebrock/eloquent-taggable

● rinvex/laravel-tags(★11)

https://packagist.org/packages/rinvex/laravel-tags

No.1326
12/03 20:46

edit

LaravelのBladeビューで現在のURLを取得する

● LaravelのBladeビューで現在のURLを取得する

・現在のURLを取得(GETパラメーター( ? 以降の文字列)も取得します。)

{{ \Request::fullUrl() }}
または
{{ request()->fullUrl() }}

例: http://test.server.tld/users/search?q=test

・現在のURLを取得(GETパラメーター( ? 以降の文字列)は取得しません)

{{ url()->current() }}

例: http://test.server.tld/users/search

・現在のURLを相対で取得(GETパラメーター( ? 以降の文字列)も取得します。)

{{ str_replace(url('/'),"",request()->fullUrl()) }}

例: /search?q=test

・リクエストURIの取得

{{ request()->path() }}

例: users/search

・リクエストのURIが指定されたパターンにマッチするか

@if ( request()->is('*users*') )
        マッチします<br>
@endif

・完全なURLを取得(クエリ文字列なし)

{{ request()->url() }}

例: http://test.server.tld/users/search

・リクエストメソッドの取得

{{ request()->method() }}

  ↓

GET

・リクエストメソッドが指定したものにマッチするか

@if( request()->isMethod('get') )
     getメソッドです<br>
@endif

引用: https://goo.gl/tojhBs

No.1325
12/20 11:01

edit

LaravelのBladeテンプレートでcsrfトークン、PUT/PATCH/DELETE メソッドを埋め込む。

● LaravelのBladeテンプレートで擬似的なPUT/PATCH/DELETE メソッドを埋め込む

例: PUT メソッドを埋め込みます

@method('PUT')

(Laravel 5.4 以下の場合) 手動で PUT メソッドを埋め込む

<input type="hidden" name="_method" value="PUT">

● LaravelのBladeテンプレートでcsrfトークンを埋め込む

@csrf

(Laravel 5.4 以下の場合) LaravelのBladeテンプレートでcsrfトークンを手動で埋め込む。

<input type="hidden" name="_token" value="{{ csrf_token() }}"
No.1324
02/05 15:08

edit

LaravelのBladeテンプレートで変数の存在確認を行う

● LaravelのBladeテンプレートで変数の存在確認を行う

LaravelのBladeテンプレートで未定義の変数を使用するとエラーとなります。 事前に isset で 確認しましょう。

@if (isset( $text ))
<p>$test</p>
@endif

変数が未定義の場合に null をセットする

@if ( ! isset($v->flag) )
<?php $v->flag = null; ?>
@endif

または @ をつけて未定義エラーを回避します

@if ( $q['data_id'] )

 ↓

@if ( @$q['data_id'] )

● LaravelのBladeテンプレートで変数が存在しない場合に null (または空文字)を表示させる

変数が存在しない場合に null (または空文字)表示でいいのなら

{{ @$hoge }}
No.1323
04/12 13:28

edit

LaravelのコントローラーでHTTPのGETパラメーターを受け取る

LaravelのコントローラーでHTTPのGETパラメーターを受け取る

1. use Illuminate\Http\Request; の追加

use Illuminate\Http\Request;

2. 使用したいメソッドで $request を受け取る

	public function show($id)
	{

 ↓ Request型の $request をコントローラーのメソッドで受け取るようにします。

	public function show(Request $request, $id)
	{

3. 使う

URL https://YOUR-SERVER.TLD/test/action?file=001 でアクセスする時

echo $request->input('file') ;

次のように出力されます

001
No.1322
12/13 10:36

edit

Laravelのbladeテンプレート内で vue.js を使用する

Laravelのbladeテンプレート、vue.jsのテンプレート、どちらも {{ }} で変数を囲う記法なので、混在するとLaravelでエラーとなります。 そこで対応方法が2つあるようです。

● {{ の 前に @ をつける

    <div>
        {{ message }}
    </div>

 ↓

    <div>
        @{{ message }}
    </div>

● vue.js側のデリミタ {{ }} を (% %) に変更する

こちらの方法がオススメです。

Vue.config.delimiters = ['(%', '%)']; でデリミタを変更して使用します。

var vm = new Vue({
    el: '#myApp',
    data: {
    }
});

  ↓

var vm = new Vue({
    delimiters: ["(%","%)"] ,
    el: '#myApp',
    data: {
    }
});

引用: https://goo.gl/Y7QqBz

No.1321
10/29 09:45

edit

LaravelのBladeテンプレートで現在のアクション(URL, メソッド名)を取得する

● LaravelのBladeテンプレート内現在のコントローラーを取得する

<?php $current_controller_name = explode('.', Route::currentRouteName())[0]; ?>
現在のコントローラー名は {{ $current_controller_name }}

引用: https://goo.gl/9WmwRR

● LaravelのBladeテンプレート内で現在のルーティングを取得する

記法 戻り値(例)
{{ Route::currentRouteName() }} projects.edit

● LaravelのBladeテンプレート内でURLを取得する

記法 戻り値(例)
{{ Request::url() }} https://MY-SERVER.TLD/projects/3/edit
{{ Request::url("/") }} https://MY-SERVER.TLD/projects/3/edit
{{ Request::root() }} https://MY-SERVER.TLD
{{ Request::fullUrl() }} https://MY-SERVER.TLD/projects/3/edit
{{ Request::path() }} projects/3/edit
{{ Request::decodedPath() }} projects/3/edit

● FORMの hidden などで渡す場合は urlencode します。

{{ urlencode(request()->fullUrl()) }}

● LaravelのBladeテンプレート内でパスを取得する

記法 戻り値(例)
{{ url("") }} https://MY-SERVER.TLD
{{ url('/') }} https://MY-SERVER.TLD
{{ app_path() }} /var/www/vhosts/MY-SERVER.TLD/laravel/my_app/app
{{ base_path() }} /var/www/vhosts/MY-SERVER.TLD/laravel/my_app
{{ config_path() }} /var/www/vhosts/MY-SERVER.TLD/laravel/my_app/config
{{ database_path() }} /var/www/vhosts/MY-SERVER.TLD/laravel/my_app/database
{{ storage_path() }} /var/www/vhosts/MY-SERVER.TLD/laravel/my_app/storage
{{ resource_path() }} /var/www/vhosts/MY-SERVER.TLD/laravel/my_app/resources
No.1318
05/28 17:21

edit

LaravelのBladeでドロップダウンリスト<select>を簡単に作成する

laravelcollective/html を使用してドロップダウンリスト( select, option リスト) を簡単に作成します。

● laravelcollective/html のインストール

https://laravelcollective.com/docs/master/html

composer require "laravelcollective/html":"^5.4.0"

● ベタがきでドロップダウンリストを生成する

@php
    $job_name_loop = [
        ''      => '選択してください' ,
        '公務員' => '公務員' ,
        '医師'   => '医師' ,
        '弁護士' => '弁護士' ,
    ];
@endphp
{{ Form::select('job_name', $job_name_loop, old('job_name'), ['class' => 'my_class']) }}

● ドロップダウンリストの配列を取得する

モデルから一覧を取得してきて、それを (key/value)方式に変換します。
コントローラーの好きなところに以下を記述します。

$clients = Client::select('id', 'client_name')->get();

// key,value ペアに直す
$client_id_loop = $clients->pluck('client_name','id');

● LaraelのBladeテンプレートにデータを渡す

return view('projects.create', compact('client_id_loop') );

● Bladeテンプレート内で Form::select を呼び出す

フォーム名「FORM_NAME」  CSSクラス名「my_class」 なドロップダウンリストを生成します。

{{ Form::select('FORM_NAME', $client_id_loop, null, ['class' => 'my_class']) }}

次のようなドロップダウンリストが生成されます

<select class="my_class" name="FORM_NAME">
    <option value="1">テスト1</option>
    <option value="2">テスト2</option>
    <option value="3">テスト3</option>
</select>

● ↑ こんなめんどくさいことをせずに Bladeのビューに1行で記述する

一撃で記述したい場合はこのように記述します。
bladeテンプレート内に記述
3つ目の引数に値を渡すと選択済みになります。null を渡すと最初の項目が選択済みになります

{{ Form::select('FORM_NAME', \App\Client::select('id', 'client_name')->get()->pluck('client_name','id')->prepend( "選択してください", ""), null, ['class' => 'form-control']) }}

● 値をを選択済み(selected)のドロップダウンリストを生成する

3つめの引数に値をセットすると selected になります

{{ Form::select('FORM_NAME', $client_id_loop, 2, ['class' => 'my_class']) }}

 ↓ 次のようなドロップダウンリストが生成されます

<select class="my_class" name="FORM_NAME">
    <option value="1">テスト1</option>
    <option value="2" selected="selected">テスト2</option>
    <option value="3">テスト3</option>
</select>

●ドロップダウンリストに「選択してください」項目を追加する

key が null の値を登録します。

{{ Form::select('FORM_NAME',  [null=>'選択してください']+$client_id_loop, 2, ['class' => 'my_class']) }}

または prepend() メソッドを使って先頭に追加します。
prepend() メソッドを使用するときは 空文字 "" を使用します。( null を渡すと 連想配列から普通の配列になります。)
また 空文字 "" のところに 0 を渡すと、配列のキーが数字の場合 キーが 0,1,2,3,4,5.... という風にリナンバーされてしまうので注意してください。 (複数人数で開発する時は prepend() メソッドは使わずに、 foreach でベタに書くのもいいと思います。)

{{ Form::select('FORM_NAME', \App\Client::select('id', 'client_name')->get()->pluck('client_name','id')->prepend( "選択してください", ""), null, ['class' => 'form-control']) }}

 ↓ 次のようなドロップダウンリストが生成されます

<select class="my_class" name="FORM_NAME">
    <option value="">選択してください</option>
    <option value="1">テスト1</option>
    <option value="2" selected="selected">テスト2</option>
    <option value="3">テスト3</option>
</select>

disabled な値を追加するにはこちら
https://goo.gl/cBJVaM

No.1317
08/12 22:51

edit

Laravelで1対1のリレーションを複数回取得する

Laravelでテーブルが2つ「clients」「users」あって、「clients」テーブル内に登録者IDと更新者IDが格納されている場合に 更新者の名前を取得する方法。

● モデルに1対1 リレーションを設定

app/Client.php

DBカラムの

「clients.created_user_id」と「users.id」と同じデータがあれば取得します。
「clients.updated_user_id」と「users.id」と同じデータがあれば取得します。
    /**
     * 1対1リレーション
     *
     * @return \Illuminate\Database\Eloquent\Relations\hasOne
     */
    public function created_user()
    {
        return $this->hasOne('App\User', 'id', 'created_user_id')->withDefault();
    }


    /**
     * 1対1リレーション
     *
     * @return \Illuminate\Database\Eloquent\Relations\hasOne
     */
    public function updated_user()
    {
        return $this->hasOne('App\User', 'id', 'updated_user_id')->withDefault();
    }

->withDefault() がミソですね。存在しないときに中身が空のオブジェクトを返します。

● with メソッドの引数を複数指定する

$clients = Client::with('created_user','updated_user')->get();

● コントローラーから取得する時に with メソッドを2回投げる。

app/Http/Controllers/ClientController.php

	public function index( Request $request )
	{
		$clients = Client::with('created_user')
							->with('updated_user')
							->orderBy('id', 'desc')->paginate( 10 );
    }

引用: https://goo.gl/6h6LEC

No.1316
03/05 16:37

edit

LaravelのCRUD操作のメソッドとルーティング一覧

LaravelのCRUD操作のメソッドとルーティング一覧

例 : teams モデルの場合

HTTPメソッド URL例 ルーティング名 メソッド名
データの一覧表示
GET,HEAD teams teams.index App\Http\Controllers\TeamController@index
データの詳細表示
GET,HEAD teams/123456 teams.show App\Http\Controllers\TeamController@show
データの新規作成
GET,HEAD teams/create teams.create App\Http\Controllers\TeamController@create
POST teams teams.store App\Http\Controllers\TeamController@store
データの更新
GET,HEAD teams/123456/edit teams.edit App\Http\Controllers\TeamController@edit
PUT,PATCH teams/123456 teams.update App\Http\Controllers\TeamController@update
データの削除
DELETE teams/123456 teams.destroy App\Http\Controllers\TeamController@destroy

● クロージャで path_info のパラメーターを渡す

Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

● クロージャで Request と パラメータ id を渡す

use \Illuminate\Http\Request;
Route::get("/{id}", function (Request $request, $id) {
    return App\Http\Controllers\HomeJaController::index( $request, $id ); }
})->where([ 'id' => '[0-9]+' ]);

( id は 正規表現で数字のみに限定しておきます。 )

● ルーターの記述方法(個別)

/routes/web.php

メソッド: 「PUT」
URL: 「teams/{id}/myteamupdate」
を ↓ 
コントローラー「TeamController」の メソッド「myteamupdate」
にルーティングする

Route::put("teams/{id}/myteamupdate", "TeamController@myteamupdate")->name('teams.myteamupdate'); // Add this line in routes.php

● ルーターの記述方法(CRUD一括)

/routes/web.php

clients のCRUD関連メソッドを一括でルーティングする

Route::resource("clients","ClientController");

● ルーターの記述方法(CRUDから一部を除外)

CRUDから一部を除外する方法です

/routes/web.php

Route::resource("clients","ClientController");

 ↓ 「SHOWメソッド」「DELETE メソッド」を除外します

Route::resource('clients', 'ClientController', ['except' => ['show', 'delete'] ]);

● ルーティング変更を確認する

コマンドラインから以下のコマンドでルーティングを確認します。
peco コマンドがある場合は peco を通しましょう。

php artisan route:list | peco
No.1315
02/12 16:32

edit

ルーティング

Laravel mailable クラスを使ってメールを送信する

Laravelにはメールを送信するクラス(mailable)が最初から用意されています。
これを使って管理者宛にメールを送信してみます。

● Laravel mailableを artisan から生成する

今回管理者宛にメール送信するプログラムなので名前を「Admin」とします。(命名は自由です好きに書き換えてください)

app/Mail/Admin.php というファイルを次のコマンドで生成します。

php artisan make:mail Admin

app/Mail/Admin.php が生成されました。 以下のように修正します。

public function build() の中を書き換えます
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class Admin extends Mailable
{
    use Queueable, SerializesModels;
    private $message;
    /**
     * Create a new message instance.
     * @return void
     */
    public function __construct( $m )
    {
        $this->message = $m;
    }
    /**
     * Build the message.
     * @return $this
     */
    public function build()
    {
        // ここを書き換えます ↓
        return $this->from('FROM-ADDRESS@YOUR-DOMAIN.COM')
                    ->subject( "エラーのお知らせ" )
                    ->view('mails.admin')
                    ->with([
                        'user_name' => 'テスト 太郎' ,
                        'my_text'   => $this->message ,
                    ]);
        // ここを書き換えます ↑
    }
}

● メール本文のテンプレートファイル resources/views/mails/admin.blade.php を作成する

まず、 resources/views へ移動し、ディレクトリ mails/ を作成します。 そしてその中にファイル admin.blade.php を以下の内容で作成します

こんにちは{{ $user_name }} !!!<br>
テストメールです<br>
{!! $my_text !!}

● コントローラーからメールを送信する

コントローラーの好きな所に記述します。

use Illuminate\Support\Facades\Mail;
use App\Mail\Admin;
// メール送信
$t = '任意のテキストをここに記述します';
Mail::to('TO-ADDRESS@YOUR-DOMAIN.COM')->send( new Admin($t) );

● .env ファイルのメールの設定を確認する

正しくメール送信が行える設定になっているか .env ファイルを確認します。
ローカルサーバではなくインターネット上にあるサーバの場合はとりあえずは以下の設定でメール送信が可能です。

MAIL_FROM_ADDRESS=<メールアドレス>
MAIL_FROM_NAME=<メールアドレスの横に表示させる名前>

MAIL_DRIVER=sendmail
MAIL_HOST=localhost
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=

● メール送信を記述したコントローラにアクセスしてメールを送信する

メール送信を記述したコントローラにアクセスしてメールを送信してみます。
このようなメールが到着します

● Laravelのメールドライバの種類

.env の MAIL_DRIVER には次の指定ができるようです。

MAIL_DRIVERの値 説明 補足
sendmail PHPのmail関数で送信する(一番簡単)
smtp SMTPサーバーへログインして送信する(id,passが必要)
mailgun MailgunというAPIベースのメール配信サービスから送信する
mandrill MailgunというAPIベースのメール配信サービスから送信する
ses Amazon AWSが提供するAmazon SESから送信する
sparkpost SparkPostというAPIベースのメール配信サービスから送信する
log 送信は行わず、ログファイルに内容を書き込む storage/logs/laravel.log に内容が記述されます
array ロジックは通りますが、送信はされません

引用: https://goo.gl/fjREcG

添付ファイル1
No.1312
05/06 10:42

edit

添付ファイル

Laravelのモデルで更新日(updated_at)のみ変更する

● Laravelのモデルで更新日(updated_at)のみ変更する

$model->touch(); 

これだけです、これだけで下記のようなSQL文が実行されます

            [query] => update `MY-TABLE` set `updated_at` = ? where `id` = ?
            [bindings] => Array
                (
                    [0] => 2018-10-17 22:44:43
                    [1] => 16090
                )
No.1311
10/17 22:47

edit

Laravel

Laravelで .env config ファイルに値を保存し取り出す

● .env ファイルに設定情報を保存する

MYAPP_URL=http://localhost

● .env ファイルから設定情報を取り出す

$aaa = env(MYAPP_URL);

設定がない場合にデフォルト値をセットすることもできます

$aaa = env(MYAPP_URL, 'default_value');

● config/xxxxx.php ファイルに設定情報を保存する

例: config/myfile.php に値を保存します

<?php
return [
	'data1' => "設定情報です" ,
];

KEY, VALUE 形式だけでなく 配列形式でも保存できます。

● config/xxxxx.php ファイルから設定情報を取り出す

次の2つのうちどちらでも取り出すことが出来ます。

config('myfile.data1');
\Config::get('myfile.data1');

● configファイルから値がとりだせない / 取り出した値が NULL になる時は

configファイルから値がとりだせない / 取り出した値が NULL になる時は次のコマンドを実行します

php artisan config:clear;

● .env ファイルに設定情報を保存する

No.1310
10/17 21:58

edit

LaravelでCLIから呼び出す自作コマンドを作成する。コマンドをcronで定期実行する

● LaravelでCLIから呼び出す自作コマンドを作成する

フレームワークによってはコントローラーを直接CLIから叩けるものもありますが、 Laravelの場合はコマンドを用意する必要があるようです。 (とはいえ artisan コマンドが用意されているので簡単です。)

● 作成するコマンド名を考える

コマンド名は

make:auth
make:migration

 のように(makeグループの中のauth)みたいな感じで、グルーピングされていますので、
自作コマンドもグルーピングしておくと、コマンドが増えた時に一覧表示で見やすくなります。

今回作成するコマンドは

myapp:print

とします。

● コマンドファイルを artisan で作成する

myapp:print コマンドを作成する時には次のように入力します

php artisan make:command  MyappPrint

するとファイル app/Console/Commands/MyappPrint.php が自動生成されます。

● 自動生成されたファイルを編集する

app/Console/Commands/MyappPrint.php を以下のようにします

protected $signature = 'myapp:print'; にコマンド名をセットします。
protected $description = 'MYAPP:にコマンドの説明をセットします。

<?php

namespace App\Console\Commands;
use Illuminate\Console\Command;

class MyappPrint extends Command
{
    /**
     * The name and signature of the console command.
     * @var string
     */
    protected $signature = 'myapp:print';


    /**
     * The console command description.
     * @var string
     */
    protected $description = 'MYAPP: ハローと表示します';


    /**
     * Create a new command instance.
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }


    /**
     * Execute the console command.
     * @return mixed
     */
    public function handle()
    {
        // ここにロジックを記述
        echo 'Hello Myapp Command !!!' . "\n";
    }
}

● ターミナルから実行する

php artisan myapp:print

結果

Hello Myapp Command !!!

● コマンドラインで引数を受け取る

1. シグネイチャを変更します

    protected $signature = 'myapp:print';

 ↓

    protected $signature = 'myapp:print {id}';

と変更します。

「引数なし」を許可するには

    protected $signature = 'myapp:print {id?}';

とします。

2.handle()メソッド内で引数を受け取ります

    public function handle()
    {
        // ここにロジックを記述
        $id = $this->argument('id');
        echo 'Hello Myapp Command !!!' . "\n";
        echo $id;
    }

ターミナルから実行時に引数を渡します

php artisan myapp:print

● Laravelでコマンドをcron定期実行する(タスクスケジュール)

Laravel で定期実行させるにはまず cron で毎分ごとにLaravelのスケジューラを実行させ、その中でLaravelで設定した時間ごとにコマンドを自動実行します。

1. crontab に設定する

crontab -e

crontabの書式に沿って以下のように記述します

* * * * * php7 /YOUR/APP/PATH/artisan schedule:run >> /dev/null 2>&1

2. app/Console/Kernel.php にコマンドを記述

コマンド「myapp:getdata」を1分ごとに起動するように設定します。

    protected function schedule(Schedule $schedule)
    {
        // RSSからデータを取得 (1分ごと)
        $schedule->command('myapp:getdata')->everyMinute();
    }

以上です。 ログを見て定期実行が行われているか確認します。

スケジュールは以下のように設定できます。

メソッド 説明
->cron('* * * * * *'); CRON記法によるスケジュール
->everyMinute(); 毎分タスク実行
->everyFiveMinutes(); 5分毎にタスク実行
->everyTenMinutes(); 10分毎にタスク実行
->everyFifteenMinutes(); 15分毎にタスク実行
->everyThirtyMinutes(); 30分毎にタスク実行
->hourly(); 毎時タスク実行
->hourlyAt(17); 一時間ごと、毎時17分にタスク実行
->daily(); 毎日深夜12時に実行
->dailyAt('13:00'); 毎日13:00に実行
->twiceDaily(1, 13); 毎日1:00と13:00時に実行
->weekly(); 毎週実行
->monthly(); 毎月実行
->monthlyOn(4, '15:00'); 毎月4日の15:00に実行
->quarterly(); 四半期ごとに実行
->yearly(); 毎年実行
->timezone('America/New_York'); タイムゾーン設定

● php artisan schedule:run で動作を確認する

cronでコマンドが呼ばれているかを確認するには

php artisan schedule:run

としますが、あらかじめ メソッドの修正が必要です!(重要)

1. schedule メソッドの実行間隔を 1分に変更する

1分に変更しないと、No scheduled commands are ready to run. が出て終了してしまいます。
また、重複起動を回避するメソッド withoutOverlapping() も外しておきましょう。

    protected function schedule(Schedule $schedule)
    {
        $schedule->command('myapp:getdata')->everyMinute();

2. schedule:run を実行する

php artisan schedule:run

とします。

これで必ず設定されたタスクが走るので、ログを見て動作を確認します。

● withoutOverlapping() メソッドはデッドロックを自動解除しない?

どうやら withoutOverlapping() はデッドロックを自動で解除できないようです。

https://goo.gl/mVchaN

No.1309
11/26 14:47

edit

Laravel
CLI
コマンド

Laravel マイグレーション時の「Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes"」の対処法

● varcharのデフォルト文字数を191文字にして(191*4 = 764バイト) 767バイトより小さくする

app\Providers\AppServiceProvider.php


    public function boot()
    {
        // ↓ この行を追加
        \Illuminate\Support\Facades\Schema::defaultStringLength(191);
    }

No.1307
11/02 23:20

edit

Laravel のコントローラーのコンストラクタで Authを使い、リダイレクトする

● Laravel のコントローラーのコンストラクタでAuthを使う

	function __construct(){

		// コンストラクタ内で Auth を使う
		$this->middleware(function ($request, $next) {
			$user = Auth::user();
			// .....

			return $next($request);
		});
	}

● Laravel のコントローラーのコンストラクタ等で強制リダイレクトを行う

use Illuminate\Support\Facades\Redirect;
Redirect::route('myerror')->withErrors(['redirect'=>'エラー発生'])->throwResponse();

引用: https://goo.gl/8rkAu5

No.1306
10/15 16:17

edit

Laraelのモデルでグローバルスコープ、ローカルスコープを動的なパラメーターでセットする

● Laravelのモデルの「スコープ」 とは

Laravelのモデルのスコープとは簡単にいうと SQL文の「 WHERE aaa = 'bbb'」みたいなWHERE句 なのですが、

・「User」モデルの中の管理者フラグを持つものだけを取り扱う
・「User」モデルが既にあるときに、現在削除されてないユーザー( WHERE is_deleted = 0 )を「Activeuser」モデルとして作成する。

みたいなところで使います。
その時に使うのが モデルの「スコープ」です。

好きな時につけたり外したりできる「ローカルスコープ」と
基本的につけっ放しの「グローバルスコープ」があります。

● グローバルスコープの作成方法(A. スコープクラスを作成して使う場合)

例:自分(ログインしているユーザー)と同じチームIDを持つという条件を
( Inmyteam )というグローバルスコープとして作成して、モデルに適用します。

1. グローバルスコープの作成

/app/Scopes/Inmyteam.php

<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;

class Inmyteam implements Scope
{
    /**
     * 自分のチーム内のユーザーに限定するスコープ
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('team_id', '=', Auth::user()->team_id);
    }
}

2. モデルファイルから呼び出す。

app/User.php のメソッドに下記の boot() メソッドを追加する

use App\Scopes\Inmyteam;
    protected static function boot()
    {
        parent::boot();
        // スコープ「Inmyteam」を適用
        static::addGlobalScope(new Inmyteam);
    }

以上です。簡単ですね。
なおグローバルスコープに変数は渡せないようなので、ダイナミックに変わる条件(動的なパラメーター)を指定したい場合はセッションなど別のルートから取って来ましょう。
取れない場合はローカルスコープで設定します。

引用: https://goo.gl/Sk2F26

● グローバルスコープの作成方法(B. クロージャを使って記述する方法)

モデルファイル内に記述します。 利点は初めてLaravelを触る方にも見落としがない、という点と記述が楽な点です。

app/User.php のメソッドに下記の boot() メソッドを追加し、その中に直接記述する

    // /**
    //  * boot method
    //  * @return void
    //  */
    protected static function boot()
    {
        parent::boot();

        // GlobalScope
        static::addGlobalScope('report_start', function(\Illuminate\Database\Eloquent\Builder $builder){
            $builder->where('team_id', '=', Auth::user()->team_id);
        });

    }

● ローカルスコープの作成方法

引用: https://goo.gl/Wfvuka

1. ローカルスコープの作成

モデルファイルの中に直接書けばOK。 ローカルスコープはメソッドに引数を渡せます。

例) あるショップ内の商品に限定するスコープを作成する

app/Items.php

    /**
     * ローカルスコープ : ->inShop(<id>) で あるショップ内の商品に限定する
     *
     * @param    \Illuminate\Database\Eloquent\Builder    $query
     * @return   \Illuminate\Database\Eloquent\Builder
     */
     public function scopeInShop($query, $shop_id)
     {
         return $query->where('shop_id', '=', $shop_id);
     }

2. ローカルスコープの呼び出し

shop_id が 99 な商品を全件取得

Item::inShop(99)->all()

実は

Item::inShop(99)
Item::inshop(99)

どちらも使うことができますが、同じようにキャメルケースで使用することをお勧めします。

No.1305
09/11 17:03

edit

Laravel でリレーションを設定し、リレーション先のテーブルを取得する

LaravelでEloquentを使ってリレーションを設定し、リレーション先のテーブルを取得する方法です。

● テーブル例

次のような2つのテーブルがあるとします
・「チーム」(teams)
・「ユーザー」(users)

このようにチームの中に複数のユーザーが所属するとします

 ─── マイチーム
     ├── 鈴木 一郎
     ├── 山田 太郎
     ├── 中村 二郎

● リレーションの設定

モデル /app/Team.php

    /**
     * 1対多リレーション
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
public function users() // 複数形(users)にする
{
    // 「is_deleted = 0」のデータを「idの大きい順」で取得する
    return $this->hasMany('App\User')->where('is_deleted', 0)->orderBy('id', 'DESC');
}

(取得条件やソート順を指定することができます。)

モデル /app/User.php

public function team() // 単数形(team)にする
{
    return $this->belongsTo('App\Team');
}

● リレーションのデータの取得

・1. チーム一覧を所属するユーザー一覧とともに取得する

use App\Team;
$all_teams = Team::with('users')->get();
dd( $all_teams->toArray() );

(実は with() メソッドを指定しなくても、Bladeテンプレートの中で リレーションオブジェクトを呼び出そうとすると自動取得されます。 ただ、自動取得が100回あると100回SQLクエリが投げられるので非効率です。with()メソッドだと in句 で一撃で取得してきます。)

・2. ログイン中のユーザーデータを(チームIDに加えて)チーム名とともに取得する

方法1: with() を使ったやり方

$user = User::with('team')->find( Auth::user()->id );
dd( $user->toArray() );

実行されるSQL文

select * from `users`";
select * from `teams` where `teams`.`id` in (?); // ? は プレースホルダ

 

方法2: LEFT JOIN を使ったやり方

$user = User::leftJoin('teams','teams.id','=','users.team_id')->find( Auth::user()->id );
Mydump::dump( $user->toArray() );

実行されるSQL文

select * from `users` left join `teams` on `teams`.`id` = `users`.`team_id` where `users`.`id` = ? limit 1

・3. 全ユーザー一覧を(チームIDに加えて)チーム名とともに取得する

$all_users = DB::table('users')
                ->leftJoin('teams','teams.id','=','users.team_id')
                ->get();
Mydump::dump( $all_users );

その他リレーションの参考: https://laravel-news.com/eloquent-tips-tricks

No.1304
01/30 15:32

edit

DB
Eloquent
モデル

LaravelでDBのシーダーを使ってデフォルトのデータをセットする

マイグレーションをやり直した時に、DBデータも自動で登録できるようにシーダーを使ってデータを作成しておくと マイグレーションのやり直しがとても楽にできます。

● DBのシーダーファイルを作る

テーブル名「clients」の場合「ClientsSeeder」や「ClientsTableSeeder」といった名前をつけて作成します。

(例: clients テーブル用のシーダーファイルを作成する )

php artisan make:seeder ClientsSeeder

database/seeds/ClientsSeeder.php が作成されます )

● 作成されたDBのシーダーファイルを編集する

database/seeds/ClientsSeeder.php

<?php
use Illuminate\Database\Seeder;
class ClientsSeeder extends Seeder
{
    /**
     * Run the database seeds.
     * @return void
     */
    public function run()
    {
        DB::table("clients")->insert([
			'id'           => 1 ,
			'client_name'  => 'テスト商事' ,
			'tel_name'     => '012-345-6789' ,
			'fax_name'     => '012-345-6780',
        ]);
        DB::table("clients")->insert([
			'id'           => 2 ,
			'client_name'  => 'てすとの商事' ,
			'tel_name'     => '112-345-6789' ,
			'fax_name'     => '112-345-6780',
        ]);
    }
}

● 呼び出し元ファイル(DatabaseSeeder.php)に記述

database/seeds/DatabaseSeeder.php に作成したシーダーファイルを記述して呼び出します

<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     * @return void
     */
    public function run()
    {
        $this->call([
          ClientsSeeder::class ,		// 追加
        ]);
    }
}

● シーダーの実行

php artisan db:seed

● Class XXXXXSeeder does not exist エラーが出る場合

composer の autoload を再読み込みしてから実行するとうまく実行できます。

composer dump-autoload
php artisan db:seed
No.1303
10/23 14:43

edit

DB

Laravelのパンくずリストlaravel-breadcrumbsを使用してページタイトルとパンくずを表示させる

● laravel-breadcrumbsのインストール

composer require diglactic/laravel-breadcrumbs

古いLaravelにインストールする場合はこちら

composer require davejamesmiller/laravel-breadcrumbs:5.x

● パンくずリストのデータを定義する

routes/breadcrumbs.php を以下の内容で作成します

<?php

// Home
Breadcrumbs::for('home', function ($trail) {
    $trail->push('Home', route('home'));
});

// Home > About
Breadcrumbs::for('about', function ($trail) {
    $trail->parent('home');
    $trail->push('About', route('about'));
});

● 設定ファイルを生成する

次の設定ファイルを生成コマンドを実行します

php artisan vendor:publish --provider="DaveJamesMiller\Breadcrumbs\BreadcrumbsServiceProvider"

● パンくずリストを表示する

ビューファイル(XXXXX.blade.php)に以下のように記述します

{{ Breadcrumbs::render('home') }}

もしくは、Route::currentRouteName() で現在のルーティングが取得できるので

{{ Breadcrumbs::render(Route::currentRouteName()) }}

としておくとビューファイルは触らなくてもいいので楽です。(レイアウトファイルにパンくずを設置してある場合はこちらがおすすめです。)

● パンくずリストをカスタマイズする

自作のパンくずリストテンプレートを使用するには config/breadcrumbs.php を編集します

25行目を次のように書き換えます

###    'view' => 'breadcrumbs::bootstrap4',
    'view' => 'my_breadcrumbs',

すると、テンプレートファイルviews/breadcrumbs.blade.phpを見に行くようになります。

テンプレートファイルを用意する

views/breadcrumbs.blade.php

(以下は例です。適宜書き換えてください。)

@if (count($breadcrumbs))
    <ol class="navbar__breadcrumb breadcrumb d-none d-sm-flex">
        @foreach ($breadcrumbs as $breadcrumb)
            @if ($breadcrumb->url && !$loop->last)
                <li class="breadcrumb-item"><a href="{{ $breadcrumb->url }}">{{ $breadcrumb->title }}</a></li>
            @else
                <li class="breadcrumb-item active">{{ $breadcrumb->title }}</li>
            @endif
        @endforeach
    </ol>
@endif

以上です。 とても簡単にパンくずリストが出来るのでLaravelを使っている場合は必須ともいえるでしょう。

● パンくずの一番後ろのテキストをページタイトルとして表示する

これも便利です。blade.php ファイルにページタイトルを記述しなくても自動的にセットされます。

    <title>{{ ($breadcrumb = Breadcrumbs::current()) ? $breadcrumb->title : 'No Name' }}</title>
No.1302
08/02 15:52

edit

Laravel

Laravelのキャッシュクリア

● Laravelのファイル更新が反映されない?時はキャッシュクリアを行いましょう

Laravelで「ファイルを更新したのに更新されてない?」
という時はキャッシュが残っている可能性が大です。
こちらのコマンドでキャッシュを削除しましょう。

php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
composer dump-autoload

・1行で書いてコピペして実行!

php artisan cache:clear; php artisan config:clear; php artisan route:clear; php artisan view:clear; composer dump-autoload

https://qiita.com/Ping/items/10ada8d069e13d729701

No.1283
03/25 09:37

edit

Laravel

Laravel で自作パッケージを簡単に作成する

PackageCreator がとても便利です

● PackageCreator

https://github.com/SUKOHI/PackageCreator

・インストール

composer require sukohi/package-creator

インストールすると「make:package コマンド」が使用できるようになります

・パッケージの作成( php artisan make:package コマンド )

php artisan  make:package   (ベンダー名)   (パッケージ名)   (保存するフォルダ名(アプリケーションからの相対パス) )  

例 )

php artisan  make:package  myname  my-package  test_packages 

自動生成されるファイル

./test_packages/Myname/MyPackage/src/Facades/MyPackage.php
./test_packages/Myname/MyPackage/src/MyPackage.php
./test_packages/Myname/MyPackage/src/MyPackageServiceProvider.php
./test_packages/Myname/MyPackage/composer.json

● パッケージ作成例

例として mydump クラスを作成してみます。

1. コマンドの実行

php artisan  make:package  myname  mydump  mypackages 

2. クラスの実装

APP/mypackages/MyName/Mydump/src/Mydump.php を編集して以下の内容にします

<?php 
namespace MyName\Mydump;
class Mydump {
    public static function dump( $mix )
    {
  		print "\n".'<pre style="text-align:left;display:block;padding:9.5px;margin:10px;font-size:13px;line-height:1.4;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px;">'."\n";
  		print '<span style="color:#999;">TYPE: ' . gettype($mix) . "</span>\n";
  		print_r($mix);
  		print "\n</pre>\n\n";
    }

    public static function dump2( $mix )
    {
      print "\n"."<!--"."\n";
  		print "\n".'<pre style="text-align:left;display:block;padding:9.5px;margin:10px;font-size:13px;line-height:1.4;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px;">'."\n";
  		print '<span style="color:#999;">TYPE: ' . gettype($mix) . "</span>\n";
  		print_r($mix);
  		print "\n</pre>\n\n";
      print "\n"."-->"."\n";
    }
}

3. configファイル と composer.json を編集

APP/config/app.php の「providers」と「aliases」に以下を追加します

    'providers' => [
        ..................... 
        MyName\Mydump\MydumpServiceProvider::class,
    'aliases' => [
        ..................... 
        'Mydump' => MyName\Mydump\Facades\Mydump::class,

composer.json を以下のように修正します

        "psr-4": {
            "App\\": "app/" 
        }

        ↓

        "psr-4": {
            "App\\": "app/" ,
            "MyName\\Mydump\\": "mypackages/MyName/Mydump/src"
        }

4. コマンドラインから次のコマンドを実行

composer dumpautoload -o
php artisan config:cache

5. Laravelのコントローラーから使用する

use Mydump;

$mix = [
    'aaa' => 'bbb' ,
    'ccc' => 'ddd' ,
];
Mydump::dump( $mix );

5. 結果

添付ファイル1
mydump.png ( 22.5 KBytes ) ダウンロード
No.1282
09/25 11:45

edit

添付ファイル

Laravel
composer

Laravelのコントローラーからビューへの値の渡し方

● with

複数データを渡すときは連想配列で渡します

$data = [
 "data1" => $data1 ,  
 "data2" => $data2 ,  
];
return view('mytemplate')->with($data);

またはwithメソッドを2回呼んでもOKです。

return view('mytemplate')->with('data1',$data1)->with('data2',$data2);

● compact

すっきりと書きたい場合は compact を使って以下のように書くことができます

return view('mytemplate',compact('data1', 'data2'));

● ページネーションにパラメーターを渡す

コントローラーから次のように渡します。

$pagination_params = [
    "aaa" => 1 ,
    "bbb" => 2 ,
];
return view('mytemplate', compact('pagination_params'));

ビューでは次のようにして呼び出します
mytemplate.blade.php

{{ $model->appends($pagination_params)->links() }}

次のようなリンクがページネーションにつきます
( 3ページ目へのリンクにつく文字列 )

?&aaa=1&bbb=2&page=3

● ビューで渡された値の展開

foreach でオブジェクトを回す

<ul>
    @foreach($data_obj as $v)
    <li><a href="#">{{$v->data_name}}</a></li>
    @endforeach
</ul>

foreach で配列を回す

<ul>
    @foreach($data_loop as $v)
    <li><a href="#">{{$v['data_name']}}</a></li>
    @endforeach
</ul>

● ビューからコントローラーへのリンクを表示する

<a href="{{ route('tweets.index') }}">Tweets</a>

またはこのようにも書けます

<a href="{{ url("/tweets") }}">Tweets</a>
No.1279
10/09 16:17

edit

Laravel のページャー(ページネーション)の表示とカスタマイズ

● Laravel paginate の実行

Eloquent モデルクエリビルダに対して paginate() メソッドを 実行することができます。

Eloquent モデルに対してpaginate()を実行する

$users = User::where('votes', '>', 100)->paginate(15);
return view('user.index', ['users' => $users]);

クエリビルダーに対してpaginate()を実行する

$users = DB::table('users')->paginate(15);
return view('user.index', ['users' => $users]);

● ページ番号などのパラメーターを指定してLaravel paginate を実行する

paginate() メソッドのパラメーターは次のようになっています。

public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null);

例:

$articles = Articles::paginate(5, ['*'], 'page', $pageNumber);

● 結果セットCollectionに対して paginate() する

Laravelの 結果セットに対してforPage() メソッドを使用してpaginationを作成することもできますが、それよりも次の方法をお勧めします。 複数のテーブルから検索して結合した後にページネーションを付けたいという場合にとても有効です。
メソッド名が変わるので注意してください。

A. 結果セットCollectionに対して paginate() する(オススメ!)

app/Providers/AppServiceProvider.php の boot()メソッド内に以下のコードを追加します

    public function boot()
    {

        /**
         * Collectionに対して paginate できるようにするマクロ
         *
         * @param int $perPage
         * @param int $total
         * @param int $page
         * @param string $pageName
         * @return array
         */
        Collection::macro('paginate', function($perPage, $total = null, $page = null, $pageName = 'page') {
            $page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
            return new LengthAwarePaginator(
                $this->forPage($page, $perPage),
                $total ?: $this->count(),
                $perPage,
                $page,
                [
                    'path' => LengthAwarePaginator::resolveCurrentPath(),
                    'pageName' => $pageName,
                ]
            );
        });

    }

これで普通に paginate() メソッドを使用することができます。

B. 結果セットCollectionに対して paginate() する(あまりオススメしません)

// forPageを使用する方法(あまりお勧めしません。 total() などのメソッドはこの方法では使用できません)
// 1つ目の引数 : ページ番号  
// 2つ目の引数 : 1ページあたりのアイテム数  
$collection = collect([1,2,3,4,5,6,7,8,9,0]);
$items = $collection->forPage($_GET['page'], 5);

● LengthAwarePaginator から Collection を取り出す

$articles_collection = $articles_paginator->getCollection();

● Laravelのページャー(ページネーション / pagination)の表示

Bladeテンプレート内で次のように記述します

{!! $tweets->render() !!}

links() というエイリアスもあります。(機能は同じ)

{{ $model->links() }}

● 検索結果ページなどパラメーターを引き継ぐ

検索結果ページでは「検索文字列」「1ページあたりの表示数」などのパラメーターを引き継ぐ必要があります。
その時は次のように appends() メソッドに連想配列をセットしてを呼びます。

bladeファイル

{!! $datas->render() !!}

  ↓

{!! $datas->appends(['q' => $q['q']])->render() !!}

● その他便利なパラメーター(次のページがあるかどうか? など)

現在のページに表示されている件数: {{ $data->count() }}
現在のページ数: {{ $data->currentPage() }}
現在のページの最初の要素: {{ $data->firstItem() }}
次のページがあるかどうか: {{ $data->hasMorePages() }}
現在のページの最後の要素: {{ $data->lastItem() }}
最後のページ数: {{ $data->lastPage() }}
次のページのURL: {{ $data->nextPageUrl() }}
1ページに表示する件数: {{ $data->perPage() }}
前のページのURL: {{ $data->previousPageUrl() }}
合計件数: {{ $data->total() }}
指定ページのURL: {{ $data->url(4) }}

引用: http://bit.ly/2w8GKa8

● ページャー(ページネーション)のカスタマイズ

ページネーションをカスタマイズして「First Page」「Last Page」のリンクを追加してみましょう。

ページネーションの生成方法を次のように links() メソッドに書き換えます

{!! $tweets->render() !!}
 ↓
{!! $tweets->links('pagination.default') !!}

resources/views/pagination/default.blade.php を以下の内容で新規作成します

@if ($paginator->lastPage() > 1)
<ul class="pagination">
    <li class="page-item {{ ($paginator->currentPage() == 1) ? ' disabled' : '' }}">
        <a class="page-link" href="{{ $paginator->url(1) }}">First Page</a>
     </li>
    <li class="page-item {{ ($paginator->currentPage() == 1) ? ' disabled' : '' }}">
        <a class="page-link" href="{{ $paginator->url(1) }}">
            <span aria-hidden="true">«</span>
            {{-- Previous --}}
        </a>
    </li>
    @for ($i = 1; $i <= $paginator->lastPage(); $i++)
        <li class="page-item {{ ($paginator->currentPage() == $i) ? ' active' : '' }}">
            <a class="page-link" href="{{ $paginator->url($i) }}">{{ $i }}</a>
        </li>
    @endfor
    <li class="page-item {{ ($paginator->currentPage() == $paginator->lastPage()) ? ' disabled' : '' }}">
        <a class="page-link" href="{{ $paginator->url($paginator->currentPage()+1) }}" >
            <span aria-hidden="true">»</span>
            {{-- Next --}}
        </a>
    </li>
    <li class="page-item {{ ($paginator->currentPage() == $paginator->lastPage()) ? ' disabled' : '' }}">
        <a class="page-link" href="{{ $paginator->url($paginator->lastPage()) }}">Last Page</a>
    </li>
</ul>
@endif

● カスタマイズした結果


         ↓

● 1ページしかない時にもページネーションを表示させる

Laravel の ページャーはデフォルトでは 1ページしかない時は表示されません
それではデザイン的にさみしい時は次のようにして表示させます。

{{-- paginate --}}
{!! $data_loop->links('pagination.front') !!}
{{-- / paginate --}}

  ↓ このように変更します

{{-- paginate --}}
@if ( $data_loop->hasPages() )
    {!! $data_loop->links('pagination.front') !!}
@else
    <div class="g_pager">
        <a class="prev"></a>
        <a class="current" href="">1</a>
        <a class="next"></a>
    </div>
@endif
{{-- / paginate --}}
添付ファイル1
添付ファイル2
No.1278
03/29 12:38

edit

添付ファイル

Laravel で DB構造取得と最後に実行したSQL文を取得する

● Laravel の Schema クラス

use Illuminate\Support\Facades\Schema;

category テーブルのカラムを配列で取得する

$columns = Schema::getColumnListing('category');
dump($columns);

● 最後に実行したSQL文を取得する

・方法その1

$data = $model->orderBy('id', 'desc')->paginate( 10 );
dump( $model->toSql() );

・方法その2

\DB::enableQueryLog();
$data = $model->orderBy('id', 'desc')->paginate( 10 );
dump(\DB::getQueryLog());

・方法その3

$query = \DB::table('users')
            ->where('status', '<>', 1);
var_dump($query->toSql(), $query->getBindings());
No.1277
03/13 15:51

edit

Laravel