3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * Business object for Saved searches.
21 class CRM_Contact_BAO_SavedSearch
extends CRM_Contact_DAO_SavedSearch
{
24 * Retrieve DB object and copy to defaults array.
26 * @param array $params
27 * Array of criteria values.
28 * @param array $defaults
29 * Array to be populated with found values.
32 * The DAO object, if found.
36 public static function retrieve($params, &$defaults = []) {
37 return self
::commonRetrieve(self
::class, $params, $defaults);
41 * Given an id, extract the formValues of the saved search.
44 * The id of the saved search.
47 * the values of the posted saved search used as default values in various Search Form
49 * @throws \CRM_Core_Exception
50 * @throws \CiviCRM_API3_Exception
52 public static function getFormValues($id) {
53 $specialDateFields = [
54 'event_start_date_low' => 'event_date_low',
55 'event_end_date_high' => 'event_date_high',
58 $fv = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_SavedSearch', $id, 'form_values');
61 // make sure u CRM_Utils_String::unserialize - since it's stored in serialized form
62 $result = CRM_Utils_String
::unserialize($fv);
65 $specialFields = ['contact_type', 'group', 'contact_tags', 'member_membership_type_id', 'member_status_id'];
66 foreach ($result as $element => $value) {
67 if (CRM_Contact_BAO_Query
::isAlreadyProcessedForQueryFormat($value)) {
68 $id = $value[0] ??
NULL;
69 $value = $value[2] ??
NULL;
70 if (is_array($value) && in_array(key($value), CRM_Core_DAO
::acceptedSQLOperators(), TRUE)) {
72 $value = $value[$op] ??
NULL;
73 if (in_array($op, ['BETWEEN', '>=', '<='])) {
74 self
::decodeRelativeFields($result, $id, $op, $value);
75 unset($result[$element]);
79 // Check for a date range field, which might be a standard date
80 // range or a relative date.
81 if (strpos($id, '_date_low') !== FALSE ||
strpos($id, '_date_high') !== FALSE) {
82 $entityName = strstr($id, '_date', TRUE);
84 // This is the default, for non relative dates. We will overwrite
85 // it if we determine this is a relative date.
86 $result[$id] = $value;
87 $result["{$entityName}_date_relative"] = 0;
89 if (!empty($result['relative_dates'])) {
90 if (array_key_exists($entityName, $result['relative_dates'])) {
91 // We have a match from a regular field.
93 $result["{$entityName}_date_relative"] = $result['relative_dates'][$entityName];
95 elseif (!empty($specialDateFields[$id])) {
96 // We may have a match on a special date field.
97 $entityName = strstr($specialDateFields[$id], '_date', TRUE);
98 if (array_key_exists($entityName, $result['relative_dates'])) {
100 $result["{$entityName}_relative"] = $result['relative_dates'][$entityName];
106 $result[$id] = $value;
108 unset($result[$element]);
111 if (!empty($value) && is_array($value)) {
112 if (in_array($element, $specialFields)) {
113 // Remove the element to minimise support for legacy formats. It is stored in $value
114 // so will be re-set with the right name.
115 unset($result[$element]);
116 $element = str_replace('member_membership_type_id', 'membership_type_id', $element);
117 $element = str_replace('member_status_id', 'membership_status_id', $element);
118 CRM_Contact_BAO_Query
::legacyConvertFormValues($element, $value);
119 $result[$element] = $value;
121 // As per the OK (Operator as Key) value format, value array may contain key
122 // as an operator so to ensure the default is always set actual value
123 elseif (in_array(key($value), CRM_Core_DAO
::acceptedSQLOperators(), TRUE)) {
124 $result[$element] = $value[key($value)] ??
NULL;
125 if (is_string($result[$element])) {
126 $result[$element] = str_replace("%", '', $result[$element]);
130 // We should only set the relative key for custom date fields if it is not already set in the array.
131 $realField = str_replace(['_relative', '_low', '_high', '_to', '_high'], '', $element);
132 if (substr($element, 0, 7) == 'custom_' && CRM_Contact_BAO_Query
::isCustomDateField($realField)) {
133 if (!isset($result[$realField . '_relative'])) {
134 $result[$realField . '_relative'] = 0;
137 // check to see if we need to convert the old privacy array
139 if (!empty($result['privacy'])) {
140 if (is_array($result['privacy'])) {
141 $result['privacy_operator'] = 'AND';
142 $result['privacy_toggle'] = 1;
143 if (isset($result['privacy']['do_not_toggle'])) {
144 if ($result['privacy']['do_not_toggle']) {
145 $result['privacy_toggle'] = 2;
147 unset($result['privacy']['do_not_toggle']);
150 $result['privacy_options'] = [];
151 foreach ($result['privacy'] as $name => $val) {
153 $result['privacy_options'][] = $name;
157 unset($result['privacy']);
161 if ($customSearchClass = CRM_Utils_Array
::value('customSearchClass', $result)) {
162 // check if there is a special function - formatSavedSearchFields defined in the custom search form
163 if (method_exists($customSearchClass, 'formatSavedSearchFields')) {
164 $customSearchClass::formatSavedSearchFields($result);
172 * Get search parameters.
178 * @throws \CRM_Core_Exception
179 * @throws \CiviCRM_API3_Exception
181 public static function getSearchParams($id) {
182 $savedSearch = \Civi\Api4\SavedSearch
::get(FALSE)
183 ->addWhere('id', '=', $id)
186 if ($savedSearch['api_entity']) {
189 $fv = self
::getFormValues($id);
190 //check if the saved search has mapping id
191 if ($savedSearch['mapping_id']) {
192 return CRM_Core_BAO_Mapping
::formattedFields($fv);
194 elseif (!empty($fv['customSearchID'])) {
198 return CRM_Contact_BAO_Query
::convertFormValues($fv);
203 * Get the where clause for a saved search.
207 * @param array $tables
208 * (reference ) add the tables that are needed for the select clause.
209 * @param array $whereTables
210 * (reference ) add the tables that are needed for the where clause.
213 * the where clause for this saved search
215 public static function whereClause($id, &$tables, &$whereTables) {
216 $params = self
::getSearchParams($id);
218 if (!empty($params['customSearchID'])) {
219 // this has not yet been implemented
222 return CRM_Contact_BAO_Query
::getWhereClause($params, NULL, $tables, $whereTables);
229 * Contact IDS Sql (whatever that means!).
235 public static function contactIDsSQL($id) {
236 $params = self
::getSearchParams($id);
237 if ($params && !empty($params['customSearchID'])) {
238 return CRM_Contact_BAO_SearchCustom
::contactIDSQL(NULL, $id);
241 $tables = $whereTables = ['civicrm_contact' => 1];
242 $where = CRM_Contact_BAO_SavedSearch
::whereClause($id, $tables, $whereTables);
246 $from = CRM_Contact_BAO_Query
::fromClause($whereTables);
255 * Deprecated function, gets a value from Group entity
259 * @param string $value
261 * @return string|null
263 public static function getName($id, $value = 'name') {
264 return parent
::getFieldValue('CRM_Contact_DAO_Group', $id, $value, 'saved_search_id');
268 * Create or update SavedSearch record.
270 * @param array $params
272 * @return \CRM_Contact_DAO_SavedSearch
274 public static function create(&$params) {
275 $loggedInContactID = CRM_Core_Session
::getLoggedInContactID();
276 if ($loggedInContactID) {
277 if (empty($params['id'])) {
278 $params['created_id'] = $loggedInContactID;
280 $params['modified_id'] = $loggedInContactID;
283 unset($params['modified_date']);
285 // Flush angular caches to refresh search displays
286 if (isset($params['api_params'])) {
287 Civi
::container()->get('angular')->clear();
289 return self
::writeRecord($params);
295 * @param string $fieldName
296 * @param array $fieldDef
297 * @param int $counter
299 protected function assignTestValue($fieldName, &$fieldDef, $counter) {
300 if ($fieldName == 'form_values') {
301 // A dummy value for form_values.
302 $this->{$fieldName} = serialize(
303 ['sort_name' => "SortName{$counter}"]);
306 parent
::assignTestValues($fieldName, $fieldDef, $counter);
311 * Store search variables in $queryParams which were skipped while processing query params,
312 * precisely at CRM_Contact_BAO_Query::fixWhereValues(...). But these variable are required in
313 * building smart group criteria otherwise it will cause issues like CRM-18585,CRM-19571
315 * @param array $queryParams
316 * @param array $formValues
318 public static function saveSkippedElement(&$queryParams, $formValues) {
319 // these are elements which are skipped in a smart group criteria
323 'display_relationship_type',
326 foreach ($specialElements as $element) {
327 if (!empty($formValues[$element])) {
328 $queryParams[] = [$element, '=', $formValues[$element], 0, 0];
334 * Decode relative custom fields (converted by CRM_Contact_BAO_Query->convertCustomRelativeFields(...))
335 * into desired formValues
337 * @param array $formValues
338 * @param string $fieldName
340 * @param array|string|int $value
342 * @throws \CiviCRM_API3_Exception
344 public static function decodeRelativeFields(&$formValues, $fieldName, $op, $value) {
345 // check if its a custom date field, if yes then 'searchDate' format the value
346 if (CRM_Contact_BAO_Query
::isCustomDateField($fieldName)) {
352 [$formValues[$fieldName . '_from'], $formValues[$fieldName . '_to']] = $value;
356 $formValues[$fieldName . '_from'] = $value;
360 $formValues[$fieldName . '_to'] = $value;
366 * Generate a url to the appropriate search form for a given savedSearch
372 public static function getEditSearchUrl($id) {
373 $savedSearch = self
::retrieve(['id' => $id]);
375 if (!empty($savedSearch->api_entity
)) {
376 return CRM_Utils_System
::url('civicrm/admin/search', NULL, FALSE, "/edit/$id");
378 // Classic search builder
379 if (!empty($savedSearch->mapping_id
)) {
380 $path = 'civicrm/contact/search/builder';
382 // Classic custom search
383 elseif (!empty($savedSearch->search_custom_id
)) {
384 $path = 'civicrm/contact/search/custom';
386 // Classic advanced search
388 $path = 'civicrm/contact/search/advanced';
390 return CRM_Utils_System
::url($path, ['reset' => 1, 'ssID' => $id]);
394 * Retrieve pseudoconstant options for $this->api_entity field
397 public static function getApiEntityOptions() {
398 return Civi\Api4\Entity
::get(FALSE)
399 ->addSelect('name', 'title_plural')
400 ->addOrderBy('title_plural')
403 ->column('title_plural');