day23(から3日遅れの)今日はfromにサブクエリを使う方法を見ていきます。
Doctrine
<?php
declare(strict_types=1);
use Doctrine\ORM\EntityManagerInterface;
require __DIR__.'/../vendor/autoload.php';
/** @var EntityManagerInterface $entityManager */
$entityManager = require __DIR__.'/bootstrap.php';
$sql = <<<EOQ
SELECT t.name, t.books_count FROM (select authors.id, authors.name, count(books.id) as books_count from authors left join books on books.author_id = authors.id group by authors.id, authors.name) as t
EOQ;
/** @var array<array{name: string, books_count: int}> $rows */
$rows = $entityManager->getConnection()->fetchAllAssociative($sql);
foreach ($rows as $row) {
echo sprintf('%s(%d)', $row['name'], $row['books_count']).PHP_EOL;
}
- fromに限らず、サブクエリを使うときはORMではなくDBALで対応することがほとんどです。無理にEntityにマップせずに一旦連想配列を取得して、必要に応じて後でDTOに詰め替えます。
Eloquent
<?php
declare(strict_types=1);
use App\Models\Author;
use Illuminate\Database\Eloquent\Collection;
require __DIR__.'/../vendor/autoload.php';
require __DIR__.'/bootstrap.php';
$subQuery = <<<EOQ
select authors.id, authors.name, count(books.id) as books_count from authors left join books on books.author_id = authors.id group by authors.id, authors.name
EOQ;
/** @var Collection<Author> $result */
$result = Author::query()->fromSub($subQuery, 't')->select('t.name', 't.books_count')->get();
foreach ($result as $author) {
// books_countはAuthorのプロパティではないが、Author::query()で始めたのでAuthorにマップされている
echo sprintf('%s(%d)', $author->name, $author->books_count).PHP_EOL;
}
- Modelは未定義の項目も受け付けるため、サブクエリを使っていてもModelとして取得することができます。
- fromに限らず、DBMSの許す限りselectやwhereにもサブクエリを利用できます。