Test Failed
Push — trunk ( db071f...8d464a )
by SuperNova.WS
06:33
created

ActiveRecord::translateNames()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 6
nop 2
dl 0
loc 13
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by Gorlum 12.06.2017 14:41
4
 */
5
6
namespace DBAL;
7
8
use Common\AccessLogged;
9
use Common\GlobalContainer;
10
11
12
/**
13
 * Class ActiveRecord
14
 *
15
 * @property int|string $id - Record ID name would be normalized to 'id'
16
 *
17
 * @package DBAL
18
 */
19
abstract class ActiveRecord extends AccessLogged {
20
  const IGNORE_PREFIX = 'Table';
21
22
  const FIELDS_TO_PROPERTIES = true;
23
  const PROPERTIES_TO_FIELDS = false;
24
25
  /**
26
   * Autoincrement index field name in DB
27
   * Would be normalized to 'id' ($id class property)
28
   *
29
   * @var string $_primaryIndexField
30
   */
31
  protected static $_primaryIndexField = 'id';
32
  protected static $_tableName = '';
33
34
  /**
35
   * Field name translations to property names
36
   *
37
   * @var string[] $_fieldsToProperties
38
   */
39
  protected static $_fieldsToProperties = [];
40
41
  // AR's service fields
42
  protected $_isNew = true;
43
44
  /**
45
   * ActiveRecord constructor.
46
   *
47
   * @param GlobalContainer|null $services
48
   */
49
  public function __construct(GlobalContainer $services = null) {
50
    parent::__construct($services);
51
  }
52
53
  /**
54
   * Finds records by params in DB
55
   *
56
   * @param array|mixed $where - ID of found record OR [$field_name => $field_value]. Pass [] to find all records in DB
57
   *
58
   * @return bool|\mysqli_result
59
   */
60
  public static function find($where) {
61
    if (!is_array($where)) {
62
      $where = [static::$_primaryIndexField => $where];
63
    }
64
65
    $dbq = static::prepareDbQuery();
66
    if (!empty($where)) {
67
      $dbq->setWhereArray($where);
68
    }
69
70
    return $dbq->doSelect();
71
  }
72
73
  /**
74
   * Gets first ActiveRecord by $where
75
   *
76
   * @param array|mixed $where - ID of found record OR [$field_name => $field_value]
77
   *
78
   * @return static|bool
79
   */
80
  public static function findOne($where) {
81
    $record = static::fromArray(static::findOneArray($where));
82
    if (!empty($record)) {
83
      $record->_isNew = false;
84
    }
85
86
    return $record;
87
  }
88
89
  /**
90
   * Gets all ActiveRecords by $where
91
   *
92
   * @param array|mixed $where - ID of found record OR [$field_name => $field_value]
93
   *
94
   * @return array|static[] - [(int) => static]
95
   */
96
  public static function findAll($where) {
97
    return static::fromArrayList(static::findAllArray($where));
98
  }
99
100
  /**
101
   * Gets all ActiveRecords by $where - array indexed by record IDs
102
   *
103
   * @param array|mixed $where - ID of found record OR [$field_name => $field_value]
104
   *
105
   * @return array|static[] - [$record_db_id => static]
106
   */
107
  public static function findAllIndexedObjects($where) {
108
    return static::fromArrayList(static::findAllIndexedArray($where));
109
  }
110
111
  /**
112
   * Get table name
113
   *
114
   * @return string
115
   */
116
  public static function tableName() {
117
    if (empty(static::$_tableName)) {
118
      static::fillTableName();
119
    }
120
121
    return static::$_tableName;
122
  }
123
124
  /**
125
   * Get used DB
126
   *
127
   * @return \db_mysql
128
   */
129
  public static function db() {
130
    return \classSupernova::$db;
131
  }
132
133
134
  /**
135
   * Gets first record by $where
136
   *
137
   * @param array|mixed $where - ID of found record OR [$field_name => $field_value]
138
   *
139
   * @return string[] - [$field_name => $field_value]
140
   */
141
  public static function findOneArray($where) {
142
    $result = empty($dbq = static::find($where)) ? [] : static::db()->db_fetch($dbq);
143
144
    return empty($result) ? [] : $result;
145
  }
146
147
  /**
148
   * Gets all records by $where
149
   *
150
   * @param array|mixed $where - ID of found record OR [$field_name => $field_value]
151
   *
152
   * @return string[][] - [(int) => [$field_name => $field_value]]
153
   */
154
  public static function findAllArray($where) {
155
    return empty($dbq = static::find($where)) ? [] : $dbq->fetch_all(MYSQL_ASSOC);
156
  }
157
158
  /**
159
   * Gets all records by $where - array indexes is a record IDs
160
   *
161
   * @param array|mixed $where - ID of found record OR [$field_name => $field_value]
162
   *
163
   * @return string[][] - [$record_db_id => [$field_name => $field_value]]
164
   */
165
  public static function findAllIndexedArray($where) {
166
    $result = [];
167
    if (!empty($dbq = static::find($where))) {
168
      while ($row = static::db()->db_fetch($dbq)) {
169
        $result[$row[static::$_primaryIndexField]] = $row;
170
      }
171
    }
172
173
    return $result;
174
  }
175
176
  /**
177
   * Normalize array
178
   *
179
   * Basically - uppercase all field names to make it use in PTL
180
   * Can be override by descendants to make more convinient, clear and robust indexes
181
   *
182
   * @return array
183
   */
184
  public function ptlArray() {
185
    $result = [];
186
    foreach ($this->values as $key => $value) {
187
      $result[strtoupper(\HelperString::camelToUnderscore($key))] = $value;
188
    }
189
190
    return $result;
191
  }
192
193
  /**
194
   * Converts property-indexed value array to field-indexed via translation table
195
   *
196
   * @param array $propertyNamed
197
   * @param bool  $fieldToProperties - translation direction: true - field to props. false - prop to fields
198
   *
199
   * @return array
200
   */
201
  protected static function translateNames(array $propertyNamed, $fieldToProperties = self::FIELDS_TO_PROPERTIES) {
202
    $translations = $fieldToProperties == self::FIELDS_TO_PROPERTIES ? static::$_fieldsToProperties : array_flip(static::$_fieldsToProperties);
203
204
    $result = [];
205
    foreach ($propertyNamed as $name => $value) {
206
      if (!empty($translations[$name])) {
207
        $name = $translations[$name];
208
      }
209
      $result[$name] = $value;
210
    }
211
212
    return $result;
213
  }
214
215
  /**
216
   * @return array|bool|\mysqli_result|null
217
   */
218
  public function update() {
219
    if (empty($this->_changes) && empty($this->_deltas)) {
220
      return true;
221
    }
222
223
    if ($result = static::prepareDbQuery()
224
      ->setValues(empty($this->_changes) ? [] : static::translateNames($this->_changes, self::PROPERTIES_TO_FIELDS))
225
      ->setAdjust(empty($this->_deltas) ? [] : static::translateNames($this->_deltas, self::PROPERTIES_TO_FIELDS))
226
      ->setWhereArray([static::$_primaryIndexField => $this->id])
227
      ->doUpdate()
228
    ) {
229
      $this->flush();
230
    };
231
232
    return $result;
233
  }
234
235
  /**
236
   * @return array|bool|\mysqli_result|null
237
   */
238
  public function insert() {
239
    die('not implemented');
240
  }
241
242
  public function save() {
243
    die('not implemented');
244
  }
245
246
  /**
247
   * Calculate table name by class name and fills internal property
248
   *
249
   * Namespaces does not count - only class name taken into account
250
   * Class name converted from CamelCase to underscore_name
251
   * Prefix "Table" is ignored - can be override
252
   *
253
   * Examples:
254
   * Class \Namespace\ClassName will map to table `class_name`
255
   * Class \NameSpace\TableLongName will map to table `long_name`
256
   *
257
   * Can be override to provide different name
258
   *
259
   */
260
  protected static function fillTableName() {
261
    $temp = explode('\\', get_called_class());
262
    $className = end($temp);
263
    if (strpos($className, static::IGNORE_PREFIX) === 0) {
264
      $className = substr($className, strlen(static::IGNORE_PREFIX));
265
    }
266
    static::$_tableName = \HelperString::camelToUnderscore($className);
267
  }
268
269
  /**
270
   * @return DbQuery
271
   */
272
  protected static function prepareDbQuery() {
273
    return DbQuery::build(static::db())->setTable(static::tableName());
274
  }
275
276
  protected function fillProperties($array) {
277
    $array = static::translateNames($array, self::FIELDS_TO_PROPERTIES);
278
    foreach ($array as $name => $value) {
279
      if ($name == static::$_primaryIndexField) {
280
        $name = 'id';
281
      }
282
283
      $this->__set($name, $value);
284
    }
285
  }
286
287
288
  /**
289
   * Instate ActiveRecord from array
290
   *
291
   * @param $array
292
   *
293
   * @return static|bool
294
   */
295
  public static function fromArray($array) {
296
    if (!is_array($array) || empty($array)) {
297
      return false;
298
    }
299
300
    $record = new static();
301
    $record->fillProperties($array);
302
303
    return $record;
304
  }
305
306
307
  /**
308
   * @param array $records
309
   *
310
   * @return array|static[]
311
   */
312
  protected static function fromArrayList($records) {
313
    if (is_array($records) && !empty($records)) {
314
      foreach ($records as &$record) {
315
        $record = static::fromArray($record);
316
        $record->_isNew = false;
317
      }
318
    } else {
319
      $records = [];
320
    }
321
322
    return $records;
323
  }
324
325
}
326