20201124のlaravelに関する記事は12件です。

LaravelにVurtifyを追加したらChromeでDevTools failed to load SourceMap

LaravelにVurtifyを追加してChromeで表示したら下記のようなエラーが。

vutifyのmapが見つからない.png
.mapファイルが見つからないっていうことみたいです。

npm install vuetify

でインストールした時に、
node_modules\vuetify\dist\vuetify.js.map
というファイルが出来ているので、このファイルを
/public/js
の下に配置してあげて解消。

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

GitHub Actionsにphp-security-auditを組み込んで、プルリク時にレビューコメントとして出力してみた

こんにちは!

GitHubのプルリク時に該当のphpコードに含まれる脆弱性を検知し、コメントとして出力してほしいという要望がありました。

なので、GitHub Actionsにphpcs-security-auditとreviewdogを組み込むことにしました。

phpcs-security-auditを導入している事例や導入方法が日本語のドキュメントで少なかったので、もし、導入してみたい方とかは参考にしてみてください。

phpcs-security-auditとは?

↓公式ドキュメントになります。
https://github.com/FloeDesignTechnologies/phpcs-security-audit

phpcs-security-audit is a set of PHP_CodeSniffer rules that finds vulnerabilities and weaknesses related to security in PHP code.

phpのコードに含まれている脆弱性を発見するためのphpcsのルールになります。

前提条件

Requires PHP CodeSniffer version 3.1.0 or higher with PHP 5.4 or higher.

  • PHP CodeSnifferのバージョンが3.1.0以上がインストールされていること
  • PHP のバージョンが5.3以上がインストールされていること

導入方法

ローカルで実行する方法

composer require --dev pheromone/phpcs-security-audit

まず、開発環境にphpcs-security-auditのルールをパッケージを追加します。

cd vendor/pheromone/phpcs-security-audit/

次はvendor/pheromone/phpcs-security-audit/配下に移動します。

../../squizlabs/php_codesniffer/bin/phpcs --standard=example_base_ruleset.xml tests.php

移動したら、../../squizlabs/php_codesniffer/bin/phpcsでphpcsコマンドを実行し、--standard=example_base_ruleset.xmlでxmlルールセットを設定します。test.phpというphpの例ファイルがあるので、それを実行することで、どんな表示が出力されるかを確認することができます。

example_base_ruleset.xml
<?xml version="1.0"?>
<ruleset name="Drupal7">
 <description>Rules for standard PHP projects</description>

<!-- Code Reviews Rules -->
<!--
 <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/>
 <rule ref="PEAR"/>
-->

<!-- Security Code Reviews Rules -->

<!-- Global properties -->
<!-- Please note that not every sniff uses them and they can be overwritten by rule -->
<!-- Paranoya mode: Will generate more alerts but will miss less vulnerabilites. Good for assisting manual code review. -->
<config name="ParanoiaMode" value="1"/>

<!-- BadFunctions -->
<!-- PHP functions that can lead to security issues -->
<rule ref="Security.BadFunctions.Asserts"/>
<rule ref="Security.BadFunctions.Backticks"/>
<rule ref="Security.BadFunctions.CallbackFunctions"/>
<rule ref="Security.BadFunctions.CryptoFunctions"/>
<rule ref="Security.BadFunctions.EasyRFI"/>
<rule ref="Security.BadFunctions.EasyXSS">
        <properties>
                <!-- Comment out to follow global ParanoiaMode -->
                <property name="forceParanoia" value="1"/>
        </properties>
</rule>
<rule ref="Security.BadFunctions.ErrorHandling"/>
<rule ref="Security.BadFunctions.FilesystemFunctions"/>
<rule ref="Security.BadFunctions.FringeFunctions"/>
<rule ref="Security.BadFunctions.FunctionHandlingFunctions"/>
<rule ref="Security.BadFunctions.Mysqli"/>
<rule ref="Security.BadFunctions.NoEvals"/>
<rule ref="Security.BadFunctions.Phpinfos"/>
<rule ref="Security.BadFunctions.PregReplace"/>
<rule ref="Security.BadFunctions.SQLFunctions"/>
<rule ref="Security.BadFunctions.SystemExecFunctions"/>

<!-- CVE -->
<!-- Entries from CVE database from vendor PHP and bugs.php.net -->
<rule ref="Security.CVE.20132110"/>
<rule ref="Security.CVE.20134113"/>

<!-- Misc -->
<rule ref="Security.Misc.BadCorsHeader"/>
<rule ref="Security.Misc.IncludeMismatch"/>
</ruleset>

しかし、このままでは上手く実行することができません。
実行したら、下記エラーが出てしまいます。

ERROR: Referenced sniff "Security.BadFunctions.Asserts" does not exist

Run "phpcs --help" for usage information

Security.BadFunctions.Assertsが存在しませんといわれてしまします。そもそも、xmlルールセットに記述されているファイルでは、いきなりSecurity.BadFunctions.Assertsを見に行っているので、上手くいきません。

なので、上から21行目に<rule ref="Security">、下から2行目に</rule>を追記し、Securityルールで囲ってあげれば、ルールセットがうまく実行できるようになります。

example_base_ruleset.xml
<?xml version="1.0"?>
<ruleset name="Drupal7">
 <description>Rules for standard PHP projects</description>

<!-- Code Reviews Rules -->
<!--
 <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/>
 <rule ref="PEAR"/>
-->

<!-- Security Code Reviews Rules -->

<!-- Global properties -->
<!-- Please note that not every sniff uses them and they can be overwritten by rule -->
<!-- Paranoya mode: Will generate more alerts but will miss less vulnerabilites. Good for assisting manual code review. -->
<config name="ParanoiaMode" value="1"/>

