エンジニアのひよこ_level10

【毎日更新!】新卒2年目エンジニアブログです! プログラムだけじゃなく、マネジメントとかも書いていきたい!

【phpunit】同じテストコードをコピペしない工夫、dataProvider【423日目】

コピペでテスト書いてませんか

よくあるのが、入力を変えると、結果が変わるというテスト。

元コード

function testFunc($data)
{
....
}

テストコード

function testFunc_いろいろ返ってくる()
{
    $data = true;
    $expected = [1,2,3],

    .....


    $this->assertSame($expected, $result);
}


function testFunc_空っぽ()
{
    $data = false;
    $expected = [],

    .....


    $this->assertSame($expected, $result);
}

他コピペでdataとexpectedを変えていく・・・

こういうのもったいない。

入力と結果を関数で渡すdataProvider

こんなときに、$dataや$expectedを関数で渡してみよう

/**
  * @test
  * @dataProvider testDataProvider
  */
function testFunc($data, $expected)
{
    .....


    $this->assertSame($expected, $result);
}


function testDataProvider()
{
    return [
        'いろいろ返ってくる' => [
            'data' => true,
            'expected' => [1,2,3]
        ],
        '空っぽ' => [
            'data' => false,
            'expected' => []
        ]
    ];
}

何してるの?

テストのコメント欄に、 @dataProviderと関数名を書くと、
引数に値を入れることが出来る。

testDataProviderで配列を渡してやると、それをいい感じにテストしてくれる。
この場合、dataがtrueのときと、falseのときのテストが動く。

それぞれexpectedには、対応するdataが使われるので、見やすい!そしてコピペいらず!

ちなみに、キーに'空っぽ'とか日本語入れてますが、エラーが出たら、このキー名が表示されます。

めっちゃわかりやすい・・・!

参考になりそうなコード

github.com

クリックしたら、該当のコードに移ります。

ぜひdataProvider使って、コピペ減らして見やすいテストコードを!

【Laravel】Modelクラスでクエリビルダが動く理由【409日目】

モデルでクエリビルダ?

モデルのクラスって、テーブルのレコードをオブジェクトにしたようなイメージでした。

でも、以下のようなコードを見ることがあると思います。

User::select('id')->first();

あれ?selectってクエリビルダだよな・・・

このUserってなんだ?クエリビルダ?モデルのクラス???

元コードを読んで見る

Modelクラス

    /**
     * Handle dynamic method calls into the model.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        if (in_array($method, ['increment', 'decrement'])) {
            return call_user_func_array([$this, $method], $parameters);
        }

        $query = $this->newQuery();

        return call_user_func_array([$query, $method], $parameters);
    }

    /**
     * Handle dynamic static method calls into the method.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public static function __callStatic($method, $parameters)
    {
        $instance = new static;

        return call_user_func_array([$instance, $method], $parameters);
    }

なにしてるの?

__callStatic

__callStaticはマジックメソッド。

静的呼び出しをされたときに使われます。

User::select('id')

このとき、 new staticでモデルのインスタンスを作成。そして call_user_func_arrayで モデルクラスのインスタンスが、 selectの関数を 'id'のパラメーターを引き継いで実行。

__call

__callStatic()によって、静的呼び出しだったものが、モデルクラスのインスタンスから呼び出されることになります。

次は、 __callです。静的呼び出しじゃなくなったので。

そこでは、メソッド名が ['increment', 'decrement']のどちらかか確認して、合っていれば、そのモデルクラスのメソッドを呼び出します。

なければここ! newQueryでクエリビルダ用にインスタンスを作って、そのインスタンスselectの関数を呼び出します!

Laravelの元コードを読むのは面白いよ!

ってことで、案外簡単に追うことができました。

Laravelの元コードは、こんな書き方あるのか!と勉強になることがたくさんあるので、是非読んでみてください!

このデザインパターン使ってるのか・・・!ってなることたくさんあります。おすすめです。

参考

PHP: in_array - Manual

PHP: call_user_func_array - Manual

【Laravel】Eloquentのcreateとfillの違いとは【336日目】

■注意

コードは5.2のコードです。ですが、そんなにバージョン上がっても変わってないはずです。

■結論

基本的にcreateで大丈夫

■違い

1.createはどうせfill通るので、fillableやguardedの部分は両方使う

2.createは、静的メソッドで、以下までしてくれる

1. インスタンス化
2. 値の確認・保存
3. インスタンスを返す

3.fillは、インスタンスを用意して、fillして、save()を呼び出すの3工程が必要

■使い方の違い

create

$user = User::create(['name' => 'Uiro']);

fill

$user = new User();

$user->fill(['name' => 'Uiro']);

$user->save();

■元コード+私のコメント

createがfillを動かしてるのは以下のコメント参照

   /**
    * Save a new model and return the instance.
    *
    * @param array $attributes
    * @return static
    */
    public static function create(array $attributes = [])
      {
        $model = new static($attributes); //ここで、呼び出し元をインスタンス化してる。なので下の__constructを通る

        $model->save();

        return $model;
      }

    /**
     * Create a new Eloquent model instance.
     *
     * @param  array  $attributes
     * @return void
     */
    public function __construct(array $attributes = [])
    {
        $this->bootIfNotBooted();

        $this->syncOriginal();

        $this->fill($attributes); //fillをしている
    }

