20190719のlaravelに関する記事は17件です。

Laravelの機能、文法

MVCフレームワークに基づく構成

view

app/resources/view

$ php artisan make:

controller

app/Http/Controllers

$ php artisan make:controller {ファイル名}

DB接続

use Illuminate\Support\Facades\DB;

app/resources/view/home.blade.php にアクセス
usersのテーブルを取得する

public function index()
    {
     $users = DB::table('users')->get();
        return view('home');
    }

トランザクション処理

public function index(){
    DB::beginTransaction();

    try{
        //処理
        DB::commit();  //成功して処理する
    }catch(/Exception $e){
        DB::rollback();  //失敗時処理されない
    }

}

model

app/database/factories

$ php artisan make:model {ファイル名}

ルーティング

app/routes

Route::get('/', 'HomeController@index');

トップページの時、HomeControllerのクラスのindexメソッドを呼び出す。

マイグレーション

app/database/migrations
マイグレーションファイルの作成

$ php artisan make:migration {ファイル名}

DBへテーブルを作成する

$ php artisan migrate
public function up()
    {
        Schema::create('{テーブル名}', function (Blueprint $table) {
            $table->string('{カラム名}');
        });
    }

ロールバック時の処理

$ php artisan migrate:rollback
public function doun()
    {
        Schema::create('{テーブル名}', function (Blueprint $table) {
            $table->string('{カラム名}');
        });
    }

マイグレーションのカラム更新パッケージインストール

composer require doctrine/dbal

seed

app/database/seeds

$ php artisan db:seed

seedの実行
database/seeds/DatabaseSeeder.php に実行する設定を記載する。

日本語に変換
app/config/app.php

'faker_locale' => 'ja_JP',

インサート文

$result = DB::table('{テーブル名}')->insert([
            '{カラム名}' => '',
        ]);

ログイン

$ php artisan make:auth

ログイン画面

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

Laravelの機能、ルール

MVCフレームワークに基づく構成

view

app/resources/view

$ php artisan make:

CSRF対策の記述

<from method="post" action="">
    @csrf
</from>

controller

app/Http/Controllers

$ php artisan make:controller {ファイル名}

app/resources/view/home.blade.php にアクセス
usersのテーブルを取得する

use Illuminate\Support\Facades\DB;

public function index()
    {
     $users = DB::table('users')->get();
        return view('home');
    }

トランザクション処理

public function index(){
    DB::beginTransaction();

    try{
        //処理
        DB::commit();  //成功して処理する
    }catch(/Exception $e){
        DB::rollback();  //失敗時処理されない
    }

}

model

app/database/factories

$ php artisan make:model {ファイル名}

論理削除の記述

use Illuminate\Database\Eloquent\SoftDeletes;

class Friend extends Model
{
    use SoftDeletes;
    protected $dates = ['{カラム名}']; 
}

作成されたクラスは、同じ名前のテーブルと同期する。
Friendクラスとfriendsテーブルが自動で同期するルールとなる。
ルールに反している場合、Friendクラスで指定する
またプライマリーキーがid以外の場合も指定する

class Friend extends Model
{
    protected $table = '{テーブル名}';
    protected $primaryKey = '{プライマリーキーのカラム名}';
}

アクセサの方法
アクセサの定義を行うと、予め取得するカラムのフォーマットを定義できる。

class Friend extends Model
{
    public function get{プロパティ名}Attribute{
        return $this->{プロパティ名}.$this->{任意の文字};
    }
}

viewでプロパティ名を指定して表示できる。

ルーティング

app/routes

Route::get('{URL}','{コントローラー名}@{メソッド名}');

マイグレーション

app/database/migrations
マイグレーションファイルの作成

$ php artisan make:migration {ファイル名}

DBへテーブルを作成する

$ php artisan migrate
public function up()
    {
        Schema::create('{テーブル名}', function (Blueprint $table) {
            $table->string('{カラム名}');
        });
    }

ロールバック時の処理

$ php artisan migrate:rollback
public function doun()
    {
        Schema::create('{テーブル名}', function (Blueprint $table) {
            $table->string('{カラム名}');
        });
    }

マイグレーションのカラム更新パッケージインストール

$ composer require doctrine/dbal

seed

app/database/seeds

$ php artisan db:seed

seedの実行
database/seeds/DatabaseSeeder.php に実行する設定を記載する。

日本語に変換
app/config/app.php

'faker_locale' => 'ja_JP',

インサート文

$result = DB::table('{テーブル名}')->insert([
            '{カラム名}' => '',
        ]);

バリデーション

コントローラーのメソッドに記載する。
区切ることでバリデーションを複数指定できる。

use App/rules/{独自バリデーションクラス名};

public function index(Reqest $reqest)
    {
     $reqest->validate([
            '{name名}' => '{バリデーション}|{バリデーション}',
            '{name名}' => '['{バリデーション}',new {独自バリデーションクラス名}]'
        ]);
    }

エラー文の日本語化
resources/lang/en/validation.php のバリデーション内容が表示される。
resources/lang/ja/validation.php ファイルを作成し日本語を記入する。
config/app.phpjaに変更する。

'locale' => 'ja',

独自のバリデーションを追加する。

$ php artisan make:rule {クラス名}

