20190507のlaravelに関する記事は11件です。

Laravel セッション切れ「The page has expired due to inactivity」の回避策

app/Exceptions/Handler.php

public function render($request, Exception $e)
    {

        if ($e instanceof \Illuminate\Session\TokenMismatchException)
        {
            return redirect()
                    ->back()
                    ->withInput($request->except('password'))
                    ->with([
                        'message' => 'Validation Token was expired. Please try again',
                        'message-type' => 'danger']);
        }   

        return parent::render($request, $e);
    }

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

mysql laravel で ユーザー別にランキング順位を求める

例えば 3位/1000人中
とかやりたい。

ただ、これは関連テーブルをSUMして・・・とやるのは無理みたい。
ということで、裏でパッチ処理させておいて、メインテーブルに格納。
それをソートする。
socials テーブル
id sum_social_day7pageviews

があって
指定ユーザーIDが
sum_social_day7pageviews 順に何位か?を求める。

参考
https://qiita.com/hmuronaka/items/1afc132ddf400363efc2

DB::statement(DB::raw('set @c:=0'));//こうやって set を分けておくのが味噌
$res = DB::select("SELECT tmp.id, tmp.sum_social_day7pageviews, tmp.rank rank FROM (SELECT id, sum_social_day7pageviews, @c:=@c+1 rank FROM uranaibako.socials ORDER BY sum_social_day7pageviews DESC) tmp WHERE id=296640879");


これで結果が

