Passed
Push — master ( 5a733a...de3595 )
by dima
06:55
created

AbstractDataMapper::setMappingFields()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 1
ccs 0
cts 0
cp 0
nc 1
1
<?php
2
3
namespace SimpleORM;
4
5
/**
6
 * Description of AbstractDataMapper
7
 *
8
 * @author Dmitriy
9
 */
10
abstract class AbstractDataMapper implements RepositoryInterface, MapperInterface
11
{
12
	/**
13
	 * адаптер для работы с бд
14
	 * @var type 
15
	 */
16
	protected $adapter;
17
	
18
	/**
19
	 * таблица для сущности
20
	 * @var type 
21
	 */
22
    protected $entityTable;	
23
24
	/**
25
	 * первичный ключ
26
	 * @var type 
27
	 */
28
	protected $key;
29
		
30
	/**
31
	 * Использование join при выборке
32
	 * @var type 
33
	 */
34
	protected $use_joins = false;
35
	
36
	/**
37
	 * Использование мягкое удаление
38
	 * @var type 
39
	 */
40
	protected $use_delete = false;
41
42
	/**
43
	 * поле для мягкого удаления
44
	 * @var type 
45
	 */
46
	protected $soft_delete_key;
47
	
48
49
	/**
50
	 * поля сущности 
51
	 * @var type 
52
	 */
53
	protected $mapping_fields = [];
54
	
55
	/**
56
	 * псевдонимы полей сущности
57
	 * @var type 
58
	 */
59
	protected $mapping_fields_aliases = [];
60
	
61
	/**
62
	 * связи с другими мапперами
63
	 * @var type 
64
	 */
65
	protected $relations = [];
66
67
	/**
68
	 * Контейнер
69
	 * @var League\Container\Container
70
	 */
71
	protected $DI;
72
			
73
	function __construct(\League\Container\Container $DI, QueryBuilderInterface $adapter, $db_name = null) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
74
		
75
		$this->DI = $DI;
0 ignored issues
show
Documentation Bug introduced by
It seems like $DI of type object<League\Container\Container> is incompatible with the declared type object<SimpleORM\League\Container\Container> of property $DI.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
76
		
77
		$this->setMappingFields();
78
		
79
		$this->setAdapter($adapter);
80
		
81
		$this->setEntityTable($db_name);
82
		
83
		if($this->getEntityTable()=='' || $this->getPrimaryKey()==''){
84
			throw new InvalidEntityPropertyException('Свойства entityTable или key не заданы');
85
		}		
86
		
87
	}
88
89
	abstract protected function setMappingFields();	
90
	
91
    public function getAdapter() {
92
        return $this->adapter;
93
    }
94
95
	public function setAdapter(QueryBuilderInterface $adapter){
96
		 $this->adapter = $adapter;
0 ignored issues
show
Documentation Bug introduced by
It seems like $adapter of type object<SimpleORM\QueryBuilderInterface> is incompatible with the declared type object<SimpleORM\type> of property $adapter.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
97
	}
98
			
99
	
100
	protected function getEntityTable() {
101
		return $this->entityTable;
102
	}
103
104
	/**
105
	 * Уставнока таблицы
106
	 */
107
	protected function setEntityTable($db_name) {
108
		$this->entityTable = !empty($db_name)? "$db_name.".$this->table : $this->table;
0 ignored issues
show
Bug introduced by
The property table does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
109
	}	
110
111
112
    public function findById($id)
113
    {
114
		$Criteria = (new Specification())->setWhere($this->key , $id);
115
		
116
        return $this->findBySpecification($Criteria);
117
    }	
118
	
119
	/**
120
	 * Cохранение сущности
121
	 * @param EntityInterface $Entity
122
	 */
123
	public function save(EntityInterface $Entity)
