문제
Lithium Model의 find()
메소드로 결과로부터 결과를 제외하고 싶습니다.MongoDB와 MySQL 데이터 소스가있는 모델을 위해이 작업을 수행해야하지만 SQL에서는 WHERE myfield NOT IN (1,2,3)
와 같습니다.
아래와 같이 not
배열에서 conditions
절을 전달할 수 있지만 가능하지 않은 것으로 보입니다.
Item::all(array('conditions' => array('not' => array('myfield' => array(1,2,3))));
.
그래서 제 질문은 내가 간과 한 방식으로 리튬에서 가능합니다.그렇지 않은 경우, 내 모델을 위해이를 구현하는 가장 리튬 이슈가 무엇인가?
해결책
Merely filtering for MongoDB can easily be achieved like this:
Item::all(array('conditions' =>
array('myfield' => array(
'$nin' => array(1,2,3)
))
));
If this is something you do a lot you could even create a custom finder for it :
class MyModel extends \lithium\data\Model {
public static function __init()
{
parent::__init();
static::finder('notin', function($self, $params, $chain) {
// Take all array keys that are not option keys
$array = array_diff_key($params['options'],
array_fill_keys(array('conditions', 'fields','order','limit','page'),0));
// Clean up options leaving only what li3 expects
$params['options'] = array_diff_key($params['options'], $array);
$params['options']['conditions'] = array(
'myfield' => array(
'$nin' => $array
)
);
return $chain->next($self, $params, $chain);
});
}
}
And call it like this :
MyModel::notin(array(1,2,3));
In the same manner you could create a custom finder for MySQL sources.
As you probably can see this creates some issues if you pass something like array('fields'=>$array)
as it would overwrite the option.
What happens is that ::notin()
(finders in general) has a distinct behavior for the (array,null) signature. If that happens it thinks the first array is options and the finder took no arguments.
Using notin($array,array())
breaks the previous finder because the first argument ends up in $params['notin']
when the real second argument (options) is passed.
If you mix data sources on the fly here I would create a custom model that does not inherit \lithium\data\Model and have it delegate
to the different models and create the conditions based on the end models data source.
class MyFacadeModel {
public static function byNotIn($conditions, $source) {
return ($source == "mongodb")
? $source::find( $rewrittenConditions)
: $source::find( $rewrittenConditionsForMysql );
}
}
(Code might be slightly incorrect as its mostly taken from the top of my head)
다른 팁
Just to clarify, Lithium's MongoDB adapter supports most SQL comparison operators as a convenience, so for either Mongo or MySQL, you could simply write the query as follows:
Item::all(array('conditions' => array(
'myfield' => array('!=' => array(1,2,3))
)));
And it should give you the results you expect. For MySQL, the query should look something like:
SELECT * FROM items WHERE myfield NOT IN (1, 2, 3);
And in Mongo:
db.items.find({ myfield: { $nin: [1, 2, 3] }})