20190526のlaravelに関する記事は5件です。

LaravelでクエリビルダのJoinを使って検索機能を作ろうとしたらハマった

当方、Laravel初心者です。

Eloquentモデルをクエリビルダのjoin()を使って関係するテーブルのカラムを使って絞り込んだら、
検索対象じゃないモデルのインスタンスが返ってきて小一時間悩みました。

まとめ

Eloquentモデルをクエリビルダのjoin()を使ってインスタンスを取得したら、
idというカラムが複数存在したため、
想定外のインスタンスが返ってきた。

同じカラム名はLaravel内の処理で上書きされる。
Select()で取得するカラムを絞ろう!

詳細

たとえばこんなコード。

$searched_posts = Post::join('comments', 'posts.id', '=', 'comments.posts_id')
  ->where('comments.text', 'like', '%あいうえお%')->get();

postsテーブルにもcommentsテーブルにも「id」というカラムがある前提です。

上記のコードをSQLに変換されると多分こんな感じになります。

select * from posts 
  inner join comments on posts.id = comments.post_id 
  where comments.text LIKE '%あいうえお%';

仮にSQLの実行結果が以下のような感じだったとします。

id post_content id text
1 あああ 2 あいうえお
3 ううう 3 あいうえお
5 おおお 4 あいうえお

これでPostモデルのインスタンスが3つ取れて、
post_contentはそれぞれ「あああ」「ううう」「おおお」となるんだろうな とおもったら
なぜか全然違うPostが返ってきました。

そうです。
idカラムが重複して存在するため、
posts.idcomments.idが上書きしてしまい、
id2,3,4であるPostがモデルインスタンスとして取得されてしまいました。

解決策

selectで取ってくるカラムを絞る。

