Merge pull request #10826 from jitendrapurohit/CRM-21038
[civicrm-core.git] / Civi / Core / SqlTrigger / TimestampTriggers.php
CommitLineData
6050e9ad
TO
1<?php
2
3/*
4 +--------------------------------------------------------------------+
5 | CiviCRM version 4.7 |
6 +--------------------------------------------------------------------+
7 | Copyright CiviCRM LLC (c) 2004-2017 |
8 +--------------------------------------------------------------------+
9 | This file is a part of CiviCRM. |
10 | |
11 | CiviCRM is free software; you can copy, modify, and distribute it |
12 | under the terms of the GNU Affero General Public License |
13 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | |
15 | CiviCRM is distributed in the hope that it will be useful, but |
16 | WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
18 | See the GNU Affero General Public License for more details. |
19 | |
20 | You should have received a copy of the GNU Affero General Public |
21 | License and the CiviCRM Licensing Exception along |
22 | with this program; if not, contact CiviCRM LLC |
23 | at info[AT]civicrm[DOT]org. If you have questions about the |
24 | GNU Affero General Public License or the licensing of CiviCRM, |
25 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
26 +--------------------------------------------------------------------+
27 */
28
29namespace Civi\Core\SqlTrigger;
30
31use Civi\Core\Event\GenericHookEvent;
32
33/**
34 * Build a set of SQL triggers for tracking timestamps on an entity.
35 *
36 * This class is a generalization of CRM-10554 with the aim of enabling CRM-20958.
37 *
38 * @package CRM
39 * @copyright CiviCRM LLC (c) 2004-2017
40 */
41class TimestampTriggers {
42
43 /**
44 * @var string
45 * SQL table name.
46 * Ex: 'civicrm_contact', 'civicrm_activity'.
47 */
48 private $tableName;
49
50 /**
51 * @var string
52 * An entity name (from civicrm_custom_group.extends).
53 * Ex: 'Contact', 'Activity'.
54 */
55 private $customDataEntity;
56
57 /**
58 * @var string
59 * SQL column name.
60 * Ex: 'created_date'.
61 */
62 private $createdDate;
63
64 /**
65 * @var string
66 * SQL column name.
67 * Ex: 'modified_date'.
68 */
69 private $modifiedDate;
70
71 /**
72 * @var array
73 * Ex: $relations[0] == array('table' => 'civicrm_bar', 'column' => 'foo_id');
74 */
75 private $relations;
76
77 /**
78 * @param string $tableName
79 * SQL table name.
80 * Ex: 'civicrm_contact', 'civicrm_activity'.
81 * @param string $customDataEntity
82 * An entity name (from civicrm_custom_group.extends).
83 * Ex: 'Contact', 'Activity'.
84 * @return TimestampTriggers
85 */
86 public static function create($tableName, $customDataEntity) {
87 return new static($tableName, $customDataEntity);
88 }
89
90 /**
91 * TimestampTriggers constructor.
92 *
93 * @param string $tableName
94 * SQL table name.
95 * Ex: 'civicrm_contact', 'civicrm_activity'.
96 * @param string $customDataEntity
97 * An entity name (from civicrm_custom_group.extends).
98 * Ex: 'Contact', 'Activity'.
99 * @param string $createdDate
100 * SQL column name.
101 * Ex: 'created_date'.
102 * @param string $modifiedDate
103 * SQL column name.
104 * Ex: 'modified_date'.
105 * @param array $relations
106 * Ex: $relations[0] == array('table' => 'civicrm_bar', 'column' => 'foo_id');
107 */
108 public function __construct(
109 $tableName,
110 $customDataEntity,
111 $createdDate = 'created_date',
112 $modifiedDate = 'modified_date',
113 $relations = array()
114 ) {
115 $this->tableName = $tableName;
116 $this->customDataEntity = $customDataEntity;
117 $this->createdDate = $createdDate;
118 $this->modifiedDate = $modifiedDate;
119 $this->relations = $relations;
120 }
121
122 /**
123 * Add our list of triggers to the global list.
124 *
125 * @param \Civi\Core\Event\GenericHookEvent $e
126 * @see \CRM_Utils_Hook::triggerInfo
127 */
128 public function onTriggerInfo($e) {
129 $this->alterTriggerInfo($e->info, $e->tableName);
130 }
131
132 /**
133 * Add our list of triggers to the global list.
134 *
135 * @see \CRM_Utils_Hook::triggerInfo
136 * @see \CRM_Core_DAO::triggerRebuild
137 *
138 * @param array $info
139 * See hook_civicrm_triggerInfo.
140 * @param string|NULL $tableFilter
141 * See hook_civicrm_triggerInfo.
142 */
143 public function alterTriggerInfo(&$info, $tableFilter = NULL) {
4ccc3cdd
TO
144 // If we haven't upgraded yet, then the created_date/modified_date may not exist.
145 // In the past, this was a version-based check, but checkFieldExists()
146 // seems more robust.
6050e9ad
TO
147 if (\CRM_Core_Config::isUpgradeMode()) {
148 if (!\CRM_Core_DAO::checkFieldExists($this->getTableName(),
149 $this->getCreatedDate())
150 ) {
151 return;
152 }
6050e9ad
TO
153 }
154
155 if ($tableFilter == NULL || $tableFilter == $this->getTableName()) {
156 $info[] = array(
157 'table' => array($this->getTableName()),
158 'when' => 'BEFORE',
159 'event' => array('INSERT'),
160 'sql' => "\nSET NEW.{$this->getCreatedDate()} = CURRENT_TIMESTAMP;\n",
161 );
162 }
163
164 // Update timestamp when modifying closely related tables
165 $relIdx = \CRM_Utils_Array::index(
166 array('column', 'table'),
167 $this->getAllRelations()
168 );
169 foreach ($relIdx as $column => $someRelations) {
170 $this->generateTimestampTriggers($info, $tableFilter,
171 array_keys($someRelations), $column);
172 }
173 }
174
175 /**
176 * Generate triggers to update the timestamp.
177 *
178 * The corresponding civicrm_FOO row is updated on insert/update/delete
179 * to a table that extends civicrm_FOO.
180 * Don't regenerate triggers for all such tables if only asked for one table.
181 *
182 * @param array $info
183 * Reference to the array where generated trigger information is being stored
184 * @param string|null $tableFilter
185 * Name of the table for which triggers are being generated, or NULL if all tables
186 * @param array $relatedTableNames
187 * Array of all core or all custom table names extending civicrm_FOO
188 * @param string $contactRefColumn
189 * 'contact_id' if processing core tables, 'entity_id' if processing custom tables
190 *
191 * @link https://issues.civicrm.org/jira/browse/CRM-15602
192 * @see triggerInfo
193 */
194 public function generateTimestampTriggers(
195 &$info,
196 $tableFilter,
197 $relatedTableNames,
198 $contactRefColumn
199 ) {
200 // Safety
201 $contactRefColumn = \CRM_Core_DAO::escapeString($contactRefColumn);
202
203 // If specific related table requested, just process that one.
204 // (Reply: This feels fishy.)
205 if (in_array($tableFilter, $relatedTableNames)) {
206 $relatedTableNames = array($tableFilter);
207 }
208
209 // If no specific table requested (include all related tables),
210 // or a specific related table requested (as matched above)
211 if (empty($tableFilter) || isset($relatedTableNames[$tableFilter])) {
212 $info[] = array(
213 'table' => $relatedTableNames,
214 'when' => 'AFTER',
215 'event' => array('INSERT', 'UPDATE'),
216 'sql' => "\nUPDATE {$this->getTableName()} SET {$this->getModifiedDate()} = CURRENT_TIMESTAMP WHERE id = NEW.$contactRefColumn;\n",
217 );
218 $info[] = array(
219 'table' => $relatedTableNames,
220 'when' => 'AFTER',
221 'event' => array('DELETE'),
222 'sql' => "\nUPDATE {$this->getTableName()} SET {$this->getModifiedDate()} = CURRENT_TIMESTAMP WHERE id = OLD.$contactRefColumn;\n",
223 );
224 }
225 }
226
227 /**
228 * @return string
229 */
230 public function getTableName() {
231 return $this->tableName;
232 }
233
234 /**
235 * @param string $tableName
236 * @return TimestampTriggers
237 */
238 public function setTableName($tableName) {
239 $this->tableName = $tableName;
240 return $this;
241 }
242
243 /**
244 * @return string
245 */
246 public function getCustomDataEntity() {
247 return $this->customDataEntity;
248 }
249
250 /**
251 * @param string $customDataEntity
252 * @return TimestampTriggers
253 */
254 public function setCustomDataEntity($customDataEntity) {
255 $this->customDataEntity = $customDataEntity;
256 return $this;
257 }
258
259 /**
260 * @return string
261 */
262 public function getCreatedDate() {
263 return $this->createdDate;
264 }
265
266 /**
267 * @param string $createdDate
268 * @return TimestampTriggers
269 */
270 public function setCreatedDate($createdDate) {
271 $this->createdDate = $createdDate;
272 return $this;
273 }
274
275 /**
276 * @return string
277 */
278 public function getModifiedDate() {
279 return $this->modifiedDate;
280 }
281
282 /**
283 * @param string $modifiedDate
284 * @return TimestampTriggers
285 */
286 public function setModifiedDate($modifiedDate) {
287 $this->modifiedDate = $modifiedDate;
288 return $this;
289 }
290
291 /**
292 * @return array
293 * Each item is an array('table' => string, 'column' => string)
294 */
295 public function getRelations() {
296 return $this->relations;
297 }
298
299 /**
300 * @param array $relations
301 * @return TimestampTriggers
302 */
303 public function setRelations($relations) {
304 $this->relations = $relations;
305 return $this;
306 }
307
308 /**
309 * Get a list of all tracked relations.
310 *
311 * This is basically the curated list (`$this->relations`) plus any custom data.
312 *
313 * @return array
314 * Each item is an array('table' => string, 'column' => string)
315 */
316 public function getAllRelations() {
317 $relations = $this->getRelations();
318
319 if ($this->getCustomDataEntity()) {
320 $customGroupDAO = \CRM_Core_BAO_CustomGroup::getAllCustomGroupsByBaseEntity($this->getCustomDataEntity());
321 $customGroupDAO->is_multiple = 0;
322 $customGroupDAO->find();
323 while ($customGroupDAO->fetch()) {
324 $relations[] = array(
325 'table' => $customGroupDAO->table_name,
326 'column' => 'entity_id',
327 );
328 }
329 }
330
331 return $relations;
332 }
333
334}