ソースを参照

custom finds behavior

euromark 13 年 前
コミット
db7016c95f

+ 113 - 0
Model/Behavior/CustomFindsBehavior.php

@@ -0,0 +1,113 @@
+<?php
+App::uses('ModelBehavior', 'Model');
+
+/**
+ * CustomFinds Behavior class
+ *
+ * Behavior for CakePHP that enables you to configure custom
+ * queries in your Model classes.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author     Ariel Patschiki, Daniel L. Pakuschewski
+ * @license    MIT License (http://www.opensource.org/licenses/mit-license.php)
+ * @copyright  Copyright 2010, MobVox Solu??es Digitais.
+ * @version    0.1
+ */
+
+/**
+ * @version 0.2:
+ * @modified Mark Scherer
+ * - works with cakephp2.x
+ * - added key: remove (to remove some custom fields again)
+ * - rewritten method: modifyQuery()
+ * - test case added
+ * 2012-07-18 ms
+ */
+class CustomFindsBehavior extends ModelBehavior {
+
+	/**
+	 * Prevent that Containable is loaded after CustomFinds.
+	 * Containable Behavior need to be loaded before CustomFinds Behavior.
+	 * @param Model $Model
+	 * @param array $query
+	 */
+	protected function _verifyContainable(Model $Model, $query) {
+		if (is_array($Model->actsAs) && in_array('Containable', $Model->actsAs) && isset($query['contain'])) {
+			if (array_search('CustomFinds', $Model->actsAs) > array_search('Containable', $Model->actsAs)) {
+				trigger_error(__('The behavior "Containable", if used together with "CustomFinds" needs to be loaded before.'), E_USER_WARNING);
+			}
+		}
+	}
+
+	protected function _modifyQuery(Model $Model, $query) {
+		$customQuery = $Model->customFinds[$query['custom']];
+		unset($query['custom']);
+
+		if (isset($query['remove'])) {
+			$removes = (array)$query['remove'];
+			unset($query['remove']);
+			$this->_remove($customQuery, $removes);
+		}
+		return Set::merge($customQuery, $query);
+	}
+
+	//TODO: fixme for deeper arrays
+	protected function _remove(&$query, $removes) {
+		foreach ($removes as $key => $remove) {
+			//$query = Set::remove($query, $remove); # doesnt work due to dot syntax
+			if (is_string($remove)) {
+				if (isset($query[$remove])) {
+					unset($query[$remove]);
+				}
+				return;
+			}
+			foreach ($remove as $subKey => $subRemove) {
+				if (is_string($subKey) && isset($query[$remove][$subKey])) {
+					return $this__remove($query[$remove][$subKey], $subRemove);
+				}
+
+				if (is_string($subRemove)) {
+					if (isset($query[$key][$subRemove])) {
+						unset($query[$key][$subRemove]);
+						return;
+					}
+					/*
+					if (is_string($subKey) && isset($subRemove, $query[$key][$subKey])) {
+						continue;
+					}
+					*/
+					/*
+					if (!isset($query[$remove])) {
+						continue;
+					}
+					*/
+					/*
+					$element = array_shift(array_keys($query[$key], $subRemove));
+					unset($query[$key][$element]);
+					return;
+					*/
+				}
+				//return $this->_remove($query[$key], $subRemove);
+			}
+		}
+	}
+
+	/**
+	 * Get customFinds at Model and merge with query.
+	 * @param Model $Model
+	 * @param array $query
+	 * @return array
+	 */
+	public function beforeFind(Model $Model, $query) {
+		if (isset($Model->customFinds) && isset($query['custom']) && isset($Model->customFinds[$query['custom']])) {
+			$query = $this->_modifyQuery($Model, $query);
+			$this->_verifyContainable($Model, $query);
+			return $query;
+		}
+		return true;
+	}
+
+
+}

+ 1 - 1
Model/Behavior/LinkableBehavior.php