<!-- BadFunctions -->
<!-- PHP functions that can lead to security issues -->
<!-- ↓のrule refを追記 -->
<rule ref="Security">
  <rule ref="Security.BadFunctions.Asserts"/>
  <rule ref="Security.BadFunctions.Backticks"/>
  <rule ref="Security.BadFunctions.CallbackFunctions"/>
  <rule ref="Security.BadFunctions.CryptoFunctions"/>
  <rule ref="Security.BadFunctions.EasyRFI"/>
  <rule ref="Security.BadFunctions.EasyXSS">
        <properties>
                <!-- Comment out to follow global ParanoiaMode -->
                <property name="forceParanoia" value="1"/>
        </properties>
  </rule>
  <rule ref="Security.BadFunctions.ErrorHandling"/>
  <rule ref="Security.BadFunctions.FilesystemFunctions"/>
  <rule ref="Security.BadFunctions.FringeFunctions"/>
  <rule ref="Security.BadFunctions.FunctionHandlingFunctions"/>
  <rule ref="Security.BadFunctions.Mysqli"/>
  <rule ref="Security.BadFunctions.NoEvals"/>
  <rule ref="Security.BadFunctions.Phpinfos"/>
  <rule ref="Security.BadFunctions.PregReplace"/>
  <rule ref="Security.BadFunctions.SQLFunctions"/>
  <rule ref="Security.BadFunctions.SystemExecFunctions"/>

  <!-- CVE -->
  <!-- Entries from CVE database from vendor PHP and bugs.php.net -->
  <rule ref="Security.CVE.20132110"/>
  <rule ref="Security.CVE.20134113"/>

  <!-- Misc -->
  <rule ref="Security.Misc.BadCorsHeader"/>
  <rule ref="Security.Misc.IncludeMismatch"/>
<!-- ↓の/ruleを追記 -->
</rule>
</ruleset>

上手く実行できると、コードごとにどんな脆弱性が含んでいるかを下記のように表示してくれます。

FILE: /var/www/html/vendor/pheromone/phpcs-security-audit/tests.php
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FOUND 22 ERRORS AND 75 WARNINGS AFFECTING 60 LINES
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   6 | WARNING | Possible XSS detected with . on echo
   6 | WARNING | User input detetected with $_POST.
   6 | ERROR   | Easy XSS detected because of direct user input with $_POST on echo
   8 | WARNING | db_query() is deprecated except when doing a static query
   8 | WARNING | db_query() is deprecated except when doing a static query
   8 | WARNING | User input detetected with $_GET.
   8 | ERROR   | Potential SQL injection found in db_query()
   8 | ERROR   | Potential SQL injection found in db_query()
   9 | WARNING | Usage of preg_replace with /e modifier is not recommended.
  10 | WARNING | Usage of preg_replace with /e modifier is not recommended.
  10 | ERROR   | User input and /e modifier found in preg_replace, remote code execution possible.
  10 | WARNING | User input detetected with $_GET.
  11 | ERROR   | User input found in preg_replace, /e modifier could be used for malicious intent.
  11 | WARNING | User input detetected with $_GET.
  11 | WARNING | User input detetected with $_GET.
  11 | WARNING | User input detetected with $_GET.
  12 | WARNING | Dynamic usage of preg_replace, please check manually for /e modifier or user input.
  12 | WARNING | User input detetected with $_GET.
  13 | WARNING | Weird usage of preg_replace, please check manually for /e modifier.
  13 | WARNING | User input detetected with $_GET.
  17 | WARNING | Crypto function md5 used.
  18 | WARNING | phpinfo() function detected
  19 | WARNING | Function handling function create_function() detected with dynamic parameter
  20 | WARNING | Unusual function ftp_exec() detected
  21 | WARNING | Filesystem function fread() detected with dynamic parameter
  22 | WARNING | Function array_map() that supports callback detected
  23 | WARNING | System execution with backticks detected with dynamic parameter
  24 | ERROR   | System execution with backticks detected with dynamic parameter directly from user input
  24 | WARNING | User input detetected with $_GET.
  25 | ERROR   | No file extension has been found in a include/require function. This implies that some PHP code is not scanned by PHPCS.
  25 | WARNING | Possible RFI detected with $a on include
  26 | WARNING | Assert eval function assert() detected with dynamic parameter
  27 | ERROR   | Assert eval function assert() detected with dynamic parameter directly from user input
  27 | WARNING | User input detetected with $_GET.
  28 | WARNING | System program execution function exec() detected with dynamic parameter
  29 | ERROR   | System program execution function exec() detected with dynamic parameter directly from user input
  29 | WARNING | User input detetected with $_GET.
  30 | WARNING | SQL function mysql_query() detected with dynamic parameter
  31 | ERROR   | SQL function mysql_query() detected with dynamic parameter  directly from user input
  31 | WARNING | User input detetected with $_GET.
  35 | WARNING | Crypto function mcrypt_encrypt used.
  36 | ERROR   | Bad use of openssl_public_encrypt without OPENSSL_PKCS1_OAEP_PADDING
  39 | WARNING | CVE-2013-4113 ext/xml/xml.c in PHP before 5.3.27 does not properly consider parsing depth, which allows remote attackers to cause a denial of service (heap
     |         | memory corruption) or possibly have unspecified other impact via a crafted document that is processed by the xml_parse_into_struct function.
  40 | WARNING | CVE-2013-2110 Heap-based buffer overflow in the php_quot_print_encode function in ext/standard/quot_print.c in PHP before 5.3.26 and 5.4.x before 5.4.16
     |         | allows remote attackers to cause a denial of service (application crash) or possibly have unspecified other impact via a crafted argument to the
     |         | quoted_printable_encode function.
  43 | WARNING | Bad CORS header detected.
  44 | ERROR   | The file extension '.xyz' that is not specified by --extensions has been used in a include/require function. Please add it to the scan process.
  47 | WARNING | User input detetected with $_GET.
  48 | WARNING | Possible XSS detected with . on print
  48 | WARNING | User input detetected with $_GET.
  48 | ERROR   | Easy XSS detected because of direct user input with $_GET on print
  49 | WARNING | User input detetected with $_GET.
  49 | ERROR   | Easy XSS detected because of direct user input with $_GET on echo
  50 | WARNING | User input detetected with $_GET.
  50 | ERROR   | Easy XSS detected because of direct user input with $_GET on echo
  51 | WARNING | Possible XSS detected with "{$_GET['a']}" on echo
  52 | WARNING | Possible XSS detected with "${_GET['a']}" on print
  53 | WARNING | Possible XSS detected with a on echo
  53 | WARNING | User input detetected with $_GET.
  53 | ERROR   | Easy XSS detected because of direct user input with $_GET on echo
  54 | WARNING | Possible XSS detected with allo on echo
  54 | WARNING | User input detetected with $_GET.
  54 | ERROR   | Easy XSS detected because of direct user input with $_GET on echo
  55 | WARNING | Possible XSS detected with arg on echo
  55 | WARNING | User input detetected with arg.
  56 | WARNING | Possible XSS detected with . on die
  56 | WARNING | User input detetected with $_GET.
  56 | ERROR   | Easy XSS detected because of direct user input with $_GET on die
  57 | WARNING | Possible XSS detected with . on exit
  57 | WARNING | User input detetected with $_GET.
  57 | ERROR   | Easy XSS detected because of direct user input with $_GET on exit
  59 | WARNING | User input detetected with $_GET.
  59 | ERROR   | Easy XSS detected because of direct user input with $_GET on <?=
  63 | WARNING | User input detetected with arg.
  64 | WARNING | Allowing symlink() while open_basedir is used is actually a security risk. Disabled by default in Suhosin >= 0.9.6
  64 | WARNING | Filesystem function symlink() detected with dynamic parameter
  65 | WARNING | Filesystem function delete() detected with dynamic parameter
  69 | WARNING | Potential SQL injection with direct variable usage in join with param #3
  70 | WARNING | Potential SQL injection with direct variable usage in innerJoin with param #3
  71 | WARNING | Potential SQL injection with direct variable usage in leftJoin with param #3
  72 | WARNING | Potential SQL injection with direct variable usage in rightJoin with param #3
  73 | WARNING | Potential SQL injection with direct variable usage in addExpression with param #1
  74 | WARNING | Potential SQL injection with direct variable usage in groupBy with param #1
  76 | WARNING | Potential SQL injection with direct variable usage in orderBy with param #1
  76 | WARNING | Potential SQL injection with direct variable usage in orderBy with param #2
  81 | WARNING | User input detetected with $_GET.
  81 | ERROR   | SQL injection found in condition with param #3
  83 | WARNING | Potential SQL injection with direct variable usage in where with param #1
  84 | WARNING | Potential SQL injection with direct variable usage in havingCondition with param #3
  85 | WARNING | Potential SQL injection with direct variable usage in having with param #1
  88 | WARNING | Possible XSS detected with $count on echo
  91 | WARNING | Potential SQL injection with direct variable usage in expression with param #1
  91 | WARNING | Potential SQL injection with direct variable usage in expression with param #2
  96 | WARNING | Potential SQL injection with direct variable usage in fields with param #1 with array key value
  97 | WARNING | Potential SQL injection with direct variable usage in fields with param #1 with array key value
 106 | WARNING | Dynamic query with db_select on table node should be tagged for access restrictions
 108 | WARNING | User input detetected with $_GET.
 108 | ERROR   | SQL injection found in fields with param #1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Time: 37ms; Memory: 8MB

