| Total Complexity | 133 |
| Total Lines | 1194 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Complex classes like BaseRepository 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 BaseRepository, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 34 | abstract class BaseRepository implements RepositoryInterface, RepositoryCriteriaInterface |
||
| 35 | { |
||
| 36 | use ComparesVersionsTrait; |
||
| 37 | |||
| 38 | /** |
||
| 39 | * @var Application |
||
| 40 | */ |
||
| 41 | protected $app; |
||
| 42 | |||
| 43 | /** |
||
| 44 | * @var Model |
||
| 45 | */ |
||
| 46 | protected $model; |
||
| 47 | |||
| 48 | /** |
||
| 49 | * @var array |
||
| 50 | */ |
||
| 51 | protected $fieldSearchable = []; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * @var PresenterInterface |
||
| 55 | */ |
||
| 56 | protected $presenter; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * @var ValidatorInterface |
||
| 60 | */ |
||
| 61 | protected $validator; |
||
| 62 | |||
| 63 | /** |
||
| 64 | * Validation Rules |
||
| 65 | * |
||
| 66 | * @var array |
||
| 67 | */ |
||
| 68 | protected $rules = null; |
||
| 69 | |||
| 70 | /** |
||
| 71 | * Collection of Criteria |
||
| 72 | * |
||
| 73 | * @var Collection |
||
| 74 | */ |
||
| 75 | protected $criteria; |
||
| 76 | |||
| 77 | /** |
||
| 78 | * @var bool |
||
| 79 | */ |
||
| 80 | protected $skipCriteria = false; |
||
| 81 | |||
| 82 | /** |
||
| 83 | * @var bool |
||
| 84 | */ |
||
| 85 | protected $skipPresenter = false; |
||
| 86 | |||
| 87 | /** |
||
| 88 | * @var \Closure |
||
| 89 | */ |
||
| 90 | protected $scopeQuery = null; |
||
| 91 | |||
| 92 | /** |
||
| 93 | * @param Application $app |
||
| 94 | */ |
||
| 95 | public function __construct(Application $app) |
||
| 96 | { |
||
| 97 | $this->app = $app; |
||
| 98 | $this->criteria = new Collection(); |
||
| 99 | $this->makeModel(); |
||
| 100 | $this->makePresenter(); |
||
| 101 | $this->makeValidator(); |
||
| 102 | $this->boot(); |
||
| 103 | } |
||
| 104 | |||
| 105 | /** |
||
| 106 | * |
||
| 107 | */ |
||
| 108 | public function boot() |
||
| 109 | { |
||
| 110 | // |
||
| 111 | } |
||
| 112 | |||
| 113 | /** |
||
| 114 | * Returns the current Model instance |
||
| 115 | * |
||
| 116 | * @return Model |
||
| 117 | */ |
||
| 118 | public function getModel() |
||
| 119 | { |
||
| 120 | return $this->model; |
||
| 121 | } |
||
| 122 | |||
| 123 | /** |
||
| 124 | * @throws RepositoryException |
||
| 125 | */ |
||
| 126 | public function resetModel() |
||
| 127 | { |
||
| 128 | $this->makeModel(); |
||
| 129 | } |
||
| 130 | |||
| 131 | /** |
||
| 132 | * Specify Model class name |
||
| 133 | * |
||
| 134 | * @return string |
||
| 135 | */ |
||
| 136 | abstract public function model(); |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Specify Presenter class name |
||
| 140 | * |
||
| 141 | * @return string |
||
| 142 | */ |
||
| 143 | public function presenter() |
||
| 144 | { |
||
| 145 | return null; |
||
| 146 | } |
||
| 147 | |||
| 148 | /** |
||
| 149 | * Specify Validator class name of Salah3id\Domains\Validator\Contracts\ValidatorInterface |
||
| 150 | * |
||
| 151 | * @return null |
||
| 152 | * @throws Exception |
||
| 153 | */ |
||
| 154 | public function validator() |
||
| 155 | { |
||
| 156 | if (isset($this->rules) && !is_null($this->rules) && is_array($this->rules) && !empty($this->rules)) { |
||
| 157 | if (class_exists('Salah3id\Domains\Validator\LaravelValidator')) { |
||
| 158 | $validator = app('Salah3id\Domains\Validator\LaravelValidator'); |
||
| 159 | if ($validator instanceof ValidatorInterface) { |
||
|
|
|||
| 160 | $validator->setRules($this->rules); |
||
| 161 | |||
| 162 | return $validator; |
||
| 163 | } |
||
| 164 | } else { |
||
| 165 | throw new Exception(trans('repository::packages.prettus_laravel_validation_required')); |
||
| 166 | } |
||
| 167 | } |
||
| 168 | |||
| 169 | return null; |
||
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Set Presenter |
||
| 174 | * |
||
| 175 | * @param $presenter |
||
| 176 | * |
||
| 177 | * @return $this |
||
| 178 | */ |
||
| 179 | public function setPresenter($presenter) |
||
| 180 | { |
||
| 181 | $this->makePresenter($presenter); |
||
| 182 | |||
| 183 | return $this; |
||
| 184 | } |
||
| 185 | |||
| 186 | /** |
||
| 187 | * @return Model |
||
| 188 | * @throws RepositoryException |
||
| 189 | */ |
||
| 190 | public function makeModel() |
||
| 191 | { |
||
| 192 | $model = $this->app->make($this->model()); |
||
| 193 | |||
| 194 | if (!$model instanceof Model) { |
||
| 195 | throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); |
||
| 196 | } |
||
| 197 | |||
| 198 | return $this->model = $model; |
||
| 199 | } |
||
| 200 | |||
| 201 | /** |
||
| 202 | * @param null $presenter |
||
| 203 | * |
||
| 204 | * @return PresenterInterface |
||
| 205 | * @throws RepositoryException |
||
| 206 | */ |
||
| 207 | public function makePresenter($presenter = null) |
||
| 208 | { |
||
| 209 | $presenter = !is_null($presenter) ? $presenter : $this->presenter(); |
||
| 210 | |||
| 211 | if (!is_null($presenter)) { |
||
| 212 | $this->presenter = is_string($presenter) ? $this->app->make($presenter) : $presenter; |
||
| 213 | |||
| 214 | if (!$this->presenter instanceof PresenterInterface) { |
||
| 215 | throw new RepositoryException("Class {$presenter} must be an instance of Salah3id\Domains\\Repository\\Contracts\\PresenterInterface"); |
||
| 216 | } |
||
| 217 | |||
| 218 | return $this->presenter; |
||
| 219 | } |
||
| 220 | |||
| 221 | return null; |
||
| 222 | } |
||
| 223 | |||
| 224 | /** |
||
| 225 | * @param null $validator |
||
| 226 | * |
||
| 227 | * @return null|ValidatorInterface |
||
| 228 | * @throws RepositoryException |
||
| 229 | */ |
||
| 230 | public function makeValidator($validator = null) |
||
| 231 | { |
||
| 232 | $validator = !is_null($validator) ? $validator : $this->validator(); |
||
| 233 | |||
| 234 | if (!is_null($validator)) { |
||
| 235 | $this->validator = is_string($validator) ? $this->app->make($validator) : $validator; |
||
| 236 | |||
| 237 | if (!$this->validator instanceof ValidatorInterface) { |
||
| 238 | throw new RepositoryException("Class {$validator} must be an instance of Salah3id\Domains\\Validator\\Contracts\\ValidatorInterface"); |
||
| 239 | } |
||
| 240 | |||
| 241 | return $this->validator; |
||
| 242 | } |
||
| 243 | |||
| 244 | return null; |
||
| 245 | } |
||
| 246 | |||
| 247 | /** |
||
| 248 | * Get Searchable Fields |
||
| 249 | * |
||
| 250 | * @return array |
||
| 251 | */ |
||
| 252 | public function getFieldsSearchable() |
||
| 253 | { |
||
| 254 | return $this->fieldSearchable; |
||
| 255 | } |
||
| 256 | |||
| 257 | /** |
||
| 258 | * Query Scope |
||
| 259 | * |
||
| 260 | * @param \Closure $scope |
||
| 261 | * |
||
| 262 | * @return $this |
||
| 263 | */ |
||
| 264 | public function scopeQuery(\Closure $scope) |
||
| 265 | { |
||
| 266 | $this->scopeQuery = $scope; |
||
| 267 | |||
| 268 | return $this; |
||
| 269 | } |
||
| 270 | |||
| 271 | /** |
||
| 272 | * Retrieve data array for populate field select |
||
| 273 | * |
||
| 274 | * @param string $column |
||
| 275 | * @param string|null $key |
||
| 276 | * |
||
| 277 | * @return \Illuminate\Support\Collection|array |
||
| 278 | */ |
||
| 279 | public function lists($column, $key = null) |
||
| 280 | { |
||
| 281 | $this->applyCriteria(); |
||
| 282 | |||
| 283 | return $this->model->lists($column, $key); |
||
| 284 | } |
||
| 285 | |||
| 286 | /** |
||
| 287 | * Retrieve data array for populate field select |
||
| 288 | * Compatible with Laravel 5.3 |
||
| 289 | * |
||
| 290 | * @param string $column |
||
| 291 | * @param string|null $key |
||
| 292 | * |
||
| 293 | * @return \Illuminate\Support\Collection|array |
||
| 294 | */ |
||
| 295 | public function pluck($column, $key = null) |
||
| 296 | { |
||
| 297 | $this->applyCriteria(); |
||
| 298 | |||
| 299 | return $this->model->pluck($column, $key); |
||
| 300 | } |
||
| 301 | |||
| 302 | /** |
||
| 303 | * Sync relations |
||
| 304 | * |
||
| 305 | * @param $id |
||
| 306 | * @param $relation |
||
| 307 | * @param $attributes |
||
| 308 | * @param bool $detaching |
||
| 309 | * |
||
| 310 | * @return mixed |
||
| 311 | */ |
||
| 312 | public function sync($id, $relation, $attributes, $detaching = true) |
||
| 313 | { |
||
| 314 | return $this->find($id)->{$relation}()->sync($attributes, $detaching); |
||
| 315 | } |
||
| 316 | |||
| 317 | /** |
||
| 318 | * SyncWithoutDetaching |
||
| 319 | * |
||
| 320 | * @param $id |
||
| 321 | * @param $relation |
||
| 322 | * @param $attributes |
||
| 323 | * |
||
| 324 | * @return mixed |
||
| 325 | */ |
||
| 326 | public function syncWithoutDetaching($id, $relation, $attributes) |
||
| 327 | { |
||
| 328 | return $this->sync($id, $relation, $attributes, false); |
||
| 329 | } |
||
| 330 | |||
| 331 | /** |
||
| 332 | * Retrieve all data of repository |
||
| 333 | * |
||
| 334 | * @param array $columns |
||
| 335 | * |
||
| 336 | * @return mixed |
||
| 337 | */ |
||
| 338 | public function all($columns = ['*']) |
||
| 339 | { |
||
| 340 | $this->applyCriteria(); |
||
| 341 | $this->applyScope(); |
||
| 342 | |||
| 343 | if ($this->model instanceof Builder) { |
||
| 344 | $results = $this->model->get($columns); |
||
| 345 | } else { |
||
| 346 | $results = $this->model->all($columns); |
||
| 347 | } |
||
| 348 | |||
| 349 | $this->resetModel(); |
||
| 350 | $this->resetScope(); |
||
| 351 | |||
| 352 | return $this->parserResult($results); |
||
| 353 | } |
||
| 354 | |||
| 355 | /** |
||
| 356 | * Count results of repository |
||
| 357 | * |
||
| 358 | * @param array $where |
||
| 359 | * @param string $columns |
||
| 360 | * |
||
| 361 | * @return int |
||
| 362 | */ |
||
| 363 | public function count(array $where = [], $columns = '*') |
||
| 364 | { |
||
| 365 | $this->applyCriteria(); |
||
| 366 | $this->applyScope(); |
||
| 367 | |||
| 368 | if ($where) { |
||
| 369 | $this->applyConditions($where); |
||
| 370 | } |
||
| 371 | |||
| 372 | $result = $this->model->count($columns); |
||
| 373 | |||
| 374 | $this->resetModel(); |
||
| 375 | $this->resetScope(); |
||
| 376 | |||
| 377 | return $result; |
||
| 378 | } |
||
| 379 | |||
| 380 | /** |
||
| 381 | * Alias of All method |
||
| 382 | * |
||
| 383 | * @param array $columns |
||
| 384 | * |
||
| 385 | * @return mixed |
||
| 386 | */ |
||
| 387 | public function get($columns = ['*']) |
||
| 388 | { |
||
| 389 | return $this->all($columns); |
||
| 390 | } |
||
| 391 | |||
| 392 | /** |
||
| 393 | * Retrieve first data of repository |
||
| 394 | * |
||
| 395 | * @param array $columns |
||
| 396 | * |
||
| 397 | * @return mixed |
||
| 398 | */ |
||
| 399 | public function first($columns = ['*']) |
||
| 400 | { |
||
| 401 | $this->applyCriteria(); |
||
| 402 | $this->applyScope(); |
||
| 403 | |||
| 404 | $results = $this->model->first($columns); |
||
| 405 | |||
| 406 | $this->resetModel(); |
||
| 407 | |||
| 408 | return $this->parserResult($results); |
||
| 409 | } |
||
| 410 | |||
| 411 | /** |
||
| 412 | * Retrieve first data of repository, or return new Entity |
||
| 413 | * |
||
| 414 | * @param array $attributes |
||
| 415 | * |
||
| 416 | * @return mixed |
||
| 417 | */ |
||
| 418 | public function firstOrNew(array $attributes = []) |
||
| 419 | { |
||
| 420 | $this->applyCriteria(); |
||
| 421 | $this->applyScope(); |
||
| 422 | |||
| 423 | $temporarySkipPresenter = $this->skipPresenter; |
||
| 424 | $this->skipPresenter(true); |
||
| 425 | |||
| 426 | $model = $this->model->firstOrNew($attributes); |
||
| 427 | $this->skipPresenter($temporarySkipPresenter); |
||
| 428 | |||
| 429 | $this->resetModel(); |
||
| 430 | |||
| 431 | return $this->parserResult($model); |
||
| 432 | } |
||
| 433 | |||
| 434 | /** |
||
| 435 | * Retrieve first data of repository, or create new Entity |
||
| 436 | * |
||
| 437 | * @param array $attributes |
||
| 438 | * |
||
| 439 | * @return mixed |
||
| 440 | */ |
||
| 441 | public function firstOrCreate(array $attributes = []) |
||
| 442 | { |
||
| 443 | $this->applyCriteria(); |
||
| 444 | $this->applyScope(); |
||
| 445 | |||
| 446 | $temporarySkipPresenter = $this->skipPresenter; |
||
| 447 | $this->skipPresenter(true); |
||
| 448 | |||
| 449 | $model = $this->model->firstOrCreate($attributes); |
||
| 450 | $this->skipPresenter($temporarySkipPresenter); |
||
| 451 | |||
| 452 | $this->resetModel(); |
||
| 453 | |||
| 454 | return $this->parserResult($model); |
||
| 455 | } |
||
| 456 | |||
| 457 | /** |
||
| 458 | * Retrieve data of repository with limit applied |
||
| 459 | * |
||
| 460 | * @param int $limit |
||
| 461 | * @param array $columns |
||
| 462 | * |
||
| 463 | * @return mixed |
||
| 464 | */ |
||
| 465 | public function limit($limit, $columns = ['*']) |
||
| 466 | { |
||
| 467 | // Shortcut to all with `limit` applied on query via `take` |
||
| 468 | $this->take($limit); |
||
| 469 | |||
| 470 | return $this->all($columns); |
||
| 471 | } |
||
| 472 | |||
| 473 | /** |
||
| 474 | * Retrieve all data of repository, paginated |
||
| 475 | * |
||
| 476 | * @param null|int $limit |
||
| 477 | * @param array $columns |
||
| 478 | * @param string $method |
||
| 479 | * |
||
| 480 | * @return mixed |
||
| 481 | */ |
||
| 482 | public function paginate($limit = null, $columns = ['*'], $method = "paginate") |
||
| 483 | { |
||
| 484 | $this->applyCriteria(); |
||
| 485 | $this->applyScope(); |
||
| 486 | $limit = is_null($limit) ? config('domains.pagination.limit', 15) : $limit; |
||
| 487 | $results = $this->model->{$method}($limit, $columns); |
||
| 488 | $results->appends(app('request')->query()); |
||
| 489 | $this->resetModel(); |
||
| 490 | |||
| 491 | return $this->parserResult($results); |
||
| 492 | } |
||
| 493 | |||
| 494 | /** |
||
| 495 | * Retrieve all data of repository, simple paginated |
||
| 496 | * |
||
| 497 | * @param null|int $limit |
||
| 498 | * @param array $columns |
||
| 499 | * |
||
| 500 | * @return mixed |
||
| 501 | */ |
||
| 502 | public function simplePaginate($limit = null, $columns = ['*']) |
||
| 503 | { |
||
| 504 | return $this->paginate($limit, $columns, "simplePaginate"); |
||
| 505 | } |
||
| 506 | |||
| 507 | /** |
||
| 508 | * Find data by id |
||
| 509 | * |
||
| 510 | * @param $id |
||
| 511 | * @param array $columns |
||
| 512 | * |
||
| 513 | * @return mixed |
||
| 514 | */ |
||
| 515 | public function find($id, $columns = ['*']) |
||
| 516 | { |
||
| 517 | $this->applyCriteria(); |
||
| 518 | $this->applyScope(); |
||
| 519 | $model = $this->model->findOrFail($id, $columns); |
||
| 520 | $this->resetModel(); |
||
| 521 | |||
| 522 | return $this->parserResult($model); |
||
| 523 | } |
||
| 524 | |||
| 525 | /** |
||
| 526 | * Find data by field and value |
||
| 527 | * |
||
| 528 | * @param $field |
||
| 529 | * @param $value |
||
| 530 | * @param array $columns |
||
| 531 | * |
||
| 532 | * @return mixed |
||
| 533 | */ |
||
| 534 | public function findByField($field, $value = null, $columns = ['*']) |
||
| 535 | { |
||
| 536 | $this->applyCriteria(); |
||
| 537 | $this->applyScope(); |
||
| 538 | $model = $this->model->where($field, '=', $value)->get($columns); |
||
| 539 | $this->resetModel(); |
||
| 540 | |||
| 541 | return $this->parserResult($model); |
||
| 542 | } |
||
| 543 | |||
| 544 | /** |
||
| 545 | * Find data by multiple fields |
||
| 546 | * |
||
| 547 | * @param array $where |
||
| 548 | * @param array $columns |
||
| 549 | * |
||
| 550 | * @return mixed |
||
| 551 | */ |
||
| 552 | public function findWhere(array $where, $columns = ['*']) |
||
| 553 | { |
||
| 554 | $this->applyCriteria(); |
||
| 555 | $this->applyScope(); |
||
| 556 | |||
| 557 | $this->applyConditions($where); |
||
| 558 | |||
| 559 | $model = $this->model->get($columns); |
||
| 560 | $this->resetModel(); |
||
| 561 | |||
| 562 | return $this->parserResult($model); |
||
| 563 | } |
||
| 564 | |||
| 565 | /** |
||
| 566 | * Find data by multiple values in one field |
||
| 567 | * |
||
| 568 | * @param $field |
||
| 569 | * @param array $values |
||
| 570 | * @param array $columns |
||
| 571 | * |
||
| 572 | * @return mixed |
||
| 573 | */ |
||
| 574 | public function findWhereIn($field, array $values, $columns = ['*']) |
||
| 575 | { |
||
| 576 | $this->applyCriteria(); |
||
| 577 | $this->applyScope(); |
||
| 578 | $model = $this->model->whereIn($field, $values)->get($columns); |
||
| 579 | $this->resetModel(); |
||
| 580 | |||
| 581 | return $this->parserResult($model); |
||
| 582 | } |
||
| 583 | |||
| 584 | /** |
||
| 585 | * Find data by excluding multiple values in one field |
||
| 586 | * |
||
| 587 | * @param $field |
||
| 588 | * @param array $values |
||
| 589 | * @param array $columns |
||
| 590 | * |
||
| 591 | * @return mixed |
||
| 592 | */ |
||
| 593 | public function findWhereNotIn($field, array $values, $columns = ['*']) |
||
| 594 | { |
||
| 595 | $this->applyCriteria(); |
||
| 596 | $this->applyScope(); |
||
| 597 | $model = $this->model->whereNotIn($field, $values)->get($columns); |
||
| 598 | $this->resetModel(); |
||
| 599 | |||
| 600 | return $this->parserResult($model); |
||
| 601 | } |
||
| 602 | |||
| 603 | /** |
||
| 604 | * Find data by between values in one field |
||
| 605 | * |
||
| 606 | * @param $field |
||
| 607 | * @param array $values |
||
| 608 | * @param array $columns |
||
| 609 | * |
||
| 610 | * @return mixed |
||
| 611 | */ |
||
| 612 | public function findWhereBetween($field, array $values, $columns = ['*']) |
||
| 613 | { |
||
| 614 | $this->applyCriteria(); |
||
| 615 | $this->applyScope(); |
||
| 616 | $model = $this->model->whereBetween($field, $values)->get($columns); |
||
| 617 | $this->resetModel(); |
||
| 618 | |||
| 619 | return $this->parserResult($model); |
||
| 620 | } |
||
| 621 | |||
| 622 | /** |
||
| 623 | * Save a new entity in repository |
||
| 624 | * |
||
| 625 | * @param array $attributes |
||
| 626 | * |
||
| 627 | * @return mixed |
||
| 628 | * @throws ValidatorException |
||
| 629 | * |
||
| 630 | */ |
||
| 631 | public function create(array $attributes) |
||
| 632 | { |
||
| 633 | if (!is_null($this->validator)) { |
||
| 634 | // we should pass data that has been casts by the model |
||
| 635 | // to make sure data type are same because validator may need to use |
||
| 636 | // this data to compare with data that fetch from database. |
||
| 637 | if ($this->versionCompare($this->app->version(), "5.2.*", ">")) { |
||
| 638 | $attributes = $this->model->newInstance()->forceFill($attributes)->makeVisible($this->model->getHidden())->toArray(); |
||
| 639 | } else { |
||
| 640 | $model = $this->model->newInstance()->forceFill($attributes); |
||
| 641 | $model->makeVisible($this->model->getHidden()); |
||
| 642 | $attributes = $model->toArray(); |
||
| 643 | } |
||
| 644 | |||
| 645 | $this->validator->with($attributes)->passesOrFail(ValidatorInterface::RULE_CREATE); |
||
| 646 | } |
||
| 647 | |||
| 648 | event(new RepositoryEntityCreating($this, $attributes)); |
||
| 649 | |||
| 650 | $model = $this->model->newInstance($attributes); |
||
| 651 | $model->save(); |
||
| 652 | $this->resetModel(); |
||
| 653 | |||
| 654 | event(new RepositoryEntityCreated($this, $model)); |
||
| 655 | |||
| 656 | return $this->parserResult($model); |
||
| 657 | } |
||
| 658 | |||
| 659 | /** |
||
| 660 | * Update a entity in repository by id |
||
| 661 | * |
||
| 662 | * @param array $attributes |
||
| 663 | * @param $id |
||
| 664 | * |
||
| 665 | * @return mixed |
||
| 666 | * @throws ValidatorException |
||
| 667 | * |
||
| 668 | */ |
||
| 669 | public function update(array $attributes, $id) |
||
| 670 | { |
||
| 671 | $this->applyScope(); |
||
| 672 | |||
| 673 | if (!is_null($this->validator)) { |
||
| 674 | // we should pass data that has been casts by the model |
||
| 675 | // to make sure data type are same because validator may need to use |
||
| 676 | // this data to compare with data that fetch from database. |
||
| 677 | $model = $this->model->newInstance(); |
||
| 678 | $model->setRawAttributes([]); |
||
| 679 | $model->setAppends([]); |
||
| 680 | if ($this->versionCompare($this->app->version(), "5.2.*", ">")) { |
||
| 681 | $attributes = $model->forceFill($attributes)->makeVisible($this->model->getHidden())->toArray(); |
||
| 682 | } else { |
||
| 683 | $model->forceFill($attributes); |
||
| 684 | $model->makeVisible($this->model->getHidden()); |
||
| 685 | $attributes = $model->toArray(); |
||
| 686 | } |
||
| 687 | |||
| 688 | $this->validator->with($attributes)->setId($id)->passesOrFail(ValidatorInterface::RULE_UPDATE); |
||
| 689 | } |
||
| 690 | |||
| 691 | $temporarySkipPresenter = $this->skipPresenter; |
||
| 692 | |||
| 693 | $this->skipPresenter(true); |
||
| 694 | |||
| 695 | $model = $this->model->findOrFail($id); |
||
| 696 | |||
| 697 | event(new RepositoryEntityUpdating($this, $model)); |
||
| 698 | |||
| 699 | $model->fill($attributes); |
||
| 700 | $model->save(); |
||
| 701 | |||
| 702 | $this->skipPresenter($temporarySkipPresenter); |
||
| 703 | $this->resetModel(); |
||
| 704 | |||
| 705 | event(new RepositoryEntityUpdated($this, $model)); |
||
| 706 | |||
| 707 | return $this->parserResult($model); |
||
| 708 | } |
||
| 709 | |||
| 710 | /** |
||
| 711 | * Update or Create an entity in repository |
||
| 712 | * |
||
| 713 | * @param array $attributes |
||
| 714 | * @param array $values |
||
| 715 | * |
||
| 716 | * @return mixed |
||
| 717 | * @throws ValidatorException |
||
| 718 | * |
||
| 719 | */ |
||
| 720 | public function updateOrCreate(array $attributes, array $values = []) |
||
| 721 | { |
||
| 722 | $this->applyScope(); |
||
| 723 | |||
| 724 | if (!is_null($this->validator)) { |
||
| 725 | $this->validator->with(array_merge($attributes, $values))->passesOrFail(ValidatorInterface::RULE_CREATE); |
||
| 726 | } |
||
| 727 | |||
| 728 | $temporarySkipPresenter = $this->skipPresenter; |
||
| 729 | |||
| 730 | $this->skipPresenter(true); |
||
| 731 | |||
| 732 | event(new RepositoryEntityCreating($this, $attributes)); |
||
| 733 | |||
| 734 | $model = $this->model->updateOrCreate($attributes, $values); |
||
| 735 | |||
| 736 | $this->skipPresenter($temporarySkipPresenter); |
||
| 737 | $this->resetModel(); |
||
| 738 | |||
| 739 | event(new RepositoryEntityUpdated($this, $model)); |
||
| 740 | |||
| 741 | return $this->parserResult($model); |
||
| 742 | } |
||
| 743 | |||
| 744 | /** |
||
| 745 | * Delete a entity in repository by id |
||
| 746 | * |
||
| 747 | * @param $id |
||
| 748 | * |
||
| 749 | * @return int |
||
| 750 | */ |
||
| 751 | public function delete($id) |
||
| 752 | { |
||
| 753 | $this->applyScope(); |
||
| 754 | |||
| 755 | $temporarySkipPresenter = $this->skipPresenter; |
||
| 756 | $this->skipPresenter(true); |
||
| 757 | |||
| 758 | $model = $this->find($id); |
||
| 759 | $originalModel = clone $model; |
||
| 760 | |||
| 761 | $this->skipPresenter($temporarySkipPresenter); |
||
| 762 | $this->resetModel(); |
||
| 763 | |||
| 764 | event(new RepositoryEntityDeleting($this, $model)); |
||
| 765 | |||
| 766 | $deleted = $model->delete(); |
||
| 767 | |||
| 768 | event(new RepositoryEntityDeleted($this, $originalModel)); |
||
| 769 | |||
| 770 | return $deleted; |
||
| 771 | } |
||
| 772 | |||
| 773 | /** |
||
| 774 | * Delete multiple entities by given criteria. |
||
| 775 | * |
||
| 776 | * @param array $where |
||
| 777 | * |
||
| 778 | * @return int |
||
| 779 | */ |
||
| 780 | public function deleteWhere(array $where) |
||
| 781 | { |
||
| 782 | $this->applyScope(); |
||
| 783 | |||
| 784 | $temporarySkipPresenter = $this->skipPresenter; |
||
| 785 | $this->skipPresenter(true); |
||
| 786 | |||
| 787 | $this->applyConditions($where); |
||
| 788 | |||
| 789 | event(new RepositoryEntityDeleting($this, $this->model->getModel())); |
||
| 790 | |||
| 791 | $deleted = $this->model->delete(); |
||
| 792 | |||
| 793 | event(new RepositoryEntityDeleted($this, $this->model->getModel())); |
||
| 794 | |||
| 795 | $this->skipPresenter($temporarySkipPresenter); |
||
| 796 | $this->resetModel(); |
||
| 797 | |||
| 798 | return $deleted; |
||
| 799 | } |
||
| 800 | |||
| 801 | /** |
||
| 802 | * Check if entity has relation |
||
| 803 | * |
||
| 804 | * @param string $relation |
||
| 805 | * |
||
| 806 | * @return $this |
||
| 807 | */ |
||
| 808 | public function has($relation) |
||
| 809 | { |
||
| 810 | $this->model = $this->model->has($relation); |
||
| 811 | |||
| 812 | return $this; |
||
| 813 | } |
||
| 814 | |||
| 815 | /** |
||
| 816 | * Load relations |
||
| 817 | * |
||
| 818 | * @param array|string $relations |
||
| 819 | * |
||
| 820 | * @return $this |
||
| 821 | */ |
||
| 822 | public function with($relations) |
||
| 823 | { |
||
| 824 | $this->model = $this->model->with($relations); |
||
| 825 | |||
| 826 | return $this; |
||
| 827 | } |
||
| 828 | |||
| 829 | /** |
||
| 830 | * Add subselect queries to count the relations. |
||
| 831 | * |
||
| 832 | * @param mixed $relations |
||
| 833 | * |
||
| 834 | * @return $this |
||
| 835 | */ |
||
| 836 | public function withCount($relations) |
||
| 837 | { |
||
| 838 | $this->model = $this->model->withCount($relations); |
||
| 839 | return $this; |
||
| 840 | } |
||
| 841 | |||
| 842 | /** |
||
| 843 | * Load relation with closure |
||
| 844 | * |
||
| 845 | * @param string $relation |
||
| 846 | * @param closure $closure |
||
| 847 | * |
||
| 848 | * @return $this |
||
| 849 | */ |
||
| 850 | public function whereHas($relation, $closure) |
||
| 851 | { |
||
| 852 | $this->model = $this->model->whereHas($relation, $closure); |
||
| 853 | |||
| 854 | return $this; |
||
| 855 | } |
||
| 856 | |||
| 857 | /** |
||
| 858 | * Set hidden fields |
||
| 859 | * |
||
| 860 | * @param array $fields |
||
| 861 | * |
||
| 862 | * @return $this |
||
| 863 | */ |
||
| 864 | public function hidden(array $fields) |
||
| 865 | { |
||
| 866 | $this->model->setHidden($fields); |
||
| 867 | |||
| 868 | return $this; |
||
| 869 | } |
||
| 870 | |||
| 871 | /** |
||
| 872 | * Set the "orderBy" value of the query. |
||
| 873 | * |
||
| 874 | * @param mixed $column |
||
| 875 | * @param string $direction |
||
| 876 | * |
||
| 877 | * @return $this |
||
| 878 | */ |
||
| 879 | public function orderBy($column, $direction = 'asc') |
||
| 880 | { |
||
| 881 | $this->model = $this->model->orderBy($column, $direction); |
||
| 882 | |||
| 883 | return $this; |
||
| 884 | } |
||
| 885 | |||
| 886 | /** |
||
| 887 | * Set the "limit" value of the query. |
||
| 888 | * |
||
| 889 | * @param int $limit |
||
| 890 | * |
||
| 891 | * @return $this |
||
| 892 | */ |
||
| 893 | public function take($limit) |
||
| 894 | { |
||
| 895 | // Internally `take` is an alias to `limit` |
||
| 896 | $this->model = $this->model->limit($limit); |
||
| 897 | |||
| 898 | return $this; |
||
| 899 | } |
||
| 900 | |||
| 901 | /** |
||
| 902 | * Set visible fields |
||
| 903 | * |
||
| 904 | * @param array $fields |
||
| 905 | * |
||
| 906 | * @return $this |
||
| 907 | */ |
||
| 908 | public function visible(array $fields) |
||
| 909 | { |
||
| 910 | $this->model->setVisible($fields); |
||
| 911 | |||
| 912 | return $this; |
||
| 913 | } |
||
| 914 | |||
| 915 | /** |
||
| 916 | * Push Criteria for filter the query |
||
| 917 | * |
||
| 918 | * @param $criteria |
||
| 919 | * |
||
| 920 | * @return $this |
||
| 921 | * @throws \Salah3id\Domains\Repository\Exceptions\RepositoryException |
||
| 922 | */ |
||
| 923 | public function pushCriteria($criteria) |
||
| 924 | { |
||
| 925 | if (is_string($criteria)) { |
||
| 926 | $criteria = new $criteria; |
||
| 927 | } |
||
| 928 | if (!$criteria instanceof CriteriaInterface) { |
||
| 929 | throw new RepositoryException("Class " . get_class($criteria) . " must be an instance of Salah3id\Domains\\Repository\\Contracts\\CriteriaInterface"); |
||
| 930 | } |
||
| 931 | $this->criteria->push($criteria); |
||
| 932 | |||
| 933 | return $this; |
||
| 934 | } |
||
| 935 | |||
| 936 | /** |
||
| 937 | * Pop Criteria |
||
| 938 | * |
||
| 939 | * @param $criteria |
||
| 940 | * |
||
| 941 | * @return $this |
||
| 942 | */ |
||
| 943 | public function popCriteria($criteria) |
||
| 944 | { |
||
| 945 | $this->criteria = $this->criteria->reject(function ($item) use ($criteria) { |
||
| 946 | if (is_object($item) && is_string($criteria)) { |
||
| 947 | return get_class($item) === $criteria; |
||
| 948 | } |
||
| 949 | |||
| 950 | if (is_string($item) && is_object($criteria)) { |
||
| 951 | return $item === get_class($criteria); |
||
| 952 | } |
||
| 953 | |||
| 954 | return get_class($item) === get_class($criteria); |
||
| 955 | }); |
||
| 956 | |||
| 957 | return $this; |
||
| 958 | } |
||
| 959 | |||
| 960 | /** |
||
| 961 | * Get Collection of Criteria |
||
| 962 | * |
||
| 963 | * @return Collection |
||
| 964 | */ |
||
| 965 | public function getCriteria() |
||
| 968 | } |
||
| 969 | |||
| 970 | /** |
||
| 971 | * Find data by Criteria |
||
| 972 | * |
||
| 973 | * @param CriteriaInterface $criteria |
||
| 974 | * |
||
| 975 | * @return mixed |
||
| 976 | */ |
||
| 977 | public function getByCriteria(CriteriaInterface $criteria) |
||
| 978 | { |
||
| 979 | $this->model = $criteria->apply($this->model, $this); |
||
| 980 | $results = $this->model->get(); |
||
| 981 | $this->resetModel(); |
||
| 982 | |||
| 983 | return $this->parserResult($results); |
||
| 984 | } |
||
| 985 | |||
| 986 | /** |
||
| 987 | * Skip Criteria |
||
| 988 | * |
||
| 989 | * @param bool $status |
||
| 990 | * |
||
| 991 | * @return $this |
||
| 992 | */ |
||
| 993 | public function skipCriteria($status = true) |
||
| 994 | { |
||
| 995 | $this->skipCriteria = $status; |
||
| 996 | |||
| 997 | return $this; |
||
| 998 | } |
||
| 999 | |||
| 1000 | /** |
||
| 1001 | * Reset all Criterias |
||
| 1002 | * |
||
| 1003 | * @return $this |
||
| 1004 | */ |
||
| 1005 | public function resetCriteria() |
||
| 1006 | { |
||
| 1007 | $this->criteria = new Collection(); |
||
| 1008 | |||
| 1009 | return $this; |
||
| 1010 | } |
||
| 1011 | |||
| 1012 | /** |
||
| 1013 | * Reset Query Scope |
||
| 1014 | * |
||
| 1015 | * @return $this |
||
| 1016 | */ |
||
| 1017 | public function resetScope() |
||
| 1018 | { |
||
| 1019 | $this->scopeQuery = null; |
||
| 1020 | |||
| 1021 | return $this; |
||
| 1022 | } |
||
| 1023 | |||
| 1024 | /** |
||
| 1025 | * Apply scope in current Query |
||
| 1026 | * |
||
| 1027 | * @return $this |
||
| 1028 | */ |
||
| 1029 | protected function applyScope() |
||
| 1030 | { |
||
| 1031 | if (isset($this->scopeQuery) && is_callable($this->scopeQuery)) { |
||
| 1032 | $callback = $this->scopeQuery; |
||
| 1033 | $this->model = $callback($this->model); |
||
| 1034 | } |
||
| 1035 | |||
| 1036 | return $this; |
||
| 1037 | } |
||
| 1038 | |||
| 1039 | /** |
||
| 1040 | * Apply criteria in current Query |
||
| 1041 | * |
||
| 1042 | * @return $this |
||
| 1043 | */ |
||
| 1044 | protected function applyCriteria() |
||
| 1045 | { |
||
| 1046 | if ($this->skipCriteria === true) { |
||
| 1047 | return $this; |
||
| 1048 | } |
||
| 1049 | |||
| 1050 | $criteria = $this->getCriteria(); |
||
| 1051 | |||
| 1052 | if ($criteria) { |
||
| 1053 | foreach ($criteria as $c) { |
||
| 1054 | if ($c instanceof CriteriaInterface) { |
||
| 1055 | $this->model = $c->apply($this->model, $this); |
||
| 1056 | } |
||
| 1057 | } |
||
| 1058 | } |
||
| 1059 | |||
| 1060 | return $this; |
||
| 1061 | } |
||
| 1062 | |||
| 1063 | /** |
||
| 1064 | * Applies the given where conditions to the model. |
||
| 1065 | * |
||
| 1066 | * @param array $where |
||
| 1067 | * |
||
| 1068 | * @return void |
||
| 1069 | */ |
||
| 1070 | protected function applyConditions(array $where) |
||
| 1071 | { |
||
| 1072 | foreach ($where as $field => $value) { |
||
| 1073 | if (is_array($value)) { |
||
| 1074 | list($field, $condition, $val) = $value; |
||
| 1075 | //smooth input |
||
| 1076 | $condition = preg_replace('/\s\s+/', ' ', trim($condition)); |
||
| 1077 | |||
| 1078 | //split to get operator, syntax: "DATE >", "DATE =", "DAY <" |
||
| 1079 | $operator = explode(' ', $condition); |
||
| 1080 | if (count($operator) > 1) { |
||
| 1081 | $condition = $operator[0]; |
||
| 1082 | $operator = $operator[1]; |
||
| 1083 | } else $operator = null; |
||
| 1084 | switch (strtoupper($condition)) { |
||
| 1085 | case 'IN': |
||
| 1086 | if (!is_array($val)) throw new RepositoryException("Input {$val} mus be an array"); |
||
| 1087 | $this->model = $this->model->whereIn($field, $val); |
||
| 1088 | break; |
||
| 1089 | case 'NOTIN': |
||
| 1090 | if (!is_array($val)) throw new RepositoryException("Input {$val} mus be an array"); |
||
| 1091 | $this->model = $this->model->whereNotIn($field, $val); |
||
| 1092 | break; |
||
| 1093 | case 'DATE': |
||
| 1094 | if (!$operator) $operator = '='; |
||
| 1095 | $this->model = $this->model->whereDate($field, $operator, $val); |
||
| 1096 | break; |
||
| 1097 | case 'DAY': |
||
| 1098 | if (!$operator) $operator = '='; |
||
| 1099 | $this->model = $this->model->whereDay($field, $operator, $val); |
||
| 1100 | break; |
||
| 1101 | case 'MONTH': |
||
| 1102 | if (!$operator) $operator = '='; |
||
| 1103 | $this->model = $this->model->whereMonth($field, $operator, $val); |
||
| 1104 | break; |
||
| 1105 | case 'YEAR': |
||
| 1106 | if (!$operator) $operator = '='; |
||
| 1107 | $this->model = $this->model->whereYear($field, $operator, $val); |
||
| 1108 | break; |
||
| 1109 | case 'EXISTS': |
||
| 1110 | if (!($val instanceof Closure)) throw new RepositoryException("Input {$val} must be closure function"); |
||
| 1111 | $this->model = $this->model->whereExists($val); |
||
| 1112 | break; |
||
| 1113 | case 'HAS': |
||
| 1114 | if (!($val instanceof Closure)) throw new RepositoryException("Input {$val} must be closure function"); |
||
| 1115 | $this->model = $this->model->whereHas($field, $val); |
||
| 1116 | break; |
||
| 1117 | case 'HASMORPH': |
||
| 1118 | if (!($val instanceof Closure)) throw new RepositoryException("Input {$val} must be closure function"); |
||
| 1119 | $this->model = $this->model->whereHasMorph($field, $val); |
||
| 1120 | break; |
||
| 1121 | case 'DOESNTHAVE': |
||
| 1122 | if (!($val instanceof Closure)) throw new RepositoryException("Input {$val} must be closure function"); |
||
| 1123 | $this->model = $this->model->whereDoesntHave($field, $val); |
||
| 1124 | break; |
||
| 1125 | case 'DOESNTHAVEMORPH': |
||
| 1126 | if (!($val instanceof Closure)) throw new RepositoryException("Input {$val} must be closure function"); |
||
| 1127 | $this->model = $this->model->whereDoesntHaveMorph($field, $val); |
||
| 1128 | break; |
||
| 1129 | case 'BETWEEN': |
||
| 1130 | if (!is_array($val)) throw new RepositoryException("Input {$val} mus be an array"); |
||
| 1131 | $this->model = $this->model->whereBetween($field, $val); |
||
| 1132 | break; |
||
| 1133 | case 'BETWEENCOLUMNS': |
||
| 1134 | if (!is_array($val)) throw new RepositoryException("Input {$val} mus be an array"); |
||
| 1135 | $this->model = $this->model->whereBetweenColumns($field, $val); |
||
| 1136 | break; |
||
| 1137 | case 'NOTBETWEEN': |
||
| 1138 | if (!is_array($val)) throw new RepositoryException("Input {$val} mus be an array"); |
||
| 1139 | $this->model = $this->model->whereNotBetween($field, $val); |
||
| 1140 | break; |
||
| 1141 | case 'NOTBETWEENCOLUMNS': |
||
| 1142 | if (!is_array($val)) throw new RepositoryException("Input {$val} mus be an array"); |
||
| 1143 | $this->model = $this->model->whereNotBetweenColumns($field, $val); |
||
| 1144 | break; |
||
| 1145 | case 'RAW': |
||
| 1146 | $this->model = $this->model->whereRaw($val); |
||
| 1147 | break; |
||
| 1148 | default: |
||
| 1149 | $this->model = $this->model->where($field, $condition, $val); |
||
| 1150 | } |
||
| 1151 | } else { |
||
| 1152 | $this->model = $this->model->where($field, '=', $value); |
||
| 1153 | } |
||
| 1154 | } |
||
| 1155 | } |
||
| 1156 | |||
| 1157 | /** |
||
| 1158 | * Skip Presenter Wrapper |
||
| 1159 | * |
||
| 1160 | * @param bool $status |
||
| 1161 | * |
||
| 1162 | * @return $this |
||
| 1163 | */ |
||
| 1164 | public function skipPresenter($status = true) |
||
| 1165 | { |
||
| 1166 | $this->skipPresenter = $status; |
||
| 1167 | |||
| 1168 | return $this; |
||
| 1169 | } |
||
| 1170 | |||
| 1171 | /** |
||
| 1172 | * Wrapper result data |
||
| 1173 | * |
||
| 1174 | * @param mixed $result |
||
| 1175 | * |
||
| 1176 | * @return mixed |
||
| 1177 | */ |
||
| 1178 | public function parserResult($result) |
||
| 1179 | { |
||
| 1180 | if ($this->presenter instanceof PresenterInterface) { |
||
| 1181 | if ($result instanceof Collection || $result instanceof LengthAwarePaginator) { |
||
| 1182 | $result->each(function ($model) { |
||
| 1183 | if ($model instanceof Presentable) { |
||
| 1184 | $model->setPresenter($this->presenter); |
||
| 1185 | } |
||
| 1186 | |||
| 1187 | return $model; |
||
| 1188 | }); |
||
| 1189 | } else if ($result instanceof Presentable) { |
||
| 1190 | $result = $result->setPresenter($this->presenter); |
||
| 1191 | } |
||
| 1192 | |||
| 1193 | if (!$this->skipPresenter) { |
||
| 1194 | return $this->presenter->present($result); |
||
| 1195 | } |
||
| 1196 | } |
||
| 1197 | |||
| 1198 | return $result; |
||
| 1199 | } |
||
| 1200 | |||
| 1201 | /** |
||
| 1202 | * Trigger static method calls to the model |
||
| 1203 | * |
||
| 1204 | * @param $method |
||
| 1205 | * @param $arguments |
||
| 1206 | * |
||
| 1207 | * @return mixed |
||
| 1208 | */ |
||
| 1209 | public static function __callStatic($method, $arguments) |
||
| 1210 | { |
||
| 1211 | return call_user_func_array([new static(), $method], $arguments); |
||
| 1212 | } |
||
| 1213 | |||
| 1214 | /** |
||
| 1215 | * Trigger method calls to the model |
||
| 1216 | * |
||
| 1217 | * @param string $method |
||
| 1218 | * @param array $arguments |
||
| 1219 | * |
||
| 1220 | * @return mixed |
||
| 1221 | */ |
||
| 1222 | public function __call($method, $arguments) |
||
| 1228 | } |
||
| 1229 | } |
||
| 1230 |