app/Rules/{クラス名}.php

    public function passes($attribute, $value)
    {
        if(preg_match({正規表現},$value){
            return true;
        }else{
            return false;
        }
    }

viewのバリデーションエラーの記載

@if($errors->any())
    @foreach($errors->all() as $error)
        {{ $error }}
    @endforeach
@endif

ログイン

$ php artisan make:auth

ログイン画面

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

Laravelインストール手順

Laravelとは

Laravelは、フルスタックなPHPフレームワークで、ルーティング、コントローラ、ビュー、ORMなど基本的な機能を備え、さらに近代的なWebアプリで活用されるジョブキューやWebストレージなども積極的に統合している。

MITライセンスの下でリリースされており、そのソースコードはGitHubにホスティングされている。

Laravelインストールの流れ

composerをインストール。
composerを使ってLaravelをインストール。

composerとは

PHP用のパッケージを管理するためのツール

composerのインストール

ComposerをPHPのパッケージ管理システムをインストールします。

$ curl -sS https://getcomposer.org/installer | php

composer.pharが存在する階層にいる場合、composerコマンドが使える。
どこでもcomposerコマンドが使えるようにcomposer.pharを下記のとする。

$ mv composer.phar /usr/local/bin/composer

移動後、ターミナルで

$ composer --version

コマンドを実行しバージョン番号を確認します。

Laravelのインストール

Composerで専用インストーラをインストールします。

$ composer global require "laravel/installer=~1.1"

Laravelコマンドが実行できるように、~/.composer/vendor/bin
(Windowsの場合は、C:\%HOMEPATH%\AppData\Roaming\Composer\vendor\bin)
ディレクトリにPATHを通します。

$ export PATH="$PATH:~/.composer/vendor/bin"

以下のようにコマンド1つでLaravelプロジェクトが作成できます。

$ laravel new [プロジェクト名]

動作確認

プロジェクトのルート(プロジェクト名)に移動し、

$ php artisan serve

を実行すると、

Laravel development server started on http://localhost:8000/

と表示されるので、ブラウザでhttp://localhost:8000/にアクセスしてみます。

laravel.png

このような画面が表示されれば、環境構築完了。

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

LaravelでClass 'FooProvider' not foundが解決できない場合

一度configのapp.phpに記述した後に、消した場合に発生する。
こんな感じのエラーメッセージ。

Class 'Aws\Laravel\AwsServiceProvider' not found 

configを読み直しても、

php artisan config:cache

composer dump-autoloadしなおしても

composer dump-autoload

なおらない。
なぜなら、クリアしてもbootstrap/cache/config.php に残っているからだ。

こいつを削除すればOK

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

Laravel 5.8 認証

認証

laravelだと以下のコマンドで認証機能に加えて
ログインユーザー登録とパスワードリセットのメール送信、
ユーザー登録時のメール確認なども付いている。

make:auth

以下のコマンドで認証に必要なControllerとviewが一式コピーされる。

$ php artisan make:auth

routes/web.phpに以下の記載が追加される。

routes/web.php
()
Route::resource('users', 'UserController');

// 以下が追加される
Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

route::listに以下のルーティングが追加される。

$ php artisan route:list
|        | GET|HEAD  | home                   | home             | App\Http\Controllers\HomeController@index                              | web,auth     |
|        | POST      | login                  |                  | App\Http\Controllers\Auth\LoginController@login                        | web,guest    |
|        | GET|HEAD  | login                  | login            | App\Http\Controllers\Auth\LoginController@showLoginForm                | web,guest    |
|        | POST      | logout                 | logout           | App\Http\Controllers\Auth\LoginController@logout                       | web          |
|        | POST      | password/email         | password.email   | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail  | web,guest    |
|        | GET|HEAD  | password/reset         | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest    |
|        | POST      | password/reset         | password.update  | App\Http\Controllers\Auth\ResetPasswordController@reset                | web,guest    |
|        | GET|HEAD  | password/reset/{token} | password.reset   | App\Http\Controllers\Auth\ResetPasswordController@showResetForm        | web,guest    |
|        | GET|HEAD  | register               | register         | App\Http\Controllers\Auth\RegisterController@showRegistrationForm      | web,guest    |
|        | POST      | register               |                  | App\Http\Controllers\Auth\RegisterController@register                  | web,guest    |

この時点で/loginにアクセスするとログイン画面が表示される。

layouts/app.blade.php

make:authで作成されます。bootstrapのnav-barで実装されていて
ログインしていないと「ログイン」「登録」メニューが表示され
ログインすると「ログイン」「登録」が非表示、
代わりにログインユーザメニューと「ログアウト」のサブメニューが表示される。

このapp.blade.phpでコンテンツ部と分けられているので
既存のユーザー管理画面もそれに倣って変更する。

@yield('content')でコンテンツ部を表示するように設定。

*.blade.phpの設定

@extends('layouts.app');で上記レイアウトファイルを呼び出す。
@section('content')~@endsection('content')で
囲んだ内容が展開される

認証の実装

各コントロールのconstructで行う。
ユーザーコントロールのconstructで設定する

app/Http/Constrollers/UsersController.php
public function __construct()
{
    $this->middleware('auth'); // 全アクセスにログイン認証が必要
    // ->except・・・認証を除外するアクション配列
    // ->only・・・認証が必要なアクション配列
}

php artisan route:listでusers系のアクションのmiddleware列で
authが追加されているのが確認できる。

ログイン後、/homeに遷移するようになっています。
変える場合はapp/Http/Conrollers/Auth/配下のファイルの$redirectToの値を変更する
RegisterController.php
VerificationController.php
ResetPasswordController.php
LoginController.php

nav-barへのユーザー一覧メニューの追加

app.blade.phpにメニューを追加。ただしログインしている場合

resources/views/layouts/app.blade.php
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav mr-auto">
                        <!-- Main Menu Links -->
                        @auth
                            <li class="nav-item">
                                <a class="nav-link" href="{{ url('users') }}">{{ __('Users') }}</a>
                            </li>
                        @endauth
                    </ul>

@auth~@endauthでログイン状態の条件、
@guest~@endguestでゲスト(非ログイン)状態の条件

この時点では、だれでもユーザー登録がし放題なので登録時にメール確認する機能を追加

メール確認

Laravel 5.8 メール確認
上記の実装通りに進める。

モデルの修正

app/User.php
//class User extends Authenticatable
class User extends Authenticatable implements MustVerifyEmail
{
()

DB修正

usersテーブルにemail_verified_atカラムを追加する必要があるが5.8では標準で含まれている。

ルーティングを変更

routes/web.php
//Auth::routes();
Auth::routes(['verify' => true]);

これだけでメール確認の機能が実装されます。
但し、厳密にはメールでの登録手続きが完了する前にログインできてしまうので
認証方法を変える必要がある。

middlewareのverifiedの設定

各コントローラクラスのコンストラクタでmiddlewareを呼び出し制限する。

app/Http/Controllers/UserController.php
public function __construct()
{
    // ログイン済(メール認証済不問)
    // $this->middleware('auth');
    // メール認証済
    $this->middleware('verified'); // usersの全アクションにメール認証済が必要
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelからSESでメール送信ができなかったときの確認項目

前回(AmazonSESを使ったメールの受信に苦労した話)に引き続き、SES関連のお話。

エラーメッセージはというと、SignatureDoesNotMatchということで、認証できていないかんじ。
configファイルもenvファイルも何度も何度も見直したけど、正しい値を入れている自信はあった。
(実際には間違っていたから送信できなかった。)

Aws/Ses/Exception/SesException with message 'Error executing "SendRawEmail" on "https://email.us-west-2.amazonaws.com"; AWS HTTP error: Client error: `POST https://email.us-west-2.amazonaws.com` resulted in a `403 Forbidden` response:
<ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
  <Error>
    <Type>Sender</Type>
    <Code>SignatureDo (truncated...)
 SignatureDoesNotMatch (client): The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. - <ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
  <Error>
    <Type>Sender</Type>
    <Code>SignatureDoesNotMatch</Code>
    <Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
  </Error>
  <RequestId>196ba786-9d8c-4d78-bf10-052eff69e893</RequestId>
</ErrorResponse>

間違っていた箇所と解決策

まず必要な設定は、公式にも記載されているとおり、SESドライバのインストールとconfigファイルの変更。
https://readouble.com/laravel/5.8/ja/mail.html

 'ses' => [
        'key' => env('SES_KEY'),
        'secret' => env('SES_SECRET'),
        'region' => env('SES_REGION', 'us-east-1'),
    ],

で、自分が間違っていたのは、SES_KEYとSES_SECRETの値。
AWSコンソールの、SES > SMTP Settings > Using SMTP to Send Email with Amazon SESで作成したユーザーIDとパスワードを入れていた。
実際は、IAM > ユーザー > (SESのwrite権限のあるユーザー) > 認証情報 > アクセスキーの作成 で作ったキーを入力。

解決法が間違っていたら教えてくださいm(_ _)m

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

【Laravel】リレーション先のデータ取得方法を調べた

LaravelのEloquent ORMは、テーブル間のリレーションを管理・操作する際の独自の表現があります。
Laravel5.8 Eloquent:リレーション

"1対多"や"多対多"のリレーションなどはよく使うのではないでしょうか。
今回は、これらのリレーションを定義したのち、実際にリレーション先のテーブルのデータをどうやって取得するのか、いくつか手法を調べました。

※ここから先は、予めモデルクラスに以下のリレーションを定義したという前提で進めます。
ブログサイトなどで、1人のユーザーが複数の投稿を登録しているという想定です。

User.php
    public function posts()
    {
        return $this->hasMany('App\Posts');
    }


1.メソッドで呼び出す

マニュアルに記載されている、基本のやり方です。

User.php
// ユーザーに紐づく投稿を全て取得し、Collectionで返す
    public function getPosts()
    {
        return $this
            ->find(1)
            ->posts()
            ->get();
    }

// 条件を付け加えることももちろん可能
// ユーザーに紐づく投稿について、題名で部分一致検索するものをCollectionで返す
    public function getPostByTitle()
    {
        return $this
            ->find(1)
            ->posts()
            ->where('title', 'LIKE', '%hoge%')
            ->get();
    }

posts メソッドが呼び出されると、システムは Posts モデルクラスを参照します。
ですから、通常のモデルクラスの操作と同じように、メソッドチェーンであらゆる条件などを追加できます。

公式マニュアル曰く

Eloquentは「動的プロパティ」を提供しているので、モデルのプロパティとして定義したリレーションメソッドへアクセスできる

ということで、以下の書き方も可能です

User.php
    public function getPosts()
    {
        return $this
            ->find(1)
            ->posts; //プロパティで呼び出す
    }

さて、筆者も初めはこの方法で全てなんとかなると思っていましたが…ちょっとした落とし穴がありました。
というのも、上記のメソッドで取得できるのは特定のユーザーに紐づく、投稿のデータのみであるため、例えばユーザーのデータと投稿のデータを同時に返したいという場合や、ある条件を満たすユーザーに紐づく投稿を取得するといった場合に不都合が生じたのです。

User.php
    public function findPostByUserIsOver25yo()
    {
        return $this
            ->where('age', '>', '25')
            ->get()
            ->posts;
            //get()した時点でcollectionオブジェクトになっており、collectionからpostsプロパティを呼び出そうとしているため、エラーになる
    }

そのような時はどうすればよいのでしょうか?

2.withメソッドを用いる

このような場合は、 with() メソッドを使うとうまくいくようです。
参考にさせていただきました

(公式マニュアルよく読んだらひっそりと使われていた…調べて存在を知るまで気がつかなかった)

User.php
    public function findPostByUserIsOver25yo()
    {
        return $this
            ->with('posts')
            ->where('age', '>', '25')
            ->get();
    }

このように取得すると、 各 User モデルクラスの relations プロパティに Post モデルクラスが紐づいた状態になり、条件に合うユーザーのユーザー情報と投稿データを一度に取得できます。

まとめ

両者を比較した上で、筆者の意見としては全部withでよくね?というのが正直なところです。
特に親テーブルのデータが不要で小テーブルの情報だけ取得したいという場合は、メソッドで呼び出すのがいいのだと思いますが、結局 relations プロパティで呼び出せるしなぁ…
もう少し色々な挙動を試してみたいです。

ご意見・ご指摘などありましたらコメントお寄せください。

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

LaravelのDocker環境開発で2つDB作ってWeb用と単体テスト用DBに分けといて開発中はコマンド1つで両方リセットしとく。

  • DBをWeb用と単体テスト用の2つ用意する。
  • 開発中はDB定義更新したらリセットして最新反映してWeb用のみシーダーを実行したい。

Dockerでテスト用のDBを作成する

テストDBを作成するSQLファイルを設置する

  • ./mysql/init/createdb.sql
  • test_databaseという名前のDBを作成
mysql/init/createdb.sql
CREATE DATABASE IF NOT EXISTS `test_database` COLLATE 'utf8_unicode_ci' ;
GRANT ALL ON `test_database`.* TO `docker` ;
  • dbのvolumesで./mysql/initディレクトリにdocker-entrypoint-initdb.dを割り当てる
docker-compose.yml
  ...省略
  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: web_database
      MYSQL_USER: docker
      MYSQL_PASSWORD: docker
    volumes:
      - db-data:/var/lib/mysql
      - ./mysql/init:/docker-entrypoint-initdb.d <-ここです。
    networks:
      - dockernetworks
networks:
  docker-net:
volumes:
  db-data:
  • ボリュームを消して反映させる
docker-compose down
docker volume rm hoge_db-data
docker-compose up

単体テスト用DBの設定

.env.testing作成

env.testing
APP_ENV=testing
APP_KEY=xxxxxxxxxx
APP_DEBUG=true

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=test_database
DB_USERNAME=docker
DB_PASSWORD=docker

単体テスト実行

% docker-compose exec app php vendor/bin/phpunit

開発中のDB更新

DBリフレッシュコマンド作成

% docker-compose exec app php artisan make:command DBRefreshCommand
app/Console/Commands/DBRefreshCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class DBRefreshCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'db:refresh';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'DB Refresh';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        if (env('APP_ENV') === 'local' && env('APP_DEBUG') === true) {
            $this->info('Refresh DB [Web用]');
            $this->call('migrate:refresh', ['--seed' => 1]);

            $this->info('Refresh DB [単体テスト用]');
            $this->call('migrate:refresh', ['--env' => 'testing']);
        } else {
            $this->error('開発のみ使用のコマンドです。');
        }
    }
}

app/Console/Kernel.php
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        App\Console\Commands\DBRefreshCommand::class,
    ];