@@ -25,7 +25,7 @@ App::uses('ModelBehavior', 'Model');
  *
  * @version 1.2:
  * @modified Mark Scherer
- * - works with cakephp2.0 (89.84 test coverage)
+ * - works with cakephp2.x (89.84 test coverage)
  */
 class LinkableBehavior extends ModelBehavior {
 

+ 92 - 0
Test/Case/Model/Behavior/CustomFindsBehaviorTest.php

@@ -0,0 +1,92 @@
+<?php
+
+App::uses('CustomFindsBehavior', 'Tools.Model/Behavior');
+App::uses('AppModel', 'Model');
+App::uses('MyCakeTestCase', 'Tools.Lib');
+
+class Test extends AppModel {
+
+
+	public $useTable = false;
+	public $actsAs = array('Tools.CustomFinds');
+
+}
+
+class CustomFindsBehaviorTest extends MyCakeTestCase {
+
+	public function setUp() {
+		$this->CustomFinds = new CustomFindsBehavior();
+
+		$this->Model = new Test();
+
+		$this->Model->customFinds = array(
+			'topSellers' => array(
+				'fields' => array('Product.name','Product.price'),
+				'contain' => array('ProductImage.source'),
+				'conditions' => array('Product.countSeller >' => 20, 'Product.is_active' => 1),
+				'recursive' => 1,
+				//All other find options
+			)
+		);
+	}
+
+	public function tearDown() {
+
+	}
+
+	public function testObject() {
+		$this->assertTrue(is_a($this->CustomFinds, 'CustomFindsBehavior'));
+	}
+
+	public function testModify() {
+		$query = array(
+			'custom' => 'topSellers',
+			'recursive' => 0,
+			'conditions' => array('Product.count >'=>0),
+		);
+
+		$res = $this->Model->Behaviors->CustomFinds->beforeFind($this->Model, $query);
+		pr($res);
+		$queryResult = $this->Model->customFinds['topSellers'];
+		$queryResult['recursive'] = 0;
+		$queryResult['conditions']['Product.count >'] = 0;
+
+		$this->assertTrue(!empty($res));
+		$this->assertSame($queryResult['recursive'], $res['recursive']);
+		$this->assertSame($queryResult['conditions'], $res['conditions']);
+	}
+
+	public function testModifyWithRemove() {
+		$query = array(
+			'custom' => 'topSellers',
+			'conditions' => array('Product.count >'=>0),
+			'remove' => array('conditions')
+		);
+
+		$res = $this->Model->Behaviors->CustomFinds->beforeFind($this->Model, $query);
+		pr($res);
+		$queryResult = $this->Model->customFinds['topSellers'];
+		$queryResult['conditions'] = array('Product.count >'=>0);
+
+		$this->assertTrue(!empty($res));
+		$this->assertSame($queryResult['recursive'], $res['recursive']);
+		$this->assertSame($queryResult['conditions'], $res['conditions']);
+
+
+		$query = array(
+			'custom' => 'topSellers',
+			'conditions' => array('Product.count >'=>0),
+			'remove' => array('conditions'=>array('Product.countSeller >'))
+		);
+
+		$res = $this->Model->Behaviors->CustomFinds->beforeFind($this->Model, $query);
+		pr($res);
+		$queryResult = $this->Model->customFinds['topSellers'];
+		unset($queryResult['conditions']['Product.countSeller >']);
+		$queryResult['conditions']['Product.count >'] = 0;
+
+		$this->assertTrue(!empty($res));
+		$this->assertSame($queryResult['conditions'], $res['conditions']);
+	}
+
+}

+ 14 - 12
Test/Case/Model/Behavior/LinkableBehaviorTest.php

@@ -22,17 +22,14 @@ class LinkableBehaviorTest extends CakeTestCase {
 	public $User;
 
 	public function startTest() {
-
 		$this->User = ClassRegistry::init('User');
 	}
 
 	public function endTest() {
-
 		unset($this->User);
 	}
 
-	public function testBelongsTo()
-	{
+	public function testBelongsTo() {
 		$arrayExpected = array(
 			'User' => array('id' => 1, 'username' => 'CakePHP'),
 			'Profile' => array ('id' => 1, 'user_id' => 1, 'biography' => 'CakePHP is a rapid development framework for PHP that provides an extensible architecture for developing, maintaining, and deploying applications.')
@@ -126,8 +123,7 @@ class LinkableBehaviorTest extends CakeTestCase {
 		$this->assertEquals($arrayExpected, $arrayResult, 'On-the-fly belongsTo association via Linkable, with order: %s');
 	}
 
-	public function testHasMany()
-	{
+	public function testHasMany() {
 		// hasMany association via Containable. Should still work when Linkable is loaded
 		$arrayExpected = array(
 			'User' => array('id' => 1, 'username' => 'CakePHP'),
@@ -186,8 +182,7 @@ class LinkableBehaviorTest extends CakeTestCase {
 		$this->assertEquals($arrayExpected, $arrayResult, 'hasMany association via Linkable: %s');
 	}
 
-	public function testComplexAssociations()
-	{
+	public function testComplexAssociations() {
 		$this->BlogPost = ClassRegistry::init('BlogPost');
 
 		$arrayExpected = array(
@@ -336,8 +331,7 @@ class LinkableBehaviorTest extends CakeTestCase {
 	 *	Series of tests that assert if Linkable can adapt to assocations that
 	 *	have aliases different from their standard model names
 	 */
-	public function _testNonstandardAssociationNames()
-	{
+	public function _testNonstandardAssociationNames() {
 		$this->Tag = ClassRegistry::init('Tag');
 
 		$arrayExpected = array(
@@ -433,8 +427,7 @@ class LinkableBehaviorTest extends CakeTestCase {
 		$this->assertEquals($arrayExpected, $arrayResult, 'hasMany association with custom foreignKey: %s');
 	}
 
-	public function _testAliasedBelongsToWithSameModelAsHasMany()
-	{
+	public function _testAliasedBelongsToWithSameModelAsHasMany() {
 		$this->OrderItem = ClassRegistry::init('OrderItem');
 
 		$arrayExpected = array(
@@ -461,6 +454,7 @@ class LinkableBehaviorTest extends CakeTestCase {
 
 		$this->assertEquals($arrayExpected, $arrayResult, 'belongsTo association with alias (requested), with hasMany to the same model without alias: %s');
 	}
+
 }
 
 
@@ -475,6 +469,7 @@ class TestModel extends CakeTestModel {
 }
 
 class User extends TestModel {
+
 	public $hasOne = array(
 		'Profile'
 	);
@@ -486,12 +481,14 @@ class User extends TestModel {
 }
 
 class Profile extends TestModel {
+
 	public $belongsTo = array(
 		'User'
 	);
 }
 
 class BlogPost extends TestModel {
+
 	public $belongsTo = array(
 		'User'
 	);
@@ -505,6 +502,7 @@ class BlogPostTag extends TestModel {
 }
 
 class Tag extends TestModel {
+
 	public $hasAndBelongsToMany = array(
 		'BlogPost'
 	);
@@ -518,6 +516,7 @@ class Tag extends TestModel {
 }
 
 class LegacyProduct extends TestModel {
+
 	public $primaryKey = 'product_id';
 
 	public $belongsTo = array(
@@ -533,6 +532,7 @@ class LegacyProduct extends TestModel {
 }
 
 class LegacyCompany extends TestModel {
+
 	public $primaryKey = 'company_id';
 
 	public $hasMany = array(
@@ -544,12 +544,14 @@ class LegacyCompany extends TestModel {
 }
 
 class Shipment extends TestModel {
+
 	public $belongsTo = array(
 		'OrderItem'
 	);
 }
 
 class OrderItem extends TestModel {
+
 	public $hasMany = array(
 		'Shipment'
 	);