$searched_posts = Post::select('posts.*')->join('comments', 'posts.id', '=', 'comments.posts_id')
  ->where('comments.text
', 'like', '%あいうえお%')->get();

select('posts.*')のように、取得するカラムを絞り込むことで、idというカラムが重複しないようにしましょう。

こういうハマりポイントは知っていればすぐ解決できるのに、知らないと結構はまってしまいますよね。
Laravelを覚えるにあたって先にハマりポイントを押さえておこうと思いました:frowning2:

そもそもクエリビルダのJoinを使って検索をするっていうのがイケてないっぽいので
よりよい方法があれば教えていただきたく思います。

whereHas()を使うといいのかな?

Post::whereHas('Comment', function($comment){
    $comment->where('text', 'like', '%あいうえお%');

以上、お役に立てれば幸いです

参考

laravelのクエリビルダでJOINしたら、同名カラムの値が上書きされるという事案発生
Laravel Eloquent でハマらないために知っておきたいこと

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

Laravel で Service 層を取り入れるときに検討したいこと

この記事について

普段何気なく使っている Service クラス(Service 層)について、書籍を中心にその役割や目的について書かれた資料を読みながら、Laravel 製のウェブアプリケーションに Service 層を取り入れる際の判断材料となるような情報や観点を、提供できればと思います。

以前にも調べたり考えたりしたんですが、そのときは明確な役割や使い方の提案ができなかったので、それの補遺的な位置づけになります。

Laravelでウェブアプリケーションをつくるときのベストプラクティスを探る (9) Service編 - Qiita

Service レイヤーとは

これ

出典: Martin Fowler's Bliki
https://martinfowler.com/eaaCatalog/serviceLayer.html

どんなときに Service 層が必要になるか

Despite their different purposes, these interfaces often need common interactions with the application to access and manipulate its data and invoke its business logic.

そういった異なる目的(訳者注: 上図の Data Loaders、User Interfaces、Integration Gateway の目的)にかかわらず、これらのインタフェースには、データを操作したりビジネスロジックを実行し対するために、アプリケーションとの共通のやりとりが必要になるときがある。

ようするに、UI からだけでなく、CUIのプログラムとか他システムとの連携部分とかで、共通のロジックを呼ぶ必要があるときに、Service レイヤー越しにドメインロジックを処理すると、共通のインタフェースで操作できるようになるよね、ということと理解できます。

たとえば、

// via Web User Interface
class SomeController extends Controller {
    public function someAction(Request $request) {
        $this->someService->doSomething($request->all());
    }
}

// via Command
class SomeCommand extends Command {
    public function handle() {
        $this->someService->doSomething($this->options());
    }
}

// via Another Service
class AnotherService {
    public function doSomething() {
        $this->someService->doSomething($params);
    }
}

このように処理をラップすることで、上記3つのクライアントがそれぞれ、引数の構築だけを担って、実際の処理を Service クラスに移譲することができるようになります。

Service クラスとは

Service には2種類ある

  • Application Service: Application 層にある Service(何の説明にもなってないですが、後述します)
  • Domain Service: Domain 層にある Service(何の(ry

Domain Service の定義

Domain といえば「ドメイン駆動設計」ですね。

「ドメイン駆動設計」では、巻末で以下のように定義されています:

SERVICE An operation offered as an interface that stands alone in the model, with no encapsulated state.

Evans, Eric. Domain-Driven Design

「サービス
モデルの中で独立したインタフェースとして提供される操作、カプセル化された状態を持たない。」

ちょっとこれだけだとよく分からないので、もう少し詳細な説明を探してみます。

When a significant process or transformation in the domain is not a natural responsibility of an ENTITY or VALUE OBJECT, add an operation to the model as a standalone interface declared as a SERVICE.

「ドメインにおける重要な処理や変換が、エンティティや値オブジェクトの自然な責務ではない場合、サービスとして宣言された独立したインタフェースとしての操作をモデルに追加してください。」

さらに、以下のような記述もあります。

Unlike ENTITIES and VALUE OBJECTS, it is defined purely in terms of what it can do for a client.

「エンティティや値オブジェクトとは異なり、純粋に、クライアントに対してなにができるか、という観点でのみ定義されます。」

ドメインとはなにか、というのは下記の記事も参照してください。

ドメインモデル、ドメインロジックとは何かをコードを交えて考えてみる - Qiita

Application Service の定義

It can be harder to distinguish application SERVICES from domain SERVICES. The application layer is responsible for ordering the notification.

Evans, Eric. Domain-Driven Design

「Application Service と Domain Service を区別するのはもう少し難しいかもしれない。Application 層の責務は通知を命令することである。」

この文の前段で、銀行のシステムで口座の残高がある閾値を超えた場合にメールを送信する、というような例が紹介されていて、閾値のチェックは Domain 層、メールの送信を命令するのは Application 層、メールを送信するのは Infrastructure 層、という切り分けであると述べています。

また他の箇所では、

layer. For example, if the banking application can convert and export our transactions into a spreadsheet file for us to analyze, that export is an application SERVICE.

Evans, Eric. Domain-Driven Design

Spreadsheet へのエクスポート機能も Application Service と言っています。

明確な定義はないものの、Infrastructure 層(メールなどの通知系、ファイルやデータベースの操作、など)へ命令を下すのが Application Service である、と言えそうです。

よい Service の特徴

  1. The operation relates to a domain concept that is not a natural part of an ENTITY or VALUE OBJECT.
  2. The interface is defined in terms of other elements of the domain model.
  3. The operation is stateless.

Evans, Eric. Domain-Driven Design

  1. その操作が、エンティティや値オブジェクトの自然な一部でないようなドメインの概念に関連したものである
  2. そのインタフェースが、ドメインモデルの他の要素の観点から定義されている
  3. その操作が、状態を持たない

Laravel における Service

Service Providers のセクションに出てきます。

Your own application, as well as all of Laravel's core services are bootstrapped via service providers.

https://laravel.com/docs/5.8/providers

あまり深くは見ませんが、config/app.php で providers に登録されている様々な Service Provider の中身を見てみると、命名だったり、初期化の仕方だったり、を参考にできるかもしれません(ただし、後述しますが、アプリケーションで Service をつくるときは、必ずしもサービスコンテナに登録する必要はないと考えています)。

Facade は一種の Service である(と言えるのではないか)

そうして見てみると、いくつかの Facade が Service Provider で登録されていることが分かりました。

そうでない Facade についても、たとえば Log の場合、

  1. ウェブアプリケーションフレームワークというドメインにおいて、ログ操作というのは、どのモデルにも属さない独立した操作である
  2. とはいえ、デバッグ、エラー、といった、アプリケーションの状態に関わるインタフェースがある
  3. 状態を持たない

といった特徴があり、よい Service クラスの特徴に合致しています。

「Facade は一種の Service である」と考えると、どういう機能を Service 化するのがいいのか、のヒントになるような気がします。

Application 層と Domain 層の境界

  • Application 層
    • UI
    • Request(アプリケーション外部からの入力パラメータ)
    • Job/Event(アプリケーションの外部に対する通知やコマンド実行)
    • Response(アプリケーション外部への出力パラメータ)
    • Command(php artisan CLI で実行されるコマンド)
    • app/Http, app/Console, Exceptions, Events, Listeners, Jobs, Policies, Providers
  • Domain 層
    • Laravel に依存しない部分、DB に依存しない部分
    • 厳密には上記の定義だが、Eloquent Model (Active Record) はドメインロジックを書く一方で、中で DB に依存した処理が含まれるものもあり、境界はあいまい
    • Laravel ではどこに配置するかは決まっていない
    • app 以下じゃなくてもいい
  • Service 層
    • Application 層と Domain 層の間に位置し、Application Service はアプリケーション層に、Domain Service は Domain 層に配置する
  • Infrastructure 層
    • DB
    • Mail
    • Notification (mail, slack, etc.)
    • Queue (database, redis, etc.)

Application Service の例

例1: Excel への Export

なんらかのデータを整形して Excel 形式のファイルに出力してダウンロードするというのは、Service 化がもっとも適した例だと思います。

$activeUsers = User::active()->get();
// 出力形式の他にテンプレートファイルを渡すのもいいかもしれません
$exporter = Factory::factory(ExportType::Excel);
// ファイルに保存するなら
$exporter->toFile($activeUsers, $dir, $name);
// Response として返すなら
$exporter->toStream($activeUsers);

例2: 複数のエンティティを一度に更新するようなケース

たとえば UI の都合で、複数のエンティティを一度に更新するといったケースでは、Controller から直接操作するよりも、Service を挟んだほうがいい気がします(UI が頻繁に変わる場合でも、Controller に影響が及ばないようにできます)。

エンティティを個別に操作するパターン

class SomeController {
    public function update(UpdateRequest $request) {
        $someModel->update($request->someAttributes());
        $otherModel->update($request->otherAttributes());
    }
}

Serivce を介して更新するパターン

class SomeController {
    public function update(UpdateRequest $request) {
        $this->updateSomeServce->update($request->validated());
    }
}

Domain Service の例

例1: ECサイトにおける Recommendation

Recommend されるのは「商品」だが、「商品」に「レコメンド」の責務を持たせるのは不自然かな、と思ったら、Service クラスに切り出すことができるでしょう。

「おすすめ」インタフェースに対して複数の実装を持たせ、内部のアルゴリズムをインタフェース越しに差し替えることもできます。

class RecommendController {
    public function __invoke() {
        $user = Auth::user();
        return $this->recommender->recommendTo($user); // Userに合わせたレコメンド
    }
}

例2: ホテルの予約サービスにおける Reservation

「予約」というエンティティがあるにはあるが、「予約」という振る舞いに対して、内部で複数のエンティティを連動して操作しなければいけないときに、Service クラスに切り出すことができるでしょう。

ロジックが巨大で複雑になるようなら、さらに「空き状況確認サービス」などといった処理ごとに別の Service に分解してもいいかもしれません。

class Reservation {
    public function invoke(Accommodation $accommodation) {
        if ($this->vacancyFinder->vacant($accommodation)) {
            throw new AlreadyOccupiedException('…');
        }
        // ...
    }
}

Service 層を取り入れる際に検討したいこと

振る舞いを Entity に持たせるか Service に持たせるか

Service を使ったバージョン

class ReservationService {
    public function cancel(Reservation $reservation) {
        $reservation->is_cancelled = true;
        $reservation->save();
    }
}
// Client
$this->reservationService->cancel($reservation);

エンティティにメソッドを持たせず、プロパティを直接変更しています。

Entity を使ったバージョン

class Reservation {
    public function cancel() {
        $this->is_cancelled = true;
        $this->save();
    }
}
// Client
$reservation->cancel();

こちらは、プロパティの変更はエンティティ内で行い、クライアントはエンティティのメソッドを呼び出しています。

「キャンセル」という操作と、実際に更新されるデータが分離されているので、仮にデータ構造が変わり、更新する対象データが変わっても、クライアントに影響が及ぶことがありません( is_cancelled の代わりに cancelled_at にキャンセル日時を入れるようにした場合、とか)

委譲するかしないか

Service を使ったバージョン

class SomeService {
    public function create(SomeModel $model) {
        return $this->repository->create($model);
    }
    public function update(SomeModel $model) {
        return $this->repository->update($model);
    }
}

いわゆる Repository パターンを使った、Controller -> Service -> Repository という流れでのデータ操作の方法です。

Entity を使ったバージョン

$someModel->create($attributes);
$someModel->update($attributes);

単純に委譲しているだけなら直接 Model を書き換えることを検討してもいいと思います(無理に Service 層と Repository を経由する必要はないと思います)。

関連する操作をまとめるか分けるか

決済関連のメソッドをまとめるパターン

class PaymentService {
    public function doPayment() {}
    public function doRefund() {}
}

Service には状態がないので、いくつ公開メソッドをつくっても基本的には問題にはならないです。

1クラス1メソッドのパターン

class PaymentService {
    public function invoke() {}
}

class RefundService {
    public function invoke() {}
}

しかし、個々のメソッドの行数が長くなって見通しが悪くなってきた場合、クラスを分けてしまったほうがいいと思います。

まとめ

Service とは何かというところから、実際に Laravel にどう組み込んで使うかというところまで、ざっと見てみましたが、どのようなケースで Service 化するといいか、というのが少し見えてきた気がします。

他にもこういう処理は Service 化するといい、などの提案や、文中の定義や例の部分で改善点みたいなのがありましたら、ぜひコメント欄にてご指摘ください :bow:

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

paiza cloudのHTTPSの制限でハマった

概要

PaizaCloudを使うと、簡単に開発環境を構築することができる。有料プランなら、常時公開もできるようになる。
https://paiza.cloud/ja/

Laravel勉強用にPaizaCloudで開発環境を構築していたところ、
HTTPS周りのことでつまずいて、初心者はきっとハマると思うので誰か困った時に参考にしてもらえるように、メモ。

詳しく

php artisan make:auth  // ログイン認証画面を作成
php artisan migrate    // DBに反映
php artisan serve --host=0.0.0.0

これで、ログイン画面ができたので、xxx.paiza-user.cloud:8000 にアクセス。
スクリーンショット 2019-05-26 11.29.19.png

しかし、右上のLOGINを押しても、

Please use HTTPS(SSL) instead of HTTP to access the URL.

と表示され、ログイン画面が表示されない…
ローカル環境ならちゃんと表示されるのに、なぜだ...

なぜなのか

  • ログイン画面へのリンクが、httpで張られていた
  • トップページがhttpsで表示されているので、当然httpsだと思ってリンクがhttpなのに気が付かなかった・・・

  • PaizaCloudで非SSLなアクセスがあった際、

解決策

[コメントを受け、追記]
ここでhttpsとならないのは、X_FORWARDED_PROTO を使った判定をしていないため。

app/Http/Middleware/TrustProxies.php
// 略
class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array|string
     */
    protected $proxies = "*"; // ここに"*"を指定する

    /**
     * The headers that should be used to detect proxies.
     *
     * @var int
     */
    protected $headers = Request::HEADER_X_FORWARDED_ALL;
}

こちらの記事で詳しく解説されている。
https://qiita.com/yamatox/items/8f3f481d88e807793ad5

以下は、forceSchemeを使う方法。この方法では、扱うURLをhttpsに強制する。

AppServiceProvider bootメソッドでUrlGeneratorクラスforceSchemeメソッドを呼び出して、
アプリ内で扱われるURLを全てhttpsにする。
こうすることで、リンクがhttpsで出力されるようになる。

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Routing\UrlGenerator;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot(UrlGenerator $url)
    {
        // force to use HTTPS
        $url->forceScheme('https');
    }
}

参考

[Laravel]常時SSLなアプリケーションでのURL生成のベストプラクティスを考える
https://qiita.com/hisash/items/4b3bb1ea47c38b0d8c86

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

Laravel で API コントローラのテストをする (FeatureTest 編)

Laravel で HTTP コントローラをテストするために FeatureTest を書く。サンプルプロジェクトは GitHub にあります。

UnitTest と FeatureTest の違い

Laravel には UnitTest(ユニット・テスト)と FeatureTest(フィーチャー・テスト)の2つのテストがある。
それぞれ保存されるディレクトリが tests/{Unit,Feature} と違うだけで、テストの書き方は変わらない。

違いはテストの 対象範囲 だけ。
UnitTest は小さなコードの一部をテストする。単体のモデルやモデルのメソッドを独立してテストする。
FeatureTest は複数のオブジェクトが関連した機能をテストする。主にコントローラのテストに使用する。

テストを実行する

トップディレクトリで phpunit または ./vendor/bin/phpunit コマンドを実行するとテストが実行される。引数がなければ tests/ ディレクトリにある全ての UnitTest と FeatureTest が実行される。

$ phpunit # 全てのテストを実行
$ phpunit tests/Feature/UserControllerTest.php # 特定のテストを実行

この他にも --filter オプションでテスト対象をフィルタリングできる。詳細は phpunit のマニュアル参照。

phpunit を実行すると結果が出力される。

$ phpunit

PHPUnit 7.5.11 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 118 ms, Memory: 16.00 MB

OK (2 tests, 2 assertions)

最終行の OK (2 tests, 2 assertions) は2つのテストが実行されて 2つの検証(アサーション)にパスしたことが表示されている。

テストの設定

phpunit を実行すると Laravel の App::environment()testing に設定される。.env を読み込んだ後に .env.testing で上書きされるので、変更したい値のみ設定することができる。

テストに使用する DB のマイグレーションは --env testing を指定して実行する。

# .env.testing で指定されたDBのマイグレーション
$ php artisan migrate --env testing
# DBのリセット
$ php artisan migrate:fresh --env testing

PHPUnit の設定は phpunit.xml で変更できる。

テストの全体像

全てのテストクラスは PHPUnit の TestCase クラスを継承する。継承したクラスは「テストケース」と呼ばれ、複数の「テスト」メソッドを保持する。

phpunit コマンドが実行されると、クラス名が Test で終わっているクラスを読み込み、全ての test で始まるメソッドを実行する。

DB はテスト毎にデータを初期化して空の状態で開始される。
Laravel には DB のトランザクションをロールバックする trait DatabaseTransactions があり、これを利用することでテスト実行後に DB を初期化することができる。

テストケースの実行フロー

全てのテストメソッドに対して、以下のフローで繰り返し実行される。

  1. TeseCase::setUp() テストケースのセットアップメソッドを実行
  2. DB のトランザクションを開始
  3. TestCase::test*() テストメソッドの実行
    1. 初期データの作成
    2. HTTP リクエスト送信
    3. レスポンス検証
    4. DB検証
  4. DB のロールバック

FeatureTest を作成する

実際にテストケースを作成して User モデルを API で操作する UserController のテストを書いてみる。

FeatureTest は php artisan make:test コマンドで作成できる。

$ php artisan make:test UserControllerTest
# パスを指定する
$ php artisan make:test Http/Controllers/UserControllerTest

作成されたテストクラスに DB 初期化用の trait とテスト前に必ず実行される setUp() メソッドを追加する。

use Illuminate\Foundation\Testing\DatabaseTransactions;

class UserControllerTest extends TestCase
{
    // データベースの初期化にトランザクションを使う
    use DatabaseTransactions;

    public function setUp(): void
    {
        parent::setUp();
    }

    public function testIndex()
    {
        // 検証(assert) をココに書く
    }
}

実際のプロジェクトではベースとなる TestCase クラスを作成して共通化する

index アクションのテスト

index アクションの仕様は GET リクエストを送信すると users テーブルのデータを JSON の配列で返す API を想定する。

テストでは事前に1件のデータを作成して GET リクエストでデータが返ってくることを検証する。

    public function testIndex()
    {
        // `users` テーブルにデータを作成 (Tips参照)
        factory(User::class)->create([
            'email' => 'user1@example.com',
        ]);

        // GET リクエスト
        $response = $this->get(route('users.index'));

        // レスポンスの検証
        $response->assertOk()  # ステータスコードが 200
            ->assertJsonCount(1) # レスポンスの配列の件数が1件
            ->assertJsonFragment([ # レスポンスJSON に以下の値を含む
                'email' => 'user1@example.com',
            ]);
    }

$this->get() を呼び出すと GET リクエストを送信することができる。レスポンス $response を取得したら検証用のメソッド assert* を呼び出して正しいレスポンスが返ってきていることを検証している。

このテストを書いて phpunit を実行すると以下のように表示される。

$ phpunit tests/Feature/UserControllerTest.php
PHPUnit 7.5.11 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 130 ms, Memory: 18.00 MB

OK (1 test, 3 assertions)

1つのテストが実行されて3件のアサーションをパスした。

POST DELETE メソッドを使ったテストも追加する。

    public function testStore()
    {
        $data = [ # 登録用のデータ
            'user' => [
                'email' => 'user@example.com',
                'name' => 'Tanaka Taro',
                'password' => '123456',
            ],
        ];

        // POST リクエスト
        $response = $this->post(route('users.store'), $data);

        // レスポンス検証
        $response->assertOk() # ステータスコードが 200
            ->assertJsonFragment([ # レスポンス JSON に以下の値を含む
                'email' => 'user@example.com',
                'name' => 'Tanaka Taro',
            ]);

    }

    public function testDestroy()
    {
        // `users` テーブルにデータを作成
        $user = factory(User::class)->create([
            'email' => 'user1@example.com',
        ]);

        // DELETE リクエスト
        $response = $this->delete(route('users.destroy', [$user->id]));

        // ステータスコード 200
        $response->assertOk();

        // `users` テーブルが0件になっている
        $this->assertEquals(0, User::count());
    }

phpunit を再実行する。

$ phpunit tests/Feature/UserControllerTest.php
PHPUnit 7.5.11 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 174 ms, Memory: 20.00 MB
OK (3 tests, 8 assertions)

3件のテストが実行され、8件のアサーションをパスした。

検証 (アサーション)

1つのテストに複数のアサーションを書くことができる。アサーションが1つでも失敗すると、そのテストは失敗(F)として表示される。

レスポンスクラスの $response->assert* メソッドはレスポンスを対象にした検証メソッドが実装されている。

TeseCase クラスの $this->assert* メソッドは値の一致、不一致、null チェックなどレスポンスに依存しないメソッドが実装されている。

また、上記の他に DB の内容を直接検証できるメソッドも存在する。

よく利用する検証メソッド

メソッド名 クラス 説明
assertEquals($a, $b) TestCase $a$bが一致する
assertTrue($b) TestCase $btrue
assertNotNull($o) TestCase $onullではない
assertArraySubset($subset, $a) TestCase 配列$aに部分配列$subsetを含む
assertOk() レスポンス ステータスコードが200
assertJsonCount($n) レスポンス(JSON) JSON配列レスポンスの件数が$n
assertHeader($h, $v) レスポンス レスポンスヘッダ$hの値が$nである
assertSee($t) レスポンス(HTML) HTMLに文字列$tが存在する
assertDatabaseHas($t, array $a) TestCase テーブル$t$aに一致するレコードが存在する
assertSoftDeleted($, array $a) TestCase テーブル$t$aに一致するレコードが論理削除されている

その他のアサーション

Tips

よく利用するイディオムや関連ツール。

モデルファクトリー

Laravel にはテストデータの作成を簡単にするファクトリーという機能がある。
事前に定義しておいた値で DB にデータを投入できる。

データの定義

// database/factories/UserFactory.php
// User モデルのファクトリクラス
use Illuminate\Support\Str;
use Faker\Generator as Faker;
use App\User;

// define() で事前データを定義する
$factory->define(App\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
        'remember_token' => Str::random(10),
    ];
});

// データに名前を付けて複数定義することもできる
$factory->state('user2')->define(...);

データを作成するときは factory() 関数にクラス名を渡す。

use App\User;

// DBに保存された User モデルが返る
$user = factory(User::class)->create();
// 特定の値だけ上書きして作成する
$user = factory(User::class)->create([
  'id' => 1,
  'email' => 'tanaka@example.com',
]);
// 10件作成
$user = factory(User::class, 10)->create();

// DBに保存せず new User() したモデルを取得
$user = factory(User::class)->make();

// 名前を付けたモデルを取得
$user = factory(User::class)->states('user2')->make();

詳細は Laravel のマニュアル を参照。

認証ユーザ

認証されたユーザを使いたいときは actingAs() メソッドでユーザを指定することができる。

// UserControllerTest.php - testIndex()
$user = factory(User::class)->create();
$this->actingAs($user); # 認証済みユーザを指定

// UserController.php - index()
$user = \Auth::user(); // actingAs() で指定したユーザになっている

レスポンスのダンプ

レスポンスの内容を直接確認したいときは $response->dump() でダンプすることができる。

例外のテスト

例外が発生することをテストしたいときはテストにアノテーションを指定する。

// UserNotFoundeException がスローされること
/**
 * @expectedException \App\Exceptions\UserNotFoudException
 */
public function testIndex()

モック

モックオブジェクトを使いたいときは Mockery を使用する。

参考

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

laravel manp 初期画面 localhost

laravelのインストールしたら

初期画面を出したいけど上手くいかない時の設定方法

laravelのインストールして、初期画面を出したいけど上手くいかない時

localhost

とURLを入力しても上手く表示されない場合

manpの設定(preferences...)のWebSeverの中にある、Documents Root を、

{laravelのディレクトリー}¥public
(例)
C:\MAMP\htdocs\laravel\public

と設定すると上手くいく。

上記の例だとLaravelがMAMPの設定上だが本来はあまりお勧めされない。
理由はMAMPを消すとデータが消えるため。
MAMPのバージョンに依存しないフォルダーに保存しておくことが望ましい。

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