リフレッシュコマンド実行

% docker-compose exec app php artisan db:refresh
Refresh DB [Web用]
Rolling back: 2014_10_12_100000_create_password_resets_table
Rolled back:  2014_10_12_100000_create_password_resets_table (0.06 seconds)
Rolling back: 2014_10_12_000000_create_users_table
Rolled back:  2014_10_12_000000_create_users_table (0.01 seconds)
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.05 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.04 seconds)
Database seeding completed successfully.
Refresh DB [単体テスト用]
Rolling back: 2014_10_12_100000_create_password_resets_table
Rolled back:  2014_10_12_100000_create_password_resets_table (0.02 seconds)
Rolling back: 2014_10_12_000000_create_users_table
Rolled back:  2014_10_12_000000_create_users_table (0.01 seconds)
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.03 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.03 seconds)

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

LaravelのDocker環境開発で2つDB作ってWeb用と単体テスト用に分けといて開発中はコマンド1つで両方リセットしとく。

  • DBをWeb用と単体テスト用の2つ用意する。
  • 開発中はDB定義更新したらリセットして最新反映してWeb用のみシーダーを実行したい。

Dockerでテスト用のDBを作成する

テストDBを作成するSQLファイルを設置する

  • ./mysql/init/createdb.sql
  • test_databaseという名前のDBを作成