ここまでで、ローカルで実行できることが確認できましたので、あとはGitHub Actionsに組み込んでいくだけです!

GitHub Actionsで実行する方法

今回はsampleのlaravelプロジェクトを作成し、それをGitHubのリポジトリとして作成しておきます。
作成しましたら、一番上の階層にtest.phpexample_base_ruleset.xmlをコピーして、適当なブランチを作成し、pushしましょう。
※ローカルで実行した時、example_base_ruleset.xml<rule ref="Security">でしたが、<rule ref="vendor/pheromone/phpcs-security-audit/Security">に変更お願いします。
これをしないと、GitHub Actionsの実行時にSecurityのルールを読み込んでくれません。

cp vendor/pheromone/phpcs-security-audit/tests.php ./
cp vendor/pheromone/phpcs-security-audit/example_base_ruleset.xml ./
git checkout -b test
git add tests.php example_base_ruleset.xml
git commit -m "add two files"
git push origin test

これでプルリク作成の準備ができました。
次に、workflowsにtest.ymlを作成します。

test.yml
# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  lint:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2

    # phpcs-security-auditのパッケージを追加
    - name: install phpcs-security-audit
      run: |
        composer require --dev pheromone/phpcs-security-audit 

    # reviewdog の setup action を追加
    - uses: reviewdog/action-setup@v1
      with:
        reviewdog_version: latest
    - name: phpcs-security-audit
      env:
        # reviewdog が コメントを書き込めるように token をセットする
        REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.CI_GITHUB_TOKEN }}
      # 構文チェックの結果を reviewdog へ渡す
      run: ./vendor/bin/phpcs --report=emacs --standard=example_base_ruleset.xml tests.php | reviewdog -reporter=github-pr-review -efm='%f:%l:%c:%m'

リポジトリのSecretsにCI_GITHUB_TOKENというNameでリポジトリのフルアクセス権限を付与したPersonal access tokensをValueとして設定してください。
※これをしないと、reviewdogがプルリクにレビューコメントを出力することができません。

image.png

先ほど、pushしたものをプルリクにだすと、上記のようにGitHub Actionsが実行されて、該当コードのところに犬のマークがついたreviewdogがどんな脆弱性があるかをコメントとして出力してくれます。

最後

私は普段、インフラ担当で、phpのlaravelに詳しくなかったので、今回のCIでの実装はいい勉強になりました。
もし、この記事を見ていただいて、表現がおかしいや誤った個所等ございましたら、コメントお願いします!

最後まで読んでいただき、ありがとうございました!

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

Vue.js と Laravel バリデーションの連携

はじめに

jQuery を極めすぎて、とんと食わず嫌いになっていた Vue.js を本格導入するにあたって、フォームのバリデーションをサーバーサイドの Laravel で実装することだけは譲れなかった。
まして、サーバーサイドとフロントエンドの両方で同じ処理(バリデーション)を、しかも異なる言語(php と javascript)で記述するなど論外。

  • Laravel v 6.0
  • Vue.js v 2.6.12
  • form-backend-validation v 2.4.0

