From df60621bc347d99e10b86a4dd73b5060ec266fed Mon Sep 17 00:00:00 2001 From: eileenmcnaugton Date: Mon, 14 Sep 2015 12:55:24 +1200 Subject: [PATCH] CRM-17075 switch membership_type_id to use entity reference This commit also adds generic handling for changing select fields to entity reference fields with tests to ensure previous smart group behaviour is respected by handling within the convertFormValues function. This reverts advanced search domain filtering to ?4.4? 4.5? behaviour which does not assume only that domain's memberships should be searched on. It make be that domain filtering + allowing an override makes most sense. I did think the pseudoconstant & getoptions use the same mechanism, but it seems they don't. Really they should both allow the same behaviour & override --- CRM/Contact/BAO/Query.php | 38 +++++++++- CRM/Core/Form/Search.php | 10 +++ CRM/Member/BAO/Query.php | 6 ++ CRM/Member/Form/Search.php | 11 ++- tests/phpunit/CRM/Member/BAO/QueryTest.php | 82 ++++++++++++++++++++++ 5 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 tests/phpunit/CRM/Member/BAO/QueryTest.php diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index 87301322f8..cd69b39b1e 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -1465,15 +1465,23 @@ class CRM_Contact_BAO_Query { * * @param string $apiEntity * + * @param array $entityReferenceFields + * Field names of any entity reference fields (which will need reformatting to IN syntax). + * * @return array */ - public static function convertFormValues(&$formValues, $wildcard = 0, $useEquals = FALSE, $apiEntity = NULL) { + public static function convertFormValues(&$formValues, $wildcard = 0, $useEquals = FALSE, $apiEntity = NULL, + $entityReferenceFields = array()) { $params = array(); if (empty($formValues)) { return $params; } foreach ($formValues as $id => $values) { + if (self::isAlreadyProcessedForQueryFormat($values)) { + $params[] = $values; + continue; + } if ($id == 'privacy') { if (is_array($formValues['privacy'])) { $op = !empty($formValues['privacy']['do_not_toggle']) ? '=' : '!='; @@ -1526,6 +1534,10 @@ class CRM_Contact_BAO_Query { continue; } } + elseif (in_array($id, $entityReferenceFields) && !empty($values) && is_string($values) && (strpos($values, ',') != + FALSE)) { + $params[] = array($id, 'IN', explode(',', $values), 0, 0); + } else { $values = CRM_Contact_BAO_Query::fixWhereValues($id, $values, $wildcard, $useEquals, $apiEntity); @@ -4392,6 +4404,30 @@ civicrm_relationship.is_permission_a_b = 0 ); } + /** + * Has this field already been reformatting to Query object syntax. + * + * The form layer passed formValues to this function in preProcess & postProcess. Reason unknown. This seems + * to come with associated double queries & is possibly damaging performance. + * + * However, here we add a tested function to ensure convertFormValues identifies pre-processed fields & returns + * them as they are. + * + * @param mixed $values + * Value in formValues for the field. + * + * @return bool; + */ + protected static function isAlreadyProcessedForQueryFormat($values) { + if (!is_array($values)) { + return FALSE; + } + if (($operator = CRM_Utils_Array::value(1, $values)) == FALSE) { + return FALSE; + } + return in_array($operator, CRM_Core_DAO::acceptedSQLOperators()); + } + /** * Create and query the db for an contact search. * diff --git a/CRM/Core/Form/Search.php b/CRM/Core/Form/Search.php index 4ff32c0c12..6d83b1c731 100644 --- a/CRM/Core/Form/Search.php +++ b/CRM/Core/Form/Search.php @@ -79,6 +79,16 @@ class CRM_Core_Form_Search extends CRM_Core_Form { */ protected $_taskList = array(); + /** + * Declare entity reference fields as they will need to be converted. + * + * The entity reference format looks like '2,3' whereas the Query object expects array(2, 3) + * or array('IN' => array(2, 3). The latter is the one we are moving towards standardising on. + * + * @var array + */ + protected $entityReferenceFields = array(); + /** * Builds the list of tasks or actions that a searcher can perform on a result set. * diff --git a/CRM/Member/BAO/Query.php b/CRM/Member/BAO/Query.php index 6100658aa3..09f8089ab5 100644 --- a/CRM/Member/BAO/Query.php +++ b/CRM/Member/BAO/Query.php @@ -409,6 +409,12 @@ class CRM_Member_BAO_Query { 'class' => 'crm-select2', )); + $form->addEntityRef('membership_type_id', ts('Membership Type(s)'), array( + 'entity' => 'MembershipType', + 'multiple' => TRUE, + 'placeholder' => ts('- any -'), + )); + $form->addElement('text', 'member_source', ts('Source')); CRM_Core_Form_Date::buildDateRange($form, 'member_join_date', 1, '_low', '_high', ts('From'), FALSE); diff --git a/CRM/Member/Form/Search.php b/CRM/Member/Form/Search.php index 47c6e488f3..7d42293edb 100644 --- a/CRM/Member/Form/Search.php +++ b/CRM/Member/Form/Search.php @@ -68,6 +68,13 @@ class CRM_Member_Form_Search extends CRM_Core_Form_Search { */ protected $_prefix = "member_"; + /** + * Declare entity reference fields as they will need to be converted to using 'IN'. + * + * @var array + */ + protected $entityReferenceFields = array('membership_type_id'); + /** * Processing needed for buildForm and later. */ @@ -114,7 +121,7 @@ class CRM_Member_Form_Search extends CRM_Core_Form_Search { ); } - $this->_queryParams = CRM_Contact_BAO_Query::convertFormValues($this->_formValues); + $this->_queryParams = CRM_Contact_BAO_Query::convertFormValues($this->_formValues, 0, FALSE, NULL, $this->entityReferenceFields); $selector = new CRM_Member_Selector_Search($this->_queryParams, $this->_action, NULL, @@ -197,7 +204,7 @@ class CRM_Member_Form_Search extends CRM_Core_Form_Search { CRM_Core_BAO_CustomValue::fixCustomFieldValue($this->_formValues); - $this->_queryParams = CRM_Contact_BAO_Query::convertFormValues($this->_formValues); + $this->_queryParams = CRM_Contact_BAO_Query::convertFormValues($this->_formValues, 0, FALSE, NULL, $this->entityReferenceFields); $this->set('formValues', $this->_formValues); $this->set('queryParams', $this->_queryParams); diff --git a/tests/phpunit/CRM/Member/BAO/QueryTest.php b/tests/phpunit/CRM/Member/BAO/QueryTest.php new file mode 100644 index 0000000000..84a5270e91 --- /dev/null +++ b/tests/phpunit/CRM/Member/BAO/QueryTest.php @@ -0,0 +1,82 @@ + 1 + */ + public function testConvertEntityFieldSingleValue() { + $formValues = array('membership_type_id' => 2); + $params = CRM_Contact_BAO_Query::convertFormValues($formValues, 0, FALSE, NULL, array('membership_type_id')); + $this->assertEquals(array('membership_type_id', '=', 2, 0, 0), $params[0]); + $obj = new CRM_Contact_BAO_Query($params); + $this->assertEquals(array('civicrm_membership.membership_type_id = 2'), $obj->_where[0]); + } + + /** + * Check that membership type is handled. + * + * We want to see the following syntaxes for membership_type_id field handled: + * 2) membership_type_id => 5,6 + * + * The last of these is the format used prior to converting membership_type_id to an entity reference field. + */ + public function testConvertEntityFieldMultipleValueEntityRef() { + $formValues = array('membership_type_id' => '1,2'); + $params = CRM_Contact_BAO_Query::convertFormValues($formValues, 0, FALSE, NULL, array('membership_type_id')); + $this->assertEquals(array('membership_type_id', 'IN', array(1, 2), 0, 0), $params[0]); + $obj = new CRM_Contact_BAO_Query($params); + $this->assertEquals(array('civicrm_membership.membership_type_id IN ("1", "2")'), $obj->_where[0]); + } + + /** + * Check that membership type is handled. + * + * We want to see the following syntaxes for membership_type_id field handled: + * 3) membership_type_id => array(5,6) + * + * The last of these is the format used prior to converting membership_type_id to an entity reference field. It will + * be used by pre-existing smart groups. + */ + public function testConvertEntityFieldMultipleValueLegacy() { + $formValues = array('membership_type_id' => array(1, 2)); + $params = CRM_Contact_BAO_Query::convertFormValues($formValues, 0, FALSE, NULL, array('membership_type_id')); + $this->assertEquals(array('membership_type_id', 'IN', array(1, 2), 0, 0), $params[0]); + $obj = new CRM_Contact_BAO_Query($params); + $this->assertEquals(array('civicrm_membership.membership_type_id IN ("1", "2")'), $obj->_where[0]); + } + + /** + * Check that running convertFormValues more than one doesn't mangle the array. + * + * Unfortunately the convertFormValues & indeed much of the query code is run in pre-process AND post-process. + * + * The convertFormValues function should cope with this until such time as we can rationalise that. + */ + public function testConvertEntityFieldMultipleValueEntityRefDoubleRun() { + $formValues = array('membership_type_id' => '1,2'); + $params = CRM_Contact_BAO_Query::convertFormValues($formValues, 0, FALSE, NULL, array('membership_type_id')); + $this->assertEquals(array('membership_type_id', 'IN', array(1, 2), 0, 0), $params[0]); + $params = CRM_Contact_BAO_Query::convertFormValues($params, 0, FALSE, NULL, array('membership_type_id')); + $this->assertEquals(array('membership_type_id', 'IN', array(1, 2), 0, 0), $params[0]); + $obj = new CRM_Contact_BAO_Query($params); + $this->assertEquals(array('civicrm_membership.membership_type_id IN ("1", "2")'), $obj->_where[0]); + } + +} -- 2.25.1