mysql/init/createdb.sql
CREATE DATABASE IF NOT EXISTS `test_database` COLLATE 'utf8_unicode_ci' ;
GRANT ALL ON `test_database`.* TO `docker` ;
  • dbのvolumesで./mysql/initディレクトリにdocker-entrypoint-initdb.dを割り当てる
docker-compose.yml
  ...省略
  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: web_database
      MYSQL_USER: docker
      MYSQL_PASSWORD: docker
    volumes:
      - db-data:/var/lib/mysql
      - ./mysql/init:/docker-entrypoint-initdb.d <-ここです。
    networks:
      - dockernetworks
networks:
  docker-net:
volumes:
  db-data:
  • ボリュームを消して反映させる
docker-compose down
docker volume rm hoge_db-data
docker-compose up

単体テスト用DBの設定

.env.testing作成

env.testing
APP_ENV=testing
APP_KEY=xxxxxxxxxx
APP_DEBUG=true

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=test_database
DB_USERNAME=docker
DB_PASSWORD=docker

単体テスト実行

% docker-compose exec app php vendor/bin/phpunit

「test」DBの設定

  • コネクションのmysqlをコピーしてmysql_testing作成、databaseを「test」にする
config/database.php
        'mysql' => [...省略...],
        'mysql_testing' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => 'test',
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

開発中のDB更新

DBリフレッシュコマンド作成

% docker-compose exec app php artisan make:command DBRefreshCommand
app/Console/Commands/DBRefreshCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class DBRefreshCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'db:refresh';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'DB Refresh';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        if (env('APP_ENV') === 'local' && env('APP_DEBUG') === true) {
            $this->info('Refresh DB [Web用]');
            $this->call('migrate:refresh', ['--seed' => 1]);

            $this->info('Refresh DB [単体テスト用]');
            $this->call('migrate:refresh', ['--database' => 'mysql_testing']);
        } else {
            $this->error('開発のみ使用のコマンドです。');
        }
    }
}

app/Console/Kernel.php
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        App\Console\Commands\DBRefreshCommand::class,
    ];

リフレッシュコマンド実行

% docker-compose exec app php artisan db:refresh
Refresh DB [Web用]
Rolling back: 2014_10_12_100000_create_password_resets_table
Rolled back:  2014_10_12_100000_create_password_resets_table (0.06 seconds)
Rolling back: 2014_10_12_000000_create_users_table
Rolled back:  2014_10_12_000000_create_users_table (0.01 seconds)
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.05 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.04 seconds)
Database seeding completed successfully.
Refresh DB [単体テスト用]
Rolling back: 2014_10_12_100000_create_password_resets_table
Rolled back:  2014_10_12_100000_create_password_resets_table (0.02 seconds)
Rolling back: 2014_10_12_000000_create_users_table
Rolled back:  2014_10_12_000000_create_users_table (0.01 seconds)
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.03 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.03 seconds)

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

Laravel 5.8 メール

認証機能のうちユーザー登録にメール確認を行うことができるようなので
予めメール送信の設定を行う。

メール設定

.envファイルかconfig/mail.phpで行う。

MAIL_DRIVER=smtp
MAIL_HOST=smtp.hoge.com
MAIL_PORT=587
MAIL_USERNAME=hoge.admin@example.com
MAIL_PASSWORD=(パスワード)
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=hoge.admin@example.com
MAIL_FROM_NAME="hoge admin"

以下のコマンドでテスト送信。nullが変えればOK。

$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.2.20 ? cli) by Justin Hileman
>>> Mail::raw('Test Mail', function($message) { $message->to('hoge@example.com')->subject('TEST Title'); });
=> null
>>> 

gmail等の場合、MAIL_ENCRYPTION=nulltlsにする必要があるが環境によっては
PHP Warningとなる。

$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.2.20 ? cli) by Justin Hileman
>>> Mail::raw('Test Mail', function($message) { $message->to('hoge@example.com')->subject('TEST Title'); });
PHP Warning:  stream_socket_enable_crypto(): Peer certificate CN=....
>>> 

sslの厳密な検証を行うと証明書がドメインに含まれてないからエラーとなっている模様。
以下の設定を追加するとWarningを無視する。

config/mail.php
'stream' => [
   'ssl' => [
       'allow_self_signed' => true,
       'verify_peer' => false,
       'verify_peer_name' => false,
   ],
],

参考URL
How to deal with self-signed TLS certificates in Laravel's SMTP driver?

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

Laravel 5.8 多言語化対応

言語ファイル

標準で用意されているのは以下の4つ
./resources/lang/en/auth.php,password.php,pagination.php,validation.php

上記のファイルについてはLaravel側で予め用意しているがカスタマイズも可能
また以下のコマンドで標準で用意されている日本語版が導入される

$ php -r "copy('https://readouble.com/laravel/5.8/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
$ php -f install-ja-lang.php
$ php -r "unlink('install-ja-lang.php');"

展開先は./resources/lang/ja/

多言語対応

Laravel 5.8 多言語化
上記4ファイル以外のメッセージの定義は各言語フォルダにmessages.phpとするか
/resources/lang/配下に言語2文字.json(ja.json / es.jsonなど)でjson形式で用意する。
key側はデフォルト言語(英語が無難か)

多言語切り替え

https://qiita.com/kumamon_engineer/items/4a83344744db605e3e52
こちらを参考にさせてもらいました。

デザインだけnav-bar用に変更

resources/views/layouts/app.blade.php
(略)
<li class="nav-item dropdown">
    <a class="nav-link dropdown-toggle" href="javascript:void(0)" id="dropdown-lang" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
        {{ Config::get('languages')[App::getLocale()] }}
    </a>
    <div class="dropdown-menu dropdown-menu-right" aria-lledby="dropdown-lang">
        @foreach (Config::get('languages') as $lang => $language)
            <a class="dropdown-item" href="{{ route('lang.switch', $lang) }}">{{ $language }}</a>
        @endforeach
    </div>
</li>
(略)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel5】認識機能を実装した際に追加される「Auth::routes()」にオプションを指定してカスタマイズする

php artisan make:authは便利だけど登録機能は別で作りたい

という場合があると思うので、標準で実装される登録機能のルーティングを外してみたりしてみます。
※Laravel5.7系以上

ソース

php artisan make:authを実行後、ルーティングに以下のコードが自動追加されると思うので、こいつに引数を渡していく

routes\web.php
Auth::routes();

ちなみに上記のルーティング情報は以下の通りです。
WS000045.JPG

Auth::routes()に対して以下のようにオプションで引数をbooleanで渡してやるとルーティングの設定をON/OFFできる

routes\web.php
Auth::routes([
    'verify'   => true, // メール確認機能(※5.7系以上のみ)
    'register' => false, // デフォルトの登録機能OFF
    'reset'    => true,  // メールリマインダー機能ON
]);

上記のルーティング情報は以下の通り。registerが消えてverifyが追加されていると思います。
WS000046.JPG

