Merge branch '5.2' into 5.3
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / DatabaseObjectEditor.class.php
1 <?php
2 namespace wcf\data;
3 use wcf\system\database\exception\DatabaseQueryExecutionException;
4 use wcf\system\database\util\PreparedStatementConditionBuilder;
5 use wcf\system\WCF;
6
7 /**
8 * Basic implementation for object editors following the decorator pattern.
9 *
10 * @author Marcel Werk
11 * @copyright 2001-2019 WoltLab GmbH
12 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
13 * @package WoltLabSuite\Core\Data
14 */
15 abstract class DatabaseObjectEditor extends DatabaseObjectDecorator implements IEditableObject {
16 /**
17 * @inheritDoc
18 */
19 public static function create(array $parameters = []) {
20 $keys = $values = '';
21 $statementParameters = [];
22 foreach ($parameters as $key => $value) {
23 if (!empty($keys)) {
24 $keys .= ',';
25 $values .= ',';
26 }
27
28 $keys .= $key;
29 $values .= '?';
30 $statementParameters[] = $value;
31 }
32
33 // save object
34 $sql = "INSERT INTO ".static::getDatabaseTableName()."
35 (".$keys.")
36 VALUES (".$values.")";
37 $statement = WCF::getDB()->prepareStatement($sql);
38 $statement->execute($statementParameters);
39
40 // return new object
41 if (static::getDatabaseTableIndexIsIdentity()) {
42 $id = WCF::getDB()->getInsertID(static::getDatabaseTableName(), static::getDatabaseTableIndexName());
43 }
44 else {
45 $id = $parameters[static::getDatabaseTableIndexName()];
46 }
47 return new static::$baseClass($id);
48 }
49
50 /**
51 * @inheritDoc
52 */
53 public function update(array $parameters = []) {
54 if (empty($parameters)) return;
55
56 $updateSQL = '';
57 $statementParameters = [];
58 foreach ($parameters as $key => $value) {
59 if (!empty($updateSQL)) $updateSQL .= ', ';
60 $updateSQL .= $key . ' = ?';
61 $statementParameters[] = $value;
62 }
63 $statementParameters[] = $this->getObjectID();
64
65 $sql = "UPDATE ".static::getDatabaseTableName()."
66 SET ".$updateSQL."
67 WHERE ".static::getDatabaseTableIndexName()." = ?";
68 $statement = WCF::getDB()->prepareStatement($sql);
69 $statement->execute($statementParameters);
70 }
71
72 /**
73 * @inheritDoc
74 */
75 public function updateCounters(array $counters = []) {
76 if (empty($counters)) return;
77
78 $updateSQL = '';
79 $statementParameters = [];
80 foreach ($counters as $key => $value) {
81 if (!empty($updateSQL)) $updateSQL .= ', ';
82 $updateSQL .= $key . ' = ' . $key . ' + ?';
83 $statementParameters[] = $value;
84 }
85 $statementParameters[] = $this->getObjectID();
86
87 $sql = "UPDATE ".static::getDatabaseTableName()."
88 SET ".$updateSQL."
89 WHERE ".static::getDatabaseTableIndexName()." = ?";
90 $statement = WCF::getDB()->prepareStatement($sql);
91 $statement->execute($statementParameters);
92 }
93
94 /**
95 * @inheritDoc
96 */
97 public function delete() {
98 static::deleteAll([$this->getObjectID()]);
99 }
100
101 /**
102 * @inheritDoc
103 */
104 public static function deleteAll(array $objectIDs = []) {
105 $affectedCount = 0;
106
107 $itemsPerLoop = 1000;
108 $loopCount = ceil(count($objectIDs) / $itemsPerLoop);
109
110 WCF::getDB()->beginTransaction();
111 for ($i = 0; $i < $loopCount; $i++) {
112 $batchObjectIDs = array_slice($objectIDs, $i * $itemsPerLoop, $itemsPerLoop);
113
114 $conditionBuilder = new PreparedStatementConditionBuilder();
115 $conditionBuilder->add(static::getDatabaseTableIndexName() . ' IN (?)', [$batchObjectIDs]);
116
117 $sql = "DELETE FROM " . static::getDatabaseTableName() . "
118 " . $conditionBuilder;
119 $statement = WCF::getDB()->prepareStatement($sql);
120 $statement->execute($conditionBuilder->getParameters());
121 $affectedCount += $statement->getAffectedRows();
122 }
123 WCF::getDB()->commitTransaction();
124
125 return $affectedCount;
126 }
127
128 /**
129 * Creates a new object, returns null if the row already exists.
130 *
131 * @param array $parameters
132 * @return IStorableObject|null
133 * @since 5.3
134 */
135 public static function createOrIgnore(array $parameters = []) {
136 try {
137 return static::create($parameters);
138 }
139 catch (DatabaseQueryExecutionException $e) {
140 // Error code 23000 = duplicate key
141 if ($e->getCode() == '23000' && $e->getDriverCode() == '1062') {
142 return null;
143 }
144
145 throw $e;
146 }
147 }
148 }