write ahead log

ロールフォワード用

Homesteadで別名のVMを作りたい

プロジェクトが複数同時に走るとか, よくある事なので.

普通はどうするんだ?

以下を見ると1つのVMを共有して使うようですね.

Homesteadを使って複数のLaravelプロジェクトを作成する簡単な方法

ホスト名で切り分けると.

VM分けたいんだけど...

VM分けたいです.

Boxエクスポートして他に回せるし, よくわかんなくなったらVMごと消せばいいので.

もっと言うとホストにPHPも入れたくない.(極力何も入れたくない)

うーん, と悩んでたら以下の記事を見つけました.

Multiple Homestead versions for simple Laravel development

試しにname指定したら普通に別名のVMになったので, これでいいかな.

Homestead.yaml

# Homestead.yaml
ip: "192.168.10.10"
memory: 2048
cpus: 1
provider: virtualbox
name: experiment-homestead7  # 追記

authorize: ~/.ssh/id_rsa.pub

このyamlってまだよくわかってないのでちょっと扱いに困る.

Vagrantのドキュメント見ればよいのか, Homesteadのドキュメント見れば良いのか...

WSLでRubyを使うとなんか警告が出て消したい

昔対処して忘れてたのでメモしておく.

Insecure worldほにゃららがやたら出る

WSLでVagrantを使ったりすると以下の様なメッセージが表示される.

warning: Insecure world writable dir /mnt/c in PATH, mode 040777

Rubyは親切なので権限が過剰じゃないかと気にしてくれているらしい.

WSLはUnixとは違うので権限がやたら与えられている.これはしょうがない.

対処方法

見つかった方法は2つ.

環境変数PATHにWindowsのパスを含めない

Windowsコマンドを使うために追加されているパスを消してしまう.

export PATH="$(echo "$PATH" | sed -r -e 's;:/mnt/[^:]+;;g')"

これでWSLからWindowsコマンドは使えなくなるが, Rubyの警告は出る.

Rubyのオプションで警告を無視する

ubuntuの~/.profileの末尾に以下を追加.

export RUBYOPT=-W0

これでWSLのRubyでは常に警告が抑制される.

どちらで行くか

開発でWSL使う人は必然的にパスを削除する側になるでしょう.
(警告無視して開発するわけにもいきませんし)

開発はVM上でという方は警告抑制が割といい気がします.

ちょっとしたスクリプティングなら気にならないので.

参考

この記事は実質, このURLを探すのに苦労したのでメモってるだけ.

ククログ - Windows 10のWindows Subsystem for Linux(WSL)を日常的に活用する

Laravelでクエリビルダを使う

Laravel 5.7.4で試した.

LaravelにはQuery Builderが備わっています.

SQLインジェクション対策も施されていて, それなりに複雑なクエリも書けるので, O/RマッパーのEloquentだと複雑になりそうな時には便利っぽいです.

試すための事前準備

DBにはPostgreSQLを使いました.

テーブルは以下のようにします.

DEPT(部署)

キー 列名 意味
Primary ID ID 数値
Unique DNO 部署コード 文字列
DNAME 部署名 文字列
BUDGET 予算 数値
LAST_UPDATE 最終更新日時 日付

EMP(従業員)

キー 列名 意味
Primary ID ID 数値
Unique ENO 従業員コード 文字列
ENAME 従業員名 文字列
Foreign DEPT_ID 部署ID 文字列
SALARY 給与 数値
LAST_UPDATE 最終更新日時 日付

C.J Dateのデータベース実践講義を元に日付を足しています. また, キー項目をIDに変更しています.

DDL文は以下の通り.

CREATE TABLE dept(
     id INTEGER PRIMARY KEY
    ,dno VARCHAR(20) UNIQUE
    ,dname VARCHAR(30)
    ,budget NUMERIC(13, 3)
);

CREATE TABLE emp (
     id INTEGER PRIMARY KEY
    ,eno VARCHAR(10) UNIQUE
    ,ename VARCHAR(50)
    ,dept_id integer
    ,salary NUMERIC(13)
    ,last_update TIMESTAMP
);

テスト用にデータも入れておく.

INSERT INTO dept VALUES(1, '001', 'sales', 1000000);
INSERT INTO dept VALUES(2, '002', 'develop', 8000000);
INSERT INTO dept VALUES(3, '003', 'legal', 300000);