大本のソースは以下(laravel\framework\src\Illuminate\Routing\Router.php)にあります。
コードを見てもらったらわかると思いますが、デフォルトでregister(登録機能)とreset(パスワードリマインダー)はスカフォールディングした段階で有効になるように書かれています。
(以下のソースはLaravel5.8系なので、新機能のメール確認機能verifyが記述されていてかつデフォルトで無効になるようになっています)

laravel\framework\src\Illuminate\Routing\Router.php
    /**
     * Register the typical authentication routes for an application.
     *
     * @param  array  $options
     * @return void
     */
    public function auth(array $options = [])
    {
        // Authentication Routes...
        $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
        $this->post('login', 'Auth\LoginController@login');
        $this->post('logout', 'Auth\LoginController@logout')->name('logout');

        // Registration Routes...
        if ($options['register'] ?? true) {
            $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
            $this->post('register', 'Auth\RegisterController@register');
        }

        // Password Reset Routes...
        if ($options['reset'] ?? true) {
            $this->resetPassword();
        }

        // Email Verification Routes...
        if ($options['verify'] ?? false) {
            $this->emailVerification();
        }
    }

おわり

  • 使用してない機能はルーティングをOFFにして不要なコントローラーは削除しておきましょう
  • ただし、↑の引数で設定を切り替えられるのは5.7系以前は実装されてないみたいなので注意してください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel5】たまに出てくる「the page has expired due to inactivity. please refresh and try again」を表示させない

多くの原因はCSRFトークンを記述していない場合で発生する

ググってみると多くの場合はフォーム内にCSRFトークンの記述漏れで発生していることが多いようです。
なのでまずはフォーム内にCSRFトークンの記述があるかを確認してください
Laravel5.6以前なら{{ csrf_field() }}を、
Laravel5.6系以降なら@csrfというシンプルに記述できるので、これをフォーム内に埋め込んでください。

以下Laravel5.6以前の記述例

form.blade.php
<form method="POST" action="/profile">
    {{ csrf_field() }}
    ...
</form>

以下Laravel5.6系以降の記述例

form.blade.php
<form method="POST" action="/profile">
    @csrf
    ...
</form>

以下参考になるのかもしれませんが少し情報が古いので注意
【Laravel】TokenMismatchExceptionが発生する原因 - Qiita

CSRFトークンの有効期限切れで表示される

