Complex classes like AbstractDataMapper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AbstractDataMapper, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 10 | abstract class AbstractDataMapper implements RepositoryInterface, MapperInterface  | 
            ||
| 11 | { | 
            ||
| 12 | |||
| 13 | use TraitDataMapperEvent;  | 
            ||
| 14 | |||
| 15 | |||
| 16 | /**  | 
            ||
| 17 | * адаптер для работы с бд  | 
            ||
| 18 | * @var type  | 
            ||
| 19 | */  | 
            ||
| 20 | protected $adapter;  | 
            ||
| 21 | |||
| 22 | /**  | 
            ||
| 23 | * таблица для сущности  | 
            ||
| 24 | * @var type  | 
            ||
| 25 | */  | 
            ||
| 26 | protected $entityTable;  | 
            ||
| 27 | |||
| 28 | /**  | 
            ||
| 29 | * первичный ключ  | 
            ||
| 30 | * @var type  | 
            ||
| 31 | */  | 
            ||
| 32 | protected $key;  | 
            ||
| 33 | |||
| 34 | /**  | 
            ||
| 35 | * Использование join при выборке  | 
            ||
| 36 | * @var type  | 
            ||
| 37 | */  | 
            ||
| 38 | protected $use_joins = false;  | 
            ||
| 39 | |||
| 40 | /**  | 
            ||
| 41 | * Использование мягкое удаление  | 
            ||
| 42 | * @var type  | 
            ||
| 43 | */  | 
            ||
| 44 | protected $use_delete = false;  | 
            ||
| 45 | |||
| 46 | /**  | 
            ||
| 47 | * поле для мягкого удаления  | 
            ||
| 48 | * @var type  | 
            ||
| 49 | */  | 
            ||
| 50 | protected $soft_delete_key;  | 
            ||
| 51 | |||
| 52 | |||
| 53 | /**  | 
            ||
| 54 | * поля сущности  | 
            ||
| 55 | * @var type  | 
            ||
| 56 | */  | 
            ||
| 57 | protected $mapping_fields = [];  | 
            ||
| 58 | |||
| 59 | /**  | 
            ||
| 60 | * псевдонимы полей сущности  | 
            ||
| 61 | * @var type  | 
            ||
| 62 | */  | 
            ||
| 63 | protected $mapping_fields_aliases = [];  | 
            ||
| 64 | |||
| 65 | /**  | 
            ||
| 66 | * связи с другими мапперами  | 
            ||
| 67 | * @var type  | 
            ||
| 68 | */  | 
            ||
| 69 | protected $relations = [];  | 
            ||
| 70 | |||
| 71 | /**  | 
            ||
| 72 | * Контейнер  | 
            ||
| 73 | * @var League\Container\Container  | 
            ||
| 74 | */  | 
            ||
| 75 | protected $DI;  | 
            ||
| 76 | |||
| 77 | 	function __construct(\League\Container\Container $DI, QueryBuilderInterface $adapter, $db_name = null) { | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 78 | |||
| 79 | $this->DI = $DI;  | 
            ||
| 80 | |||
| 81 | $this->setMappingFields();  | 
            ||
| 82 | |||
| 83 | $this->setAdapter($adapter);  | 
            ||
| 84 | |||
| 85 | $this->setEntityTable($db_name);  | 
            ||
| 86 | |||
| 87 | 		if($this->getEntityTable()=='' || $this->getPrimaryKey()==''){ | 
            ||
| 88 | 			throw new InvalidEntityPropertyException('Свойства entityTable или key не заданы'); | 
            ||
| 89 | }  | 
            ||
| 90 | |||
| 91 | }  | 
            ||
| 92 | |||
| 93 | abstract protected function setMappingFields();  | 
            ||
| 94 | |||
| 95 |     public function getAdapter() { | 
            ||
| 98 | |||
| 99 | 	public function setAdapter(QueryBuilderInterface $adapter){ | 
            ||
| 102 | |||
| 103 | |||
| 104 | 	protected function getEntityTable() { | 
            ||
| 107 | |||
| 108 | /**  | 
            ||
| 109 | * Уставнока таблицы  | 
            ||
| 110 | */  | 
            ||
| 111 | 	protected function setEntityTable($db_name) { | 
            ||
| 114 | |||
| 115 | /**  | 
            ||
| 116 | * Получение записи по ключу  | 
            ||
| 117 | * @param type $id  | 
            ||
| 118 | * @return type  | 
            ||
| 119 | */  | 
            ||
| 120 | public function findById($id)  | 
            ||
| 121 |     { | 
            ||
| 122 | $Criteria = (new Specification())->setWhere($this->key , $id);  | 
            ||
| 123 | |||
| 124 | return $this->findBySpecification($Criteria);  | 
            ||
| 125 | }  | 
            ||
| 126 | |||
| 127 | /**  | 
            ||
| 128 | * Сохранение сущности без спобытий  | 
            ||
| 129 | * @param \SimpleORM\EntityInterface $Entity  | 
            ||
| 130 | */  | 
            ||
| 131 | 	public function saveWithoutEvents(EntityInterface $Entity) { | 
            ||
| 132 | |||
| 133 | $data = $this->unbuildEntity($Entity);  | 
            ||
| 134 | |||
| 135 | if(method_exists($this, 'onPrepareData' )) $this->onPrepareData( $Entity , $data );  | 
            ||
| 136 | |||
| 137 | $id = $data[$this->getPrimaryKey()];  | 
            ||
| 138 | unset($data[$this->getPrimaryKey()]);  | 
            ||
| 139 | |||
| 140 | //insert  | 
            ||
| 141 | 		if (empty($id)) { | 
            ||
| 142 | |||
| 143 | unset($data[$this->setSoftDeleteKey()]);  | 
            ||
| 144 | |||
| 145 | $this->getAdapter()->insert($this->getEntityTable(),$data);  | 
            ||
| 146 | |||
| 147 | 			if (!$id = $this->getAdapter()->insert_id()) { | 
            ||
| 148 | return false;  | 
            ||
| 149 | }  | 
            ||
| 150 | |||
| 151 | $Entity->setId($id);  | 
            ||
| 152 | }  | 
            ||
| 153 | //update  | 
            ||
| 154 | 		else { | 
            ||
| 155 | |||
| 156 | 			if(!$this->getAdapter()->update($this->getEntityTable(), $data, "{$this->getPrimaryKey()} = '{$id}'")){ | 
            ||
| 157 | return false;  | 
            ||
| 158 | }  | 
            ||
| 159 | |||
| 160 | }  | 
            ||
| 161 | |||
| 162 | return true;  | 
            ||
| 163 | }  | 
            ||
| 164 | |||
| 165 | /**  | 
            ||
| 166 | * Cохранение сущности  | 
            ||
| 167 | * @param EntityInterface $Entity  | 
            ||
| 168 | */  | 
            ||
| 169 | public function save(EntityInterface $Entity)  | 
            ||
| 181 | |||
| 182 | |||
| 183 | /**  | 
            ||
| 184 | * получение мапперов в порядке их использования с учетом вложенности  | 
            ||
| 185 | * @return array  | 
            ||
| 186 | */  | 
            ||
| 187 | 	protected function createListRelation(){ | 
            ||
| 188 | $rel_list = [];  | 
            ||
| 189 | |||
| 190 | $rel_map = $this->getRelations();  | 
            ||
| 191 | |||
| 192 | $this->createListRelationReq($rel_map,$rel_list);  | 
            ||
| 193 | |||
| 194 | return $rel_list;  | 
            ||
| 195 | }  | 
            ||
| 196 | |||
| 197 | /**  | 
            ||
| 198 | * Выстроивает порядок использования мапперов в иерархии  | 
            ||
| 199 | * @param array $rel_map  | 
            ||
| 200 | * @param type $rel_list  | 
            ||
| 201 | */  | 
            ||
| 202 | 	protected function createListRelationReq(array $rel_map,&$rel_list,$obj_parent_link = null) { | 
            ||
| 203 | |||
| 204 | 		foreach ($rel_map as $rel){ | 
            ||
| 205 | |||
| 206 | $obj_link = '#'.$rel['alias'].'()';  | 
            ||
| 207 | |||
| 208 | 			if(count($rel['relations'])>0){ | 
            ||
| 209 | $this->createListRelationReq($rel['relations'],$rel_list,$obj_parent_link.'get'.$rel['alias'].'()');  | 
            ||
| 210 | $rel_list [$obj_parent_link.$obj_link]= $rel['name'];  | 
            ||
| 211 | }  | 
            ||
| 212 | 			else{ | 
            ||
| 213 | $rel_list [$obj_parent_link.$obj_link] = $rel['name'];  | 
            ||
| 214 | }  | 
            ||
| 215 | }  | 
            ||
| 216 | }  | 
            ||
| 217 | |||
| 218 | |||
| 219 | /**  | 
            ||
| 220 | * получить связи  | 
            ||
| 221 | */  | 
            ||
| 222 | 	protected function getRelations(){ | 
            ||
| 223 | $rel_map = [];  | 
            ||
| 224 | 		foreach ($this->mapping_fields as $field => $cfg){ | 
            ||
| 225 | 			if(isset($cfg['relation'])){		 | 
            ||
| 226 | $rels = $this->DI->get($cfg['relation'])->getRelations();  | 
            ||
| 227 | $rel_map []= [  | 
            ||
| 228 | 'name' => $cfg['relation'],  | 
            ||
| 229 | 'alias' => $field,  | 
            ||
| 230 | 'relations' => $rels  | 
            ||
| 231 | ];  | 
            ||
| 232 | }  | 
            ||
| 233 | }  | 
            ||
| 234 | return $rel_map;  | 
            ||
| 235 | }  | 
            ||
| 236 | |||
| 237 | |||
| 238 | |||
| 239 | |||
| 240 | |||
| 241 | |||
| 242 | |||
| 243 | /**  | 
            ||
| 244 | * Подготавливаем конечный вариант Сущности  | 
            ||
| 245 | *  | 
            ||
| 246 | * @param \Core\Infrastructure\EntityInterface $Entity  | 
            ||
| 247 | * @param array $row  | 
            ||
| 248 | * @return \Core\Infrastructure\EntityInterface  | 
            ||
| 249 | * @throws BadMethodCallException  | 
            ||
| 250 | */  | 
            ||
| 251 | 	protected function buildEntity(EntityInterface $Entity, array $row){ | 
            ||
| 252 | |||
| 253 |         foreach ($this->mapping_fields as $alias => $cfg ) { | 
            ||
| 254 | |||
| 255 | $value = false;  | 
            ||
| 256 | |||
| 257 | $field = $cfg['field'];  | 
            ||
| 258 | |||
| 259 | $method_set = 'set' . ucfirst($alias);  | 
            ||
| 260 | |||
| 261 | 			if(!method_exists($Entity, $method_set )){ | 
            ||
| 262 | 				throw new BadMethodCallException("Метод {$method_set}  не определен"); | 
            ||
| 263 | }  | 
            ||
| 264 | |||
| 265 | //событие на формирование поля  | 
            ||
| 266 | 			if( isset($cfg['build']) && is_object($cfg['build']) ){ | 
            ||
| 267 | $value = call_user_func($cfg['build'], $row);  | 
            ||
| 268 | }  | 
            ||
| 269 | //на связь  | 
            ||
| 270 | 			elseif(isset($cfg['relation'])){ | 
            ||
| 271 | |||
| 272 | $mapper = $this->DI->get($cfg['relation']);  | 
            ||
| 273 | |||
| 274 | 				if($this->use_joins===true || empty($row[$field])){ | 
            ||
| 275 | $value = $mapper->createEntity($row);  | 
            ||
| 276 | }  | 
            ||
| 277 | 				else{ | 
            ||
| 278 | $fkey = isset($cfg['on']) ? $cfg['on'] :$mapper->key;  | 
            ||
| 279 | $value = $mapper->findBySpecification((new Specification)->setWhere($fkey, $row[$field]));  | 
            ||
| 280 | }  | 
            ||
| 281 | |||
| 282 | }  | 
            ||
| 283 | 			elseif(is_string($field) && isset($row[strtolower($field)])){ | 
            ||
| 284 | $value = $row[strtolower($field)];  | 
            ||
| 285 | }  | 
            ||
| 286 | |||
| 287 | if($value!==false)  | 
            ||
| 288 | 				$Entity->{$method_set}($value); | 
            ||
| 289 | |||
| 290 | }  | 
            ||
| 291 | |||
| 292 | return $Entity;  | 
            ||
| 293 | }  | 
            ||
| 294 | |||
| 295 | |||
| 296 | /**  | 
            ||
| 297 | * из объекта формирует массив  | 
            ||
| 298 | * @param \Core\Infrastructure\EntityInterface $Entity  | 
            ||
| 299 | * @return \Core\Infrastructure\EntityInterface  | 
            ||
| 300 | * @throws BadMethodCallException  | 
            ||
| 301 | */  | 
            ||
| 302 | 	protected function unbuildEntity(EntityInterface $Entity){ | 
            ||
| 303 | |||
| 304 | $row = [];  | 
            ||
| 305 | |||
| 306 |         foreach ($this->mapping_fields as $alias => $cfg ) { | 
            ||
| 307 | |||
| 308 | $field = $cfg['field'];  | 
            ||
| 309 | |||
| 310 | $method_get = 'get' . ucfirst($alias);  | 
            ||
| 311 | |||
| 312 | 			if(!method_exists($Entity, $method_get )){ | 
            ||
| 313 | 				throw new BadMethodCallException("Метод {$method_get}  не определен"); | 
            ||
| 314 | }  | 
            ||
| 315 | |||
| 316 | //--------------------------------------------------------------------  | 
            ||
| 317 | 			if( isset($cfg['unbuild']) && is_object($cfg['unbuild']) ){ | 
            ||
| 318 | 				$value = call_user_func($cfg['unbuild'], $Entity->{$method_get}() ); | 
            ||
| 319 | }  | 
            ||
| 320 | 			elseif(isset($cfg['relation']) && is_object($Entity->{$method_get}()) ){ | 
            ||
| 321 | |||
| 322 | if(isset($cfg['on']))  | 
            ||
| 323 | $fkey = $this->DI->get($cfg['relation'])->getFieldAlias($cfg['on']);  | 
            ||
| 324 | else  | 
            ||
| 325 | $fkey = 'id';  | 
            ||
| 326 | |||
| 327 | 				$value = $Entity->{$method_get}()->{'get'.$fkey}(); | 
            ||
| 328 | |||
| 329 | }  | 
            ||
| 330 | 			else{ | 
            ||
| 331 | 				$value = $Entity->{$method_get}(); | 
            ||
| 332 | }  | 
            ||
| 333 | |||
| 334 | $row[$field] = $value;  | 
            ||
| 335 | |||
| 336 | }  | 
            ||
| 337 | |||
| 338 | return $row;  | 
            ||
| 339 | }  | 
            ||
| 340 | |||
| 341 | /**  | 
            ||
| 342 | * Установка поля для маппинга  | 
            ||
| 343 | */  | 
            ||
| 344 | 5 | 	protected function addMappingField($alias,$field = null){ | 
            |
| 345 | |||
| 346 | 5 | 		if(is_string($field)){ | 
            |
| 347 | 1 | $field = ['field' => $field];  | 
            |
| 348 | 1 | }  | 
            |
| 349 | 4 | 		elseif( (is_array($field) && !isset($field['field'])) || empty($field)){ | 
            |
| 350 | 1 | $field['field'] = $alias;  | 
            |
| 351 | 1 | }  | 
            |
| 352 | |||
| 353 | 5 | $this->mapping_fields[$alias] = $field;  | 
            |
| 354 | |||
| 355 | 5 | 		if(isset($field['primary']) && $field['primary']===true){ | 
            |
| 356 | 1 | $this->key = $field['field'];  | 
            |
| 357 | 1 | }  | 
            |
| 358 | |||
| 359 | 5 | 		if(isset($field['softdelete']) && $field['softdelete']===true){ | 
            |
| 360 | 1 | $this->soft_delete_key = $field['field'];  | 
            |
| 361 | 1 | }  | 
            |
| 362 | |||
| 363 | 5 | $this->mapping_fields_aliases[$field['field']] = $alias;  | 
            |
| 364 | |||
| 365 | 5 | return $this;  | 
            |
| 366 | }  | 
            ||
| 367 | |||
| 368 | |||
| 369 | |||
| 370 | /**  | 
            ||
| 371 | * Установка ключа  | 
            ||
| 372 | */  | 
            ||
| 373 | 	protected function getPrimaryKey() { | 
            ||
| 376 | |||
| 377 | /**  | 
            ||
| 378 | * Устанвка поля для мягкого удаляения  | 
            ||
| 379 | */  | 
            ||
| 380 | 	protected function setSoftDeleteKey() { | 
            ||
| 383 | |||
| 384 | |||
| 385 | |||
| 386 | 	public function getFieldAlias($field){ | 
            ||
| 387 | |||
| 388 | return $this->mapping_fields_aliases[$field];  | 
            ||
| 389 | |||
| 390 | }  | 
            ||
| 391 | |||
| 392 | |||
| 393 | /**  | 
            ||
| 394 | *  | 
            ||
| 395 | * @param ISpecificationCriteria $specification  | 
            ||
| 396 | * @return type  | 
            ||
| 397 | */  | 
            ||
| 398 | 	public function findBySpecification(ISpecificationCriteria $specification){ | 
            ||
| 399 | |||
| 400 | //псеводо удаление  | 
            ||
| 401 | $this->setSoftDelete($specification);  | 
            ||
| 402 | |||
| 403 | $this->setRelations($specification);  | 
            ||
| 404 | |||
| 405 | $specification->setLimit(1);  | 
            ||
| 406 | |||
| 407 | //получение записей  | 
            ||
| 408 | $res = $this->getAdapter()->getResultQuery($this->getEntityTable(),$specification);  | 
            ||
| 409 | |||
| 410 |         if (!$row = $res->row_array()) { | 
            ||
| 411 | return null;  | 
            ||
| 412 | }  | 
            ||
| 413 | return $this->createEntity($row);  | 
            ||
| 414 | }  | 
            ||
| 415 | |||
| 416 | /**  | 
            ||
| 417 | * Удаление записи  | 
            ||
| 418 | * @param EntityInterface $Entity  | 
            ||
| 419 | * @return boolean  | 
            ||
| 420 | */  | 
            ||
| 421 | public function delete(EntityInterface $Entity)  | 
            ||
| 422 | 	{ | 
            ||
| 423 | $result = false;  | 
            ||
| 424 | |||
| 425 | $delete_key = $this->setSoftDeleteKey();  | 
            ||
| 426 | |||
| 427 | if (  | 
            ||
| 428 | $delete_key > '' &&  | 
            ||
| 429 | 				$Entity->getId() > 0){ | 
            ||
| 430 | 				$result = $this->getAdapter()->update($this->getEntityTable(), [ $delete_key => 1 ], "{$this->getPrimaryKey()} = '{$Entity->getId()}'"); | 
            ||
| 431 | }  | 
            ||
| 432 | 		elseif($Entity->getId() > 0){ | 
            ||
| 433 | |||
| 434 | 			if($result = $this->getAdapter()->delete($this->getEntityTable(), $this->getPrimaryKey()."  = ".$Entity->getId())){ | 
            ||
| 435 | 				if(method_exists($this, 'onBeforeDelete' )){ $result = $this->onBeforeDelete( $Entity );} | 
            ||
| 436 | }  | 
            ||
| 437 | }  | 
            ||
| 438 | |||
| 439 | return $result;  | 
            ||
| 440 | }  | 
            ||
| 441 | |||
| 442 | public function findAllBySpecification(ISpecificationCriteria $specification)  | 
            ||
| 464 | |||
| 465 | public function findAll()  | 
            ||
| 469 | |||
| 470 | /**  | 
            ||
| 471 | * Выборка удаленных моделей  | 
            ||
| 472 | * @param ISpecificationCriteria $specification  | 
            ||
| 473 | */  | 
            ||
| 474 | 	private function setSoftDelete(ISpecificationCriteria $specification){ | 
            ||
| 482 | |||
| 483 | /**  | 
            ||
| 484 | * Построение join-ов  | 
            ||
| 485 | *  | 
            ||
| 486 | * @todo добавить типы связей  | 
            ||
| 487 | * has_many - один к многим (пост и коммеентарии)  | 
            ||
| 488 | * belongs_to - многие к многим (пользователь имет множество оплат одного заказа)  | 
            ||
| 489 | * has_one - один к одному  | 
            ||
| 490 | */  | 
            ||
| 491 | 	protected function setRelations(ISpecificationCriteria $Specification){ | 
            ||
| 492 | |||
| 493 | $joins = [];  | 
            ||
| 494 | |||
| 520 | |||
| 521 | /**  | 
            ||
| 522 | * Использование join-ов  | 
            ||
| 523 | */  | 
            ||
| 524 | public function useJoins()  | 
            ||
| 530 | |||
| 531 | 	public function withDelete(){ | 
            ||
| 536 | |||
| 537 | }  | 
            ||
| 538 | 
Adding explicit visibility (
private,protected, orpublic) is generally recommend to communicate to other developers how, and from where this method is intended to be used.