fillはsaveまでしてないので、saveを書かないといけない。

また、静的メソッドではないので、インスタンス化してください。

/**
     * Fill the model with an array of attributes.
     *
     * @param  array  $attributes
     * @return $this
     *
     * @throws \Illuminate\Database\Eloquent\MassAssignmentException
     */
    public function fill(array $attributes)
    {
        $totallyGuarded = $this->totallyGuarded();

        foreach ($this->fillableFromArray($attributes) as $key => $value) {
            $key = $this->removeTableFromKey($key);

            if ($this->isFillable($key)) {
                $this->setAttribute($key, $value);
            } elseif ($totallyGuarded) {
                throw new MassAssignmentException(sprintf(
                    'Add [%s] to fillable property to allow mass assignment on [%s].',
                    $key, get_class($this)
                ));
            }
        }

        return $this;
    }

■使い分け

1.インスタンスが既にある
2.saveを後回しにしたい

ならfillを使って、基本的にはcreateで良いと思います。

プログラミングにおける『依存』『密結合』とは【326日目】

こんなワード聞きませんか

『このクラスは、このクラスに依存しているね』
『このクラスとこのクラス、密結合になってるな・・・』

依存って何なんでしょう。
イメージとしては、
『○○さんがいないと、私生きていけない・・・』 みたいなものが思い浮かぶと思います。

だいたいあってます。

依存しているコードの例

【例はPHPですが、依存の話は別言語でも同様のお話です】

Child.php

class Child {
    private $name;

    public function __construct(Parent $parent)
    {
        $this->name = $parent->generate_name();
    }

    public function play_game(Friend $friend)
    {
        //
    }
}

これは、ChildはParentに依存しています。
play_game()もFriendに依存していますね。

Friendがいないと、遊ぶことが出来ない。
Parentに名前つけてもらわないと、生まれることすら出来ない。

こんなふうに、コンストラクタやメソッドの引数に、特定のクラスがあるなどして、
そのクラスが存在しないと生きられない!

それが依存です。

相互依存の『密結合』

依存というのは一方的なものです。

『〇〇さんがいないと、私生きていけない・・・』
は片方の人が言うセリフですね。

しかし、お互いがお互いに依存している場合もあります。
それが密結合と呼ばれます。

片方バグ起こると、共倒れしてしまうやーつです。

余談

普段はプログラミングでは『疎結合』にしようとか、
極力『密結合』を避けようとしますよね。

では、『密結合』でなければ何が嬉しいの?

『〇〇さんがいないと、私生きていけない・・・』
と言われても、その〇〇さんは普通に生きていけます。

他にもバグのチェックも楽だったりと良いことがあるのですが、
それはまた次の機会に書きます。

【Laravel】download関数のpathToFileの落とし穴【321日目】

ユーザーにファイルをダウンロードさせたい

http://domain_name.com/storage/images/upfile.jpegで画像が出るものをダウンロードさせたい。

return response()->download('/storage/images/upfile.jpeg');

あれ?なぜか動かない。

最初のスラッシュは不要

return response()->download('storage/images/upfile.jpeg');

これでダウンロード出来ました。

【Laravel】Cookieを付与する。view関数でbladeも使えます【320日目】

こんな時

LaravelでCookieを付与したい。
(この保存方法だけでは、暗号化された状態で保存されます。なので、ユーザーは内容がわからないです。)

書き方

return response()
    ->view('blade_file_name')
    ->cookie('cookie_name', 'value', 30);

何をしてるの

blade_file_name.blade.phpを使って画面を表示しながら、Cookie内のcookie_nameにvalueという値を保存する。

また、Cookieの有効期限は、この場合30分になります。

viewに変数も渡したい

compact関数とかをいつもどおり追加すればいいです。

return response()
    ->view('blade_file_name', compact('number', 'input'))
    ->cookie('cookie_name', 'value', 30);

【nginx+phpfpm】Laravelでpublic配下のファイルが取得出来ない【306日目】

こんなことがありました。

docker-composeで管理、nginx+php-fpmで環境構築しました!

Laravelのチュートリアルページ表示されました!

・・・favicon.icoが反映されない?
public/app.cssや、public/app.jsが読み込めない・・・

起こってること

phpは、nginxの設定で通信をしてからリクエストを返してるので問題ない。
だから、phpファイルだけはうまくいく。

問題は、try_filesでファイルを取得しているところっぽい。

解決

nginxのコンテナに、public配下のファイルを置いていなかった(マウントしてなかった)せいでした。

今回は、docker-compose.ymlの方の設定ミス。

  nginx:
    container_name: study-laravel_nginx
    build:
      context: ./nginx
    volumes:
      - ./web:/var/www
    ports:
      - "8000:80"
    links:
      - php-fpm

volumesのパスを書き間違えてました・・・つっら・・・

おまけ:そんなこんなで作ったプルリク

ってことで、詰まって出来上がった、Laravelでbootstrapまで呼び出す実装のプルリク。参考に。

github.com