2020/05/25

プロジェクト作成

composer でプロジェクト名「laravel_app」を作成します。


$ composer create-project laravel/laravel laravel_app

Laravel のバージョンを確認しておきます。


Laravel Framework 7.12.0

UIパッケージをインストール

laravel_app に移動して、ComposerでLaravelのUIパッケージ laravel/ui をインストールします。


$ cd laravel_app
$ composer require laravel/ui

Reactをインストール

Reactを利用できるようにスカフォールドをインストールします。


$ php artisan ui react
//Authを利用する場合
$ php artisan ui react --auth

npmでインストール&ビルド(開発)

package.jsonに記述された内容で依存パッケージをインストールとLaravel Mixでビルドします。


$ npm install
$ npm run dev

関連ファイルの監視

関連ファイルの変更を監視しておきます。


$ npm run watch

これでReactがインストールされたLaravelプロジェクト作成が完了しました。

ここで Laravelサーバを起動して確認してみましょう。

おなじみの Laravel の画面は表示されましたが、この画面ではReact の動作実装されていません。

Reactを確認する

Laravel で React をインストールするとあらかじめサンプルファイルが作成されています。

app.js

resources/js/app.js には Bootstrap と Reactコンポーネント「Example」が読み込まれています。


require('./bootstrap');
require('./components/Example');

Example.js

resources/js/components/Example.js には、Reactコンポーネント「Example」の中身です。
Reactでは、HTMLレンダリングにJSXを利用しますが、JavaScriptに直接HTMLタグを記述できる点で便利です。


import React from 'react';
import ReactDOM from 'react-dom';

function Example() {
    return (
        ...JSX...
    );
}

export default Example;
...

レイアウトファイルの修正

現状だと Reactコンポーネントは表示できないため、Laravelのレイアウトファイル「resources/views/welcom.blade.php」を修正します。

Laravelのmix()関数で app.jsを読み込んでいます。


<script src="{{ mix('js/app.js') }}"></script>

また、Exampleコンポーネントを表示する場合、HTMLタグにidを指定して呼び出します。


<div id="example"></div>

動作確認

もういちど、サーバを確認してみると今度は、Exampleコンポーネントを読み込んだ状態で表示されました。

  2020/03/30

ペジネーションビューの書き出し

artisan コマンドで、ペジネーションビューをプロジェクト内に書き出します。


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

resources/views/vendor/pagination/ にページネーションのBladeファイルが作成されます。

あとで説明しますが、デフォルトは「bootstrap-4」で書き出されますが、ページネーションメソッドで他のBladeファイルを指定することができます。
よって、利用したいBladeファイルをカスタマイズしてください。

Eloquent モデルで取得

Eloquent モデルには、paginate()、simplePaginate() メソッドを利用できます。


$limit = 10;
$offset = 0;
$user = User::paginate($limit, '[*]', 'page', $offset);

AppServiceProvider に追加

AppServiceProvider::boot() でページネーションのBlade ファイル名を指定します。


    public function boot()
    {
        Paginator::defaultView('pagination::bootstrap-4');
        Paginator::defaultSimpleView('pagination::simple-bootstrap-4');
    }

Blade にページネーションを実装

Blade ファイルでは、Eloquent モデルデータの links() メソッドでページネーション表示できます。


{!! $user->links() !!}

また、Blade 側で明示的に指定することもできます。


{{ $paginator->links('pagination::bootstrap-4', ['key' => 'value']) }}

Eloquent のページネーションメソッドは以下を参照してください。

https://readouble.com/laravel/7.x/ja/pagination.html

  2020/03/29

SQL が発行されるたびに Log ファサードを利用して SQLログを書き出してみます。

Laravel のLogファサードの仕様は以下を参照してください。

https://readouble.com/laravel/7.x/ja/logging.html

LogSqlServiceProvider の設定

プロバイダは、AppServiceProvider に登録しても良いですが、今回はカスタムで LogSqlServiceProvider を作成します。


$ php artisan make:provider LogSqlServiceProvider

app/Providers/LogSqlServiceProvider.php が作成されているのを確認します。

AppServiceProvider に登録

config/app.php の providers に LogSqlServiceProvider を登録します。


    'providers' => [
...
        App\Providers\LogSqlServiceProvider::class,
...
    ],

SQLログ処理を記述

app/Providers/LogSqlServiceProvider.php の register() に記述します。


namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class AppServiceProvider extends ServiceProvider
{
    const SQL_LOG_ENVIROMENT = [
        'local',
        'development',
    ];
....

    public function register()
    {
        if (in_array(App::environment(), self::SQL_LOG_ENVIROMENT)) {
            DB::listen(function ($query) {
                $sql = $query->sql;
                for ($i = 0; $i < count($query->bindings); $i++) {
                    $sql = preg_replace("/\?/", $query->bindings[$i], $sql, 1);
                }
                Log::channel('sql')->debug("SQL", ["time" => sprintf("%.2f ms", $query->time), "sql" => $sql]);                                                      
            });
        }
    }

App::environment() で現在の環境を取得し、任意に設定した SQL_LOG_ENVIROMENT の場合に、ログを書き出します。

環境による設定は、.env の boolean で設定しても良いかも知れません。

SQLログファイルパス設定

config/logging.php で設定でき、デフォルト「storage/logs/larave.log」となります。

Log::debug() の場合、デフォルトで single チャンネルで書き出されるため、Log::channel('チャンネル名')で、SQL用のログを設定します。
(内部的に、Illuminate\Log\LogManagerクラスで、driver() に設定される模様)


    'channels' => [
...
        'sql' => [
            'driver' => 'sql',
            'path' => storage_path('logs/sql.log'),
            'level' => 'debug',
        ],
...
    ],

またロガーは RFC 5424 の以下の8つのログレベルを提供しています。

  • emergency
  • alert
  • critical
  • error
  • warning
  • notice
  • info
  • debug

Logファサードは、最終的にPsr\Log\LoggerInterface を実装した Monolog\Logger を利用しています。

  2020/03/24

Laravel 7 で認証ページを作成してみます。

※Laravel7 の認証ページ

https://readouble.com/laravel/7.x/ja/authentication.html

プロジェクト作成


$ composer create-project laravel/laravel laravel-auth --prefer-dist
$ chmod -R 777 storage
$ chmod -R bootstrap/cache

この時点で、 config/auth.php などが作成されています。

  • config/auth.php
  • app/User.php
  • app/Http/Middleware/Authenticate.php
  • database/migrations/xxxx_create_users_table.php
  • database/migrations/xxxx_create_password_resets_table.php
  • database/factories/UserFactory.php

User モデル

デフォルトで app/User.php が作成されています。


namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;
    protected $fillable = [
        'name', 'email', 'password',
    ];
    protected $hidden = [
        'password', 'remember_token',
    ];
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

今回は、App/Models/User.php にパスを変更しておきます。
(namespace など修正が必要)

Admin モデル作成

マルチ認証用に、管理者(Admin)モデルを作成してみます。

Models/Admin.php を作成します。


$ php artisan make:model Models/Admin

Admin マイグレートファイル作成

databases/ に Admin用のマイグレーションファイルを作成します。


$ php artisan make:migration create_admins_table

モデル&マイグレートファイルを同時に作成

上記のモデルとマイグレーションファイルを同時に作成することもできます。

Models/Admin.php を作成します。


$ php artisan make:model Models/Admin -m

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
 
class CreateAdminsTable extends Migration
{
    public function up()
    {
        if (Schema::hasTable('admins')) return;
        Schema::create('admins', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->string('email');
            $table->string('name');
            $table->string('password');
            $table->string('remember_token')->nullable();
        });
    }
    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

Guard の設定

Guard は Auth認証用のドライバークラスで、config/auth.php に設定をします。


    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
      'admin' => [
         'driver' => 'session',
         'provider' => 'admins',
      ],
    ],

Guard を設定することにより、Controllerなどに記載するミドルウェアでの認証が可能となります。


$this->middleware('auth:admin');

Eloquent 設定

Eloquent に App\Models\User クラスを設定します。
※User.php の namespace は App\Models\ に変更


    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
       'admins' => [
        'driver' => 'eloquent',
        'model' => App\Models\Admin::class,
      ],
    ],

artisan で auth を作成する

make:auth 廃止

Laravel6 から 以下のコマンドは廃止されました。


$ php artisan make:auth

laravel/ui を利用

代わりに、「laravel/ui」でログイン画面を作成するようになりました。

更に、リリースノートには、2.0 を指定するように記載されています。


$ composer require laravel/ui "^2.0"
vue の部分は bootstrap などの設定もできます。

認証用ファイル

  • resources/views/auth
  • App/Http/Controllers/Auth
