Zend_Db-モデルオブジェクト、関係、複雑な条件

1.データモデル、テーブル、および関係



多くの人は、メソッドを正しく設定する方法を知りました。



$記事-> findAllByCategoryId($ categoryId);

または

$ category-> findAllArticles();



さらに、両方のオプションにはオブジェクトのリンクが含まれます。



あまりにも多くを知っているそのような方法なしで行うことは可能ですか? 誰かが教義を好むが、私は自分のやり方で行った-Zend_Db_Table_AbstractとZend_Db_Table_Row_Abstractから継承していくつかのクラスを書いた



目標は

1) 薄くて読みやすいコントローラーを取得し、

(驚いたことに、このアプローチを使用したモデルもそれほど厚くありませんでした)

2)モデルの人間化されたオブジェクトを作成し、

3)getAllSomethingByAnythingAlsoWhere(...)メソッドのヒープの実装を取り除く

4)また、モデルがお互いについて何も知らないようにします(*)。



コントローラーやモデルでselect、adapter、fetch、Zend_Db_Expr、quoteなどの単語を使用せずに実行しようとしました。



「真空中の球形の馬」であるにもかかわらず、すべてを例で考えるのは簡単です。



コントローラー:



<?php

class User_IndexController extends EXT_Controller_Action

{

/* Action 1 */

public function indexAction()

{

$table = new User_Model_DbTable_Users();

$users = $table->with( 'Address' , 'User_Model_DbTable_Address' , 'address_id' )

->with( 'Location' , 'User_Model_DbTable_Locations' , 'Location.id = Address.location_id' )

->addRelationMany( 'Places' , 'User_Model_DbTable_Address' , 'user_id' )

->addRelationMany( 'Friends' , 'User_Model_DbTable_Friends' , 'user_id' , true )

->findAll();

$ this ->view->users = $users;

}



/* Action 2 */

public function listAction()

{

$table = new User_Model_DbTable_Users();

$users = $table->withRelated()

->addRelationMany( 'Places' , 'User_Model_DbTable_Address' , 'user_id' )

->addRelationMany( 'Friends' , 'User_Model_DbTable_Friends' , 'user_id' , true )

->addFilter(array( 'OR' ,

array( 'name' , 'LIKE' , 'Alex%' ),

array( 'AND' ,

array( 'Location.title' , '=' , 'Swietokrzyskie' ),

array( 'Address.street' , 'LIKE' , 'Sovi%' ) )))

->setOrder( 'created_at DESC' )

->setLimit(10)

->findAll();

$ this ->view->users = $users;

}

}


* This source code was highlighted with Source Code Highlighter .








データベースオブジェクト(モデル)は次のとおりです。



/* */

class User_Model_DbTable_Users extends EXT_Db_Table

{

/* */

protected $_name = 'user__users' ;

/* - */

protected $_rowClass = 'User_Model_User' ;

/* . , $_relations */

protected $_relations = array( 'Address' => array( 'User_Model_DbTable_Address' , 'address_id' ),

'Location' => array( 'User_Model_DbTable_Locations' , 'Location.id = Address.location_id' ));

/* id () */

protected $_isIndexedRowset = true ;



const AUTH_NAMESPACE = 'auth' ;



/* . */

public function loadDefaultFilters()

{

if (!$ this ->isLoadDefaultFilters()) {

return $ this ;

}

$ this ->addFilter( 'is_deleted' , '<>' , 1);

return $ this ;

}

}



/* */

class User_Model_User extends EXT_Db_Table_Row

{

}



/* */

class User_Model_DbTable_Address extends EXT_Db_Table

{

protected $_name = 'user__address' ;

protected $_rowClass = 'User_Model_Address' ;

protected $_relations = array( 'Location' => array( 'User_Model_DbTable_Locations' , 'location_id' ),

'User' => array( 'User_Model_DbTable_Users' , 'user_id' ),);

}



/* */

class User_Model_Address extends EXT_Db_Table_Row

{

}



/* , , user'' */

class User_Model_DbTable_Friends extends EXT_Db_Table

{

protected $_name = 'user__friends' ;

protected $_rowClass = 'User_Model_Friend' ;



protected $_relations = array( 'User' => array( 'User_Model_DbTable_Users' , 'user_id' ),

'Friend' => array( 'User_Model_DbTable_Users' , 'friend_id' ));

}


* This source code was highlighted with Source Code Highlighter .








例では、2つのほぼ同一のアクション-a。 ユーザーの選択。



メインSQLクエリには、-> with()を使用して1対1のユーザーテーブルに関連付けられたデータが含まれ、次のような追加のデータベースクエリなしでユーザーオブジェクトからアクセスできます。

$ user-> getLocation()-> title // $ user-> Location__titleの同義語

$ user-> getAddress()-> street // $ user-> Address__streetの同義語

当然、getAddress()およびgetLocation()関数はありません-これらは__call()によって動的に実装されます。



ここで、トピックの最初に戻ります。

私のビューではテンプレートが使用されています

$ user-> getAllFriends()//ユーザーのすべての友達を取得

$ user-> getAllPlaces()//すべてのユーザーアドレスを取得

これらの存在しない(__call()-呼び出し)メソッドは、コントローラーで指定された接続を参照します

1)$ table-> with(...)// 1対1の関係を設定します-このデータはメインクエリfindAll()、findRow()、findAllBy()、findRowBy()に含まれます。

