LINQは、SQLに似たクエリをコードに直接記述できるようにするものです。 実際、LINQ to Objectsを使用すると、オブジェクト、配列、およびコード内で操作するすべてのものにクエリを書き込むことができます。
これは何のためですか?
ベースがある場合、お気に入りのORM(またはお気に入りのベアSQL-好きな人)があります。 ただし、オブジェクトはWebサービスやファイルから取得される場合があり、実際にオブジェクトを隠す暗闇では、重要な処理が必要になる場合があります。変換、フィルタリング、並べ替え、グループ化、集計...通常のORMまたはSQLを適用します-データベースはありません。 LINQ to Objectsが助けになります。この場合はYaLinqoです。
何ができる?
- PHPで最も包括的な.NET LINQポートと、多くの追加メソッド。 合計で、70以上のメソッドが実装されています。
- 元のLINQのように、遅延計算、テキスト例外などがあります。
- 各メソッドの詳細なPHPDocドキュメント。 MSDNから改編された記事のテキスト。
- 100%のユニットテストカバレッジ。
- コールバックは、クロージャー、文字列と配列の形式の「関数へのポインタ」、いくつかの構文をサポートする文字列「ラムダ」によって定義できます。
- キーは値と同じくらい注意を払っています。変換は両方に適用できます。 ほとんどのコールバックは両方を受け入れます。 可能であれば、変換中にキーは失われません。
- 自転車の最小限の発明:Iterator、IteratorAggregateなどが反復に使用されます(Enumerableと共に使用できます)。 可能な限り、例外はネイティブのハングアップなどで使用されます。
- Composerがサポートされており、Packagistにパッケージがあります。
- 外部依存関係はありません。
どうした
PHP 5.5がジェネレーターや固定イテレーターなどのあらゆる種類の機能を備えてから1年が経過しました。 私の良心でPHPの最も本格的なLINQポートであるため、私はそれを更新して新しい言語機能を使用する時であると判断しました。
新機能
速度は新しいです。 大量のコードが捨てられます(Git計算によると約800行):
Iterator
を生成する
Enumerator
ような松葉杖はありません。 無駄なコレクションはありませんでした。実際、キーのオブジェクトを保存することだけが利点でした。
call_user_func
はしませんでした...そして最も重要なことは、イテレーターを生成するための変更不可能なコードがなく、理解できないことです。
foreach
と
yield
です。 合計すると、速度が大幅に向上しました。
私はこの怪物に取って代わった喜びを知らない。
return new Enumerable(function () use ($self, $inner, $outerKeySelector, $innerKeySelector, $resultSelectorValue, $resultSelectorKey) { /** @var $self Enumerable */ /** @var $inner Enumerable */ /** @var $arrIn array */ $itOut = $self->getIterator(); $itOut->rewind(); $lookup = $inner->toLookup($innerKeySelector); $arrIn = null; $posIn = 0; $key = null; return new Enumerator(function ($yield) use ($itOut, $lookup, &$arrIn, &$posIn, &$key, $outerKeySelector, $resultSelectorValue, $resultSelectorKey) { /** @var $itOut \Iterator */ /** @var $lookup \YaLinqo\collections\Lookup */ while ($arrIn === null || $posIn >= count($arrIn)) { if ($arrIn !== null) $itOut->next(); if (!$itOut->valid()) return false; $key = call_user_func($outerKeySelector, $itOut->current(), $itOut->key()); $arrIn = $lookup[$key]; $posIn = 0; } $args = array($itOut->current(), $arrIn[$posIn], $key); $yield(call_user_func_array($resultSelectorValue, $args), call_user_func_array($resultSelectorKey, $args)); $posIn++; return true; }); });
簡潔にする:
return new Enumerable(function () use ($inner, $outerKeySelector, $innerKeySelector, $resultSelectorValue, $resultSelectorKey) { $lookup = $inner->toLookup($innerKeySelector); foreach ($this as $ok => $ov) { $key = $outerKeySelector($ov, $ok); if (isset($lookup[$key])) foreach ($lookup[$key] as $iv) yield $resultSelectorKey($ov, $iv, $key) => $resultSelectorValue($ov, $iv, $key); } });
さらに、最終的にヒューマンバージョンタグをリポジトリに追加し、
composer.json
にブランチエイリアスを記述したので、Composerを使用することで痛みが軽減されるはずです。
結局のところ、それは何ですか?
配列があるとしましょう:
$products = array( array('name' => 'Keyboard', 'catId' => 'hw', 'quantity' => 10, 'id' => 1), array('name' => 'Mouse', 'catId' => 'hw', 'quantity' => 20, 'id' => 2), array('name' => 'Monitor', 'catId' => 'hw', 'quantity' => 0, 'id' => 3), array('name' => 'Joystick', 'catId' => 'hw', 'quantity' => 15, 'id' => 4), array('name' => 'CPU', 'catId' => 'hw', 'quantity' => 15, 'id' => 5), array('name' => 'Motherboard', 'catId' => 'hw', 'quantity' => 11, 'id' => 6), array('name' => 'Windows', 'catId' => 'os', 'quantity' => 666, 'id' => 7), array('name' => 'Linux', 'catId' => 'os', 'quantity' => 666, 'id' => 8), array('name' => 'Mac', 'catId' => 'os', 'quantity' => 666, 'id' => 9), ); $categories = array( array('name' => 'Hardware', 'id' => 'hw'), array('name' => 'Operating systems', 'id' => 'os'), );
名前でソートされた対応するカテゴリーにゼロ以外の数量の製品を配置する必要があり、カテゴリー内で最初に数量の降順で製品をソートし、次に名前でソートする必要があるとします。 これで、3回のネストされたループ、配列の関数呼び出し、適切な並べ替え関数の接頭辞を覚えようとすることを頭の中に構築し始めています...これに代えて、次のように記述できます。
$result = from($categories) ->orderBy('$cat ==> $cat["name"]') ->groupJoin( from($products) ->where('$prod ==> $prod["quantity"] > 0') ->orderByDescending('$prod ==> $prod["quantity"]') ->thenBy('$prod ==> $prod["name"]'), '$cat ==> $cat["id"]', '$prod ==> $prod["catId"]', '($cat, $prods) ==> [ "name" => $cat["name"], "products" => $prods ]' );
PHPの作成者が頑固なロバではなく、ラムダによるプルリクエストを放棄しなかった場合、次のように書くこともできます。
$result = from($categories) ->orderBy($cat ==> $cat['name']) ->groupJoin( from($products) ->where($prod ==> $prod['quantity'] > 0) ->orderByDescending($prod ==> $prod['quantity']) ->thenBy($prod ==> $prod['name']), $cat ==> $cat['id'], $prod ==> $prod['catId'], ($cat, $prods) ==> [ 'name' => $cat['name'], 'products' => $prods, ] );
何らかの方法で、出力では次のようになります。
Array ( [hw] => Array ( [name] => Hardware [products] => Array ( [0] => Array ( [name] => Mouse [catId] => hw [quantity] => 20 [id] => 2 ) [1] => Array ( [name] => CPU [catId] => hw [quantity] => 15 [id] => 5 ) [2] => Array ( [name] => Joystick [catId] => hw [quantity] => 15 [id] => 4 ) [3] => Array ( [name] => Motherboard [catId] => hw [quantity] => 11 [id] => 6 ) [4] => Array ( [name] => Keyboard [catId] => hw [quantity] => 10 [id] => 1 ) ) ) [os] => Array ( [name] => Operating systems [products] => Array ( [0] => Array ( [name] => Linux [catId] => os [quantity] => 666 [id] => 8 ) [1] => Array ( [name] => Mac [catId] => os [quantity] => 666 [id] => 9 ) [2] => Array ( [name] => Windows [catId] => os [quantity] => 666 [id] => 7 ) ) ) )
麻酔薬とオプティマイザの場合、「文字列ラムダ」ではなく、匿名関数を使用できます。
'$prod ==> $prod["quantity"] > 0'
代わりに、
function ($prod) { return $prod['quantity'] > 0; }
function ($prod) { return $prod['quantity'] > 0; }
野生の難読化ツールでは、デフォルトの引数名(vは値、kはキーなど)を使用できます。つまり、
'$v["quantity"] > 0'
(複雑なネストされたクエリにはお勧めできません)と書くだけです。
LINQ to Databaseはどこにありますか?
はい、実際には.NETの世界ではLINQクエリもORMで使用されます(これは一般に主な目的であると言う人がいます)が、具体的にはこの機能はライブラリにありません。実装しようとすると、松葉杖、ブレーキなどが大量に発生するからです言語レベルでのサポートの欠如による見苦しいこと(特に式の解析)。 LINQ to Objectsは「文字列ラムダ」の形で松葉杖なしではできませんでしたが、ここでは、完全な分析と大量の最適化を備えたPHPからSQLへの本格的な翻訳者が必要です。
さあ!
PS古いバージョンはPHP 5.3をサポートしています。 機能的には劣りませんが、やや遅くなります(反復子-s)。
PPS Saneのライバル(Ginq)がついに登場しました。 SPL、Symfony、その他のアーキテクチャがたくさんあり、コメントはありません。多くのブレーキがあります(私のバージョンに対してx2からx50のオーバーヘッド)。 プロセスのベンチマークは、私が次回書くでしょう。