web-dev-qa-db-ja.com

Laravelカスタムモデルメソッド

Eloquentモデルに追加のロジックを追加するたびに、モデルのファサードから呼び出すために、staticメソッド(つまり、理想的ではないメソッド)にする必要があります。適切な方法でこれを行う方法について多くのことを検索してみましたが、ほとんどすべての結果は、クエリビルダーインターフェイスの一部を返すメソッドの作成について説明しています。私は、何でも返すことができ、モデルのファサードを使用して呼び出されるメソッドを追加する方法を見つけようとしています。

たとえば、Carというモデルがあり、それらすべてを取得したいとします。

_$cars = Car::all();
_

今を除いて、結果をmakeによって多次元配列にソートして、結果が次のようになるようにしたいとします。

_$cars = array(
  'Ford' => array(
     'F-150' => '...',
     'Escape' => '...',
  ),
  'Honda' => array(
     'Accord' => '...',
     'Civic' => '...',
  ),
);
_

その理論的な例を取り上げると、次のように呼び出すことができるメソッドを作成したいと思います。

_$cars = Car::getAllSortedByMake();
_

しばらくの間、ひどいメソッド名と、それがデータ構造と密結合しているという事実を忘れましょう。モデルでこのようなメソッドを作成する場合:

_public function getAllSortedByMake()
{
   // Process and return resulting array
   return array('...');
}
_

そして最後に私のコントローラーで呼び出すと、この例外がスローされます:

非静的メソッドCar :: getAllSortedByMake()は、互換性のないコンテキストから$ thisを想定して静的に呼び出されるべきではありません

TL; DR:静的メソッドにせずにモデル内に存在する意味のあるカスタム機能を追加し、モデルのファサードを使用して呼び出すにはどうすればよいですか?


編集:

これは理論的な例です。おそらく、質問の言い換えはより理にかなっているでしょう。 all()which()などの特定の非静的メソッドがEloquentモデルのファサードで使用できるのに、モデルに追加された追加のメソッドが使用できないのはなぜですか?これは、___call_マジックメソッドが使用されていることを意味しますが、モデル内の自分の関数をどのように認識させることができますか?

「ソート」よりも良い例は、データの一部に対して計算またはアルゴリズムを実行する必要がある場合です。

_$validSPG = Chemical::isValidSpecificGravity(-1.43);
_

私にとっては、ドメイン固有であるため、そのようなものがモデルに含まれているのは理にかなっています。

35
Jeremy Harris

私の質問は、なぜファサードを介してall()にアクセスできるのかなど、より基本的なレベルです。

Laravel Core-all()は実際には静的関数です

public static function all($columns = array('*'))

次の2つのオプションがあります。

public static function getAllSortedByMake()
{
    return Car::where('....')->get();
}

または

public function scopeGetAllSortedByMake($query)
{
    return $query->where('...')->get();
}

両方ともあなたができるようになります

Car::getAllSortedByMake();
53
Laurence

実際、Eloquent Builderを拡張して、そこにカスタムメソッドを配置できます。

ビルダーを拡張する手順:

1。カスタムビルダーの作成

<?php

namespace App;

class CustomBuilder extends \Illuminate\Database\Eloquent\Builder
{
    public function test()
    {
        $this->where(['id' => 1]);

        return $this;
    }
}

2。このメソッドをベースモデルに追加します:

public function newEloquentBuilder($query)
{
    return new CustomBuilder($query);
}

。カスタムビルダー内のメソッドでクエリを実行します:

User::where('first_name', 'like', 'a')
    ->test()
    ->get();

上記のコードで生成されたmysqlクエリは次のようになります。

select * from `users` where `first_name` like ? and (`id` = ?) and `users`.`deleted_at` is null

PS:

First Laurenceの例は、モデルではなくリポジトリにより適したコードですが、このアプローチではより多くのメソッドをパイプすることもできません:

public static function getAllSortedByMake()
{
    return Car::where('....')->get();
}

Second Laurence例は最悪のイベントです。

public function scopeGetAllSortedByMake($query)
{
    return $query->where('...')->get();
}

多くの人々は、extend laravel=ビルダーにスコープを使用することを提案しますが、スコープは雄弁なビルダーによって分離されており、スコープ内とスコープ外で同じコマンドで同じクエリを取得できないため、実際には悪いソリューションです。スコープを分離する必要があるかどうかの変更のPRでしたが、Taylorは私を無視しました。

詳細説明:たとえば、次のようなスコープがある場合:

public function scopeWhereTest($builder, $column, $operator = null, $value = null, $boolean = 'and')
{
    $builder->where($column, $operator, $value, $boolean);
}

および2つの雄弁なクエリ:

User::where(function($query){
    $query->where('first_name', 'like', 'a');
    $query->where('first_name', 'like', 'b');
})->get();

User::where(function($query){
    $query->where('first_name', 'like', 'a');
    $query->whereTest('first_name', 'like', 'b');
})->get();

生成されるクエリは次のとおりです。

select * from `users` where (`first_name` like ? and `first_name` like ?) and `users`.`deleted_at` is null

select * from `users` where (`first_name` like ? and (`id` = ?)) and `users`.`deleted_at` is null

一見するとクエリは同じように見えますが、そうではありません。この単純なクエリでは問題にならないかもしれませんが、複雑なクエリでは問題になるため、ビルダーを拡張するためにスコープを使用しないでください:)

2
fico7489

モデルクラス名「車」を使用するよりも、動的コードを改善するために、

「静的」または「自己」を使用してください

public static function getAllSortedByMake()
{
    //to return "Illuminate\Database\Query\Builder" class object you can add another where as you want
    return static::where('...');

    //or return already as collection object
    return static::where('...')->get();
}
1
jalmatari