INSERT INTO emp VALUES(1, '001', 'yamada', 1, 200000, TO_TIMESTAMP('2000/04/01 10:15:00', 'YYYY/MM/DD HH24:MI:SS'));
INSERT INTO emp VALUES(2, '002', 'saito', 1, 280000, TO_TIMESTAMP('1996/04/01 10:15:00', 'YYYY/MM/DD HH24:MI:SS'));
INSERT INTO emp VALUES(3, '003', 'tanaka', 2, 280000, TO_TIMESTAMP('1996/04/01 10:15:00', 'YYYY/MM/DD HH24:MI:SS'));
INSERT INTO emp VALUES(4, '004', 'sonoda', 2, 380000, TO_TIMESTAMP('1990/04/01 10:15:00', 'YYYY/MM/DD HH24:MI:SS'));
INSERT INTO emp VALUES(5, '005', 'honda', 3, 300000, TO_TIMESTAMP('2001/04/01 10:15:00', 'YYYY/MM/DD HH24:MI:SS'));

プロジェクトとコントローラを作る

適当な名前でプロジェクトを作ります.

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

コントローラも適当に作ります.

$ php artisan make:controller Employees

コントローラからクエリビルダを使う

普通, クエリビルダを使う状況はコントローラからでしょう.

コントローラから使う場合のテンプレートはこんな感じです.

<?php                                                                                             1
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;   #<= 追加

class Employees extends Controller
{
    public function index()
    {
        # empテーブルの値を全て取得してemps変数へ入れる
        # empsはPHPのstdClassオブジェクトのコレクション
        $emps = DB::table('emp')->get();
    
        # ビューへ渡す
        return view('employees.index', ['employees' => $emps]);
    }
}

クエリビルダは結果をPHPstdClassインスタンスとして返します.

getは結果をIlluminate\Support\Collectionインスタンス(配列みたいな感じで扱える)で返すメソッドなので, そのままビューに渡してBladeなんかで扱えます.

<table>
    <tr>
        <th>社員名</th>
    </tr>
@foreach ($employees as $emp)
    <tr>
        <td>{{ $emp->ename }}</td>
    </tr>
@endforeach
</table>

tinkerを使う

クエリビルダを試すためだけに毎回コントローラいじるのは面倒なので, consoleを使います.

RailsのconsoleみたいなREPLはLaravelではtinkerのようです.

ちなみにtinkerは下手な職人の意らしく, artisanの弟子なんでしょうか.洒落がきいてます.

vagrant@homestead:~/code$ php artisan tinker
Psy Shell v0.9.8 (PHP 7.2.5-1+ubuntu18.04.1+deb.sury.org+1 — cli) by Justin Hileman
>>>

試しに上述のコントローラで書いたクエリを実行してみます.