が作成されているので、管理者用にコピーしておくと良いでしょう。
  • resources/views/admin/auth
  • App/Http/Controllers/Admin/Auth

データベース作成

PostgreSQL


$ createdb sample_auth

MySQL


# mysql -u root
mysql > create database sample_auth;

.env

各種DBの設定を合わせる


DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=sample
DB_USERNAME=postgres
DB_PASSWORD=secret

DBマイグレート

DB設定が一通り完了したら、マイグレートしてみます。


$ php artisan migrate

テーブルが作成したことを確認したら、サンプルデータを作成してみます。

Seeder 作成

サンプルデータを作成する場合、artisan に seeder コマンドにも用意されています。

ユーザ、管理者の Seeder を作成すると「database/seeds/」にファイルが書き出されます。


$ php artisan make:seeder UsersTableSeeder
$ php artisan make:seeder AdminsTableSeeder

UsersTableSeeder.php


    public function run()
    {
        DB::table('users')->insert([
            'name' => Str::random(10),
            'email' => Str::random(10).'@gmail.com',
            'password' => Hash::make('password'),
        ]);
    }

AdminsTableSeeder.php


    public function run()
    {
        DB::table('admins')->insert([
                'email' => 'xxxx@xxxx.com',
                'name' => 'Admin',
                'password' => Hash::make('password')
            ]
        );
    }

DatabaseSeeder.php

Seeder ファイルが作成されたら、「database/seeds/DatabaseSeeder.php」に seeder を実行できるよう設定します。


public function run()
{
     $this->call(UsersTableSeeder::class);
     $this->call(AdminsTableSeeder::class);
}

設定したら、artisan コマンドで Seeder を実行します。


$ php artisan db:seed

実際に、テーブルにデータが作成されているか確認してみましょう。

Factory

ここでは割愛しますが、Factory の faker を利用する方法で seeder を実行できます。
Factory ファイルは以下のコマンドで作成できます。


$ php artisan make:factory UserFactory

コントローラー作成

各種コントローラーを作成します。

管理者ホーム

App/Http/Controllers/Admin/ にコントローラーを作成します。


$ php artisan make:controller Admin/HomeController

Admin/HomeController.php

ファイルを作成したら、index の実装と、コンストラクタに認証用のミドルウェアを記述(Guard で設定したパラメータ)をします。
以下の例は、管理者の認証制御となります。

    public function __construct()
    {
        $this->middleware('auth:admin');
    }

    public function index(Request $request)
    {
        return view('admin.index');
    }

ただコントローラが増えてくると、コンストラクタに毎度記述するのも面倒なので、Route のグループ設定で一括管理すると良いでしょう。
(認証用の親コントローラを継承する方法もありますが)

Route の設定

認証が必要・不要のページを考えて、グループ化した Route を設定します。

認証が不要のページ

ログイン、ログアウト、登録ページなどの認証不要のページのルートを作成します。
管理者URIは、admin で統一したいので「admin」プリフィックスを利用しています。

routes/web.php


Route::group(['prefix' => 'admin'], function () {
    Route::get('login', 'Admin\Auth\LoginController@index')->name('admin.login');
    Route::post('login', 'Admin\Auth\LoginController@login')->name('admin.login');
    Route::get('logout', 'Admin\Auth\LoginController@logout')->name('admin.logout');
    Route::post('logout', 'Admin\Auth\LoginController@logout')->name('admin.logout');
});

認証が必要のページ

認証が必要なページのルートを設定します。
ルートグループのパラメータに、ミドルウェアの Guard 設定します。


Route::group(['prefix' => 'admin/topic', 'middleware' => 'auth:admin'], function() {
    Route::get('/', 'Admin\HomeController@index')->name('admin.index');
});

  2020/03/19

2020/03/03 に Laravel7 がリリースされたので、ちょっとだけ見てみました。

(2020/3/19 現在、バージョンは 7.1.3)

https://laravel.com/docs/7.x/upgrade

Symphony 5 リリース

  • Laravel7 では Symphony5 以上
  • PHP 7.2.5 以上
  • laravel/framework ^7.0
  • nunomaduro/collision ^4.1
  • phpunit/phpunit ^8.5
  • facade/ignition ^2.0
  • laravel/tinker ^2.0
  • facade/ignition  ^2.0
  • report()、render() は、Exception から Throwable に変更

use Throwable;
public function report(Throwable $exception);
public function render($request, Throwable $exception);

7.x にアップデート

composer.json 設定

