Commit | Line | Data |
---|---|---|
8bcc0d86 CW |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
fee14197 | 4 | | CiviCRM version 5 | |
8bcc0d86 | 5 | +--------------------------------------------------------------------+ |
6b83d5bd | 6 | | Copyright CiviCRM LLC (c) 2004-2019 | |
8bcc0d86 CW |
7 | +--------------------------------------------------------------------+ |
8 | | This file is a part of CiviCRM. | | |
9 | | | | |
10 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
11 | | under the terms of the GNU Affero General Public License | | |
12 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
13 | | | | |
14 | | CiviCRM is distributed in the hope that it will be useful, but | | |
15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
17 | | See the GNU Affero General Public License for more details. | | |
18 | | | | |
19 | | You should have received a copy of the GNU Affero General Public | | |
20 | | License and the CiviCRM Licensing Exception along | | |
21 | | with this program; if not, contact CiviCRM LLC | | |
22 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
23 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
25 | +--------------------------------------------------------------------+ | |
26 | */ | |
27 | namespace Civi\API; | |
28 | ||
29 | /** | |
30 | */ | |
31 | class Api3SelectQuery extends SelectQuery { | |
32 | ||
33 | protected $apiVersion = 3; | |
34 | ||
35 | /** | |
36 | * @inheritDoc | |
37 | */ | |
38 | protected function buildWhereClause() { | |
c64f69d9 | 39 | $filters = []; |
8bcc0d86 CW |
40 | foreach ($this->where as $key => $value) { |
41 | $table_name = NULL; | |
42 | $column_name = NULL; | |
43 | ||
44 | if (substr($key, 0, 7) == 'filter.') { | |
45 | // Legacy support for old filter syntax per the test contract. | |
46 | // (Convert the style to the later one & then deal with them). | |
47 | $filterArray = explode('.', $key); | |
c64f69d9 | 48 | $value = [$filterArray[1] => $value]; |
8bcc0d86 CW |
49 | $key = 'filters'; |
50 | } | |
51 | ||
52 | // Legacy support for 'filter's construct. | |
53 | if ($key == 'filters') { | |
54 | foreach ($value as $filterKey => $filterValue) { | |
55 | if (substr($filterKey, -4, 4) == 'high') { | |
56 | $key = substr($filterKey, 0, -5); | |
c64f69d9 | 57 | $value = ['<=' => $filterValue]; |
8bcc0d86 CW |
58 | } |
59 | ||
60 | if (substr($filterKey, -3, 3) == 'low') { | |
61 | $key = substr($filterKey, 0, -4); | |
c64f69d9 | 62 | $value = ['>=' => $filterValue]; |
8bcc0d86 CW |
63 | } |
64 | ||
65 | if ($filterKey == 'is_current' || $filterKey == 'isCurrent') { | |
66 | // Is current is almost worth creating as a 'sql filter' in the DAO function since several entities have the concept. | |
67 | $todayStart = date('Ymd000000', strtotime('now')); | |
68 | $todayEnd = date('Ymd235959', strtotime('now')); | |
69 | $a = self::MAIN_TABLE_ALIAS; | |
70 | $this->query->where("($a.start_date <= '$todayStart' OR $a.start_date IS NULL) | |
71 | AND ($a.end_date >= '$todayEnd' OR $a.end_date IS NULL) | |
72 | AND a.is_active = 1"); | |
73 | } | |
74 | } | |
75 | } | |
76 | // Ignore the "options" param if it is referring to api options and not a field in this entity | |
77 | if ( | |
78 | $key === 'options' && is_array($value) | |
79 | && !in_array(\CRM_Utils_Array::first(array_keys($value)), \CRM_Core_DAO::acceptedSQLOperators()) | |
80 | ) { | |
81 | continue; | |
82 | } | |
83 | $field = $this->getField($key); | |
84 | if ($field) { | |
85 | $key = $field['name']; | |
86 | } | |
87 | if (in_array($key, $this->entityFieldNames)) { | |
88 | $table_name = self::MAIN_TABLE_ALIAS; | |
89 | $column_name = $key; | |
90 | } | |
91 | elseif (($cf_id = \CRM_Core_BAO_CustomField::getKeyID($key)) != FALSE) { | |
23a57f3b JV |
92 | // If we check a custom field on 'IS NULL', it should also work when there is no |
93 | // record in the custom value table, see CRM-20740. | |
ada40b3e | 94 | $side = empty($value['IS NULL']) ? 'INNER' : 'LEFT OUTER'; |
23a57f3b | 95 | list($table_name, $column_name) = $this->addCustomField($this->apiFieldSpec['custom_' . $cf_id], $side); |
8bcc0d86 CW |
96 | } |
97 | elseif (strpos($key, '.')) { | |
98 | $fkInfo = $this->addFkField($key, 'INNER'); | |
99 | if ($fkInfo) { | |
100 | list($table_name, $column_name) = $fkInfo; | |
101 | $this->validateNestedInput($key, $value); | |
102 | } | |
103 | } | |
104 | // I don't know why I had to specifically exclude 0 as a key - wouldn't the others have caught it? | |
105 | // We normally silently ignore null values passed in - if people want IS_NULL they can use acceptedSqlOperator syntax. | |
106 | if ((!$table_name) || empty($key) || is_null($value)) { | |
107 | // No valid filter field. This might be a chained call or something. | |
108 | // Just ignore this for the $where_clause. | |
109 | continue; | |
110 | } | |
fe59b2a7 | 111 | $operator = is_array($value) ? \CRM_Utils_Array::first(array_keys($value)) : NULL; |
17e0598e | 112 | if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { |
c64f69d9 | 113 | $value = ['=' => $value]; |
8bcc0d86 | 114 | } |
fe59b2a7 CW |
115 | $filters[$key] = \CRM_Core_DAO::createSQLFilter("{$table_name}.{$column_name}", $value); |
116 | } | |
117 | // Support OR groups | |
118 | if (!empty($this->where['options']['or'])) { | |
119 | $orGroups = $this->where['options']['or']; | |
120 | if (is_string($orGroups)) { | |
121 | $orGroups = array_map('trim', explode(',', $orGroups)); | |
122 | } | |
123 | if (!is_array(\CRM_Utils_Array::first($orGroups))) { | |
c64f69d9 | 124 | $orGroups = [$orGroups]; |
fe59b2a7 CW |
125 | } |
126 | foreach ($orGroups as $orGroup) { | |
c64f69d9 | 127 | $orClause = []; |
fe59b2a7 CW |
128 | foreach ($orGroup as $key) { |
129 | if (!isset($filters[$key])) { | |
130 | throw new \CiviCRM_API3_Exception("'$key' specified in OR group but not added to params"); | |
131 | } | |
132 | $orClause[] = $filters[$key]; | |
133 | unset($filters[$key]); | |
8bcc0d86 | 134 | } |
fe59b2a7 | 135 | $this->query->where(implode(' OR ', $orClause)); |
8bcc0d86 CW |
136 | } |
137 | } | |
fe59b2a7 CW |
138 | // Add the remaining params using AND |
139 | foreach ($filters as $filter) { | |
140 | $this->query->where($filter); | |
141 | } | |
8bcc0d86 CW |
142 | } |
143 | ||
144 | /** | |
145 | * @inheritDoc | |
146 | */ | |
147 | protected function getFields() { | |
148 | require_once 'api/v3/Generic.php'; | |
149 | // Call this function directly instead of using the api wrapper to force unique field names off | |
c64f69d9 | 150 | $apiSpec = \civicrm_api3_generic_getfields([ |
8bcc0d86 CW |
151 | 'entity' => $this->entity, |
152 | 'version' => 3, | |
c64f69d9 CW |
153 | 'params' => ['action' => 'get'], |
154 | ], FALSE); | |
8bcc0d86 CW |
155 | return $apiSpec['values']; |
156 | } | |
157 | ||
158 | /** | |
159 | * Fetch a field from the getFields list | |
160 | * | |
161 | * Searches by name, uniqueName, and api.aliases | |
066c4638 TO |
162 | * |
163 | * @param string $fieldName | |
164 | * Field name. | |
165 | * @return NULL|mixed | |
8bcc0d86 CW |
166 | */ |
167 | protected function getField($fieldName) { | |
168 | if (!$fieldName) { | |
169 | return NULL; | |
170 | } | |
171 | if (isset($this->apiFieldSpec[$fieldName])) { | |
172 | return $this->apiFieldSpec[$fieldName]; | |
173 | } | |
174 | foreach ($this->apiFieldSpec as $field) { | |
175 | if ( | |
176 | $fieldName == \CRM_Utils_Array::value('uniqueName', $field) || | |
c64f69d9 | 177 | array_search($fieldName, \CRM_Utils_Array::value('api.aliases', $field, [])) !== FALSE |
8bcc0d86 CW |
178 | ) { |
179 | return $field; | |
180 | } | |
181 | } | |
182 | return NULL; | |
183 | } | |
184 | ||
185 | } |