CSRFトークンの有効期限切れで表示される場合(例えばログイン画面で長時間放置してからログインしたりする
以下のような画面が表示され、リロードしろと促されます。

419.png

正直、この画面がでても何のことかわからないし、あまりにも突然表示されたりするので、
CSRFトークンが有効期限切れの状態でログインしたりした際、ログイン画面へリダイレクトさせる処理app/Exceptions/Handler.phpに記述していきます。

laravel\app\Exceptions\Handler.php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Session\TokenMismatchException; // add
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{

// ...

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        // 「the page has expired due to inactivity. please refresh and try again」を表示させない
        if ($exception instanceof TokenMismatchException) {
            return redirect('/login')->with('message', 'セッションの有効期限が切れました。再度ログインしてください。');
        }

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

これでTokenMismatchExceptionが発生した場合はログイン画面へエラーメッセージとともにリダイレクトさせることができる

ステータスコード(419)の場合にリダイレクトさせる

ちなみにステータスコードで判定するようにもできるので以下のようにも書けます。

laravel\app\Exceptions\Handler.php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Support\ViewErrorBag; // add
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; // add

class Handler extends ExceptionHandler
{

// ...

    /**
     * Render the given HttpException.
     *
     * @param  \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface  $e
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function renderHttpException(HttpExceptionInterface $e)
    {
        $this->registerErrorViewPaths();

        // 「the page has expired due to inactivity. please refresh and try again」を表示させない
        if ($e->getStatusCode() === 419) {
            return return redirect('/login');
        }

        if (view()->exists($view = "errors::{$e->getStatusCode()}")) {
            return response()->view($view, [
                'errors' => new ViewErrorBag,
                'exception' => $e,
            ], $e->getStatusCode(), $e->getHeaders());
        }

        return $this->convertExceptionToResponse($e);
    }

}

ただステータスコード419が必ずしもCSRFトークンの有効期限切れで返しているかわからないので、TokenMismatchExceptionが発生した場合のみログイン画面へリダイレクトさせた方がいいかなと思っていますがどうでしょう?

おわり

  • もっといい方法あれば教えていただきたいです草々不一

参考URL

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

Laravel で updateOrCreate を使う際に Unknown column 'id' in 'where clause' でハマった

概要

Laravel の Eloquent が持っている updateOrCreate という関数を使った。
https://readouble.com/laravel/5.7/ja/eloquent.html

MySQL でも気軽に Upsert 処理ができて便利だと思った。

実装例
App\VeryGoodSystem::updateOrCreate(
['user_id' => 12345],
['is_registered' => true]
);

いざ利用してみたところ、下記のようなエラーが出た。
Unknown column 'id' in 'where clause' ...

エラーを追うと、 updateOrCreate で実行されている SQL 文に where `id` is null という条件が付いていることがわかった。

コードを見ても、 id という変数を使っている所は無く、途方に暮れながら調べていたら、良い Q&A を見つけた。(最下部、参考サイト)

つまり、 model に primaryKey が設定されていないと default で id という文字列を使って updateOrCreate 用の SQL を作成してしまうようだった。

利用している Model に $primaryKey = 'user_id' の一行を追加することで解決した。

参考サイト

https://laracasts.com/discuss/channels/eloquent/understanding-of-updateorcreate

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

DockerにLaravel環境を構築する

Docker/Laravel環境構築の備忘録です。

ゴールは、Docker上でLaravelが動くまでです。Macで実施します。

フォルダ構成/ファイルについて

今回は、以下のディレクトリ構成としました。

tree
project
├── docker
│   ├── db
│   │   ├── Dockerfile               ①
│   │   ├── conf.d
│   │   │   └── my.cnf               ②
│   │   ├── data                     ③
│   │   └── initdb.d                 ④
│   ├── docker-compose.yml           ⑤
│   └── web
│       ├── Dockerfile               ⑥
│       ├── conf.d
│       │   └── 000-default.conf     ⑦
│       └── ini
│           └── php.ini              ⑧
└── laravel                          ⑨

順番に説明します。

① project/docker/db/Dockerfile

Dockerコンテナの構成内容を記述します。
ここでは、Dockerイメージとして、MySQLを指定しています。

project/docker/db/Dockerfile
FROM mysql:5.7

② project/docker/db/conf.d/my.cnf

MySQLの設定ファイルです。
⑤にて、/etc/mysql/conf.d にマウントします。

project/docker/db/conf.d/my.cnf
[mysqld]
character-set-server=utf8mb4
explicit-defaults-for-timestamp=1

[client]
default-character-set=utf8mb4

③ project/docker/db/data

ディレクトリです。
⑤にて、/var/lib/mysql にマウントします。
/var/lib/mysql は、MySQLのデータディレクトリとなります。
(Dockerを停止しても消えないように)

④ project/docker/db/initdb.d

こちらもディレクトリです。
⑤にて、/docker-entrypoint-initdb.d にマウントします。
現状、何も置いておりませんが、/docker-entrypoint-initdb.d にSQLやスクリプトを置くことで、初期データを設定可能です。
(そのうち使うかもということで用意しています)

⑤ project/docker/docker-compose.yml

Dockerコンテナの起動オプションを記述するファイルです。

project/docker/docker-compose.yml
services:

  web:
    build: ./web/
    volumes:
      - ./web/ini/php.ini:/usr/local/etc/php/php.ini
      - ./web/conf.d/000-default.conf:/etc/apache2/sites-available/000-default.conf
      - ../laravel:/var/www/laravel
    ports:
      - 80:80

  db:
    build: ./db/
    environment:
      MYSQL_DATABASE: laravel_db
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: rootpassword
    volumes:
      - ./db/initdb.d:/docker-entrypoint-initdb.d
      - ./db/conf.d:/etc/mysql/conf.d
      - ./db/data:/var/lib/mysql
    ports:
      - "3306:3306"

以下、簡単ですが説明です。

  • build:Dockerfileのあるディレクトリパスの指定
  • environment:環境変数の指定(ここでは、MySQLに必要なものを定義)
  • volumes:マウントの指定(ホスト:コンテナ)
  • ports:ポートの指定(ホスト:コンテナ)

⑥ project/docker/web/Dockerfile

①と同様、Dockerコンテナの構成内容を記述します。
ここでは、Dockerイメージとして、PHPを指定しています。
また、RUNで必要なモジュールを追加しています。

project/docker/web/Dockerfile
FROM php:7.3-apache

RUN set -x && \
  pecl install xdebug && \
  docker-php-ext-enable xdebug && \
  a2enmod rewrite && \
  docker-php-ext-install pdo_mysql
  • xdebug:PHPStormでリモートデバッグしたかったので追加
  • rewrite:laravelの認証機能を追加した時、上手く動かなかったので追加

⑦ project/docker/web/conf.d/000-default.conf

Apacheの設定ファイルです。
⑤にて、/etc/apache2/sites-available/000-default.conf にマウントします。
Laravelの公開フォルダは、public のため、DocumentRoot を /var/www/laravel/public としています。
(laravelは、⑤で /var/www/laravel にマウントしています)

project/docker/web/conf.d/000-default.conf
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/laravel/public
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

⑧ project/docker/web/ini/php.ini

PHPの設定ファイルです。
⑤にて、/usr/local/etc/php/php.ini にマウントします。
ここでは、xdebugの設定を行なっています。

project/docker/web/ini/php.ini
; xdebug
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20180731/xdebug.so
xdebug.remote_enable = On
xdebug.remote_autostart = On
xdebug.remote_connect_back = Off
xdebug.remote_host = docker.for.mac.localhost

⑨ project/laravel

project直下で、以下のコマンドを投入すると、laravelディレクトリが作成されます。
中には、プロジェクト一式揃っています。

$ composer create-project laravel/laravel --prefer-dist

 ※ composerの導入は割愛します。

Dockerの起動

project/docker配下で実施します。まずは、buildから。

$ docker-compose build
Building web
Step 1/2 : FROM php:7.3-apache
 ---> 59d2cf691156
Step 2/2 : RUN set -x &&   pecl install xdebug &&   docker-php-ext-enable xdebug &&   a2enmod rewrite &&   docker-php-ext-install pdo_mysql
 ---> Using cache
 ---> bc81175279b3
Successfully built bc81175279b3
Successfully tagged docker_web:latest
Building db
Step 1/2 : FROM mysql:5.7
 ---> 7452c4ea4f17
Step 2/2 : RUN touch /var/log/mysql/mysqld.log
 ---> Using cache
 ---> 3c43b21359e9
Successfully built 3c43b21359e9
Successfully tagged docker_db:latest

次に起動します。

$ docker-compose up -d
Creating docker_db_1  ... done
Creating docker_web_1 ... done

念の為、起動していることを確認します。

$ docker-compose ps
    Name                  Command               State                 Ports
-----------------------------------------------------------------------------------------
docker_db_1    docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp, 33060/tcp
docker_web_1   docker-php-entrypoint apac ...   Up      0.0.0.0:80->80/tcp

ブラウザから
http://localhost
にアクセスすると、laravelの画面が表示されます。

最後に

ここでは書いていないですが、この環境にLaravelの認証機能を追加してみたところ、ユーザ追加が出来ました。
MySQLにも追加したユーザのデータがありましたので、DBアクセスも問題なさそうです。

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

0からAWSでLAMPとLaravel環境構築(Windows)

手順

1.AWSアカウントを作成。
参照:AWS アカウント作成の流れ

2.EC2というAmazonのクラウド上の仮想サーバーを構築。
参照:AWS EC2でWebサーバーを構築してみる

3.LAMP環境を構築。
参照:チュートリアル: Amazon Linux 2 に LAMP ウェブサーバーをインストールする

4.Laravelをインストール。
参照:AWSのEC2を立ち上げてLaravelのログイン機能を動かすまで
   AWSでLaravelを立ち上げた

1.AWSアカウントを作成

AWS は Amazon Web Services の略で、Amazon のクラウドサーバーを使用できるサービスです。
一年間無料でいろんな機能を試せます。
アカウントの登録は参照(AWS アカウント作成の流れ)に沿って行えばすぐできます。

2.EC2構築

AWSにログインすると最初はこの画面が表示されると思います。
image.png
右上の地域はリージョンといい、自分の所在地に合わせて選択します。これは利用するデータセンターの場所を意味します。理論上近いほどレスポンスタイムが短いので、近いところを選びましょう。自分の場合は「東京」です。

そしたら、左上のサービスからEC2を選択。
image.png
左のメニューの「インスタンス」 → 「インスタンス作成」を選択
image.png
一番目のAmazon Linux2 AMIを選択。
image.png
無料利用枠のt2.microにチェックを入れ、
image.png
他の設定は特にいじらずセキュリティグループの設定まで飛ばします。「新しいセキュリティグループを作成する」を選択し、「セキュリティグループ名と「説明」を好きなように入力し、画像のように五つのタイプを追加します。「ソース」のところでIP指定ができます。「任意の場所」を選択するとどこでもアクセスすることができ、「マイIP」を選択すると自分が現在使用しているネットワーク環境のIPでしかアクセスできなくなります。趣味程度の個人利用でしたら任意の場所で問題ありません。
image.png
右下の「確認と作成」 → 「起動」を押してから、このように表示されます。「新しいキーペアの作成」でキーペア名を入力。重要ですが、キーペアのダウンロードは必ずしてください。サーバーにアクセスするために必要となります。
image.png
「インスタンスの作成」 → 「インスタンスの表示」を順番に進めると、インスタントの画面に戻ります。
インスタンスの状態が running になっていれば作成成功です。
image.png

プラスα(オプション)

今作ったインスタンスに固定IPを割り当てることができます。左のメニューから「Elastic IP
」 → 「新しいアドレスの割り当て」 → 「割り当て」 → 「閉じる」で固定IPを一つゲットします。リストからIPを右クリック → 「アドレスの関連付け」で作成したインスタンスを選択し結びつけれます。

3.LAMP環境の構築

①インスタンスに接続

サーバーに接続するため、ssh接続できるターミナルTeraTermをダウンロードします。インスタンス画面下の説明から固定IPとっていれば「IPv4 パブリック IP」、とっていなければ「パブリック DNS (IPv4)」をTeraTermのホストに入力して「OK」します。
image.png
image.png
次にユーザー名に「ec2-user」、秘密鍵に先ほどインスタンスを作るときにダウンロードしたキーペアファイルをセットして「OK」。
image.png
サーバーに接続できました。
image.png
直接とは関係ないですがコマンドでsudoを打つかどうかについて「root権限で`sudo`を付けた場合と付けない場合の違いに`su`は何の略?」を読めばわかると思います。

②EC2のタイムゾーン設定

Amazon EC2のタイムゾーンを日本時間に変更する方法」でわかりやすく書かれていますが、いくつか補足があります。
/etc/sysconfig/clockにはこのように変更すると書いてありますが、

# ZONE="UTC"
ZONE="Japan"
UTC=true

trueのところをfalseにしないと再起動するときにタイムゾーンがUTCに戻ることがあります。

# ZONE="UTC"
ZONE="Japan"
UTC=flase

ここでルート権限を持たないため、vimで保存するときにきっとこのようなエラーが出ると思います。

E45: 'readonly' option is set (add ! to override)

この解決法として自分がrootになるか、「[vim]read only のファイルをsudoで強制的に保存する」を参照してください。

オプションとして、「AWSの初期設定でrootパスワードを設定する」。一行目のsshなんちゃらはすでにTeraTermログイン時にできてますので無視。

lnのオプションの意味は【 ln 】コマンド――ファイルのハードリンクとシンボリックリンクを作るに書いてあります。

③LAMP環境構築

AWSの公式チュートリアル「チュートリアル: Amazon Linux 2 に LAMP ウェブサーバーをインストールする」の順序を追えばできます。自分はSQLに慣れているため、オプションの phpMyAdmin のインストールはしていません。phpMyAdmin はデータベースをGUIで管理できるツールです。

4.Laravelのインストール

①Composerをダウンロード

Composerについては「PHP開発でComposerを使わないなんてありえない!基礎編」。
以下ダウンロードコマンドです。

$ curl -sS https://getcomposer.org/installer | sudo php
$ sudo mv composer.phar /usr/local/bin/composer

これでcomposer.pharというファイルが/usr/local/bin/composer/の下に置かれます。
このcomposer.pharファイルをcomposerというコマンドで実行できたら便利なので、composerというコマンドを作ります。

alias composer='php /usr/local/bin/composer/composer.phar'

ただし、これだけだと再起動するとリセットされてcomposerが効かなくなりますので、常に成立するようにルートに存在する.bashrcというファイルに書き込みます。

$ cd ~          # ルートに移動
$ ls -la        # .bashrcがあるかどうかを確認
$ vi .bashrc    # 「alias composer='php /usr/local/bin/composer/composer.phar'」を書き込む

そしたらターミナルが起動すると読み込まれる.bash_profile.bashrcを参照するようにします。

$ vi .bash_profile    # 「source ~/.bashrc」を一番下に書き込む

②拡張ライブラリをダウンロード

Laravelをダウンロードするのに必要なライブラリーは3つあります。mbstringmysqlndxmlです。

$ sudo yum install -y php-mbstring php-mysqlnd php-xml

③Laravelをインストール

Laravel をインストールするディレクトリに移動します。自分の場合は/var/www/の下にしました。

$ cd /var/www
$ composer create-project --prefer-dist laravel/laravel

自分の場合ここで「proc_open(): fork failed errors」というエラーが出ました。「[PHP]Composer使用時に「proc_open(): fork failed errors」エラーが出た時の対処法」を見て解決できたので共有します。

-prefer-distって何ぞやと知りたい方には「composer の–prefer-distってよく使うけど何してる?」へ。

④Apacheのドキュメントルートの設定と.htaccessの有効化

$ sudo vi /etc/httpd/conf.d/custom.conf

custom.conf
# ドキュメントルート
DocumentRoot "/var/www/laravel/public"

# .htaccess 有効化
<Directory /var/www/laravel/public>
    AllowOverride All
</Directory>

を加えます。
そしたらApacheを再起動してください。

$ sudo service httpd restart

⑤パーミッションを変更

自分はパーミッションについてまだちんぷんかんぷんで、どの権限をどうすればいいか自分ではわかっていないので、他の方のブログを見たほうがいいかもしれません。基本的にパーミッションが合ってないとシステムにこのファイルに書き込めないよと怒られます。どうやらLaravelの場合はstoragebootstrapの権限を変更する必要があるらしい。

$ cd ~
$ sudo groupadd www
$ sudo usermod -a -G www ec2-user
$ exit

一度ログアウトして再度入り直して、

$ cd /var/www
$ sudo chmod -R 777 laravel/storage
$ sudo chmod -R 775 laravel/bootstrap/cache
$ sudo chown -R root:www /var/www

これでLaravelが使えるようになったはず。

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

Laravel-Excel(Maatwebsite/Excel)CSVの解釈の検証

Laravel-Excel のCSVの解釈の検証

Laravel-Excel(maatwebsite/excel) のcsvインポートの際のcsvの記述の仕方による値の解釈がどうなるのか検証を行いました。検証は下記のDummyImportクラスインスタンスをExcelファサードのtoArray()の引数として渡し、取得した行の配列の値の解釈を確認します。

app/Import/DummyImport.php
<?php
namespace App\Import;
use Maatwebsite\Excel\Concerns\ToModel;
use Excel;
class DummyImport implements ToModel
{
    public function model(array $row)
    {
        return $row;
    }
}

//DummyImportインスタンスをExcelファサードの`toArray()`の第一引数として渡す。
//併せて、対象csvファイルへのパスを第二引数として渡す。
$data = Excel::toArray(new DummyImport(), $filepath);

検証1:文字リテラルの解釈

case1.csv
"a","b","c"
a,b,c

クオーテーション""を付けない時、付ける時の違いがあるのか検証しました。

tests/Unit/CSVExtractTest.php
<?php
namespace Tests\Unit;
use Excel;
use App\Import\DummyImport;
use Tests\TestCase;

class CSVExtractTest extends TestCase
{
    /** @test */
    public function CSV検証_文字列クオーテーションありなし()
    {
        $data = Excel::toArray(new DummyImport(), base_path().'/case1.csv');
        $this->assertEquals(2, \count($data[0]));

        //クオーテーションあり
        $this->assertEquals(3, \count($data[0][0]));
        $this->assertEquals('a', $data[0][0][0]);
        $this->assertEquals('b', $data[0][0][1]);
        $this->assertEquals('c', $data[0][0][2]);

        //クオーテーションなし
        $this->assertEquals(3, \count($data[0][1]));
        $this->assertEquals('a', $data[0][1][0]);
        $this->assertEquals('b', $data[0][1][1]);
        $this->assertEquals('c', $data[0][1][2]);
    }
}
テスト結果.txt
Time: 5.22 seconds, Memory: 16.00 MB

OK (1 test, 9 assertions)

違いはないようでした。

検証2:カンマ入り文字列の解釈

文字列にカンマの入った a,a という文字列をインポートする場合はどうなるのか検証してみました

case2.csv
"a,a","b","c"
a,a,b,c
"a,a",b,c

1列目はクオーテーション""をすべての値に付けました。2列目はクオーテーション""を付けません。(おそらくこれを3カラムと解釈されない。)3列目はa,aにだけクオーテーションを付けました。

tests/Unit/CSVExtractTest.php
<?php
namespace Tests\Unit;
use Excel;
use App\Import\DummyImport;
use Tests\TestCase;

class CSVExtractTest extends TestCase
{
    protected $extractedData;
    public function setUp():void
    {
        parent::setUp();        
        $this->extractedData = Excel::toArray(new DummyImport(), base_path().'/case2.csv');
    }

    /** 中略 */

    /** @test */
    public function CSV検証_1列目_すべての値にクオーテーションを付ける()
    {
        $data = $this->extractedData;
        //1列目のカラム数は3であること
        $this->assertEquals(3, \count($data[0][0]));
        //値の検証
        $this->assertEquals('a,a', $data[0][0][0]);
        $this->assertEquals('b', $data[0][0][1]);
        $this->assertEquals('c', $data[0][0][2]);
    }

    /** @test */
    public function CSV検証_2列目_カンマの入った文字列にクオーテーションを付けない()
    {
        $data = $this->extractedData;
        //2列目のカラム数は3であること
        $this->assertEquals(3, \count($data[0][1]));
        //値の検証
        $this->assertEquals('a,a', $data[0][1][0]);
        $this->assertEquals('b', $data[0][1][1]);
        $this->assertEquals('c', $data[0][1][2]);
    }

    /** @test */
    public function CSV検証_3列目_カンマありのデータにだけクオーテーションを付ける
    {
        $data = $this->extractedData;
        //カラム数は3であること
        $this->assertEquals(3, \count($data[0][2]));
        //値の検証
        $this->assertEquals('a,a', $data[0][2][0]);
        $this->assertEquals('b', $data[0][2][1]);
        $this->assertEquals('c', $data[0][2][2]);
    }
}
テスト結果.txt
FFF.                                                                4 / 4 (100%)

Time: 6.49 seconds, Memory: 16.00 MB

There were 3 failures:

1) Tests\Unit\CSVExtractTest::CSV検証_1列目_すべての値にクオーテーションを付ける
Failed asserting that 4 matches expected 3.

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:23

2) Tests\Unit\CSVExtractTest::CSV検証_2列目_カンマの入った文字列にクオーテーションを付けない
Failed asserting that 4 matches expected 3.

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:35