composer.json に必要なライブラリのバージョンを書き換えます。


    "require": {
        "php": "^7.2.5",
        "fideloper/proxy": "^4.2",
        "fruitcake/laravel-cors": "^1.0",
        "guzzlehttp/guzzle": "^6.3",
        "laravel/framework": "^7.3",
        "laravel/tinker": "^2.0"
    },
    "require-dev": {
        "facade/ignition": "^2.0",
        "fzaninotto/faker": "^1.9.1",
        "laravel/ui": "^2.0",
        "mockery/mockery": "^1.3.1",
        "nunomaduro/collision": "^4.1",
        "phpunit/phpunit": "^8.5"
    },

composer update


$ composer update

Authentication

「Authentication Scaffolding」が変更となったようです。

Scaffolding

  • 認証の自動作成の改修
  • Illuminate\Auth\Passwords\TokenRepositoryInterface
  • recentlyCreatedToken メソッドが追加

Eloquent

Date Serialize

  • Eloquent Model で Carbon の日付フォーマット改修
  • ISO-8601 による、timezone と 秒の小数部の対応
  • 現状の仕様をキープしたい場合は、serializeDate をオーバーライド

DB と Eloquent のフォーマットがことなると、Exception になるのでこれが改修されたか?検証が必要

Unique Route Names

  • 同じ Route名を指定できなくなった
  • 重複する場合は、重大なエラーが発生する

CORS サポート

  • Cross-Origin Resource Sharing (CORS) でクロスドメイン通信
  • 「\Fruitcake\Cors\HandleCors::class」を グローバルの Middleware に追加

$ composer require fruitcake/laravel-cors

CastsAttributes

「CastsAttributes」を implements することで、カスタムでキャストすることができます。

カスタムクラスに、get、set メソッドを追加し、Eloquent モデルに $casts を設定することで、自動的にキャストされるようです。

下記の例は、json_decode()、json_encode() を利用して JSON型のキャストを行なっています。


namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class Json implements CastsAttributes
{
    public function get($model, $key, $value, $attributes)
    {
        return json_decode($value, true);
    }

    public function set($model, $key, $value, $attributes)
    {
        return json_encode($value);
    }
}

namespace App;

use App\Casts\Json;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $casts = [
        'options' => Json::class,
    ];
}

Blade

The component Method

  • 「Blade::component」 から 「Blade::aliasComponen」に名称変更

Blade Component Tags

Viewコンポーネントクラスにプロパティやメソッドを設定することで、Blade の記述をシンプルにできます。

下記の例は、Alert コンポーネントクラスを作成し「alert.blade.php」に対して、class を動的に設定します。


namespace App\View\Components;

use Illuminate\View\Component;

class Alert extends Component
{
    public $type;

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

    public function classForType()
    {
        return $this->type == 'danger' ? 'alert-danger' : 'alert-warning';
    }

    public function render()
    {
        return view('components.alert');
    }
}

Factory Types

  • Laravel7 から factory types が削除された

プロジェクト作成

実際に、認証処理を実装していきます。

※Laravel7 の認証ページ

https://readouble.com/laravel/7.x/ja/authentication.html


$ composer create-project laravel/laravel laravel-auth --prefer-dist
$ chmod -R 777 storage
$ chmod -R bootstrap/cache

バージョン確認


$ php artisan --version
Laravel Framework 7.1.3

この時点で、 config/auth.php などが作成されています。

  • config/auth.php
  • app/User.php
  • app/Http/Middleware/Authenticate.php
  • database/migrations/xxxx_create_users_table.php
  • database/migrations/xxxx_create_password_resets_table.php
  • database/factories/UserFactory.php

app/User.php


namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;
    protected $fillable = [
        'name', 'email', 'password',
    ];
    protected $hidden = [
        'password', 'remember_token',
    ];
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

最近のバージョンアップが早く、仕様も日々変わりつつあるのでチェックが必要ですね。

  2020/03/15

Laravel + Vue.js 環境において、Vue コンポーネントを利用してみます。

プロジェクト作成

Laravel プロジェクトを作成します。


$ composer create-project --prefer-dist laravel/laravel laravel-vuemix
$ cd laravel-vue-mix
$ chmod 777 storage
$ chmod 777 bootstrap/cache/

Laravel Vue UI 追加

Vue に対応した Laravel UI を作成します。


$ composer require laravel/ui --dev
$ php artisan ui vue
$ npm install

ファイルを確認する

Webpack Mix

