main::get_data()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
c 0
b 0
f 0
nc 2
nop 5
dl 0
loc 14
rs 10
1
<?php
2
/**
3
 *
4
 * PayPal Donation extension for the phpBB Forum Software package.
5
 *
6
 * @copyright (c) 2015-2024 Skouat
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 */
10
11
namespace skouat\ppde\entity;
12
13
use phpbb\db\driver\driver_interface;
14
use phpbb\language\language;
15
16
abstract class main
17
{
18
	/** @type string */
19
	protected $u_action;
20
	/**
21
	 * Declare overridden properties
22
	 */
23
	protected $db;
24
	protected $data;
25
	protected $lang_key_prefix;
26
	protected $lang_key_suffix;
27
	protected $language;
28
	protected $table_name;
29
	protected $table_schema;
30
31
	/**
32
	 * Construct
33
	 *
34
	 * @param driver_interface $db              Database object
35
	 * @param language         $language        Language object
36
	 * @param string           $lang_key_prefix Prefix for the messages thrown by exceptions
37
	 * @param string           $lang_key_suffix Suffix for the messages thrown by exceptions
38
	 * @param string           $table_name      Table name
39
	 * @param array            $table_schema    Array with column names to overwrite and type of data
40
	 */
41
	public function __construct(
42
		driver_interface $db,
43
		language $language,
44
		string $lang_key_prefix = '',
45
		string $lang_key_suffix = '',
46
		string $table_name = '',
47
		array $table_schema = []
48
	)
49
	{
50
		$this->db = $db;
51
		$this->language = $language;
52
		$this->lang_key_prefix = $lang_key_prefix;
53
		$this->lang_key_suffix = $lang_key_suffix;
54
		$this->table_name = $table_name;
55
		$this->table_schema = $table_schema;
56
	}
57
58
	/**
59
	 * Set data in the $entity object.
60
	 *
61
	 * @param array $data_ary
62
	 */
63
	public function set_entity_data(array $data_ary): void
64
	{
65
		foreach ($data_ary as $key => $value)
66
		{
67
			$schema_key = 'item_' . $key;
68
			if (isset($this->table_schema[$schema_key]))
69
			{
70
				settype($value, $this->table_schema[$schema_key]['type']);
71
				$this->data[$this->table_schema[$schema_key]['name']] = $value;
72
			}
73
			else
74
			{
75
				$method_name = 'set_' . $key;
76
				if (method_exists($this, $method_name))
77
				{
78
					$this->{'set_' . $key}($value);
79
				}
80
			}
81
		}
82
	}
83
84
	/**
85
	 * Parse data to the entity
86
	 *
87
	 * @param string $run_before_insert Name of the function to call before SQL INSERT
88
	 *
89
	 * @return string
90
	 */
91
	public function add_edit_data($run_before_insert = ''): string
92
	{
93
		if ($this->get_id())
94
		{
95
			// Save the edited item entity to the database
96
			$this->save($this->check_required_field());
97
			return 'UPDATED';
98
		}
99
100
		// Insert the data to the database
101
		$this->insert($run_before_insert);
102
103
		// Get the newly inserted identifier
104
		$id = $this->get_id();
105
106
		// Reload the data to return a fresh entity
107
		$this->load($id);
108
		return 'ADDED';
109
	}
110
111
	/**
112
	 * Insert the item for the first time
113
	 *
114
	 * Will throw an exception if the item was already inserted (call save() instead)
115
	 *
116
	 * @param string $run_before_insert
117
	 *
118
	 * @return main $this object for chaining calls; load()->set()->save()
119
	 */
120
	public function insert($run_before_insert = '')
121
	{
122
		if (!empty($this->data[$this->table_schema['item_id']['name']]))
123
		{
124
			// The item already exists
125
			$this->display_warning_message($this->lang_key_prefix . '_EXIST');
126
		}
127
128
		// Run some stuff before insert data in database
129
		$this->run_function_before_action($run_before_insert);
130
131
		// Make extra sure there is no item_id set
132
		unset($this->data[$this->table_schema['item_id']['name']]);
133
134
		// Insert the item data to the database
135
		$sql = 'INSERT INTO ' . $this->table_name . ' ' . $this->db->sql_build_array('INSERT', $this->data);
136
		$this->db->sql_query($sql);
137
138
		// Set the item_id using the id created by the SQL insert
139
		$this->data[$this->table_schema['item_id']['name']] = (int) $this->db->sql_last_inserted_id();
140
141
		return $this;
142
	}
143
144
	/**
145
	 * Display a user warning message
146
	 *
147
	 * @param string $lang_key
148
	 * @param string $args
149
	 */
150
	protected function display_warning_message($lang_key, $args = ''): void
151
	{
152
		$message = call_user_func_array([$this->language, 'lang'], array_merge([strtoupper($lang_key), $args])) . $this->adm_back_link_exists();
153
		trigger_error($message, E_USER_WARNING);
154
	}
155
156
	/**
157
	 * Checks if the adm_back_link function is loaded
158
	 *
159
	 * @return string
160
	 */
161
	protected function adm_back_link_exists(): string
162
	{
163
		return (function_exists('adm_back_link')) ? adm_back_link($this->u_action) : '';
164
	}
165
166
	/**
167
	 * Run function before do some alter some data in the database
168
	 *
169
	 * @param string $function_name
170
	 *
171
	 * @return bool
172
	 */
173
	private function run_function_before_action($function_name): bool
174
	{
175
		$func_result = true;
176
		if ($function_name)
177
		{
178
			$func_result = (bool) $this->$function_name();
179
		}
180
181
		return $func_result;
182
	}
183
184
	/**
185
	 * Save the current settings to the database
186
	 *
187
	 * This must be called before closing or any changes will not be saved!
188
	 * If adding a item (saving for the first time), you must call insert() or an exception will be thrown
189
	 *
190
	 * @param bool $required_fields
191
	 *
192
	 * @return main $this object for chaining calls; load()->set()->save()
193
	 */
194
	public function save($required_fields)
195
	{
196
		if ($required_fields)
197
		{
198
			// The item already exists
199
			$this->display_warning_message($this->lang_key_prefix . '_NO_' . $this->lang_key_suffix);
200
		}
201
202
		$sql = 'UPDATE ' . $this->table_name . '
203
			SET ' . $this->db->sql_build_array('UPDATE', $this->data) . '
204
			WHERE ' . $this->db->sql_escape($this->table_schema['item_id']['name']) . ' = ' . $this->get_id();
205
		$this->db->sql_query($sql);
206
207
		return $this;
208
	}
209
210
	/**
211
	 * Get id
212
	 *
213
	 * @return int Item identifier
214
	 */
215
	public function get_id(): int
216
	{
217
		return (int) ($this->data[$this->table_schema['item_id']['name']] ?? 0);
218
	}
219
220
	/**
221
	 * Check the Identifier of the called data exists in the database
222
	 *
223
	 * @param string $sql SQL Query
224
	 *
225
	 * @return bool
226
	 */
227
	public function data_exists($sql): bool
228
	{
229
		$this->db->sql_query($sql);
230
		$this->set_id($this->db->sql_fetchfield($this->table_schema['item_id']['name']));
231
232
		return (bool) $this->data[$this->table_schema['item_id']['name']];
233
	}
234
235
	/**
236
	 * Set item Identifier
237
	 *
238
	 * @param int $id
239
	 *
240
	 * @return main $this object for chaining calls; load()->set()->save()
241
	 */
242
	public function set_id(int $id)
243
	{
244
		$this->data[$this->table_schema['item_id']['name']] = $id;
245
246
		return $this;
247
	}
248
249
	/**
250
	 * SQL Query to return the ID of selected item
251
	 *
252
	 * @return string
253
	 */
254
	public function build_sql_data_exists(): string
255
	{
256
		$sql_ary = [
257
			'SELECT' => 't.' . $this->table_schema['item_id']['name'],
258
			'FROM'   => [$this->table_name => 't'],
259
			'WHERE'  => 't.' . $this->db->sql_escape($this->table_schema['item_id']['name']) . ' = ' . (int) $this->data[$this->table_schema['item_id']['name']],
260
		];
261
262
		return $this->db->sql_build_query('SELECT', $sql_ary);
263
	}
264
265
	/**
266
	 * Load the data from the database
267
	 *
268
	 * @param int $id
269
	 *
270
	 * @return main $this object for chaining calls; load()->set()->save()
271
	 */
272
	public function load($id)
273
	{
274
		$sql_ary = [
275
			'SELECT' => '*',
276
			'FROM'   => [$this->table_name => 't'],
277
			'WHERE'  => 't.' . $this->db->sql_escape($this->table_schema['item_id']['name']) . ' = ' . (int) $id,
278
		];
279
280
		$sql = $this->db->sql_build_query('SELECT', $sql_ary);
281
		$result = $this->db->sql_query($sql);
282
		$this->data = $this->db->sql_fetchrow($result);
283
		$this->db->sql_freeresult($result);
284
285
		if ($this->data === false)
286
		{
287
			// An item does not exist
288
			$this->display_warning_message($this->lang_key_prefix . '_NO_' . $this->lang_key_suffix);
289
		}
290
291
		return $this;
292
	}
293
294
	/**
295
	 * Get Item name
296
	 *
297
	 * @return string Item name
298
	 */
299
	public function get_name(): string
300
	{
301
		return (string) ($this->data[$this->table_schema['item_name']['name']] ?? '');
302
	}
303
304
	/**
305
	 * Set page url
306
	 *
307
	 * @param string $u_action Custom form action
308
	 */
309
	public function set_page_url($u_action): void
310
	{
311
		$this->u_action = $u_action;
312
	}
313
314
	/**
315
	 * Check if required field is set
316
	 *
317
	 * @return bool
318
	 */
319
	public function check_required_field(): bool
320
	{
321
		return false;
322
	}
323
324
	/**
325
	 * Delete data from the database
326
	 *
327
	 * @param int    $id
328
	 * @param string $action_before_delete Function to start before deleting data.
329
	 * @param string $sql_where
330
	 * @param bool   $all                  Set to true if you want delete all data from the table.
331
	 *
332
	 * @return bool
333
	 */
334
	public function delete($id, $action_before_delete = '', $sql_where = '', $all = false): bool
335
	{
336
		$where_clause = '';
337
338
		if (!$all)
339
		{
340
			if (empty($sql_where) && $this->disallow_deletion($id))
341
			{
342
				// The item selected does not exists
343
				$this->display_warning_message($this->lang_key_prefix . '_NO_' . $this->lang_key_suffix);
344
			}
345
346
			$where_clause = !empty($sql_where) ? $sql_where : ' WHERE ' . $this->db->sql_escape($this->table_schema['item_id']['name']) . ' = ' . (int) $id;
347
		}
348
349
		$this->run_function_before_action($action_before_delete);
350
351
		// Delete data from the database
352
		$sql = 'DELETE FROM ' . $this->table_name . $where_clause;
353
		$this->db->sql_query($sql);
354
355
		return (bool) $this->db->sql_affectedrows();
356
	}
357
358
	/**
359
	 * Returns if we can proceed to item deletion
360
	 *
361
	 * @param int $id
362
	 *
363
	 * @return bool
364
	 */
365
	private function disallow_deletion($id): bool
366
	{
367
		return empty($this->data[$this->table_schema['item_id']['name']]) || ((int) $this->data[$this->table_schema['item_id']['name']] !== $id);
368
	}
369
370
	/**
371
	 * Get data from the database
372
	 *
373
	 * @param string $sql
374
	 * @param array  $additional_table_schema
375
	 * @param int    $limit
376
	 * @param int    $limit_offset
377
	 * @param bool   $override
378
	 *
379
	 * @return array
380
	 */
381
	public function get_data($sql, $additional_table_schema = [], $limit = 0, $limit_offset = 0, $override = false): array
382
	{
383
		$entities = [];
384
		$result = $this->limit_query($sql, $limit, $limit_offset);
385
386
		while ($row = $this->db->sql_fetchrow($result))
387
		{
388
			// Import each row into an entity
389
			$entities[] = $this->import($row, $additional_table_schema, $override);
390
		}
391
		$this->db->sql_freeresult($result);
392
393
		// Return all entities
394
		return $entities;
395
	}
396
397
	/**
398
	 * Use query limit if requested
399
	 *
400
	 * @param string $sql
401
	 * @param int    $limit
402
	 * @param int    $offset
403
	 *
404
	 * @return mixed
405
	 */
406
	private function limit_query($sql, $limit, $offset)
407
	{
408
		return empty($limit) ? $this->db->sql_query($sql) : $this->db->sql_query_limit($sql, $limit, $offset);
409
	}
410
411
	/**
412
	 * Import and validate data
413
	 *
414
	 * Used when the data is already loaded externally.
415
	 * Any existing data on this item is over-written.
416
	 * All data is validated and an exception is thrown if any data is invalid.
417
	 *
418
	 * @param array $data Data array, typically from the database
419
	 * @param array $additional_table_schema
420
	 * @param bool  $override
421
	 *
422
	 * @return array $this->data
423
	 */
424
	public function import($data, $additional_table_schema = [], $override = false): array
425
	{
426
		// Clear out any saved data
427
		$this->data = [];
428
429
		// Adds additional field to the table schema
430
		$this->table_schema = !$override ? array_merge($this->table_schema, $additional_table_schema) : $additional_table_schema;
431
432
		// Go through the basic fields and set them to our data array
433
		foreach ($this->table_schema as $field)
434
		{
435
			// If the data wasn't sent to us, throw an exception
436
			if (!isset($data[$field['name']]))
437
			{
438
				$this->display_warning_message('EXCEPTION_INVALID_FIELD', $field['name']);
439
			}
440
441
			// settype passes values by reference
442
			$value = $data[$field['name']];
443
444
			// We're using settype to enforce data types
445
			settype($value, $field['type']);
446
447
			$this->data[$field['name']] = $value;
448
		}
449
450
		return $this->data;
451
	}
452
}
453