Array
(
    [0] => stdClass Object
        (
            [id] => 296640879
            [sum_social_day7pageviews] => 97
            [rank] => 2
        )



やったね!

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

Laravelでよく使うバリデーションルール

Laravelでよく使うバリデーションルールをメモしました。

めっちゃ使う

  • required
    必須チェック

  • numeric
    数値であるか

  • integer
    整数であるか

  • max:値
    文字列の場合は、文字数になるし、数値の場合は数値になるという賢いやつ!!

  • min:値
    文字列の場合は、文字数になるし、数値の場合は数値になるという賢いやつ!!

  • between:min,max

    フィールドが指定された最小値と最大値の間のサイズであることをバリデートします。

めっちゃ使うとかいいながら、存在を忘れていた。

  • email
    メールアドレスの形式であるか

  • confirmed
    パスワード(確認用)、メールアドレス(確認用)でよく使う

    フィールドがそのフィールド名+_confirmationフィールドと同じ値であることをバリデートします。

ということなので、htmlのname属性に注意!

日付関連

  • date
    日付の形式であるか

  • after:日付
    対象日より後の日付であるか

    日付はPHPのstrtotime関数で処理されます。
    strtotimeにより評価される日付文字列を渡す代わりに、その日付と比較する他のフィールドを指定することもできます。

  • after_or_equal:日付
    対象日以降の日付であるか

  • before:日付
    対象日より前の日付であるか

  • before_or_equal:日付
    対象日以前の日付であるか

  • date_equals:日付

    バリデーションされる値が、指定した日付と同じことをバリデートします。

いまさら気づいた

今後、使おう。

  • nullable
    nullも許可するよ。
    よくある場面は、メールアドレスが任意入力だけど、確認用メールアドレスと一致しているかチェックするときとか

  • alpha

    フィールドが全部アルファベット文字であることをバリデートします。

  • alpha_dash

    フィールドが全部アルファベット文字と数字、ダッシュ(-)、下線(_)であることをバリデートします。

  • alpha_num

    フィールドが全部アルファベット文字と数字であることをバリデートします。

メールアドレスのバリデーションサンプル

confirmedemailnullableを使ってみる。

  • メールアドレスは任意入力
  • メールアドレスが入力されている場合は、確認用メールアドレスと一致している必要がある
public function rules()
{
    return [
        'email' => 'confirmed|email|nullable',
    ];
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで前回の入力値を表示する方法

Laravelで前回の入力値を表示する方法を紹介します。

サンプルblade

この状態だと、入力エラーになっても、前回の入力値は表示されません。

<div class="wrapper">
    @if(count($errors) > 0)
        <ul class="bg-danger">
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    @endif

    <form action="{{ route('sample.store') }}" method="post">
        @csrf
        <div class="form-group">
            <label>名称<span class="required">※必須</span></label>
            <input type="text" name="name" class="form-control">
        </div>

        <button type="submit" class="btn btn-success">登録</button>
    </form>
</div>

oldメソッド

ここで活躍するのが、oldメソッドです!

<input type="text" name="name" class="form-control" value="{{ old('name') }}">

oldの引数に、対象のhtmlのname属性を指定すると、前回の入力値を表示してくれます。
でも編集のときとか、すでに保存されている値がある場合は、どうしたらいいんや?

実はoldメソッドは第二引数にデフォルトの値を受け取ることができます。

<input type="text" name="name" class="form-control" value="{{ old('name', 'デフォルトの値') }}">

では、実践向きに記述しますね。

新規登録画面と編集画面は同じbladeファイルを使い回すと思いますので、それに対応したいと思います。

まずは、bladeのinputのvalueを以下のようにします。

<input type="text" name="name" class="form-control" value="{{ old('name', isset($defaultName) ? $defaultName : '') }}">

Controllerのcreateメソッド

public function create()
{
    return view('sample');
}

createメソッドのrouteにアクセスすると、入力欄は空欄です。

Controllerのeditメソッド

public function edit($id)
{
    return view('sample')->with([
        'defaultName' => 'デフォルトの名称',
    ]);
}

editメソッドのrouteにアクセスすると、入力欄には「デフォルトの名称」が表示されます。

ちょいと解説

value="{{ old('name', isset($defaultName) ? $defaultName : '') }}"

第一引数には、htmlのname属性を入れます。こちらは、入力エラーがあった場合に表示されます。

第二引数は、初期表示の場合に表示されます。
新規登録時は$defaultNameの変数が設定されていないので、三項演算子の後半に行って、空になります。
編集時は、$defaultNameの変数が設定されているので、参考演算子の前半に行って、その値が表示されます。

まとめ

素phpユーザーから、Laravelを始めて、最初につまづいたのがここだった。。
そのうちチェックボックス・ラジオボタンのoldの値の表示の仕方もまとめたい。

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

laravelにVue.jsを入れる前の、5分間の事前学習

◆コンパイル

ソースコードを機械語に変換する

◆ビルド

バグがないことを確かめて(静的解析)、問題がなければ実行できる形のファイルに組み立てること
(プリプロセッサ:定数置き換え・コメント削除などコンパイル前の準備) このあとコンパイル。
コンパイルはビルドの中に含まれる

◆sass

cssの機能を拡張した言語(効率よくかける)
scssも同様
sassはインデントで分けて、scssはcssと書き方は同じだがこちらもインデントで分ける

◆node.js参考記事

サーバーサイドで使えるJavaScriptの代表的なものの一つ
クライアントもサーバーサイドもJavaScriptで書ければ楽ということが利用される最大の理由

◆npm参考記事

node.jsのパッケージ管理ツール
node.jsのパッケージは用意された便利機能の集まり

◆webpack

複数javaScriptのファイルがあり、またそのファイルごとに依存関係がある(なくてももちろんいい)場合でも、それを解決し、
一つのファイルにまとめてくれる機能(ビルド)
laravelのwebpack.mix.jsファイルを見てみると、

let mix = require('laravel-mix');

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

resources/assets/js/app.jsがビルドしたいファイル
public/jsが出力先
ほかにも指定したいものがあれば、どんどん指定できる。

◆npm run dev

laravelにvue.jsを導入するときに使うと、思うのですが、これで先ほどwebpack.mix.jsで指定した内容をビルドして、出力してくれる。

◆laravel mix関数参考記事

バージョン付けしたファイルの最新の名前を自動的に解決

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

npm run devで出力されているため、asset('js/app.js')でも参照できる。バージョン管理がない場合はassetでいいと思います
参考記事

◆導入

参考記事をもとに進めました。
laravel-vue.js-docker-comose

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

Laravelでのバリデーション(値チェック)の方法

Laravelでのバリデーション(値チェック)の方法を紹介します。

サンプルblade

<div class="wrapper">
    @if(count($errors) > 0)
        <ul class="bg-danger">
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    @endif

    <form action="{{ route('sample.store') }}" method="post">
        @csrf
        <div class="form-group">
            <label>名称<span class="required">※必須</span></label>
            <input type="text" name="name" class="form-control">
        </div>

        <div class="form-group">
            <label>チェックボックス<span class="required">※必須</span></label>
            <div class="checkbox">
                <label class="checkbox-inline">
                    <input type="checkbox" name="checkbox" value="1">A
                </label>
                <label class="checkbox-inline">
                    <input type="checkbox" name="checkbox" value="2">B
                </label>
                <label class="checkbox-inline">
                    <input type="checkbox" name="checkbox" value="3">C
                </label>
            </div>
        </div>

        <div class="form-group">
            <label>ラジオボタン<span class="required">※必須</span></label>
            <div class="radio">
                <label class="radio-inline">
                    <input type="radio" name="radio" value="1">A
                </label>
                <label class="radio-inline">
                    <input type="radio" name="radio" value="2">B
                </label>
                <label class="radio-inline">
                    <input type="radio" name="radio" value="3">C
                </label>
            </div>
        </div>

        <button type="submit" class="btn btn-success">登録</button>
    </form>
</div>

バリデーションルール作成

以下のコマンドを実行します

$ php artisan make:request ValidateSample

すると、以下のファイルが作成されます。

app/Http/Requests/ValidateSample.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ValidateSample extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

まず、authorizeメソッドのreturn falsereturn trueに修正します。
(本来は権限チェックが必要な場合は、ここでチェックすることもできます。でも他の箇所ですることが多いです。)

では、rulesメソッドの中を書いていきます。

public function rules()
{
    return [
        'name' => 'required|max:30',
        'checkbox' => 'required',
        'radio' => 'required',
    ];
}

連想配列のキーが、html側のnameと一致させるように書きます。
連想配列の値に適用したいルールを書きます。

ルールはこちらの「使用可能なバリデーションルール」を参照。
(使うものはだいたい限られますが)

続いて、作成したルールが適用されるようにControllerを修正します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests\ValidateSample; //★追加★

class SampleController extends Controller
{
    ...
public function store(ValidateSample $request) //★もともとRequestだった箇所をValidateSampleに置き換え★
{
    ...

では、画面から登録ボタンを押してみましょう。

スクリーンショット 2019-05-07 14.30.05.png

あ!エラーメッセージが英語だったぞ...

エラーメッセージを日本語化する場合は、こちらから日本語ファイルを持ってくると便利です。
(今回、関係あるのはvalidation.phpだけですが...)
配置する場所はresources/lang/jaの中です。

すると、こうなります。
スクリーンショット 2019-05-07 14.49.04.png

いや、ちょっと英語残ってるやんけ。

というわけで、先程作成したバリデーションファイルにattributesメソッドを追加します。

app/Http/Requests/ValidateSample.php
public function attributes()
{
    return [
        'name' => '名称',
        'checkbox' => 'チェックボックス',
        'radio' => 'ラジオボタン',
    ];
}

連想配列のキーにhtmlのname属性、値に日本語訳をつっこみます。で、登録!

スクリーンショット 2019-05-07 15.07.30.png

よし!日本語なった!
あー、でも、もうちょっと親切なメッセージにしたいぜ。というそこのアナタ!

messagesメソッドを追加しましょう。

app/Http/Requests/ValidateSample.php
public function messages()
{
    return [
        'checkbox.required' => 'チェックボックスは最低1つはチェックを入れてください。',
    ];
}

連想配列のキーに、htmlのname属性と、さらに.でつなげて、バリデーションの値を書きます。
連想配列の値はフルメッセージです。

すると、こうなる。
スクリーンショット 2019-05-07 14.58.26.png

おわりに

基本的にwebシステムなどを作成するときは、JavaScriptとphp両方でバリデーションすると思いますので、あまりphpのエラーメッセージは重要視されないかもしれませんが...

JavaScriptでやれば、エラーメッセージを対象の項目の下に出すのも楽ですからね。(私はVue.jsを使用しています)

マスター関連とかは、めんどくさくなってphpだけにしちゃったりしますので、そういうときに結構役立つかも。

そういえば、エラーになったときに、前回の入力値はどうやって表示するの?という疑問を持った方のための記事も作成する予定です。

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

Laravelのフラッシュメッセージ表示のメモ

Laravelのフラッシュメッセージ表示のメモです。
以下のように、保存後などにメッセージを表示したい場合

スクリーンショット 2019-05-07 14.01.42.png

blade側

メッセージを表示したい場所に、以下の通り記載

@if (Session::has('flash_message'))
    <p class="bg-success">{!! Session::get('flash_message') !!}</p>
@endif

@if (Session::has('error_message'))
    <p class="bg-danger">{!! Session::get('error_message') !!}</p>
@endif

Controller側

public function store(Request $request)
{
    //DBへの登録処理など...

    \Session::flash('flash_message', '保存しました。');
    return redirect(route(('sample.create')));
}

Session::flashの第一引数と、blade側の変数名が対応しています。
サンプルの場合は、flash_messageだけ表示しています。

DBの登録処理を失敗した場合などは、error_messageを使ってエラーを表示させます。

flash_message , error_messageは好きな名前を使えます。

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

Laravel のテストであるクラスのプロパティをテストから書き換えて初期化するには

この記事について

半分個人メモです。

ある特殊なテストを行いたくて、private なプロパティをテストメソッドから書き換えて実行する方法を探っていたところ、以下のやり方を思いついたので書き残しておきます。

はじめに

概要

あるクラスが config から読み取った値を private なプロパティにセットして初期化していて、その値を、あるテストメソッドでのみ、特殊な環境変数で上書きして実行したい、というケースに遭遇した際、リフレクションと無名クラスで対応できそうだったのでやってみました。

環境

  • PHP: 7.3.2
  • Laravel: 5.8.13
  • PHPUnit: 7.5.8

詳細

とあるクラス

<?php

namespace App\Services;

class SomeService
{
    private $config;

    public function __construct()
    {
        $this->config = config('services.some.config');
    }

    public function doSomething()
    {
        return $this->config;
    }
}

テストクラス

<?php

namespace Tests\Feature\Services;

use App\Services\SomeService;
use Tests\TestCase;

class SomeServiceTest extends TestCase
{
    public function testDoSomethingInGeneralEnvironment()
    {
        $service = app(SomeService::class);

        $this->assertEquals('hoge', $service->doSomething());
    }

    public function testDoSomethingInSpecialEnvironment()
    {
        $service = new class extends SomeService {
            public function __construct()
            {
                $class = new \ReflectionClass(SomeService::class);
                $property = $class->getProperty('config');
                $property->setAccessible(true);
                $property->setValue($this, env('SOME_SERVICE_CONFIG_OVERWRITTEN'));
            }
        };

        $this->assertEquals('fuga', $service->doSomething());
    }
}

config/services.php

    'some' => [
        'config' => env('SOME_SERVICE_CONFIG'),
    ],

.env

SOME_SERVICE_CONFIG=hoge
SOME_SERVICE_CONFIG_OVERWRITTEN=fuga

SOME_SERVICE_CONFIG_OVERWRITTEN は、.env.testing とか phpunit.xml とかに書いてもいいかもしれないです(テストでしか使わないやつなので)。

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

Laravel5のRequestクラス覚え書き

Webアプリケーションフレームワーク LaravelIlluminate\Http\Request の書き方メモ。リファレンス的なもの。

Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function store(Request $request)
    {
        $name = $request->input('name');
        //
    }
}
View
@extends('layouts.app')
@section('body')
<form method="POST" action="{{ route('XXXX') }}">
  @csrf
  <input type="text" name="name" value="">
  // 
</form>
@stop

入力の取得

  • 動的プロパティで入力を取得
    $name = $request->name;

  • 全入力を連想配列で取得
    $input = $request->all();

  • アップロードファイルを除いて、全入力を連想配列で取得
    $input = $request->input();

  • すべてのクエリストリングを連想配列で取得(上記のallやinputにもクエリストリングは含まれる)
    $input = $request->query();

  • アップロードファイルの取得
    $file = $request->file('csv');

  • "name" の入力を取得
    $name = $request->input('name');

  • デフォルト値を指定して取得
    $request->input('name', 'Tom');

  • "name" と "password" のみ取得
    $input = $request->only('name', 'password');

  • "password" 以外を取得
    $input = $request->except('password');

  • リクエストに値が存在するか
    $request->has('name')

  • すべて存在するか
    $request->has(['name', 'email'])

  • リクエストに値が存在し、かつ空でないか
    $request->filled('name')

フラッシュデータ(次のリクエストの間だけ利用できるデータ)の保存

  • すべてをフラッシュデータに保存
    $request->flash();

  • "name" と "email" のみフラッシュデータに保存
    $request->flashOnly(['name', 'email']);

  • "password" を除いてフラッシュデータに保存
    $request->flashExcept('password');

セッション

  • "key" をセッションから取得
    $request->session()->get('key');

  • デフォルト値を指定してセッションから取得
    $request->session()->get('key', 'default');

その他

  • リクエストURIを返す
    $uri = $request->path();

  • HTTPメソッド名(GET、POST等)を返す
    $method = $request->method();

  • POSTメソッドか
    $request->isMethod('POST')

  • リクエストURIがパターンに合致するか
    $request->is('admin/*')

  • User-Agentを返す
    $request->header('User-Agent');

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

Laravel5.8で簡単画像アップロード機能の実装

Laravelを使い初めて、画像アップロード公開がものすごく簡単に実装できて感動したので、投稿します。

1、画像を保存。

(1) 下準備

/config/filesystem.phpの設定を変更。

'default' => env('FILESYSTEM_DRIVER', 'local'),

localのままだと不都合があるため(これから説明)、.envファイルに下記を追記しpublicに設定変更してあげる。

/.env

FILESYSTEM_DRIVER=public

(2) 画像をアップロード

$image = $request->image->store('posts');

この1行で画像自体のアップロードは完了。
後は必要に応じて返り値の画像パスをDBに保存するだけ。

formから送られてきた画像データに対し、storeメソッドを呼ぶと、storage/app/public配下にデータが保存される。例のようにstoreメソッドでディレクトリを指定することもできる。今回の場合は、storage/app/public/posts配下に保存される。

※localのままだと、storage/app直下に保存されるので、これから説明するシンボリックリンクがはれない。

2、画像を表示

(1) viewで表示できるように、laravelの公開用publicフォルダにシンボリックリンクをはる。

php artisan storage:link;

上記コマンドをターミナルで実行すると、
public配下にstorageリンクが作成される。

/public/storage/画像パス

(2) viewで表示

<img src="{ asset('storage/画像のパス') }}">

で保存した画像をviewで表示できる。

※asset()を使うとフルパスになる。

3、アップロードした画像の削除

DBから画像のパスを削除した際に、Storageフォルダにアップロードした画像を同時に削除する方法。

Storageファサードのdeleteメソッドで保存していた画像を削除できる。

use Illuminate\Support\Facades\Storage;
Storage::delete('画像パス');

データ削除した際に、上記メソッドを行えば、画像データも同時に削除できる。

以上、で簡単に画像のCRUDが実装できます。

参考:(公式ドキュメント)
https://readouble.com/laravel/5.8/ja/filesystem.html

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

LaravelでLeague/Csvを使ってアップロードされたcsvを読み込みDBに保存する

環境

Laravel 5.6
League/Csv 9.2

前提

  • Qiita初投稿です、お手柔らかにお願いします。
  • 今回はバリデーションについては実装、言及しません。

導入

League/Csvをインストールする。
composer dump-autoloadは多分必要。

composer require league/csv
composer dump-autoload

今回ぶち込みたいCSV

company_name name email
hoge株式会社 佐藤 exapmle1@example.com
foo株式会社 山田 exapmle2@example.com

実装

UserController.php
<?php

namespace App\Http\Controllers;

use App\Eloquent\User;
use Illuminate\Http\Request;
use League\Csv\Reader;
use League\Csv\Statement;

class UserController extends Controller
{

    // 省略

    public function importCSV(Request $request, Statement $stmt, User $user)
    {
        $file_path = $request->file('file')->getPathname();
        // ReaderはDIできないらしい。
        $csv = Reader::createFromPath($file_path, 'r')->setHeaderOffset(0);
        $records = $stmt->process($csv);
        $data = [];

        // 後ほど解説。
        foreach ($records as $record) {
            $record['created_at'] = now();
            $record['updated_at'] = now();
            $data[] = $record;
        }
        $user->insert($data);

        return redirect()->route('user.index');
    }
}

解説

ここでリクエストされたfileの一時保管パス?的なのを取得する。

$file_path = $request->file('file')->getPathname();

そしてここ、

foreach ($records as $record) {
            $record['created_at'] = now();
            $record['updated_at'] = now();
            $data[] = $record;
        }
        $user->insert($data);

$recordsには

ResultSet {#375 ▼
  #records: LimitIterator {#372 ▶}
  #header: array:4 [▶]
}

というオブジェクトが入っているのでforeachで取得していくとなんと、各recordには

[ "company_name" => "hoge株式会社"
  "name" => "佐藤",
  "email" => "example1@example.com"
]

などの配列が入っているというわけ。

Laravelだとinsertの引数に各レコードを値にもつ多次元配列を渡すことで複数レコードまとめて挿入できる。
しかし、insertは「created_at」「updated_at」が更新されないので、foreachで回しながら入れている。

おまけ

今回のroute

web.php
Route::post('/users/csv', "UserController@importCSV")->name('user.importCSV');

今回のview

users.blade.php
  {{ Form::open(['url' => route('user.importCSV'), 'method' => 'POST', 'class' => '', 'files' => true]) }}

  <div class='form-group'>
    <input type="file" name="file" value="">
  </div>

  <button type="submit">csv読み込み</button>

  {{ Form::close() }}

応用編

大切なことはすべて公式ドキュメントが教えてくれた。
9.0のドキュメント↓
https://github.com/thephpleague/csv/blob/master/docs/9.0/index.md

<?php
use League\Csv\Reader;
use League\Csv\Statement;

$csv = Reader::createFromPath('/path/to/your/csv/file.csv', 'r');

// headerは何行目か。
$csv->setHeaderOffset(0);

$header = $csv->getHeader();
//return [0 => "企業"
//  1 => "名前"
//  2 => "メールアドレス"]

// 10行飛ばして25行とる
$stmt = (new Statement())
    ->offset(10)
    ->limit(25);
$stmt->process($csv);

まとめ

2019年でPHPでcsvをいじりたいってなると、goodbyか、このLeague/Csvの二強な感じがしていて、今回書きやすそうだしGitHubのスターもダントツでLeague/Csvの方が高かったのでこちらを採用しました。
今回は使わなかったのですが、goodbyは省メモリを売りにしているそうなのでメモリを気にする量のcsvを裁くときは視野に入れたいですね。
結局、Laravelが好きです。

引用

https://github.com/thephpleague/csv/blob/master/docs/9.0/index.md

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