124
	{
125
		
126
		$data = $this->unbuildEntity($Entity);
127
	
128
		//protected function onAfterSave(\SimpleORM\EntityInterface $Entity, &$data)
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
129
		if(method_exists($this, 'onAfterSave' )) $this->onAfterSave( $Entity, $data );
0 ignored issues
show
Bug introduced by
The method onAfterSave() does not seem to exist on object<SimpleORM\AbstractDataMapper>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
130
		
131
		$id = $data[$this->getPrimaryKey()];
132
		unset($data[$this->getPrimaryKey()]);		
133
		
134
		//insert
135
		if (empty($id)) {
136
			
137
			unset($data[$this->setSoftDeleteKey()]);
138
			
139
			$this->getAdapter()->insert($this->getEntityTable(),$data);
140
			
141
			if (!$id = $this->getAdapter()->insert_id()) {
142
				return false;
143
			}
144
			
145
			$Entity->setId($id);
146
		}
147
		//update
148
		else {
149
			
150
			if(!$this->getAdapter()->update($this->getEntityTable(), $data, "{$this->getPrimaryKey()} = '{$id}'")){
151
				return false;
152
			}
153
154
		}		
155
		
156
		if(method_exists($this, 'onBeforeSave' )) $this->onBeforeSave( $Entity );
157
158
		
159
		return true;
160
	}
161
	
162
	/**
163
	 * На успешное сохранение
164
	 * @param \SimpleORM\EntityInterface $Entity
165
	 */
166
	protected function onBeforeSave(EntityInterface $Entity){
167
		
168
		foreach ($this->relations as $alias => $mapper) {
169
		
170
			$SaveEntity = $Entity->{'get'.$alias}();
171
			
172
			if(!$mapper->save($SaveEntity)){
173
				throw new \Autoprice\Exceptions\EntityNotSaveException('Unable to save Entity!');
174
			}
175
			
176
			unset($SaveEntity);
177
		}
178
		
179
		return true;
180
	}
181
	
182
	/**
183
	 * На успешное удаление
184
	 * @param \SimpleORM\EntityInterface $Entity
185
	 */
186
	protected function onBeforeDelete(EntityInterface $Entity) {
187
		foreach ($this->relations as $alias => $cfg) {
188
			$mapper = $cfg['mapper'];
189
			//если связь один к одному то удаляем сущность
190
			if($cgg['reltype'] == 'has_one'){
0 ignored issues
show
Bug introduced by
The variable $cgg does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
191
				$Entity = $Entity->{'get'.$alias}();
192
				if(!$mapper->delete($Entity)){
193
					throw new \Autoprice\Exceptions\EntityNotDeleteException('Unable to delete Entity!');
194
				}
195
			}
196
		}
197
		
198
		return true;
199
	}
200
201
202
203
	/**
204
	 * Подготавливаем конечный вариант Сущности
205
	 * 
206
	 * @param \Core\Infrastructure\EntityInterface $Entity
207
	 * @param array $row
208
	 * @return \Core\Infrastructure\EntityInterface
209
	 * @throws BadMethodCallException
210
	 */
211
	protected function buildEntity(EntityInterface $Entity, array $row){
212
		
213
        foreach ($this->mapping_fields as $alias => $cfg ) {
214
			
215
			$value = false;
216
			
217
			$field = $cfg['field'];
218
			
219
			$method_set = 'set' . ucfirst($alias);
220
			
221
			if(!method_exists($Entity, $method_set )){
222
				throw new BadMethodCallException("Метод {$method_set}  не определен");
223
			}			
224
			
225
			//событие на формирование поля
226
			if( isset($cfg['build']) && is_object($cfg['build']) ){
227
				$value = call_user_func($cfg['build'], $row);
228
			}
229
			//на связь
230
			elseif(isset($cfg['relation'])){
231
				
232
				$mapper = $this->DI->get($cfg['relation']);
233
				
234
				if($this->use_joins===true || empty($row[$field])){
235
					$value = $mapper->createEntity($row);
236
				}
237
				else{
238
					$fkey = isset($cfg['on']) ? $cfg['on'] :$mapper->key;
239
					$value = $mapper->findBySpecification((new Specification)->setWhere($fkey, $row[$field]));
240
				}				
241
				
242
			}
243
			elseif(is_string($field) && isset($row[strtolower($field)])){
244
				$value = $row[strtolower($field)];
245
			}
246
			
247
			if($value!==false)
248
				$Entity->{$method_set}($value);
249
			
250
        }
251
		
252
        return $Entity;		
253
	}	