webpack.mix.js が作成されています。


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

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

デフォルトでは、以下が作成&設定されています。

  • resources/js/app.js
  • resources/sass/app.scss

resources/js/app.js


Vue.component('example-component', require('./components/ExampleComponent.vue').default);

const app = new Vue({
    el: '#app',
}
});

サンプル Vue コンポーネント

Vue UIを作成すると、「resources/js/components/ExampleComponent.vue」が作成されています。

このコンポーネントファイルは、app.js で設定されています。


Vue.component('example-component', require('./components/ExampleComponent.vue').default);

第1引数に「タグ名」、第2引数にコンポーネントを設定します。


Vue.component('タグ名', require('.vueファイルパス').default)

コンテンツ作成

コントローラー、ビュー(レイアウト、ページ)を作成し「app.js」「app.css」も読み込むようにします。

Controller

SampleController 作成


$ php artisan make:controller SampleController --resource

SampleController.php の index() を修正


    public function index()
    {
        return view('sample.index');
    }

Route 追加

sample/index にアクセスできるように、route/web.php に追記します。


Route::get('/sample', 'SampleController@index')->name('sample');

ビュー作成

レイアウトファイル

resources/views/layouts/app.blade.php を作成

head タグに app.js 用の scriptタグと app.css 用の linkタグを blade で追記します。


src="{{ asset('js/app.js') }} //script タグで head などに追記
href="{{ asset('css/app.css') }}  //link タグ head などにで追記

ページファイル

resources/views/sample/index.blade.php を作成し、ExampleComponent.vue を読み込みます。

app.js で設定した example-component タグで利用可能です。

カスタム Vue コンポーネント作成

「resources/js/components/」に MessageComponent.vue コンポーネントを作成します。

template 部分

script 部分

script タグで以下を記述します。


export default {
    data: function() {
        return {
            message: "Message Component Message!",
        }
    }
}

app.js

app.js に「MessageComponent.vue」の設定を追記します。


Vue.component('message-component', require('./components/MessageComponent.vue').default);

ビルド

npm または yarn でビルドし、app.js を書き出します。

書き出し先は、「webpack.mix.js」で設定した通りです。


$ npm install
$ npm run dev
//または
$ yarn install
$ yarn run dev

watch で監視

毎回、run dev するのは面倒なので、watch を使ってファイル修正を監視します。


$ npm run watch
//または
$ yarn run watch

Laravel で確認

実際に、Laravel にアクセス(SampleController@index)すると、 「example-component」「message-component」に対応して表示されます。

  2020/03/01

Eloquent で DBから日付を扱う場合、$date にカラム指定することで自動キャストされます。
(ただし、DBデータの日付フォーマットには注意が必要です)

日付カラム指定


    protected $dates = [
        'created_at',
        'updated_at',
        'posted_at',
    ];

Carbon フォーマット

$datesで指定したカラムは、DB取得データで Carbon にキャストされているため、以下のように Blade でも読みやすい記述ができます。

 
@foreach ($values as $value)
{{ $value->posted_at->format('Y/m/d') }}
@endforeach

フォーマット指定

日付フォーマットも指定できる。


protected $dateFormat = 'Y-m-d H:i:s.u'; 

が、DBデータフォーマットと不整合があると、Exception(Carbon の Create.php)が発生するので注意が必要です。

個人的には「Y-m-d H:i:s」にした方が無難な気がします。

PostgreSQLの場合

  • created_at は CURRENT_TIMESTAMP
  • timstamp without time zone
  • timestamp(0) 

明示的に指定する場合は、


$table->timestamp('created_at')->default(DB::raw('now()::timestamp(0)'));

  2020/02/06

Freee SDK

ありそうでなかった会計向けSDK
freee が国内初なようで、2020/02時点の対応言語は「C#」「Java」「PHP」

freee API と統制をとるために「OpenApi generator」を利用して有志が開発しているとのこと

C#

https://github.com/freee/freee-accounting-sdk-csharp/

Java

https://github.com/freee/freee-accounting-sdk-java

PHP

https://github.com/freee/freee-accounting-sdk-php/

PHPの場合は、composer で用意されており、さらにサンプル用に Docker & Laravel が公開されている。

サンプルの動作例

freee API

freee API

Docker & Laravel でサンプルを動かす

コールバックURL

freee APIアプリの「コールバックURL」は以下を設定しておく
※デフォルト:urn:ietf:wg:oauth:2.0:oob
※Laravel dでも後に設定する


