July 15, 2021 • ☕️ 6 min read
Laravel の eloquent Model には、「query()」と「newQuery()」といった、名前も役割も非常によく似たメソッドがあります。
Begin querying the model.
public static query ( ) : Builder
return
Get a new query builder for the model's table.
public newQuery ( ) : Builder
return
正直、何が違うのか良く分からないし、ググってもちゃんと答えを見つけ切れなかったので、Laravel のソースを読んでみました。
以下、該当箇所です。
https://github.com/laravel/framework/blob/8.x/src/Illuminate/Database/Eloquent/Model.php#L1232
/**
* Begin querying the model.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function query()
{
return (new static)->newQuery();
}
/**
* Get a new query builder for the model's table.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function newQuery()
{
return $this->registerGlobalScopes($this->newQueryWithoutScopes());
}
一緒やんけー!
でも、もしかしたら自分の読み間違いかもしれないので、こんなコードを書いて実験してみた。
$query = Item::query();
$query->select('id', 'code','name');
Model.php は、以下のようにログを仕込む。
プロジェクトから探す場合、以下のパスです。
vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php
以下、ログに出力された内容です。
[2021-07-21 14:59:04] local.INFO: Illuminate\Database\Eloquent\Model::query
[2021-07-21 14:59:04] local.INFO: Illuminate\Database\Eloquent\Model::newQuery
という事で、
「query()」と「newQuery()」は、全く同じ
と断言できそうです。
違いを挙げるとしたら使い方とか。
query() は static なので静的に使える(クラスから直接コールできる)けど、newQuery() はそれができないので、「self::newQuery() 」といった使い方ができない。
なので、こんな感じとか。
// case 1
$item = new Item();
$query1 = $item->newQuery();
$query1->select('id', 'code','name');
// case 2
$query2 = app()->make(Item::class)->newQuery();
$query2->select('id', 'code','name');
その時のログ。
[2021-07-21 15:33:20] local.INFO: Illuminate\Database\Eloquent\Model::newQuery
[2021-07-21 15:33:21] local.INFO: Illuminate\Database\Eloquent\Model::newQuery
でも、こういう使い方もできる。
// case 1
$item = new Item();
$query1 = $item->query();
$query1->select('id', 'code','name');
// case 2
$query2 = app()->make(Item::class)->query();
$query2->select('id', 'code','name');
その時のログ
[2021-07-21 15:34:16] local.INFO: Illuminate\Database\Eloquent\Model::query
[2021-07-21 15:34:16] local.INFO: Illuminate\Database\Eloquent\Model::newQuery
[2021-07-21 15:34:16] local.INFO: Illuminate\Database\Eloquent\Model::query
[2021-07-21 15:34:16] local.INFO: Illuminate\Database\Eloquent\Model::newQuery
PHP はオブジェクト指向言語としては相当緩いので、インスタンスから静的メソッドをコールする、といった事が出来てしまう。
そういえば推奨されてなかったっけか・・? と思い、PHP公式を見てみたが、特にそういった制限は無いみたいだ。
ただ、逆(インスタンスメソッドを静的メソッドのようにコール)は、警告扱いだった。
https://www.php.net/manual/ja/language.oop5.static.php
警告 static でないメソッドを静的にコールすると、Error がスローされます。
PHP 8.0.0 より前のバージョンでは、 static でないメソッドを静的にコールすることが非推奨になっており、 E_DEPRECATED レベルの警告が発生していました。
うっすら記憶していたのはこっちの方だった。
インスタンスから静的メソッドをコースするのは PHP的には特に問題なさそう。(2021年 7月時点)
とはいえ、この挙動が気持ち悪いというのは十分理解できるので、オブジェクト指向的に正しく使いたいなら、以下のように使い分けをした方が良さそう。
一応、差を並べてはみたものの、違いを意識して使い分けるよりも、全部 query() で良いのでは?
というルールにしてもアリなんじゃないかと思っている。
ここは天下の適当言語 PHPの恩恵を受ける意味でも、「query()」に寄せる意見が出たとしても、特に反対はしない。
ちなみに PHP の言語仕様は、結構いい加減だと思ってるし、世間でもよくディスられてるけど、時にはそれが柔軟性と拡張性の高いコードに繋がるケースも多々あるので、使い方次第なんじゃないかと思ってる。
個人的には、言語仕様のいい加減さは魅力。
オブジェクト指向的に「正しく」作るよりも、実用性に寄せるという考え方は、むしろ好き。
(new static) という表現があまり見かけなかったので調べてみた。
という事らしい。
https://blog.sarabande.jp/post/10539365200
https://gotohayato.com/content/488/
実験したところ、こんな感じ。
class ParentClass {
// 「self」は、定義されたクラスを指す。
// この場合、どこからコールされても、クラス「ParentClass」を指す
public static function getNewSelfName() {
return get_class(new self());
}
// 「static」は、呼び出された時のクラスを指す。
// このクラスを継承した ChildClass からコールされる場合、「ChildClass」を指す
public static function getNewStaticName() {
return get_class(new static());
}
}
class ChildClass extends ParentClass {
}
// 「self」は、定義されたクラスを指す
echo ParentClass::getNewSelfName() . PHP_EOL; //=> ParentClass
echo ChildClass::getNewSelfName() . PHP_EOL; //=> ParentClass
// 「static」は、呼び出されたクラスを指す
echo ParentClass::getNewStaticName() . PHP_EOL; //=> ParentClass
echo ChildClass::getNewStaticName() . PHP_EOL; //=> ChildClass