>>> $emps = DB::table('emp')->get();
=> Illuminate\Support\Collection {#2893
     all: [
       {#2891
         +"id": 1,
         +"eno": "001",
         +"ename": "yamada",
         +"dept_id": 1,
         +"salary": "200000",
         +"last_update": "2000-04-01 10:15:00",
       },
       {#2899
         +"id": 2,

いい感じです.

ちなみにtinkerを終了させるには「q」とだけタイプします.

>>> q
Exit:  Goodbye

あれこれ試す

適当に使いそうなやつだけかいつまんで触ってみます.

データを取得する

全部手に入れる

getを使うと指定条件下のデータが全部手に入ります.

>>> $data = DB::table('dept')->get()
=> Illuminate\Support\Collection {#2895
     all: [
       {#2897
         +"id": 1,
         +"dno": "001",
         +"dname": "sales",
         +"budget": "1000000.000",
       },
       {#2888
         +"id": 2,
         +"dno": "002",
         +"dname": "develop",
         +"budget": "8000000.000",
       },
       {#2901
         +"id": 3,
         +"dno": "003",
         +"dname": "legal",
         +"budget": "300000.000",
       },
     ],
   }

1件だけ手に入れる

firstメソッドで指定条件下の最初の1件だけ手に入れます.

>>> $data = DB::table('dept')->first()
=> {#2904
     +"id": 1,
     +"dno": "001",
     +"dname": "sales",
     +"budget": "1000000.000",
   }

特定カラムだけ手に入れる

pluckメソッドで指定カラムだけ手に入れます.

>>> $data = DB::table('dept')->pluck('dno')
=> Illuminate\Support\Collection {#2912
     all: [
       "001",
       "002",
       "003",
     ],
   }

複数カラム指定できないのかな?ちょっと惜しい.

普通のWhere

>>> $data = DB::table('dept')->where('dno', '=', '001')->get()
=> Illuminate\Support\Collection {#2903
     all: [
       {#2909
         +"id": 1,
         +"dno": "001",
         +"dname": "sales",
         +"budget": "1000000.000",
       },
     ],
   }

WhereでOR

>>> $data = DB::table('dept')->where('dno', '=', '001')->orWhere('dno', '=', '002')->get()
=> Illuminate\Support\Collection {#2914
     all: [
       {#2912
         +"id": 1,
         +"dno": "001",
         +"dname": "sales",
         +"budget": "1000000.000",
       },
       {#2901
         +"id": 2,
         +"dno": "002",
         +"dname": "develop",
         +"budget": "8000000.000",
       },
     ],
   }

Whereは他にも強力なのがたくさんあって凄い.

INNER JOIN

普通にjoinできて便利.

>>> $data = DB::table('dept')->join('emp', 'dept.id', '=', 'emp.dept_id')->get()
=> Illuminate\Support\Collection {#2922
     all: [
       {#2900
         +"id": 1,
         +"dno": "001",
         +"dname": "sales",
         +"budget": "1000000.000",
         +"eno": "001",
         +"ename": "yamada",
         +"dept_id": 1,
         +"salary": "200000",
         +"last_update": "2000-04-01 10:15:00",
       },
       [略]

もっと知りたい

以下は更に詳しいので使う時にはリファレンス的に.

Group Byやunionなどの複雑なクエリも使えるのでいい感じ.

Laravel bladeで長い文字列を丸める

Laravel Framework 5.7.4で確認.

ググっても案外パッとは出てこなかったので書いておく.

Railsのtruncateみたいなのが欲しかった.

str_limitを使えば良いらしい.

{{ str_limit($string, $limit = 150, $end = '...') }}

Helperがいっぱいあるので一通り見ておいた方が良いのかもしれん.

参考

StackOverFlow - Truncate string in Laravel blade templates

Laravel - Homestead環境でPostgreSQLを使う時のメモ

一応メモっとく.

Homesteadで環境は作ってある前提

プロジェクトを作る&バージョン確認

codeというプロジェクトを作成.

vagrant@homestead:~$ composer create-project --prefer-dist laravel/laravel code

バージョンは5.7.4

vagrant@homestead:~/code$ php artisan --version

PostgreSQLのバージョンを確認

vagrant@homestead:~/code$ psql --version
psql (PostgreSQL) 10.3 (Ubuntu 10.3-1)

とりあえずpsqlでつないでみる

  • ユーザーはhomestead
  • パスワードはsecret
  • データベースはhomestead

で最初から用意されていた.

vagrant@homestead:~/code$ psql -U homestead -h localhost
Password for user homestead:
psql (10.3 (Ubuntu 10.3-1))
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

homestead=#

Laravelの環境設定

.envに書いてあるので変更します.

vagrant@homestead:~/code$ pwd
/home/vagrant/code
vagrant@homestead:~/code$ vi .env

mysqlがデフォルトなのでpgsqlへ変更. ポート番号も変えておく.

#DB_CONNECTION=mysql
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
#DB_PORT=3306
DB_PORT=5432
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

マイグレーションしてみる

プロジェクト作成時に用意されるusersテーブルをマイグレーションしてみる.

vagrant@homestead:~/code$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table

確認

一式生成されていた.

vagrant@homestead:~/code$ psql -U homestead -h localhost
Password for user homestead:
psql (10.3 (Ubuntu 10.3-1))
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

homestead=# \d
                 List of relations
 Schema |       Name        |   Type   |   Owner
--------+-------------------+----------+-----------
 public | migrations        | table    | homestead
 public | migrations_id_seq | sequence | homestead
 public | password_resets   | table    | homestead
 public | users             | table    | homestead
 public | users_id_seq      | sequence | homestead
(5 rows)

homestead=#

とても簡単だ.良く出来ている.

Laravel標準の機能で認証フォームを作る

Homestead上のLaravel 5.7.4で試した.

Laravelには認証を実装するための機能が標準で付いてきています.

認証はどこでも必要になるので入門するには良いとっかかりということで, 利用してみました.

実装できる機能

標準でもこれだけ実装できます.

ほとんどのプロジェクトはこれで十分でしょう.

  • ユーザ登録
  • ログイン
  • ログアウト
  • パスワードリセット

プロジェクトの作成

Homesteadのデフォルトに合わせてcodeプロジェクトにしました.

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

認証機能用コードのScaffolding

認証機能を使うには, まずはプロジェクトフォルダ直下で以下のコマンドを実行します.

$ php artisan make:auth

これだけで認証機能のベースになるコードが生成(Scaffolding)されます.

$ tree app/Http/Controllers
app/Http/Controllers/
├── Auth
│   ├── ForgotPasswordController.php
│   ├── LoginController.php
│   ├── RegisterController.php
│   ├── ResetPasswordController.php
│   └── VerificationController.php
├── Controller.php
└── HomeController.php

Authフォルダ以下とHomeController.phpが生成されています.

DBのマイグレーション

認証に使うユーザ情報はDBに保持します.

database/migrations以下を見ると先のコマンドで既にマイグレーションファイルが生成されています.

$ ls database/migrations/
2014_10_12_000000_create_users_table.php  2014_10_12_100000_create_password_resets_table.php

そのままマイグレーションします.

$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table

実行してみる

なんと実装はこれだけで, トップページへアクセスしてみると既にナビゲーションバーにloginとregisterの項目ができています.

Auth::routes();

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

確認のために雑にページを作る

ログインしていないユーザがアクセスできないページが欲しいので, 雑にページを生成します.

PostsControllerを作ります.
(不要なアクションも含めresourceオプションで生成)

$ php artisan make:controller PostsController
Controller created successfully.

アクションを追加.

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

public function create()
{
    return view('posts.create');
}

Postsのビューも用意して.

$ mkdir resources/views/posts
$ echo "index" > resources/views/posts/index.blade.php
$ echo "create" > resources/views/posts/create.blade.php

ルートも追加しておきます.

# routes/web.php
Route::get('/posts/', 'PostsController@index');
Route::get('/posts/create', 'PostsController@create');

認証による制御

ルートのフィルタ

ルートへのアクセスの可否を制御するには2つの方法があるっぽい.

ルート定義に記述

ルート定義にメソッドチェーンして, middleware('auth')を呼び出す.

# routes/web.php
Route::get('/posts/create', 'PostsController@create')->middleware('auth');
コントローラのコンストラクタに記述

ただし, この場合はコントローラのアクション全体に認証によるフィルタがかかる.

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

認証済みか調べる

コントローラ内で簡単に調べられます.

use Illuminate\Support\Facades\Auth;

if (Auth::check() === true) {
    // ユーザーはログインしている
}

ログインユーザを調べる

use Illuminate\Support\Facades\Auth;

// ログインユーザーの取得
$user = Auth::user();

// ログインユーザーのIDを取得
$user_id = Auth::id();

ログイン後などのリダイレクト先の変更

デフォルトでは

/home

へリダイレクトされるが, カスタマイズできる.

app/Http/Controllers/Auth/

配下にある3つのコントローラの定義をそれぞれ変更すればよい.

  • LoginController
  • RegisterController
  • ResetPasswordController
# これを変える
protected $redirectTo = '/home';

ログアウトさせる

以下を呼び出す.

Auth::logout();

ビューを変える

resources/views/auth/にビューがあるので普通に変えればよいです.

言語化

resources/lang/enに

が出来ていました.

テスト

actingAsメソッドで認証できる.

public function testAccessSecurePage()
{
    $user = factory(App\User::class)->create();
    $response = $this->actingAs($user)
                     ->get('/posts/create');
    $response->assertStatus(200);
}

public function testDenySecurePage()
{
    $response = $this->get('/posts/create');
    $response->assertStatus(302);
}

参考

すごく良い翻訳ドキュメントがあった.

最初からこれを読めばよかった.

Github - laravel-ja/ja-docs-5.7

公式 - testing

Postgresのダンプの取得とリストアの方法

毎回忘れるので.

アーカイブ形式で取る場合の例を記載.

Fcオプションを外せばスクリプト形式(SQL出るやつ)になる.

バックアップ(ダンプ)の取得.

pg_dump -U [ユーザ名] -Fc -d [データベース名] > [保存先フルパス]

リストア

1. ユーザを作成
> psql -U postgres
postgres => CREATE TABLE [ユーザ名] WITH LOGIN PASSWORD 'パスワード';
2. DBを作成
> psql -U postgres
postgres => CREATE DATABASE [DB名] OWNER=[オーナーユーザ名]
3. ダンプを投入
pg_restore -d [DB名] -Fc -U [ユーザ名] [ダンプファイルのパス]

cオプションをつけると投入前に既存テーブルをdropする.

pg_restore -d [DB名] -Fc -U [ユーザ名] -c [ダンプファイルのパス]

CオプションをつけるとDBも作ってくれるらしいが使ったことない.

4. 確認
> psql -U [ユーザ名] -d [DB名]