3) Tests\Unit\CSVExtractTest::CSV検証_3列目_カンマありのデータにだけクオーテーションを付ける
Failed asserting that 4 matches expected 3.

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:47

FAILURES!
Tests: 4, Assertions: 12, Failures: 3.

予想よりもエラーが多かったのですが、arrayのカラム数はファイル内最大のカラム数となるようでした。
カラム数のassertをコメントアウトして実行したところ、予想通り、取得される値は2列目が不正となり、1列目、3列目は有効でした。

テスト結果.txt
PHPUnit 7.5.13 by Sebastian Bergmann and contributors.

.F..                                                                4 / 4 (100%)

Time: 6.23 seconds, Memory: 16.00 MB

There was 1 failure:

1) Tests\Unit\CSVExtractTest::CSV検証_2列目_カンマの入った文字列にクオーテーションを付けない
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'a,a'
+'a'

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:37

検証3:数値リテラル(整数、浮動小数点数)の検証

case3.csv
1,-2147483648,2147483647
1.0,2.231224,00030141.213
tests/Unit/CSVExtractTest.php
<?php
namespace Tests\Unit;
use Excel;
use App\Import\DummyImport;
use Tests\TestCase;

class CSVExtractTest extends TestCase
{
    /** 中略 */

    public function CSV検証_整数浮動小数点数()
    {
        $data = Excel::toArray(new DummyImport(), base_path().'/case3.csv');
        $this->assertEquals(2, \count($data[0]));

        //整数
        $this->assertEquals(3, \count($data[0][0]));
        $this->assertEquals(1, $data[0][0][0]);
        $this->assertEquals(-2147483648, $data[0][0][1]);
        $this->assertEquals(2147483647, $data[0][0][2]);
        //浮動小数点数
        $this->assertEquals(3, \count($data[0][1]));
        $this->assertSame(1.0, $data[0][1][0]);
        $this->assertSame(2.231224, $data[0][1][1]);
        $this->assertSame(30141.213, $data[0][1][2]);
    }
}

