| Total Complexity | 53 |
| Total Lines | 408 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Complex classes like Repository 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.
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 Repository, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 66 | class Repository implements RepositoryInterface |
||
| 67 | { |
||
|
|
|||
| 68 | |||
| 69 | /** |
||
| 70 | * The entity class |
||
| 71 | * @var class-string |
||
| 72 | */ |
||
| 73 | protected string $entityClass; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * |
||
| 77 | * @var EntityManager |
||
| 78 | */ |
||
| 79 | protected EntityManager $manager; |
||
| 80 | |||
| 81 | /** |
||
| 82 | * The list of relation to load with the query |
||
| 83 | * @var array<int, string>|array<string, Closure> |
||
| 84 | */ |
||
| 85 | protected array $with = []; |
||
| 86 | |||
| 87 | /** |
||
| 88 | * Whether need load relation data immediately |
||
| 89 | * @var bool |
||
| 90 | */ |
||
| 91 | protected bool $immediate = false; |
||
| 92 | |||
| 93 | /** |
||
| 94 | * The order by column(s) |
||
| 95 | * @var string|Closure|Expression|string[]|Expression[]|Closure[] |
||
| 96 | */ |
||
| 97 | protected $orderColumns = ''; |
||
| 98 | |||
| 99 | /** |
||
| 100 | * The order direction |
||
| 101 | * @var string |
||
| 102 | */ |
||
| 103 | protected string $orderDir = 'ASC'; |
||
| 104 | |||
| 105 | /** |
||
| 106 | * The offset to use |
||
| 107 | * @var int |
||
| 108 | */ |
||
| 109 | protected int $offset = -1; |
||
| 110 | |||
| 111 | /** |
||
| 112 | * The limit to use |
||
| 113 | * @var int |
||
| 114 | */ |
||
| 115 | protected int $limit = 0; |
||
| 116 | |||
| 117 | /** |
||
| 118 | * The filters list |
||
| 119 | * @var array<string, mixed> |
||
| 120 | */ |
||
| 121 | protected array $filters = []; |
||
| 122 | |||
| 123 | /** |
||
| 124 | * Create new instance |
||
| 125 | * @param EntityManager $manager |
||
| 126 | * @param class-string $entityClass |
||
| 127 | */ |
||
| 128 | public function __construct(EntityManager $manager, string $entityClass) |
||
| 129 | { |
||
| 130 | $this->manager = $manager; |
||
| 131 | $this->entityClass = $entityClass; |
||
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * {@inheritedoc} |
||
| 136 | */ |
||
| 137 | public function query($with = [], bool $immediate = false): EntityQuery |
||
| 138 | { |
||
| 139 | if (empty($with) && !empty($this->with)) { |
||
| 140 | $with = $this->with; |
||
| 141 | |||
| 142 | $this->with = []; |
||
| 143 | } |
||
| 144 | $this->immediate = false; |
||
| 145 | |||
| 146 | $query = $this->manager->query($this->entityClass); |
||
| 147 | $query->with($with, $immediate); |
||
| 148 | |||
| 149 | if (!empty($this->orderColumns)) { |
||
| 150 | $query->orderBy($this->orderColumns, $this->orderDir); |
||
| 151 | |||
| 152 | $this->orderColumns = ''; |
||
| 153 | $this->orderDir = 'ASC'; |
||
| 154 | } |
||
| 155 | |||
| 156 | if ($this->offset >= 0 && $this->limit >= 0) { |
||
| 157 | $query->offset($this->offset) |
||
| 158 | ->limit($this->limit); |
||
| 159 | |||
| 160 | $this->offset = -1; |
||
| 161 | $this->limit = 0; |
||
| 162 | } |
||
| 163 | |||
| 164 | if (!empty($this->filters)) { |
||
| 165 | $query->filter($this->filters); |
||
| 166 | |||
| 167 | $this->filters = []; |
||
| 168 | } |
||
| 169 | |||
| 170 | return $query; |
||
| 171 | } |
||
| 172 | |||
| 173 | /** |
||
| 174 | * {@inheritedoc} |
||
| 175 | */ |
||
| 176 | public function with($with, bool $immediate = false): self |
||
| 177 | { |
||
| 178 | if (!is_array($with)) { |
||
| 179 | $with = [$with]; |
||
| 180 | } |
||
| 181 | $this->with = $with; |
||
| 182 | $this->immediate = $immediate; |
||
| 183 | |||
| 184 | return $this; |
||
| 185 | } |
||
| 186 | |||
| 187 | /** |
||
| 188 | * {@inheritedoc} |
||
| 189 | */ |
||
| 190 | public function orderBy($columns, string $order = 'ASC'): self |
||
| 191 | { |
||
| 192 | $this->orderColumns = $columns; |
||
| 193 | $this->orderDir = $order; |
||
| 194 | |||
| 195 | return $this; |
||
| 196 | } |
||
| 197 | |||
| 198 | /** |
||
| 199 | * {@inheritedoc} |
||
| 200 | */ |
||
| 201 | public function limit(int $offset, int $limit): self |
||
| 202 | { |
||
| 203 | $this->offset = $offset; |
||
| 204 | $this->limit = $limit; |
||
| 205 | |||
| 206 | return $this; |
||
| 207 | } |
||
| 208 | |||
| 209 | /** |
||
| 210 | * {@inheritedoc} |
||
| 211 | */ |
||
| 212 | public function filters(array $filters = []): self |
||
| 213 | { |
||
| 214 | $this->filters = $filters; |
||
| 215 | |||
| 216 | return $this; |
||
| 217 | } |
||
| 218 | |||
| 219 | /** |
||
| 220 | * {@inheritedoc} |
||
| 221 | */ |
||
| 222 | public function all(array $columns = []): array |
||
| 223 | { |
||
| 224 | return $this->query()->all($columns); |
||
| 225 | } |
||
| 226 | |||
| 227 | /** |
||
| 228 | * {@inheritedoc} |
||
| 229 | */ |
||
| 230 | public function create(array $columns = []): Entity |
||
| 231 | { |
||
| 232 | $mapper = $this->manager->getEntityMapper($this->entityClass); |
||
| 233 | |||
| 234 | return new $this->entityClass( |
||
| 235 | $this->manager, |
||
| 236 | $mapper, |
||
| 237 | $columns, |
||
| 238 | [], |
||
| 239 | false, |
||
| 240 | true |
||
| 241 | ); |
||
| 242 | } |
||
| 243 | |||
| 244 | /** |
||
| 245 | * {@inheritedoc} |
||
| 246 | */ |
||
| 247 | public function find($id): ?Entity |
||
| 248 | { |
||
| 249 | return $this->query()->find($id); |
||
| 250 | } |
||
| 251 | |||
| 252 | /** |
||
| 253 | * {@inheritedoc} |
||
| 254 | */ |
||
| 255 | public function findBy(array $conditions): ?Entity |
||
| 256 | { |
||
| 257 | $query = $this->query(); |
||
| 258 | foreach ($conditions as $name => $value) { |
||
| 259 | $query->where($name)->is($value); |
||
| 260 | } |
||
| 261 | return $query->get(); |
||
| 262 | } |
||
| 263 | |||
| 264 | /** |
||
| 265 | * {@inheritedoc} |
||
| 266 | */ |
||
| 267 | public function findAll(...$ids): array |
||
| 268 | { |
||
| 269 | return $this->query()->findAll(...$ids); |
||
| 270 | } |
||
| 271 | |||
| 272 | /** |
||
| 273 | * {@inheritedoc} |
||
| 274 | */ |
||
| 275 | public function findAllBy(array $conditions): array |
||
| 282 | } |
||
| 283 | |||
| 284 | /** |
||
| 285 | * {@inheritedoc} |
||
| 286 | */ |
||
| 287 | public function save(Entity $entity): bool |
||
| 288 | { |
||
| 289 | $data = Proxy::instance()->getEntityDataMapper($entity); |
||
| 290 | |||
| 291 | if ($data->isNew()) { |
||
| 292 | return (bool) $this->insert($entity); |
||
| 293 | } |
||
| 294 | |||
| 295 | return $this->update($entity); |
||
| 296 | } |
||
| 297 | |||
| 298 | /** |
||
| 299 | * {@inheritedoc} |
||
| 300 | */ |
||
| 301 | public function insert(Entity $entity) |
||
| 360 | } |
||
| 361 | |||
| 362 | /** |
||
| 363 | * {@inheritedoc} |
||
| 364 | */ |
||
| 365 | public function update(Entity $entity): bool |
||
| 366 | { |
||
| 367 | $data = Proxy::instance()->getEntityDataMapper($entity); |
||
| 368 | $mapper = $data->getEntityMapper(); |
||
| 369 | $eventsHandlers = $mapper->getEventHandlers(); |
||
| 370 | |||
| 371 | if ($data->isDeleted()) { |
||
| 372 | throw new EntityStateException('The record was deleted'); |
||
| 373 | } |
||
| 374 | |||
| 375 | if ($data->isNew()) { |
||
| 376 | throw new EntityStateException('Can\'t update an unsaved entity'); |
||
| 377 | } |
||
| 378 | |||
| 379 | if (!$data->wasModified()) { |
||
| 380 | return true; |
||
| 381 | } |
||
| 382 | |||
| 383 | $modified = $data->getModifiedColumns(); |
||
| 384 | if (!empty($modified)) { |
||
| 385 | $connection = $this->manager->getConnection(); |
||
| 386 | $result = $connection->transaction(function (Connection $connection) use ($data, $mapper, $modified) { |
||
| 387 | $columns = array_intersect_key($data->getRawColumns(), array_flip($modified)); |
||
| 388 | |||
| 389 | $updatedAt = null; |
||
| 390 | |||
| 391 | if ($mapper->hasTimestamp()) { |
||
| 392 | list(, $updatedAtCol) = $mapper->getTimestampColumns(); |
||
| 393 | $columns[$updatedAtCol] = $updatedAt = date($this->manager->getDateFormat()); |
||
| 394 | } |
||
| 395 | $data->markAsUpdated($updatedAt); |
||
| 396 | |||
| 397 | $update = new Update($connection, $mapper->getTable()); |
||
| 398 | |||
| 399 | $primaryKeys = $mapper->getPrimaryKey()->getValue($data->getRawColumns(), true); |
||
| 400 | if (is_array($primaryKeys)) { |
||
| 401 | foreach ($primaryKeys as $pkColumn => $pkValue) { |
||
| 402 | $update->where($pkColumn)->is($pkValue); |
||
| 403 | } |
||
| 404 | } |
||
| 405 | |||
| 406 | return $update->set($columns) >= 0; |
||
| 407 | }); |
||
| 408 | |||
| 409 | if ($result === false) { |
||
| 410 | return false; |
||
| 411 | } |
||
| 412 | |||
| 413 | if (isset($eventsHandlers['update'])) { |
||
| 414 | foreach ($eventsHandlers['update'] as $callback) { |
||
| 415 | $callback($entity, $data); |
||
| 416 | } |
||
| 417 | } |
||
| 418 | |||
| 419 | return true; |
||
| 420 | } |
||
| 421 | |||
| 422 | $connection = $this->manager->getConnection(); |
||
| 423 | return $connection->transaction(function (Connection $connection) use ($data) { |
||
| 424 | $data->executePendingLinkage(); |
||
| 425 | |||
| 426 | return true; |
||
| 427 | }); |
||
| 428 | } |
||
| 429 | |||
| 430 | /** |
||
| 431 | * {@inheritedoc} |
||
| 432 | */ |
||
| 433 | public function delete(Entity $entity, bool $force = false): bool |
||
| 474 | } |
||
| 475 | } |
||
| 476 |