EAVデータストレージモデルに対するLINQとPHPの類似性

PHPでのLINQに関する投稿を見て、この分野のベストプラクティスをすぐに共有することにしました。

私の実装は完全なLINQにはほど遠いですが、このテクノロジーの最も顕著な特徴である外部クエリ文字列の欠如が含まれています。



なんで?


私の活動は、仕事でもそうでもありませんが、EAVデータストレージモデルを持つデータベースに関連しています。 つまり、エンティティの数が増えても、テーブルの数は増えません。 すべての情報は2つのテーブルに保存されます。

画像

EAVモデルのデータテーブル

当然、そのような構造から「レコード」を取得するには、通常のデータベース構造を使用した同様のクエリとはまったく異なるクエリを作成する必要があります。

例:

SELECT field_1, field_2, field_3 FROM object
      
      





そしてeavで

 SELECT f1.value_bigint, f2.value_bigint, f3.value_bigint FROM objects ob, attributes_values f1, attributes_values f2, attributes_values f3 WHERE ob.ID_type="object" AND f1.ID_object = ob.ID_object AND f1.ID_attribute = 1 AND f2.ID_object = ob.ID_object AND f2.ID_attribute = 2 AND f3.ID_object = ob.ID_object AND f3.ID_attribute = 3
      
      





彼らが言うように-違いを感じます。

多くのオブジェクトが、同様にリクエストを膨らませる関係によって相互接続されているという事実により、状況は複雑になっています。



クエリジェネレーター


ある時点では、サポートコードの50%〜70%を含む、読みにくい麺を書くのにうんざりしていました。 その後、自動的にリクエストを生成するというアイデアが浮上しました。 IQB-Irro Query Builderが誕生しました。 その概念は、Drupalのデータベースとの相互作用がどのように機能するかに触発されました。

IQBの上記のクエリは次のようになります。

 $q = new IQB(); $query = $q->from(array(new IQBObject('ob','object'), new IQBAttr('f1',1,INT), new IQBAttr('f2',2,INT), new IQBAttr('f3',3,INT) )) ->where('f1','with','ob')->where('f2','with','ob')->where('f3','with','ob') ->select('f1')->select('f2')->select('f3') ->build();
      
      





コードの量は減りませんが、読みやすさは私には思えますが、増えました。

このリクエストは、すべての基本的な方法を使用してリクエストを生成します。

from()メソッドは、データベーステーブルを表すクラスまたはクラスの配列を受け入れます。 テーブルは2つしかないため、クラスの数は同じです。 テーブルクラスのコンストラクターは、テーブルのエイリアス、その条件型およびデータ型(属性テーブルの場合)を受け入れます。

テーブルエイリアスは、クエリジェネレータの他のすべてのメソッドで使用されます。 オブジェクトのテーブルの条件タイプは、検索が実行されるエンティティの名前であり、属性のテーブルの場合、条件タイプは1つのオブジェクトの属性を区別するためにのみ必要です。 データのタイプ。データを取得するテーブルのフィールドを示します これが必要な理由は オブジェクトの属性は、データ用に4つのフィールドを持つ構造であり、そのうち1つだけが使用され、データが格納されるフィールドには、明示的に指定する必要があります。

where()メソッドは、選択に条件を課します。 常に3つの引数を取ります:テーブルエイリアス、条件、値。 条件に応じて、別のテーブルのエイリアス、値、またはテーブルフィールドと比較する値を持つ配列を値として渡すことができます。

例:

 $q->where('attr','with','object');
      
      





条件を設定する

 attr.ID_object = object.ID_object
      
      





そのような表現から

 $q->where('attr','=','object');
      
      





同様だが完全に異なる表現を取得する

 attr.value_bigint = object.ID_object
      
      





オブジェクトテーブルがfrom()で宣言されていない場合、これは判明します(属性データ型が文字列に変更された場合)

 attr.value_ntext = "object"
      
      





条件として、文字列「=」、「!=」、「> =」、「<=」、「>」、「<」、「いいね」および「with」を使用できます。属性は特定のオブジェクトに属します。

select()メソッドは、どのテーブル値が選択に含まれるべきかジェネレータに伝えます 。 さらに、3番目の引数でメソッドに「SUM($)」などの行を渡すことで、この値を関数で「ラップ」できます。テーブルフィールドは、関数のドルの代わりに使用されます。 2番目の引数は、選択範囲内のフィールドのエイリアスです。

groupBy()およびorderBy()メソッドと合わせて、これは平均読み取り要求を作成するのに十分です。



ただし、すべてがそれほど単純ではありません。

通常のデータベースのエンティティのようなオブジェクトは、関係によって接続できます。

奇妙に思えるかもしれませんが、コミュニケーションもオブジェクトです。 属性付き。 また、オブジェクトAの子であるオブジェクトBを取得するには、次の操作を行う必要があります。

 $q->from(array( new IQBObject('b','B'), new IQBAttr('parent',23,INT), new IQBAttr('child',24,INT) )) ->where('parent','=',123456) // ID_object   ->where('child','with','parent') ->where('child','=','b')
      
      





単純な「AからB子会社を取得」するには多すぎます。 オブジェクトのバインドを自動化するために、IQBにはlinked()メソッドがあります。

このメソッドは、ID_objectまたは既知のオブジェクトのエイリアス、子/親のエイリアス、および「Uフラグ」を受け入れます。 ヒント-子オブジェクトまたは親オブジェクトを探します。 したがって、上記のコードは次のように記述できます。

 $q->from(new IQBObject('b','B'))->linked(123456,'b');//     .
      
      





これを終了することは可能ですが、クエリジェネレーターがある程度制限されているタスクが時々あります。 たとえば、しばらくの間、いくつかの属性が存在しない可能性のあるオブジェクトに遭遇し始めました。 この問題を解決するために、属性テーブルのLEFT JOINをオブジェクトテーブルに作成するjoinTo()メソッドが追加されました。

また、非常にエキゾチックな要求には、要求の任意の部分を入力できるrawWhere()およびrawSelect()があります。



おわりに


ライブラリを一般的な用途にしようとはしなかったので、必要が生じたときにのみ新しい機会を導入しました。 これに関して、開発の初期段階で行われた設計エラーは、古いコードとの互換性と新しい機能をサポートするために必要な、松葉杖の数層で大きくなりすぎました。

IQBを使用してかなり複雑なクエリを実現する可能性はありますが、それは柔軟なものとしか言えません。 そのため、クエリ条件を設定するときに文字数をさらに減らすことができる、より柔軟なジェネレーターの概念が形成されていますが、これはまったく別の話です。



All Articles