4 +--------------------------------------------------------------------+
6 +--------------------------------------------------------------------+
7 | Copyright CiviCRM LLC (c) 2004-2020 |
8 +--------------------------------------------------------------------+
9 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
29 namespace Civi\Core\SqlTrigger
;
32 * Build a set of SQL triggers for tracking timestamps on an entity.
34 * This class is a generalization of CRM-10554 with the aim of enabling CRM-20958.
37 * @copyright CiviCRM LLC (c) 2004-2020
39 class TimestampTriggers
{
44 * Ex: 'civicrm_contact', 'civicrm_activity'.
50 * An entity name (from civicrm_custom_group.extends).
51 * Ex: 'Contact', 'Activity'.
53 private $customDataEntity;
65 * Ex: 'modified_date'.
67 private $modifiedDate;
71 * Ex: $relations[0] == array('table' => 'civicrm_bar', 'column' => 'foo_id');
76 * @param string $tableName
78 * Ex: 'civicrm_contact', 'civicrm_activity'.
79 * @param string $customDataEntity
80 * An entity name (from civicrm_custom_group.extends).
81 * Ex: 'Contact', 'Activity'.
82 * @return TimestampTriggers
84 public static function create($tableName, $customDataEntity) {
85 return new static($tableName, $customDataEntity);
89 * TimestampTriggers constructor.
91 * @param string $tableName
93 * Ex: 'civicrm_contact', 'civicrm_activity'.
94 * @param string $customDataEntity
95 * An entity name (from civicrm_custom_group.extends).
96 * Ex: 'Contact', 'Activity'.
97 * @param string $createdDate
100 * @param string $modifiedDate
102 * Ex: 'modified_date'.
103 * @param array $relations
104 * Ex: $relations[0] == array('table' => 'civicrm_bar', 'column' => 'foo_id');
106 public function __construct(
109 $createdDate = 'created_date',
110 $modifiedDate = 'modified_date',
113 $this->tableName
= $tableName;
114 $this->customDataEntity
= $customDataEntity;
115 $this->createdDate
= $createdDate;
116 $this->modifiedDate
= $modifiedDate;
117 $this->relations
= $relations;
121 * Add our list of triggers to the global list.
123 * @param \Civi\Core\Event\GenericHookEvent $e
124 * @see \CRM_Utils_Hook::triggerInfo
126 public function onTriggerInfo($e) {
127 $this->alterTriggerInfo($e->info
, $e->tableName
);
131 * Add our list of triggers to the global list.
133 * @see \CRM_Utils_Hook::triggerInfo
134 * @see \CRM_Core_DAO::triggerRebuild
137 * See hook_civicrm_triggerInfo.
138 * @param string|NULL $tableFilter
139 * See hook_civicrm_triggerInfo.
141 public function alterTriggerInfo(&$info, $tableFilter = NULL) {
142 // If we haven't upgraded yet, then the created_date/modified_date may not exist.
143 // In the past, this was a version-based check, but checkFieldExists()
144 // seems more robust.
145 if (\CRM_Core_Config
::isUpgradeMode()) {
146 if (!\CRM_Core_BAO_SchemaHandler
::checkIfFieldExists($this->getTableName(),
147 $this->getCreatedDate())
153 if ($tableFilter == NULL ||
$tableFilter == $this->getTableName()) {
155 'table' => [$this->getTableName()],
157 'event' => ['INSERT'],
158 'sql' => "\nSET NEW.{$this->getCreatedDate()} = CURRENT_TIMESTAMP;\n",
162 // Update timestamp when modifying closely related tables
163 $relIdx = \CRM_Utils_Array
::index(
165 $this->getAllRelations()
167 foreach ($relIdx as $column => $someRelations) {
168 $this->generateTimestampTriggers($info, $tableFilter,
169 array_keys($someRelations), $column);
174 * Generate triggers to update the timestamp.
176 * The corresponding civicrm_FOO row is updated on insert/update/delete
177 * to a table that extends civicrm_FOO.
178 * Don't regenerate triggers for all such tables if only asked for one table.
181 * Reference to the array where generated trigger information is being stored
182 * @param string|null $tableFilter
183 * Name of the table for which triggers are being generated, or NULL if all tables
184 * @param array $relatedTableNames
185 * Array of all core or all custom table names extending civicrm_FOO
186 * @param string $contactRefColumn
187 * 'contact_id' if processing core tables, 'entity_id' if processing custom tables
189 * @link https://issues.civicrm.org/jira/browse/CRM-15602
192 public function generateTimestampTriggers(
199 $contactRefColumn = \CRM_Core_DAO
::escapeString($contactRefColumn);
201 // If specific related table requested, just process that one.
202 // (Reply: This feels fishy.)
203 if (in_array($tableFilter, $relatedTableNames)) {
204 $relatedTableNames = [$tableFilter];
207 // If no specific table requested (include all related tables),
208 // or a specific related table requested (as matched above)
209 if (empty($tableFilter) ||
isset($relatedTableNames[$tableFilter])) {
211 'table' => $relatedTableNames,
213 'event' => ['INSERT', 'UPDATE'],
214 'sql' => "\nUPDATE {$this->getTableName()} SET {$this->getModifiedDate()} = CURRENT_TIMESTAMP WHERE id = NEW.$contactRefColumn;\n",
217 'table' => $relatedTableNames,
219 'event' => ['DELETE'],
220 'sql' => "\nUPDATE {$this->getTableName()} SET {$this->getModifiedDate()} = CURRENT_TIMESTAMP WHERE id = OLD.$contactRefColumn;\n",
228 public function getTableName() {
229 return $this->tableName
;
233 * @param string $tableName
234 * @return TimestampTriggers
236 public function setTableName($tableName) {
237 $this->tableName
= $tableName;
244 public function getCustomDataEntity() {
245 return $this->customDataEntity
;
249 * @param string $customDataEntity
250 * @return TimestampTriggers
252 public function setCustomDataEntity($customDataEntity) {
253 $this->customDataEntity
= $customDataEntity;
260 public function getCreatedDate() {
261 return $this->createdDate
;
265 * @param string $createdDate
266 * @return TimestampTriggers
268 public function setCreatedDate($createdDate) {
269 $this->createdDate
= $createdDate;
276 public function getModifiedDate() {
277 return $this->modifiedDate
;
281 * @param string $modifiedDate
282 * @return TimestampTriggers
284 public function setModifiedDate($modifiedDate) {
285 $this->modifiedDate
= $modifiedDate;
291 * Each item is an array('table' => string, 'column' => string)
293 public function getRelations() {
294 return $this->relations
;
298 * @param array $relations
299 * @return TimestampTriggers
301 public function setRelations($relations) {
302 $this->relations
= $relations;
307 * Get a list of all tracked relations.
309 * This is basically the curated list (`$this->relations`) plus any custom data.
312 * Each item is an array('table' => string, 'column' => string)
314 public function getAllRelations() {
315 $relations = $this->getRelations();
317 if ($this->getCustomDataEntity()) {
318 $customGroupDAO = \CRM_Core_BAO_CustomGroup
::getAllCustomGroupsByBaseEntity($this->getCustomDataEntity());
319 $customGroupDAO->is_multiple
= 0;
320 $customGroupDAO->find();
321 while ($customGroupDAO->fetch()) {
323 'table' => $customGroupDAO->table_name
,
324 'column' => 'entity_id',