パッケージの選択

Vuetify

  • リアルタイムバリデーションのパッケージ。
  • フォームエレメントを <v-form や <v-input のように記述し、Vue.js とは別の学習コストが必要。
  • そもそも、リアルタイムバリデーションを javascript で定義する気はないので却下。

FormVuelar

  • サーバーサイドバリデーションのパッケージ。
  • Vuetify の <-v-form が <ivl-from に変わるもので学習コストがさらに増えるので却下。

form-backend-validation

  • 同じくサーバーサイドバリデーションのパッケージ。
  • Vue.js の data に与える from オブジェクトを拡張するだけなので扱いやすい。
  • フォーム Blade が、通常の Laravel の記述に近い。
  • ララジャパンの記事 がわかりやすい。

fom-backend-validation の導入

共通処理の切り分け

ララジャパンの記事 の最後にあるように、フォームを持つすべての Blade に Vue.js 初期化のインラインスクリプトを繰り返し記述することは避けたいので、共通処理を切り分けてみた。

resources/js/app.js

  • Vue.js の初期化は各ページに任せるので app.js は最小限。
require('./bootstrap');

window.Vue = require('vue');

import Form from 'form-backend-validation';

window.Form = Form;

各 Blade の記述

  • フォームにはブラウザのバリデーションが発生しないよう novalidate を付与。
  • インラインスクリプトで定義するのは、フォーム内のカラム要素 data.fields だけ。
  • ページ固有の処理のため data に定義を追加したり、watch 等を追加してもよい。
@extends('layouts.app')