254
255
	
256
	/**
257
	 * из объекта формирует массив
258
	 * @param \Core\Infrastructure\EntityInterface $Entity
259
	 * @return \Core\Infrastructure\EntityInterface
260
	 * @throws BadMethodCallException
261
	 */
262
	protected function unbuildEntity(EntityInterface $Entity){
263
		
264
		$row = [];
265
266
        foreach ($this->mapping_fields as $alias => $cfg ) {
267
			
268
			$field = $cfg['field'];
269
			
270
			$method_get = 'get' . ucfirst($alias);
271
			
272
			if(!method_exists($Entity, $method_get )){
273
				throw new BadMethodCallException("Метод {$method_get}  не определен");
274
			}		
275
			
276
			//--------------------------------------------------------------------
277
			if( isset($cfg['unbuild']) && is_object($cfg['unbuild']) ){
278
				$value = call_user_func($cfg['unbuild'], $Entity->{$method_get}() );
279
			}
280
			elseif(isset($cfg['relation'])){
281
				
282
				if(isset($cfg['on']))
283
					$fkey = $this->DI->get($cfg['relation'])->getFieldAlias($cfg['on']);
284
				else
285
					$fkey = 'id';
286
				
287
				$value = $Entity->{$method_get}()->{'get'.$fkey}();
288
				
289
			}			
290
			else{
291
				$value = $Entity->{$method_get}();
292
			}			
293
						
294
			$row[$field] = $value;
295
296
        }
297
		
298
		ed($data);
0 ignored issues
show
Bug introduced by
The variable $data does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
299
			
300
        return $row;		
301
	}	
302
	
303
	/**
304
	 * Установка поля для маппинга
305
	 */
306 5
	protected function addMappingField($alias,$field = null){
307
		
308 5
		if(is_string($field)){
309 1
			$field = ['field'	=>	$field];
310 1
		}
311 4
		elseif( (is_array($field) && !isset($field['field'])) || empty($field)){
312 1
			$field['field']	= $alias;
313 1
		}
314
	
315 5
		$this->mapping_fields[$alias] = $field;
316
317 5
		if(isset($field['primary']) && $field['primary']===true){
318 1
			$this->key = $field['field'];
319 1
		}
320
		
321 5
		if(isset($field['softdelete']) && $field['softdelete']===true){
322 1
			$this->soft_delete_key = $field['field'];
323 1
		}
324
		
325 5
		$this->mapping_fields_aliases[$field['field']] = $alias;
326
		
327 5
		return $this;
328
	}	
329
	
330
331
	
332
	/**
333
	 * Установка ключа
334
	 */
335
	protected function getPrimaryKey() {
336
		return $this->key;
337
	}	
338
	
339
	/**
340
	 * Устанвка поля для мягкого удаляения
341
	 */
342
	protected function setSoftDeleteKey() {
343
		return $this->soft_delete_key;
344
	}
345
346
347
	
348
	public function getFieldAlias($field){
349
		
350
		return $this->mapping_fields_aliases[$field];
351
		
352
	}	
353
	
354
	
355
	/**
356
	 * 
357
	 * @param ISpecificationCriteria $specification
358
	 * @return type
359
	 */
360
	public function findBySpecification(ISpecificationCriteria $specification){
361
362
		//псеводо удаление
363
		$this->setSoftDelete($specification);
364
		
365
		$this->setRelations($specification);
366
		
367
		$specification->setLimit(1);
368
		
369
		//получение записей
370
		$res = $this->getAdapter()->getResultQuery($this->getEntityTable(),$specification);
371
372
        if (!$row = $res->row_array()) {
373
            return null;
374
        }
375
        return $this->createEntity($row);				
376
	}
377
	
378
	/**
379
	 * Удаление записи
380
	 * @param EntityInterface $Entity
381
	 * @return boolean
382
	 */
383
	public function delete(EntityInterface $Entity)
