whereNameOrEmail() とは。Laravelのソースコード読み

今日はLaravelのソースコードを読んでいました。 読む時に、環境構築が面倒なので色々と工夫した点があるのですが、詳しくはconnpassのスライド or PHP カンファレンス沖縄にて。 Laravel MeetUp Okinawa 第15回@ZORKS沖縄 - connpass

それで、今日見つけた面白いメソッドを紹介します。

例えば、User table が name と email という属性を持っているとします。

それで、Laravelだと、User::whereName('hoge')->all() みたいなコードを書くと、WHERE name = "hoge" の制約があるSQLを発行してDBにアクセスしてくれます。ソースコードを読んだ結果、下記のような書き方も許容されていました。 User::whereNameOrEmail('nameHoge', 'emailHoge');

SQLWHERE name = "nameHoge" OR email = "emailHoge" です。

まあ滅多に使うことないだろうなぁと思いつつも、誰も知ってなさそうな面白いメソッドを発見できたので満足でした。

ちなみに、この処理はここらへんに書かれてあります。

$year++

次の一年の抱負は"色々チャレンジする"

めちゃ抽象的です。 想定としては、普段買わないような物を買ってみるとか、普段着ないようなシャツを着てみるとか。 もちろん、普段触らない言語に触ってみるとか、普段食べない料理を食べたりなども。

わりとここ一年くらい、安定的に活動してきて、何かにチャレンジするということをあまりしていない気がするので、抱負にしてみました。 多分、人生の中で一番チャレンジングな時期だったのは高校時代で、あの時がきっかけで続けたこと、途中でやめたこと、色々ありますが、ほとんどが糧となっていると強く思います。 なので、この一年はチャレンジングな時期を到来させて、将来の糧の貯金をするみたいな感じです。 もちろん、新しいこと、違うことに取り組むのが面白いからということもありますが。

というわけで、今更ですが一年間ありがとうございました。 特に、大学で研究していた頃はnal先生&&研究室, kono研&&先生にとてもお世話になっていました。 また、社会人になってからは弊社の社員&&社長にとてもよくしてもらっています。 また家族と彼女と友人にも助けられています。 こんな感じな人ですが、次の一年もどうぞよろしくお願いします!!

Re:Laravelのlazyとchunk

昨日、ブログを書いて5分後に気づいたんですけど、書いたコードが間違ってて正しく比較できていませんでした。 あるあるだよなぁ。

というわけで、今日はその間違っていた箇所を見直していきます。 そもそも間違っていた箇所は、

Product::all()->lazy()->each(function ($product) {
    $this->assertNotNull($product->name);
});

ここで、allしてlazy()すると、Collectionのlazyが呼ばれるのでダメで、正しくは、Builderのlazyを呼び出さないとダメです。 なので、

Product::query()->lazy()->each(function ($product) {
    $this->assertNotNull($product->name);
});

が正しい。

それで、発行するクエリですが、chunkと一緒です。 LimitとOffsetで数を絞ってselectしていく感じです。 ただ、やっぱり気になるのはメモリへのデータの乗り方。 odenさんいわく、1行ずつ値を取る方法があるらしくて、それがPDOStatement::fetchらしいです。 ちょっと気になるのでがっつりデバッグすることにしました。

それで、lazy()の中身をデバッグしていたんですけど、どうやら$resultsにはCollectionが入っていて、もうこの時点ではメモリに載っているようです。 なので、resultsの中を読めば良い感じなのでまた明日...

Laravelのlazyとchunk

クエリの組み立て方ミスってたので全然比較できてないことに書いて5分後に気づきました!!!! all()->lazy() で取っちゃダメやん。query()->lazy()でとらないとダメ。なお、この記事は修正してないのでミスってるままです(sad)

翌日書いた記事はこれ

Laravelでクエリを構成する際に、lazy()とchunk()というメソッドを使うことができます。 この2つのメソッドの使い道ですが、結構な数のデータを扱う際に、メモリの消費を抑えてくれるらしいです。 公式ドキュメント

それで、どういうクエリを発行しているか気になったので、ちょっと覗きに行きました。

テストコードはこんな感じ。

class LazyTest extends TestCase
{
    use RefreshDatabase;
    public function test_lazy(): void
    {
        Product::factory(100)->create();
        DB::enableQueryLog();
        Product::all()->lazy()->each(function ($product) {
            $this->assertNotNull($product->name);
        });

        $queries = DB::getQueryLog();

        foreach ($queries as $query) {
            logger()->info([
                'sql' => $query['query'],
                'bindings' => $query['bindings'],
                'time' => $query['time'],
            ]);
        }
    }