http://localhost:8000/auth-callback

サンプルダウンロード & 設定

サンプルは SDK の中に入っているので git clone


$ git clone https://github.com/freee/freee-accounting-sdk-php.git

.env を設定する


$ cd <code>freee-accounting-sdk-php.git

freeeアプリ の Client ID、Secret ID を記述


FREEE_ACCOUNTING_CLIENT_ID=Client ID
FREEE_ACCOUNTING_CLIENT_SECRET=Secret ID

Dockerの起動

freee-accounting-sdk-php にあるサンプルを利用する

$ cd freee-accounting-sdk-php/samples/BasicWebApp
$ cd samples
$ docker-compose build
$ docker-compose up -d
$ docker exec -it samples_webapp_1 /bin/bash

docker-compose.yaml


version: "3.7"
services:
  console:
    build:
      context: .
      dockerfile: Dockerfile
    image: freee-accounting-sdk-php-console
    volumes:
      - "./BasicConsole:/usr/src/app"
    command: /bin/sh -c "while sleep 1000; do :; done"
  webapp:
    build:
      context: .
      dockerfile: Dockerfile
    image: freee-accounting-sdk-php-webapp
    ports:
      - "80:80"
      - "8000:8000"
    volumes:
      - "./BasicWebApp:/usr/src/app"
    command: /bin/sh -c "while sleep 1000; do :; done"

Dockerfile