@section('content)
  (中略)
  <div class="alert" :class="messageClass" v-if="message">
    <a class="float-right close" v-on:click="clearMessage()">x</a>
      @{{ message }}
  </div>

  <form @submit.prevent="onSubmit" 
    @keydown="form.errors.clear($event.target.name);" novalidate>
    <div class="form-group row">
      <label for="email" class="col-md-4 col-form-label text-md-right">メールアドレス</label>
      <div class="col-md-6">
        <input id="email" type="email" v-model="form.email"
          class="form-control" :class="{ 'is-invalid': form.errors.has('email') }">
        <div class="invalid-feedback"
          v-if="form.errors.has('email')"
          v-text="form.errors.first('email')"></div>
      </div>
    </div>    
  (中略)
@endsection

@section('script')
  import {formBackendValidation} from "{{ mix('/js/libs.js') }}";
  formBackendValidation({
    data: {
      fields: {
        email:     "{{ $user->email }}",
        password:  "",
        name:      "{{ $user->name }}",
        is_active: {{ $user->is_active }},
      }
    }
  }, "{{ route('admin.user.update', $user) }}");
@endsection

resources/js/libs.js

  • 各ページから呼び出される共通処理。
  • ページ側で Vue.js に渡す定義が追加できるように、ディープマージしている。
  • ページ側で form を直接定義せずに fields で渡してるのは、将来の拡張の可能性のため(後段参照)。
/**
 * Vue.js with form-backend-validation の初期定義
 * 
 * @param {object} option data.fields を含む Vue へ渡すオプション
 * @param {string} url    フォームのポスト先URL
 */
export function formBackendValidation(option, url) {
  const app = new Vue(merge({
    el: '#app',
    data: {
      form: new Form(option.data.fields),
      message: '',
      messageClass: ''
    },
    methods: {
      onSubmit() {
        this.form['post'](url)
          .then(res => {
            if (res.redirect) {
              location.href = res.redirect
            } else if (res.message) {
              this.displayMessage(message, true);
            } else {
              this.displayMessage('更新しました。', true);
            }
          })
          .catch(res => this.displayMessage('エラーを確認してください。', false));
      },
      displayMessage(message, success) {
        this.messageClass = 'alert-' + (success ? 'success' : 'danger')
        this.message = message;
      },
      clearMessage() {
        this.message = '';
      },
    },
  }, option));
  return app;
}

/**
 * deep merge
 */
function merge() {
  return [].reduce.call(arguments, function merge(a, b) {
    Object.keys(b).forEach(function (key) {
      a[key] = (typeof a[key] === "object" && typeof b[key] === "object")
        ? a[key] = merge(a[key], b[key]) : a[key] = b[key];
    });
    return a;
  });
}

webpack.mix.js

  • libs.js を mix.js() で処理すると webpack 変換されて import できなかったので、app.js とは処理を分けた。
mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');

// モジュールで呼び出される js は webpack変換されないよう .scripts を使う
mix.scripts(['resources/js/libs.js'], 'public/js/libs.js');

mix.version();

resources/views/layouts/app.blade.php

  • CSRFトークン、app.js の組み込みは Laravel 初期通りに必須。
  • Balde で定義したインラインスクリプトの @yield ディレクティブを type="module" で追加。
  • コンテンツコンテナに id="app"
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

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

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

  <!-- Scripts -->
  <script src="{{ mix('js/app.js') }}" defer></script>
  <script type="module">
    @yield('script')
  </script>
  (中略)
</head>
<body>
  <div id="app">
    (中略)
    <main class="py-4">
      @yield('content')
    </main>
  </div>
</body>
<body>

コントローラ

バリデーションが通りDB更新後は、libs.js の then メソッドで返り値を受け取って分岐できるよう、リダイレクト先の URL を json で返す。

    /**
     * @param \App\Http\Requests\UserPost $request
     * @param \App\Models\User            $user
     * @return \Illuminate\Http\JsonResponse
     */
    public function update(UserPost $request, User $user)
    {
        $validated = $request->validated();
        $user->update($validated);
        return response()->json(['redirect' => route('admin.user.show', $user)]);
    }

以上で、フォームのサブミットボタンをクリックしたときに、画面遷移を伴わないバリデーション処理が実現できた。万歳。

Laravel によるリアルタイムバリデーションの模索

this.form の変更を watch して1項目ずつサーバーに問い合わせることができれば、一元管理されたバリデーション定義で、リアルタイムバリデーションも実現できるのでは?・・・と、試してみた。

  • libs.js に computedapp.$watch のテストコードを追加した。
/**
 * Vue.js with form-backend-validation の初期定義
 * 
 * @param {object} option data.fields を含む Vue へ渡すオプション
 * @param {string} url    フォームのポスト先URL
 */
export function formBackendValidation(option, url) {
  const app = new Vue(merge({
    el: '#app',
    data: {
      form: new Form(option.data.fields),
      message: '',
      messageClass: ''
    },
    methods: {
      onSubmit() {
        this.form['post'](url)
          .then(res => {
            if (res.redirect) {
              location.href = res.redirect
            } else if (res.message) {
              this.displayMessage(message, true);
            } else {
              this.displayMessage('更新しました。', true);
            }
          })
          .catch(res => this.displayMessage('エラーを確認してください。', false));
      },
      displayMessage(message, success) {
        this.messageClass = 'alert-' + (success ? 'success' : 'danger')
        this.message = message;
      },
      clearMessage() {
        this.message = '';
      },
    },
    computed: {
      computedForm() {
        return JSON.parse(JSON.stringify(this.form))
      }
    }
  }, option));

  app.$watch('computedForm', function (newVal, oldVal) {
    for (let key in app.fields) {
      if (newVal[key] != oldVal[key]) {
        console.log(key, newVal[key], oldVal[key]);
      }
    }
  }, {deep: true});

  return app;
}
後略

deep: true で定義した this.form 一括 watch では、2つの引数 newVal と oldVal の値が同じになってしまうため、一旦 computed で計算した値を watch する必要があったが、これで変更があったカラムを抽出することが可能となった。

次の手順としては・・・

  • フロント側
    1. 変更がなかったカラムを disabled にし、変更があったカラムだけでフォームポストする。
    2. または、変更があったカラム名を、hidden 要素としてフォームに追加したポストする。
  • Laravel 側
    • 2の場合は、変更があったカラムのポストだけに Laravel 側で絞り込む。
    • バリデーション定義にはすべて somtimes を加え、ポストされたカラムに対してだけ判定を返す。

2の方法では、フォームに file 要素があった場合に負荷が大きすぎるので、1の方法がベターか。
なんとか実現の方向性が見えてきたように気がした・・・この時点では。

リアルタムバリデーション計画の行方は・・・

1文字入力するたびにサーバーへ通信するわけにはいかないので、v-model に修飾子 lazy を追加する。これにより入力を終えてフォーカスが移ったときの判定になる・・・はずだった。

<input id="email" type="email" v-model.lazy="form.email"
  class="form-control" :class="{ 'is-invalid': form.errors.has('email') }">

ところがなんと、この追加で入力要素において1文字以上入力できない問題が発生した!

ネイティブの Vue.js では生じない症状なので、form-backend-validation 固有の問題であるよう。この問題が解決するまでは、リアルタムバリデーションはお預けとなった。

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

DockerでLaravelを構築してみた

Laradockとは

Docker環境でLaravel環境を簡単に構築できるDockerイメージです。
Laradockのプロジェクトは大きくなり、SymfonyやWordpress環境までも構築出来るようになりました。

機能としては、Nginx, Apacheなどのwebサーバー、PHPの各バージョン、Mysql, PostgreSQL等の各データベースを自由に選択できます。

今回はNginx、PHP-FPMが動く環境を作り、Laravelを立ち上げるまでを記載します。

前提

・dockerインストールしていること
・docker-composeコマンドが実行できるようになっていることが必要。

環境

Windows10 Pro

手順

[1] CMD開く。作業ディレクトリを作成してそこまで移動する

cd C:\Users\user

[2] フォルダ内でLaradockをgithubからcloneします。

git clone https://github.com/laradock/laradock.git

[3] Laravel環境を構築するまでの下準備

nginx(webサーバー。Apacheでも良いが、今回はLaradockの公式マニュアルに沿いました)
php-fpm(PHPを実行する環境。いわゆるfastCGI)

.envの作成

copy env-example .env

dockerコンテナの立ち上げ

docker-compose up -d nginx

image.png

コンテナが立ち上がっているかを確認する

docker-compose ps

image.png

workspaceにログインし、Laravelをセットアップする

docker-compose exec workspace bash

workspaceにログインすると、最初の状態では/var/wwwにいる。この場所は、Windows側のprojectsフォルダ直下と同様の場所を表している。(laradockディレクトリ直下ではない)

/var/wwwの位置でLaravelをセットアップする

composer create-project laravel/laravel app01

現時点でディレクトリ構成は以下のようになっているかと思います。
親:projects
子1:laradock … Dockerコンテナ
子2:app1 … Laravel本体

image.png

Dockerダッシュボード状態

image.png

ブラウザで動作確認する

image.png

ginx(webサーバー)の設定をします。設定ファイルを編集するため、一度、dockerコンテナを停止します。Windows側のlaradockディレクトリ直下で以下のコマンドを叩く

image.png

dockerコンテナが全てストップします。

image.png

image.png

コンテナを再度立ち上げる

docker-compose up -d nginx

コンテナが立ち上がったら、ブラウザでhttp://localhost へアクセス

image.png

参考

  1. http://laradock.io
  2. http://laradock.io/introduction/
  3. 環境作成時に次のエラーが出た場合

UnexpectedValueException
The stream or file "/var/www/storage/logs/laravel.log" could not be opened:
failed to open stream: Permission denied

laravel.logに書き込もうとして権限がなかった時に発生します。

sudo chmod 777 -R storage/

参考になったURL
https://teratail.com/questions/236475

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

laravelでsitemap.xmlからHTTPテストを作る

経緯

「とりあえずHTTPテストやりたいけど、構造はよく変わるしテストの維持がとても大変」
「関係ないけどsitemap.xmlの更新も面倒だなぁ」
「そうだ!sitemap.xmlからテストを作れば1度の修正で済むぞ!」

環境

laravel 6.19
PHPUnit 8.2.5

備考

前提として、テストを行うルート(sitemap.xmlに載せているルート)にnameが設定されている必要があります。

このテストは「sitemap.xml(このサイトに存在すると世界に示しているページ)にアクセス可能か」ということだけを検証するものです。
テストの管理が難しい環境で、最低限のHTTPテストとしてとりあえず入れてみるのには良いのでは無いかと思います。

出来たもの

sitemap/index.blade.php
<?php echo '<?xml version="1.0" encoding="UTF-8" ?>' . "\n"; ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
        <loc>{{ route('home') }}</loc>
        <changefreq>daily</changefreq>
    </url>

    <url>
        <loc>{{ route('info') }}</loc>
        <changefreq>daily</changefreq>
    </url>

    {{--こういうshowページあったとする--}}
    @foreach($articles as $article)
        <url>
            <loc>{{ route('article.show', ['id' => $article->id]) }}</loc>
            <changefreq>daily</changefreq>
        </url>
    @endforeach

</urlset>
SiteMapTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class SiteMapTest extends TestCase
{

    /**
     * 同一ルートを何個試すか
     */
    private $count = 3;

    /**
     * @test
     * @group sitemap
     * @dataProvider additionPrivateItems
     * @param string $url
     */
    public function test(string $url)
    {
        $response = $this->get($url);
        $response->assertStatus(200);
    }

    public function additionPrivateItems()
    {
        $xmlData = simplexml_load_string(view('sitemap.index')->render());
        $data = json_decode(json_encode($xmlData), TRUE)['url'];
        shuffle($data);

        foreach ($data as $url) {
            $url = $url['loc'];
            $routeName = $this->getRouteName(parse_url($url, PHP_URL_PATH) ?? '');

            if (empty($routes[$routeName]))
                $routes[$routeName] = 1;
            elseif ($routes[$routeName] >= $this->count)
                continue;
            else
                $routes[$routeName]++;

            yield [$url];
        }
    }

    private function getRouteName(string $uri)
    {
        return app('router')->getRoutes()->match(app('request')->create($uri))->getName();
    }
}

コードの中身

PHPunitのdataProviderを使ってテストケースを作っています。

laravelなら大体sitemap.xmlはblade使って生成していると思うので、

  • viewを->render()で文字列として取得
  • xml文字列をsimplexml_load_stringで取得
  • あれやこれやして配列で取得
  • 毎回ランダムなルートをテスト出来るようにシャッフルする
  • foreachで回し、それぞれURIからルート名を取得
  • 同じルートだったら個数制限する

という流れです。

PHPunitのgroupを設定しているので

./vendor/bin/phpunit --group=sitemap

これで実行出来ます。

終わり

これは本当に最低限の機能テストなので、フォームの送信テストなどPOST系のテストは別に用意出来ると良いです。
全部ルーティングから自動生成出来たらな。

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

【Laravel】中間テーブルに値を追加・更新する方法

実現したいこと

中間テーブルに値を追加・更新する。

タグ機能を作る際に、プロジェクトテーブルとタグーテーブル紐付ける際に
中間テーブルを作り、値を追加・更新することができたので、紹介します。

テーブル設計

タグテーブル

id name
1 Javascript
2 PHP

プロジェクトテーブル

id title
1 フロントエンドを学習しよう
2 バックエンドを学習しよう

中間テーブル(プロジェクトタグテーブル)

id tag_id project_id
1 1 1
2 1 2
3 2 2

中間テーブルはこのように、テーブ同士が多対多の関係の時に必要になります。

中間テーブルに値を追加する方法

中間テーブルに値を入れるためのステップ

  1. テーブルのリレーションの設定
  2. 中間テーブルにアクセスし、値の追加

1. テーブルのリレーションの設定

タグのモデルでプロジェクトと紐付けます。belongsToManyは多対多のリレーション
時に使います。これで中間テーブルにアクセスするための準備は完了です。

   public function project()
    {
        return $this->belongsToMany(Project::class);
    }

2. 中間テーブルにアクセスし、値の追加

実際に値を追加します。値を追加する場合はattach()を使います。
まず、どのプロジェクトにタグを紐ずけるか、設定します。
今回は、projectのid:1にタグid:1を設定し、attachメソッドで追加します。

$project = Project::find(1);
//idが1番のプロジェクトを取得します。
$project->tags()->attach(1);
//プロジェクトid:1にタグid:1を追加します。

中間テーブルを確認すると下記のようになります。

id tag_id project_id
1 1 1

このように、中間テーブルに値を追加できます。

中間テーブルの値を更新する場合

更新したい場合は、syncメソッドを使います。
syncメソッドの仕組みは値の削除、追加を行います。
今回の例なら、project_id:1にtag_id:1が登録されています。
syncを実行することで、tag_id:1を削除します。そして、$project->tags()->sync(2);
としているので、project_id:1のtag_id:に2を追加します。
なので、attachを使わなくても、追加、更新はsyncでできてしまうのです。

$project = Project::find(1);
//idが1番のプロジェクトを取得します。
$project->tags()->sync(2);
//中間テーブルのproject_id:1のtag_idを更新

中間テーブルを確認すると下記のようになります。

id tag_id project_id
1 2 1

まとめ

中間テーブルへの値の追加はattach()メソッド、値の更新はsync()メソッド。
attach()を使わなくても、syncメソッドは追加、更新ができる。

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

Laravel学習 Viewとテンプレートについて

はじめに

laravel学習をしていて下記について学んだことをまとめてみました。

①Viewについて

②テンプレートとは

③クエリー文字列

④Bladeについて

⑤ヘルパ関数とは

⑥ディレクティブ

⑦コンポーネント

①Viewとは

・画面表示を担当。画面表示させる部分をわかりやすい形で作れるようになっている。
・HTMLを使ってそのまま表示内容を記述できる仕組み

②テンプレートとは

・Laravelの中でもViewを担当する重要な”部品”のこと
・画面表示のベースとなるもの。
・テンプレートを読み込む→変数など必要な情報を当てはめて実際の表示を生成する(レンダリング)
・レンダリングはテンプレートエンジンによって行われる
・テンプレートエンジンとはデータとテンプレートを合体させ、文字列を作る仕組みのこと
・テンプレートエンジンBladeと呼ばれるLravel既存のテンプレート使うか、自分で作成するかで使用する

▼値をテンプレートに返す
・コントローラ側から、テンプレート側へ必要な変数などの値を受け渡す。

views/作成フォルダ/作成ファイル(テンプレート側)
<html>

<body>
  <h1>Hello/index</h1>
  <p><?php echo $msg?></p>
  <p><?php echo date("Y年n日j日");?></p>
</body>
</html>
#Http/○○Contoroller.php(コントローラ側)

class ○○Contoroller extends Controller
{
    public function index() {
        $変数=['msg'=>'コントローラから渡されたメッセージ'];
    return view('フォルダ名.ファイル名',$変数);

     //この場合のviewメソッドは ”return view('テンプレート',配列);”となる

    }}

この場合のviewメソッドは ”return view('テンプレート',配列);”となるindexアクションのviewメソッドの部分で値をテンプレート側に呼び出している。コントローラ側で配列として用意した値はviewでテンプレート側に渡されて使えるようになる。

③クエリー文字列

・クエリー文字列とはアドレスの後に?○○=✖️✖️と言った形式で付けられたテキスト部分のこと
・クエリー文字列の受け取り方は$request->idというような渡し方をする。

④Blade

・Laravel内に独自に用意されているテンプレートエンジン
・Bladeは「○○○.blade.php」という形でファイルを作成する。
・変数は{{$変数}}としてテンプレート内に埋め込める
・同名のファイル名があれば’blade’が優先される。

⑤ヘルパ関数

→テンプレートで必要となるコードの生成を手助けしてくれるもの。

▼csr_field()
CSRF対策のために用意されたヘルパ関数。フォームに「トークン」と呼ばれるランダムな文字列を非表示で追加し、そのトークンの値が正しいフォームだけを受け受けるようにすることでセキュリティを強化する。

▼CSRF
webサイト攻撃の一つ。外部からのプログラムなどによってフォームを送信する攻撃。

※LaravelはCSRF対策がなされていないフォームの送信はエラーが発生し、受け付けない仕組みとなっている。フォームを利用するときは必ず、csr_field()をフォーム内に準備する
#csr_field関数の使い方例

  <form method="POST" action="/hello">
  {{ csr_field() }}
  <input type="text" name="msg" >
  <input type="submit">
  </form>

Bladeの構文

▼値の表示
{}の間に文を書くことでその文が返す値をその場に書き出すことができる。

{{値・変数・式・関数など}}

{{!!値・変数・式・関数など!!}}
//エスケープ処理されて欲しくない場合

⑥ディレクティブ

言語における構文おような役割を担う機能。(@〜の構文)

▼Ifディレクティブ
条件に応じて表示する内容を制御するのが@ifディレクティブ
↓条件がtrueの時に表示する

@if(条件)
ーーー出力内容ーーー
@endif

↓条件によって異なる表示

@if(条件)
ーーー出力内容ーーー
@else 
ーーー出力内容ーーー
@endif

↓複数の条件による表示

@if(条件)
ーーー出力内容ーーー
@elseif(条件)
ーーー出力内容ーーー
@else 
ーーー出力内容ーーー
@endif

▼繰り返しディレクティブ
@for,@foreach,@while

$loop変数を使うことで繰り返しの状態を指定できる。

▼@phpディレクティブ
変数の定義などに使用

▼@yield()
@section内のテキストを表示させる。セクションの内容をはめこんで使用する・

▼@section
セクションの作成。
一番上の@sectionでは@endsectionではなく、@Showを使用する

⑦コンポーネント @component

・独立したテンプレート
・一部w切り離して組み込みたいときに使用する。

▼@slot
{{}}で指定された変数に値を設定するためのもの

@slot(名前(変数名))
設定する内容
@endslot

▼ビューコンポーザ
ビューをレンダリングする際に必要な部品のこと

▼サービスプロバイダ
継承(extends ServiceProvider~)して使用
・サービスを提供するための仕組み
・Bootメソッドをしよう
→割り込み処理

最後に

認識違いなどありましたらご指摘いただけると幸いです。

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

Illuminate\Database\QueryException : could not find driver (SQL: PRAGMA foreign_keys = ON;)の解決方法

結論 SQLiteの接続用ドライバーをインストール

まずPHPのバージョン確認

$ php -v
PHP 7.4.12 (cli) (built: Oct 31 2020 17:04:25) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.12, Copyright (c), by Zend Technologies

PHPのバージョンに合わせてインストール

$ sudo apt-get install php7.4-sqlite3

無事に "php artisan migrate" できました

$  php artisan migrate
Migration table created successfully.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel 環境構築 (Ubuntu 18.04 LTS )

1. PHP7.4のインストール

下準備

$ sudo apt-get update
$ sudo apt -y install software-properties-common
$ sudo add-apt-repository ppa:ondrej/php
$ sudo apt-get update

これでPHP 7.4 をインストール可能なリポジトリを apt で使えるようになった。

インストール

$ sudo apt install php7.4 php7.4-mbstring php7.4-dom

他に必要なものも一緒にインストールしておく。

インストールできたか確認

$ php -v
PHP 7.4.12 (cli) (built: Oct 31 2020 17:04:09) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.12, Copyright (c), by Zend Technologies

こうなればOK!

2. Comporserのインストール

LaravelをインストールするためにはComposerというのもをインストールする必要があります。
ComposerはPHPのパッケージ管理ツールで、Composerを使うとパッケージを効率よく管理することができるらしい。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

公式サイトのコマンドを使用 (https://getcomposer.org/download/)

確認

$ ./composer.phar -V
Composer version 1.10.1 2020-03-13 20:34:27

これでOK

いつでもComposerを使えるようにする

$ sudo mv composer.phar /usr/local/bin/composer
$ sudo chmod +x /usr/local/bin/composer
$ composer -v
   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 2.0.7 2020-11-13 17:31:06

以下省略。

zipコマンドをインストール

$ sudo apt install -y zip unzip

Laravelのインストールを行う際にunzipコマンドが必要になるため、zipコマンドのインストール。

Laravel6.xをインストール

$composer create-project --prefer-dist laravel/laravel="6.*" laravel6

下記を実行すると実行フォルダの下にlaravel6というフォルダが作成され、そのフォルダの中にLaravelに必要なファイルが保存される。

laravel6のところは好きな名前でOK

サーバー起動して確認

$ cd laravel6
$ php artisan serve

SharedScreenshot.jpg
http://127.0.0.1:8000/
にアクセスしてこの画面になればOK!

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

Laravelのバージョンを指定する方法

結論 composerコマンドで指定する

ターミナル
$ composer create-project --prefer-dist laravel/laravel="6.*" sample_app

これでLaravel6系統がインストールされる。

バージョン確認

ターミナル
$ php artisan -V
Laravel Framework 6.20.4
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP、Laravel 学習 コントローラーの説明①

コントローラーの説明

まずはこの画像をみてください
MVCmodel-1024x492.jpg
ルーティングの先にあるコントローラーを説明していきます。

コントローラーはユーザーから送られてきた指令に対してどのようにアクションするかを決める場所です。
要するに総合窓口ですね。。。市役所とか電話すると「そのご相談しでしたら○○課ですね〜」って案内する人いるじゃないですか?それがコントローラーです。
ルーティングから直接ビューを見せることはたまにありますが、基本的にコントローラーを介して見せる場合が多いです
(モデルはルーティングから直接見せることはほぼない)

コントローラーの記法、RestFulコントローラー(リソースコントローラー)について

基本的な記法を以下に書いておきます

//HomeController
public function top(){
return view('home.top');
}

このソースコードは「top」という命令がきたら、「home」フォルダーの中にある「top」という名前のファイルを表示してくださいね、というコントローラーです。これが基本中の基本です。
これ以外で基本的なところで言えば

1、リストを表示させる
2、入力した情報をデータベースに保存させる
3、保存したデータを編集する
4、保存したデータを削除する
...etc

がありますが
「いや〜、ルーティングに対していちいちコントローラー書いてるのめんどくさいっすよ。っていうかルーティングもコントローラーと同じ数書くんですか〜?」

という方に朗報があります。それはRestFulなコントローラー(リソースコントローラー)です
(以下、リソースコントローラーで統一します。)

簡単に説明するとルーティングにを一回書いたらその中に大体の人が使うであろうルートとコントローラーを作ってくれると言う代物です。公式が用意してくれてるわけですね。。。

用意してくれている内容は以下になります。
※ほぼ公式からの抜粋

動詞 URI アクション ルート名
GET /photos index photos.index
GET /photos/create create photos.create
POST /photos store photos.store
GET /photos/{id} show photos.show
GET /photos/{id}/edit edit photos.edit
PUT/PATCH /photos/{id} update photos.update
DELETE /photos/{id} destroy photos.destroy

こちらはルーティングのことが記載されている表ですが、もちろんこれに合わせたコントローラーも自動生成されてます。(リソースコントローラーの自動生成の仕方は後述)

上から一つづつ説明していきます。

①index

保存したデータのリストを表示する時に使うアクション

②create

データを保存するためのビューを表示させるためのアクション

③store

送られてきたデータを保存するためのアクション(基本的にcreateから送られて来る)

④show

一つのデータの詳細を表示するアクション

⑤edit

一つのデータを編集するためのビューを表示するためのアクション

⑥update

送られてきた「こう言う風に変更してくれ」と言う命令をきくアクション(基本的にeditから送られて来る)

⑦destroy

送られてきたデータを削除するアクション

↑で説明したものは基本的にこう言う風に使われますよと言うものの例で必ずしもこれにのっとった使い方をしろと言うわけではないです、ただこの通りに進めて行けば迷わずに開発が進められるので私はこのリソースコントローラーを使って開発をすることにしました。あと何より記述が少なくて済む!

リソースコントローラーを使っていく

では勿体ぶって申し訳ございません、リソースコントロラーの自動生成をしていきます。
まずはじめに以下のコマンドをterminalで入力しましょう

$ php artisan make:controller PhotoController --resource

ちなみに公式にのっとってやっているのでコントローラー名(Photo)のところは好きに変えてOKです。
これを入力するだけで↑で説明した7つのコントローラーが自動生成されます。

あとはルーティングを書いてきますが。。。

Route::resource('photos', PhotoController::class);

こちらもこれを書くだけでこの中に7つのルーティングが内包されています。
いや〜少ない記述で便利。

ここから使い方について説明していこうと思ったのですがこの時点で長くなりすぎてしまったので次回にしたいと思います。

次回以降の予定

①コントローラーの残りの説明
②Viewの詳しい説明
③Viewの継承(@extend)について
④Bootstrapについて

ちゃんと説明しようと思うとつい長くなりがちですね。。。
あっ、ちなみにこの記事で至らない点等ございましたら是非、ご指導ご鞭撻ください。

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

laravel dumpが原因でphpunit Target class [config] does not exist

備忘録です。
コントローラーを書いている時など、dump()は画面で確認出来て便利なのですが、
そのdumpが残ったままphpunit で当該コントローラーのテストを行うと、
Target class [config] does not exist
のエラーが起こる事があります。

Target class [config] does not exist
が起こる状態を色々見ていて、どうもdump()が怪しいと気が付いたのでした。
そこで、テスト時と実行時で切りわけるdumpもどきを作成してみました。

class MyFunctions
{

public static function mydump(...$d){
    //$_SERVER['APP_ENV'] は phpunit 実行時のみセットされるみたい
    if( isset($_SERVER['APP_ENV'])){
        foreach($d as $n){ var_dump($n); }
    }else{
        foreach($d as $n){ dump($n); }
    }
}

}

呼び出し可能な状態にしておいて、dump()と書いている部分を、MyFunctions::Mydump()に書き換えていけばどちらでも通るようになりました。
実運用する際にmydumpの中味書き換えてしまえばdump()が残ることもなさそうです。

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