PHPでの効率的なループ処理
PHP開発において、データベースの結果セットやCSVファイルなどの反復可能なデータセットを処理する場面は多々あります。これらのデータを効率的に扱い、テンプレートエンジンのようなループ情報を提供し、さらにエンティティクラスにデータを代入する機能を持つ便利なライブラリがKoriym.Loopです。本記事では、このPHPライブラリの基本的な使い方とその利便性を紹介します。
Koriym.Loopとは?
Koriym.Loopの主な特徴は以下の通りです。
- エンティティリストジェネレーターに変換: データセットをエンティティクラスのリストに変換
- ループ情報の提供: テンプレートエンジンのようなループ情報(isFirst、isLast、index、iteration)を提供
- 依存性の注入: エンティティに依存インスタンスを注入可能
- イテレーターのサポート: 配列だけでなくイテレーターもサポート
インストール方法
Koriym.LoopはComposerで簡単にインストールできます。
composer require koriym/loop
基本的な使用例
以下の例では、シンプルなUserクラスと結果セットを使用してKoriym.Loopの基本的な使い方を示します。
class User
{
public function __construct(
public readonly int $id,
public readonly string $name
){}
}
// データベースの結果セットやCSVの内容
$resultSet = [
['id' => 1, 'name' => 'ray'],
['id' => 2, 'name' => 'bear'],
['id' => 3, 'name' => 'alps'],
];
/** @var Generator<Loop, User, mixed, void> $users */
$users = (new LoopGen)($resultSet, User::class);
foreach ($users as $user) {
echo $user->name;
}
ループ情報の利用
ループ情報は配列のキーからアクセスでき、リスト内の現在のアイテムが最初または最後かどうかなどの詳細を提供します。
/** @var Loop $loop */
foreach ($users as $loop => $user) {
echo match(true) {
$loop->isFirst && $loop->isLast => "<ul><li>{$user->name}</ul>",
$loop->isFirst => "<ul><li>{$user->name}",
$loop->isLast => "<li>{$user->name}</ul>",
default => "<li>{$user->name}"
};
}
依存性の注入
エンティティに依存インスタンスを注入することも可能です。キーをパラメータ名として、値をインスタンスとして指定します。
イテレーターのサポート
Koriym.Loopは配列だけでなくイテレーターもサポートしています。以下はCSVイテレーターを使用した例です。
class Row
{
public function __construct(
public readonly int $id,
public readonly string $name
){}
}
// CSVファイルの内容をリスト<Row>として取得
$csvIterator = new ogrrd\CsvIterator\CsvIterator($csvFilePath);
/** @var list<Row> $csvRowList */
$csvRowList = (new LoopGen)($csvIterator, Row::class);
foreach ($csvRowList as $loop => $row) {
if ($loop->isFirst) {
continue; // ヘッダーをスキップ
}
echo $row->name;
}
応用例1:図形の辺の長さと面積
Koriym.Loopを使用して図形の辺の長さを与え、その面積を計算してプロパティとして持たせるエンティティを作成することもできます。例えば、正方形の辺の長さから面積を計算する例を見てみましょう。
class Square
{
public readonly float $area;
public function __construct(
public readonly float $sideLength
){
$this->area = $this->sideLength ** 2;
}
}
$resultSet = [
['sideLength' => 2.0],
['sideLength' => 3.5],
['sideLength' => 4.0],
];
/** @var Generator<Loop, Square, mixed, void> $squares */
$squares = (new LoopGen)($resultSet, Square::class);
foreach ($squares as $square) {
echo "Side Length: {$square->sideLength}, Area: {$square->area}\n";
}
応用例2:ロガーを利用したエンティティの例
以下の例では、Userクラスにロガー(LoggerInterface)を注入し、ユーザーのアクションをログに記録します。
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
class User
{
public function __construct(
public readonly int $id,
public readonly string $name,
private readonly LoggerInterface $logger
){
$this->logAction('created');
}
public function logAction(string $action): void
{
$this->logger->log(LogLevel::INFO, "User {$this->name} performed action: {$action}");
}
}
class SimpleLogger implements LoggerInterface
{
public function log($level, $message, array $context = [])
{
echo "[{$level}] {$message}\n";
}
public function emergency($message, array $context = []) {}
public function alert($message, array $context = []) {}
public function critical($message, array $context = []) {}
public function error($message, array $context = []) {}
public function warning($message, array $context = []) {}
public function notice($message, array $context = []) {}
public function info($message, array $context = []) {}
public function debug($message, array $context = []) {}
}
$resultSet = [
['id' => 1, 'name' => 'ray'],
['id' => 2, 'name' => 'bear'],
['id' => 3, 'name' => 'alps'],
];
$dependencies = [
'logger' => new SimpleLogger()
];
/** @var Generator<Loop, User, mixed, void> $users */
$users = (new LoopGen)($resultSet, User::class, $dependencies);
foreach ($users as $user) {
// 追加のアクションログ
$user->logAction('login');
}
この例では、SimpleLoggerクラスを依存性として注入し、Userクラスがそのロガーを利用してエンティティの生成時に「created」アクションをログに記録します。また、必要に応じて追加のアクションをログに記録することもできます。
応用例3:他のイテレーターと組み合わせた使用例
class User
{
public function __construct(
public readonly int $id,
public readonly string $name
){}
public function getUpperCaseName(): string
{
return strtoupper($this->name);
}
}
// データベースからの結果セットの例
$resultSet = new ArrayIterator([
['id' => 1, 'name' => 'ray'],
['id' => 2, 'name' => 'bear'],
['id' => 3, 'name' => 'alps'],
['id' => 4, 'name' => 'charlie'],
]);
// フィルタリングするイテレーター
$filteredIterator = new CallbackFilterIterator($resultSet, function($current) {
return $current['id'] % 2 === 0; // 偶数IDのユーザーのみ
});
/** @var Generator<Loop, User, mixed, void> $users */
$users = (new LoopGen)($filteredIterator, User::class);
foreach ($users as $user) {
echo $user->getUpperCaseName() . "\n";
}
応用例4:テンプレートエンジンでの使用
Koriym.Loopで生成されたジェネレーターは、Twigなどのテンプレートエンジンにアサインして使用することもできます。
require_once '/path/to/vendor/autoload.php';
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
class User
{
public function __construct(
public readonly int $id,
public readonly string $name
){}
}
// Twigの設定
$loader = new FilesystemLoader('/path/to/templates');
$twig = new Environment($loader);
// データベースの結果セットの例
$resultSet = [
['id' => 1, 'name' => 'ray'],
['id' => 2, 'name' => 'bear'],
['id' => 3, 'name' => 'alps'],
];
// Koriym.Loopを使用してジェネレーターを作成
/** @var Generator<Loop, User, mixed, void> $users */
$users = (new LoopGen)($resultSet, User::class);
// テンプレートにアサインしてレンダリング
echo $twig->render('user_list.twig', ['users' => $users]);
user_list.twig
テンプレートの例:
<ul>
{% for user in users %}
<li>{{ user.name }}</li>
{% endfor %}
</ul>
まとめ
Koriym.Loopは、PHPでの結果セットや他の反復可能なデータ構造の処理を柔軟かつ便利にするライブラリです。ループの状態に関する追加情報を提供することで、テンプレートエンジンのような使いやすさを実現します。ぜひ、プロジェクトで試してみてください。
注)この記事はREADMEからAIが生成したものを加筆・編集しています。