クエリで必要な添付フィールドのみを指定して、それを減らすことができます。

でも、クラス名ではなく、$ friendsTable(フィルターとそのリレーションがインストールされたテーブル)を渡すことができます。

一般に、すべてのニュアンスを記述することは、クラス自体を記述するよりも長くなる可能性があります。 :)

2)$ table-> addRelationMany(...)//「to many」リレーションシップを設定します-データはリクエストに含まれませんが、最初にアクセスしたときにデータベースからロードされます($ user-> getAllFriends())

接続フィールドは、名前、名前+プレフィックス、フィールドが等しいかどうかの完全な通常の条件で指定できます。



つまり モデル自体は接続されていませんが、接続は透過的に使用できます。



*保護された$ _relations = ...

それでも、わかりやすい、常に使用されるリンクがあり、利便性のために、(必要であれば)冗長リンクでオブジェクトを台無しにせずにクラスヘッダーに単一(!)の場所に書き込むことができます。

2番目のアクションでは、$ table-> withRelated()メソッドは、これらの関係をメインリクエストに含める必要があることを示しています。

これが行われない場合、$ user-> getAddress()が呼び出されると、各ユーザーに対してSQLが追加で呼び出されます。

(「ロケーション」に注意-ツリーテーブル「Country-Region-City」)



2.フィルター-サンプリング条件



Zend_Dbには、()-SQLクエリの条件に適したビルダーがないと思います。

つまり 条件(field_a = 1 OR field_b = 2)AND(field_c = 1 OR field_d = 2)は、すでに構築にいくつかの困難を引き起こしています。

コントローラーの2番目のアクションeでは、自家製のフィルタークラスに基づいて複雑な条件を構築する例を示しました。

このフィルターは通常、IS NULL / IS NOT NULL、IN(1,2,3)/ NOT IN(1,2,3)などの等号/不等式を「ダイジェスト」します。

結果として、このフィルターとfindAll()を組み合わせてモンスターコンストラクトが構築されたものを次に示します。



SELECT `user__users`.*,

`Address`.`id` AS `Address__id`,

`Address`.`street` AS `Address__street`,

`Address`.`house` AS `Address__house`,

`Address`.`floor` AS `Address__floor`,

`Address`.`flat` AS `Address__flat`,

`Address`.`location_id` AS `Address__location_id`,

`Address`.`user_id` AS `Address__user_id`,

`Location`.`id` AS `Location__id`,

`Location`.`parent_id` AS `Location__parent_id`,

`Location`.`root_path` AS `Location__root_path`,

`Location`.`title` AS `Location__title`,

`Location`.` level ` AS `Location__level`,

`Location`.`code` AS `Location__code`

FROM `user__users`

LEFT JOIN `user__address` AS `Address`

ON user__users.address_id=Address.id

LEFT JOIN `user__locations` AS `Location`

ON Location.id = Address.location_id

WHERE (((`user__users`.`is_deleted` != 1)

AND ((`user__users`.`name` LIKE 'Alex%' )

OR ((`Location`.`title` = 'Swietokrzyskie' ) AND (`Address`.`street` LIKE 'Sovi%' )))))

ORDER BY `created_at` DESC

LIMIT 10


* This source code was highlighted with Source Code Highlighter .








括弧の余分なペアを含む微妙な違いがありますが、これはリクエストの正確さに影響しません。

一般に、例よりも単純なサンプリング条件が頻繁に使用されます。

-> addFilter( 'created_at'、 '> ='、$ dateStart)

-> addFilter( 'is_new'、 '='、1)

このclearFilters(false)またはこのsetIsLoadDefaultFilters(false)によって強制的にリセットされない場合、loadDefaultFilters()で指定されたフィルターは自動的に適用されます。 並べ替えとデフォルトの制限も設定されます。



アドオンを別のライブラリのZendに配置しました。 リファクタリングや他の研削が必要ですが、それでも既に機能的で非常に便利です(imho)。

ダウンロード -例のライブラリ、モジュール、およびテーブルのみがあります

「ピックアップ」するか、Zendアプリケーションに固定して起動し、再度ピックアップすることができます。



PS興味深い場合は、Zend_Aclに基づいてACLの説明をレイアウトすることもできます-興味深い(おそらく上記のような)解決策があります。 また、ネストされたセットのない単純なツリークラスTree-なしで行う理由がありました。 さらに、Zendのネストされたセットは、私なしですでに正常に実装されています。



PPS

ビューテンプレートを表示するのを忘れました。 そして、なぜコントローラーにそんなに注意が払われているのだろうか。

ここにあります:



< div class ="container2" >

< div class ="container" >

<? php foreach ($ this- > users as $user):? >

< div >

<? = $user- > name ? > ( <? = $user- > email ? > ),

<? = $user- > Location__title ? > <? = $user- > getAddress()- > street ? > , <? = $user- > Address__house ? >

< ul >

<? php foreach ($ user- > getAllPlaces() as $place):? >

< li ><? = $place- > street ? > <? = $place- > house ? > <? = $place- > getLocation()- > title ? ></ li >

<? php endforeach ;? >

</ ul >

< ul >

<? php foreach ($ user- > getAllFriends() as $friend):? >

< li > friend: <? = $friend- > Friend__name ? > </ li >

<? php endforeach ;? >

</ ul >

</ div >

<? php endforeach ;? >

</ div >

</ div >




* This source code was highlighted with Source Code Highlighter .







All Articles