384
	{
385
		$result = false;
386
		
387
		$delete_key = $this->setSoftDeleteKey();
388
		
389
		if (
390
				$delete_key > '' && 
391
				$Entity->getId() > 0){
392
				$result = $this->getAdapter()->update($this->getEntityTable(), [ $delete_key => 1 ], "{$this->getPrimaryKey()} = '{$Entity->getId()}'");
393
		}
394
		elseif($Entity->getId() > 0){
395
			
396
			if($result = $this->getAdapter()->delete($this->getEntityTable(), $this->getPrimaryKey()."  = ".$Entity->getId())){
397
				if(method_exists($this, 'onBeforeDelete' )){ $result = $this->onBeforeDelete( $Entity );}
398
			}
399
		}
400
		
401
		return $result;
402
	}
403
404
	public function findAllBySpecification(ISpecificationCriteria $specification)
405
	{
406
407
		$entities = [];
0 ignored issues
show
Unused Code introduced by
$entities is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
408
		
409
		//псеводо удаление
410
		$this->setSoftDelete($specification);		
411
		
412
		$this->setRelations($specification);
413
		
414
		$res = $this->getAdapter()->getResultQuery($this->getEntityTable(),$specification);
415
		
416
		if (!$rows = $res->result_array()) {
417
            return null;
418
        }	
419
		
420
		foreach($rows as $k =>  $row){
421
			$rows[$k] = $this->createEntity($row);
422
		}
423
		
424
		return $rows;		
425
	}
426
427
	public function findAll()
428
	{
429
		return $this->findAllBySpecification(new Specification());
430
	}
431
	
432
	/**
433
	 * Выборка удаленных моделей
434
	 * @param ISpecificationCriteria $specification
435
	 */
436
	private function setSoftDelete(ISpecificationCriteria $specification){
437
		if(
438
				$this->use_delete === false &&
439
				$this->setSoftDeleteKey()>'' 
440
				&& !isset($specification->getWhere()[$this->setSoftDeleteKey()])
441
				)
442
		$specification->setWhere($this->setSoftDeleteKey(),0);
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
443
	}
444
	
445
	/**
446
	 * Построение join-ов
447
	 * 
448
	 * @todo добавить типы связей 
449
	 * has_many - один к многим (пост и коммеентарии)
450
	 * belongs_to - многие к многим (пользователь имет множество оплат одного заказа)
451
	 * has_one - один к одному
452
	 */
453
	protected function setRelations(ISpecificationCriteria $Specification){
454
455
		$joins = [];
456
457
		foreach ($this->mapping_fields as $field => $cfg){
458
			if(isset($cfg['relation'])){
459
				
460
				$this->relations[$field] = [
461
					'mapper'	=>	$mapper = $this->DI->get($cfg['relation']),
462
					'reltype'	=>  isset($cfg['reltype']) ? $cfg['reltype'] : 'belongs_to'
463
				];
464
465
				$table = $mapper->getEntityTable();
466
467
				$relation_key = isset($cfg['on'])? $cfg['on'] : $mapper->key;
468
				
469
				$joins[$table] = [
470
						'alias'	=> $field,
471
						//'type'	=> 'INNER',
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
472
						'on'	=> "`{$this->table}`.{$cfg['field']} = `{$field}`.{$relation_key}"
473
				];
474
475
			}
476
		}	
477
478
		if($this->use_joins===true){
479
			$Specification->setJoins($joins);
480
		}			
481
	}
482
	
483
	/**
484
	 * Использование join-ов
485
	 */
486
	public function useJoins()
487
	{
488
		$o = clone $this;
489
		$o->use_joins = true;
0 ignored issues
show
Documentation Bug introduced by
It seems like true of type boolean is incompatible with the declared type object<SimpleORM\type> of property $use_joins.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
490
		return $o;
491
	}
492
	
493
	public function withDelete(){
494
		$o = clone $this;
495
		$o->use_delete = true;
0 ignored issues
show
Documentation Bug introduced by
It seems like true of type boolean is incompatible with the declared type object<SimpleORM\type> of property $use_delete.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
496
		return $o;
497
	}
498
499
}
500