FROM php:7.3-cli
RUN apt-get update; \
    curl -sL https://deb.nodesource.com/setup_12.x | bash -; \
    apt-get install -y --no-install-recommends \
        git \
        libzip-dev \
        nodejs \
        unzip \
        zlib1g-dev \
    ; \
    rm -rf /var/lib/apt/lists/*

RUN docker-php-ext-install zip
WORKDIR /tmp
RUN curl https://raw.githubusercontent.com/composer/getcomposer.org/76a7060ccb93902cd7576b67264ad91c8a2700e2/web/installer -o - -s | php -- --quiet; \
    mv composer.phar /usr/local/bin/composer
WORKDIR /usr/src/app
RUN composer global require laravel/installer
ENV PATH /root/.composer/vendor/bin:$PATH
EXPOSE 8000

Composer インストール

webpackが用意されているので、すぐインストールできる


# composer install

composer、Laravel を直にインストールする場合


$ composer require freee/freee-accounting-sdk
$ composer require socialiteproviders/generators
$ composer require socialiteproviders/manager

artisan で socialiteproviders/generators を実行


$ php artisan make:socialite FreeeAccounting --spec=oauth2 --authorize_url=https://accounts.secure.freee.co.jp/public_api/authorize --access_token_url=https://accounts.secure.freee.co.jp/public_api/token --user_details_url=https://api.freee.co.jp/api/1/users/me
$ composer dumpautoload

Laravel/UI を作成


$ composer require laravel/ui --dev
$ php artisan ui vue --auth
$ npm install && npm run dev

Laravel artisanサーバ起動


# php artisan key:generate
# php artisan serve
# php artisan serve --host 0.0.0.0

プロジェクトルートは「samples/BasicWebApp/」

認証処理

routes/web.php


Auth::routes([
    'register' => false,
    'reset' => false,
]);

Route::get('login', 'Auth\LoginController@redirectToProvider')->name('login');
Route::get('auth-callback', 'Auth\LoginController@handleProviderCallback')->name('authCallback');

config/service.php

「client_id」「client_secret」「redirect」をそれぞれ設定 (リダイレクトURLは、freeeのアプリ管理と合わせる)


 'freeeaccounting' => [
        'client_id' => env('FREEE_ACCOUNTING_CLIENT_ID'),
        'client_secret' => env('FREEE_ACCOUNTING_CLIENT_SECRET'),
        'redirect' => 'http://localhost:8000/auth-callback',
    ],

config/auth.php

freee用ドライバー、プロバイダー設定

    'defaults' => [
        'guard' => 'freee',
        'passwords' => 'users',
    ],
....
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
        'freee' => [
            'driver' => 'freee',
            'provider' => 'freee',
        ],
    ],
...
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        'freee' => [
            'driver' => 'freee',
        ],
    ],

app\Http\Auth\LoginController.php

ログイン処理は、Laravel Socialite ドライバーを利用し、GenericUser で認証する


use App\Http\Controllers\Controller;
use Illuminate\Auth\GenericUser;
use Illuminate\Support\Facades\Auth;
use Socialite;

...

    public function redirectToProvider()
    {
        return Socialite::driver('freeeaccounting')->redirect();
    }

    public function handleProviderCallback()
    {
        $user = Socialite::driver('freeeaccounting')->user();

        $genericUser = $user->getRaw();
        $genericUser['token'] = $user->token;
        $genericUser['remember_token'] = '';
        Auth::login(new GenericUser($genericUser));

        return redirect()->intended($this->redirectTo);
    }

    public function logout()
    {
        Auth::logout();
        return redirect()->intended('/');
    }

認証用サービスプロバイダー

app/Providers/AuthServiceProvider.php


namespace App\Providers;

use App\Extensions\SampleSessionGuard;
use App\Extensions\FreeeUserProvider;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;

class AuthServiceProvider extends ServiceProvider
{
  protected $policies = [
    // 'App\Model' => 'App\Policies\ModelPolicy',
  ];
  public function boot()
  {
    $this->registerPolicies();

    Auth::extend('freee', function ($app, $name, array $config) {
      return new SampleSessionGuard(
        $name,
        Auth::createUserProvider($config['provider']),
        $app['session.store']
      );
    });

    Auth::provider('freee', function ($app, array $config) {
      return new FreeeUserProvider();
    });
  }
}

イベントサービスプロバイダー

app/Providers/EventServiceProvider.php


namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use SocialiteProviders\Manager\SocialiteWasCalled;
use SocialiteProviders\FreeeAccounting\FreeeAccountingExtendSocialite;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
        SocialiteWasCalled::class => [
            FreeeAccountingExtendSocialite::class,
        ],
    ];

    public function boot()
    {
        parent::boot();
    }
}

SocialiteProviders設定

SocialiteProviders/src/FreeeAccounting/Provider.php

https://api.freee.co.jp/api/1/users/me からユーザを返す関数を作成


    protected function getUserByToken($token)
    {
        $response = $this->getHttpClient()->get('https://api.freee.co.jp/api/1/users/me', [
            'headers' => [
                'Authorization' => 'Bearer '.$token,
            ],
        ]);

        $body = json_decode($response->getBody(), true);
        return $body['user'];
    }

User のマッピング


    protected function mapUserToObject(array $user)
    {
        $user['name'] = $user['last_name'] . ' ' . $user['first_name'];
        return (new User())->setRaw($user)->map([
            'id'       => $user['id'],
            'name'     => $user['name'],
            'email'    => $user['email'],
            'display_name' => $user['display_name'],
            'first_name' => $user['first_name'],
            'last_name' => $user['last_name'],
            'first_name_kana' => $user['first_name_kana'],
            'last_name_kana' => $user['last_name_kana'],
        ]);
    }

事業所と取引(収入/支出)一覧の取得

app/Http/AccountController.php

ログイン認証したユーザトークンを、Configuration クラスに設定


$user = Auth::user();
$config = Configuration::getDefaultConfiguration()->setAccessToken($user->token);
ちなみに、getAccessToken() でアクセストークンを確認できる。

$config->getAccessToken()

CompaniesApi で事業所IDを取得


$companiesApiInstance = new CompaniesApi(null, $config);
$companiesResponse = $companiesApiInstance->getCompanies();
$targetCompanyId = $companiesResponse->getCompanies()[0]->getId();

DealsApi で取引(収入/支出)一覧の取得


$limit = 5;
$dealsApiInstance = new DealsApi(null, $config);
$dealsResponse = $dealsApiInstance->getDeals(
            $targetCompanyId,
            null, null, null, null, null, null, null, null, null, null, null, null,
            $limit);
$deals = $dealsResponse->getDeals();

getDeals() の引数がちょっとどうなんだろ?

  2020/01/12

Laravel 6.x Laravel Homestead を利用して Laravel 6.x の環境を構築する。 Homestead は Laravel の vagrant box 版です。

一からコマンドベースで構築するとかなり大変(というか npm の依存とかでエラー多発)なので、公式でも Homestead を推奨しています。

Vagrant バージョンの確認

Download Vagrant から最新の vagrant をインストール(アップデートする) Laravel 6.x Homestead は 2.2.6 以上が必要

$ vagrant --version
Vagrant 2.2.6

プラグインアップデート

Vagrant アップデートした場合は、プラグインもアップデートする必要がある。

$ vagrant plugin update
Updating installed plugins...
Updated 'vagrant-vbguest' to version '0.23.0'!

Vagrant に Homestead を追加

$ vagrant box add laravel/homestead
==> box: Loading metadata for box 'laravel/homestead'
    box: URL: https://vagrantcloud.com/laravel/homestead
==> box: Adding box 'laravel/homestead' (v9.2.2-alpha1) for provider: virtualbox
    box: Downloading: https://vagrantcloud.com/laravel/boxes/homestead/versions/9.2.2-alpha1/providers/virtualbox.box
    box: Download redirected to host: vagrantcloud-files-production.s3.amazonaws
...

Vagrant Box の確認

$ vagrant box list
bento/ubuntu-18.04 (virtualbox, 201906.18.0)
centos/7           (virtualbox, 1905.1)
debian/stretch64   (virtualbox, 9.9.1)
laravel/homestead  (virtualbox, 9.2.2-alpha1)
ubuntu/trusty64    (virtualbox, 20190429.0.1)

Homestead 設定

Git から Homestead 設定を cloneする

$ cd
$ git clone https://github.com/laravel/homestead.git Homestead

初期化

$ bash init.sh
Homestead initialized!

SSH鍵ファイルの作成

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/yoo/.ssh/id_rsa):                   
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
...

id_rsa と id_rsa.pub が作成される。

Homestead.yaml 編集

Homestead の起動設定ファイル Homestead.yaml を設定する


ip: "192.168.10.10" #プライベートIPを設定する
memory: 2048
cpus: 2
provider: virtualbox

authorize: ~/.ssh/id_rsa.pub

keys:
    - ~/.ssh/id_rsa

folders:
    - map: ~/vagrant/homestead/projects #ホスト側のプロジェクトパスを設定
      to: /home/vagrant/projects #Vagrant側のプロジェクトパスを設定
      type: "nfs" #nfs を追記する

sites:
    - map: homestead.test #サーバホストを設定する
      to: /home/vagrant/projects/blog/public #Laravel Webルートを設定

databases:
    - homestead

features:
    - mariadb: false
    - ohmyzsh: false
    - webdriver: false

# ports:
#     - send: 50000
#       to: 5000
#     - send: 7777
#       to: 777
#       protocol: udp

上記の設定は、以下のようなパスを設定している


・ホスト側(Mac)
~/vagrant/homestead/projects #プロジェクトパス

・リモート側(Vagrant)
/home/vagrant/projects #プロジェクトパス
/home/vagrant/projects/blog/public #Laravel の public

注意すべくは「folders:」の設定 デフォルトの設定だとNFSマウント(同期)できないので「type: "nfs"」を追記する

vagrant-bindfs プラグインインストール

NFSを利用した場合、ファイルやフォルダへのアクセス権限を維持する為に、vagrant-bindfs プラグインのインストールが必要

$ vagrant plugin install vagrant-bindfs

Vagrant 起動

$ vagrant up

Homestead Vagrant

Vagrant SSHログイン

$ vagrant ssh

各バージョン確認

$ cat /etc/issue
Ubuntu 18.04.3 LTS \n \l
$ laravel -V
Laravel Installer 3.0.1
$ composer -V
Composer version 1.9.1 2019-11-01 17:20:17
$ node --version
v12.14.0
$ yarn --version
1.21.1
$ npm --version
6.13.4
$ php --version
PHP 7.4.1 (cli) (built: Dec 18 2019 14:44:22) ( NTS )
$ psql -V
psql (PostgreSQL) 11.6 (Ubuntu 11.6-1.pgdg18.04+1)

Laravel プロジェクト作成

Vagrant側で Laravel プロジェクトを作成する。 (あらかじめ、~/projects/ ディレクトリ作成済み)

$ cd projects/
$ composer create-project --prefer-dist laravel/laravel blog

ブラウザから Webサーバにアクセス

http://homestead.local にアクセスできるか確認する。 Homestead Laravel

PostgreSQL設定のロケール

en_US.UTF-8 がデフォルトになっているので、ja_JP.UTF-8 に変更する ロケールの再構築 参照

  2019/08/09
try-catch でエラー検出する場合、namespace内でException を待ち受けするため「 Exception」と記述する必要がある。

Laravel の QueryException を検知する場合

    use  Illuminate Database QueryException;
....
    public function store(Request $request)
    {
        try {
            $user = new User;
            $user->fill($request->all())->save();
        } catch ( QueryException $e) {

        }
    }
QueryException
<< Top < Prev Next > Last >>