テスト結果.txt
PHPUnit 7.5.13 by Sebastian Bergmann and contributors.

...F                                                                4 / 4 (100%)

Time: 5.61 seconds, Memory: 16.00 MB

There was 1 failure:

1) Tests\Unit\CSVExtractTest::CSV検証_整数、浮動小数点数
Failed asserting that '00030141.213' is identical to 30141.213.

/home/vagrant/code/ttn-1129-laravel/tests/Unit/CSVExtractTest.php:88

FAILURES!
Tests: 4, Assertions: 24, Failures: 1.

00030141.213は文字列として扱うようでした。

検証4:特殊文字を含む場合

エスケープで使うバックスラッシュ\などを含んだ文字列でcsvファイルから値を取得します。

case4.csv
@#@!@$!#$,^^^^^32143()_----,\\++====51`5
tests/Unit/CSVExtractTest.php
<?php
namespace Tests\Unit;
use Excel;
use App\Import\DummyImport;
use Tests\TestCase;

class CSVExtractTest extends TestCase
{
    /** 中略 */

    /** @test */
    public function CSV検証_特殊文字を含む場合()
    {
        $data = Excel::toArray(new DummyImport(), base_path().'/case4.csv');
        $this->assertEquals(1, \count($data[0]));

        //整数
        $this->assertEquals(3, \count($data[0][0]));
        $this->assertEquals('@#@!@$!#$', $data[0][0][0]);
        $this->assertEquals('^^^^^32143()_----', $data[0][0][1]);
        $this->assertEquals('\\\\++====51`5', $data[0][0][2]);
    }
}
テスト結果.txt
PHPUnit 7.5.13 by Sebastian Bergmann and contributors.

.....                                                               5 / 5 (100%)

Time: 5.96 seconds, Memory: 16.00 MB

OK (5 tests, 29 assertions)

バックスラッシュ\だけ、php側で、エスケープをちゃんとすることで検証できるようでした。

参照

Laravel/Excelのインストール等に関しましては @rito328 さんのこちらのサイトに詳しく書いて頂いておりましたので参考にさせて頂きました。
https://www.ritolab.com/entry/160

以上です。

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