    public function test_chunk(): void
    {

        Product::factory(100)->create();
        DB::enableQueryLog();
        Product::query()->chunk(10, function ($products) {
            $this->assertCount(10, $products);
        });

        $queries = DB::getQueryLog();

        foreach ($queries as $query) {
            logger()->info([
                'sql' => $query['query'],
                'bindings' => $query['bindings'],
                'time' => $query['time'],
            ]);
        }
    }

lazyはこんな感じ。

[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products`',
  'bindings' => 
  array (
  ),
  'time' => 0.27,
)  

chunkはこれ。

[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 0',
  'bindings' => 
  array (
  ),
  'time' => 0.2,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 10',
  'bindings' => 
  array (
  ),
  'time' => 0.14,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 20',
  'bindings' => 
  array (
  ),
  'time' => 0.13,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 30',
  'bindings' => 
  array (
  ),
  'time' => 0.13,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 40',
  'bindings' => 
  array (
  ),
  'time' => 0.14,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 50',
  'bindings' => 
  array (
  ),
  'time' => 0.13,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 60',
  'bindings' => 
  array (
  ),
  'time' => 0.12,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 70',
  'bindings' => 
  array (
  ),
  'time' => 0.13,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 80',
  'bindings' => 
  array (
  ),
  'time' => 0.14,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 90',
  'bindings' => 
  array (
  ),
  'time' => 0.12,
)  
[2023-07-23 13:20:38] testing.INFO: array (
  'sql' => 'select * from `products` order by `products`.`id` asc limit 10 offset 100',
  'bindings' => 
  array (
  ),
  'time' => 0.11,
)  

そもそも発行しているqueryが異なる感じ。 挙動として、chunkがすごくシンプルで limit と offset で数を絞ってDBを叩いて、取ってきた結果がcollectionになる。 そのcollectionを開発者側で操作する感じ。

lazyは1件のレコードの操作を開発者が指定して書く感じ。 ただ、SQLは一回発行して終わりなのがちょっと謎で、結局メモリに全データを乗っけないといけないのでは?という感想。 というか、それならall()で取ってきてforeachするのと大差ないじゃんという。

ただ、内部的にPHPのジェネレータという処理を使っているっぽくて、それが省メモリのキモらしいです。もちろん何もわかってません。

というわけで、こちらがジェネレータの説明書です。

読んだ感想ですが、なるほど... 確かに、xrangeのような使い方だと一度に大きな配列を返さなくていいのでメモリのエコになりますが、そもそも全データをメモリに乗っけてあるなら意味なくね?という感想。多分この考えが間違っているはず。

lazyの中身を見ると思い違いが発覚して理解できそうなので、後日debugします

論理的思考力

なんだそれは...

ロジカルシンキング(logical thinking)とは、一貫していて筋が通っている考え方、あるいは説明の仕方のことである。日本語訳として論理思考あるいは論理的思考(参考→思考#思考の種類)と置き換えられることが多い。

wikipediaより

個人的には、logical thinking よりも論理的思考のほうがしっくりきます。 それで、このlogical thinkingですが、結構多用します。恐らく、日常的にみんなしていると思っている。

ちなみに、タイトルが「論理的思考」であるにも関わらず、この記事では論理的思考が何者なのかについては書きません。 その代わりに僕がいつも物事を考えるときに重要視していることを書きます。

それは原点回帰

よく思考をしているときに迷走します。 まじで迷います。自分がいる場所がわからなくなります。 そうした結果、よくわかんない論理的思考ができあがることが多々あります。 そのときに有効なのが原点回帰です。ちなみに僕はこの言葉が好きです。

論理的思考の原点回帰だと、「なぜ私は論理的思考をしているのだろうか」みたいな感じですね。 もっと原点に戻ってもよくて、「そもそも論理的思考の目的はなんだったのだろう」とか。 個人的に、色々な行動の原点が結びついて繋がっていく感じがすごい楽しいです。

ちなみに、原点がわからない状態で物事を考えてもあまり面白くないしただの作業になるし、前述したとおり迷走しまくった結果よくわかんない結果になるのでやめたほうがいいです。 ちなみに、常に原点を意識する必要はなくて、大事なのは忘れたときに思い出せることだと思います。

なにか作業をしていて、「なんでこれやってるんだっけ」という気持ちになったことが1回くらいはみんなあるかなぁと思っているのですが、それが原点回帰だと思っています。

全ての源

「全ての源」ってカッコいいですね。 色々な物事の原点がノードとエッジのように繋がっていくと、恐らくツリー構造みたいな感じになるのかなぁと思っています。 ただ、絶対的な単一のルートがあるというわけではなくて、複数の木構造があって、何かをきっかけにくっついて大きくなっていくみたいな感じだと思っています。 それで、他人が持っている原点について俯瞰したときに、その木が大きければ大きいほど「エモい」のかなぁと先週の金曜日に思いました。

久しぶりに大学に行きました

3カ月くらい行ってなかったので久しぶりに大学に行きました。 とりあえずkono研には人がいるだろうという想像で、前日に連絡して突撃すると、野生のyoshiakiとB3がいました。 ちなみに、nal研は誰もいなさそう && nal先生もいなさそうだったので今度ちゃんとアポ取っていきます...

あまり知らない人がいなかったので、今度はもっと知らない人がたくさんいる時に行きたいという気持ちがあるような。 本当は月1くらいで顔出したいので、仕事終わりにしれーーと行こうかなぁ。 そうすると電撃激突訪問になるんだよなぁw

OpenTTD

家族がコロナになって、金曜からひきこもることになったので、OpenTTDというゲームをしてました。

store.steampowered.com

citysSkyline系のゲームなんですが、こっちは主に鉄道やらバスやらを引く感じです。 結構古いゲームということもあって、UIも親切ではないし、ゲームの内容も分かりにくい感じがしますが、それでも面白いです。

あと、マップがめっちゃ広くて、最大255人でマルチができるらしいです。 BF超えましたね あと無料なので、とりあえずやってみることをおすすめします。 多分、最初は進め方が全然わからないと思うので、steamのレビューを見ながら適当に触ってやることをおすすめします。