%PDF- %PDF-
Direktori : /var/www/html/news/vendor/czim/laravel-repository/src/ |
Current File : /var/www/html/news/vendor/czim/laravel-repository/src/BaseRepository.php |
<?php namespace Czim\Repository; use Czim\Repository\Contracts\BaseRepositoryInterface; use Czim\Repository\Contracts\CriteriaInterface; use Czim\Repository\Criteria\NullCriteria; use Czim\Repository\Exceptions\RepositoryException; use Closure; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Query\Builder as DatabaseBuilder; use Illuminate\Support\Collection; use Illuminate\Container\Container as App; use InvalidArgumentException; /** * Class BaseRepository * * Basic repository for retrieving and manipulating Eloquent models. * * One of the main differences with Bosnadev's repository is that With this, * criteria may be given a key identifier, by which they may later be removed * or overriden. This way you can, for instance, set a default criterion for * ordering by a certain column, but in other cases, without reinstantiating, order * by other columns, by marking the Criteria that does the ordering with key 'order'. * * implements Contracts\RepositoryInterface, Contracts\RepositoryCriteriaInterface */ abstract class BaseRepository implements BaseRepositoryInterface { /** * @var App */ protected $app; /** * @var Model|EloquentBuilder */ protected $model; /** * Criteria to keep and use for all coming queries * * @var Collection */ protected $criteria; /** * The Criteria to only apply to the next query * * @var Collection */ protected $onceCriteria; /** * List of criteria that are currently active (updates when criteria are stripped) * So this is a dynamic list that can change during calls of various repository * methods that alter the active criteria. * * @var array */ protected $activeCriteria = null; /** * Whether to skip ALL criteria * * @var bool */ protected $ignoreCriteria = false; /** * Default number of paginated items * * @var integer */ protected $perPage = 1; /** * @param App $app * @param Collection $collection * @throws RepositoryException */ public function __construct(App $app, Collection $collection) { if ($collection->isEmpty()) { $collection = $this->defaultCriteria(); } $this->app = $app; $this->criteria = $collection; $this->onceCriteria = new Collection(); $this->activeCriteria = new Collection(); $this->makeModel(); } /** * Returns specified model class name. * * @return string */ public abstract function model(); /** * Creates instance of model to start building query for * * @param bool $storeModel if true, this becomes a fresh $this->model property * @return Model * @throws RepositoryException */ public function makeModel($storeModel = true) { $model = $this->app->make($this->model()); if ( ! $model instanceof Model) { throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); } if ($storeModel) $this->model = $model; return $model; } // ------------------------------------------------------------------------- // Retrieval methods // ------------------------------------------------------------------------- /** * Give unexecuted query for current criteria * * @return EloquentBuilder */ public function query() { $this->applyCriteria(); if ($this->model instanceof Model) { return $this->model->query(); } return clone $this->model; } /** * Does a simple count(*) for the model / scope * * @return int */ public function count() { return $this->query()->count(); } /** * Returns first match * * @param array $columns * @return Model|null */ public function first($columns = ['*']) { return $this->query()->first($columns); } /** * Returns first match or throws exception if not found * * @param array $columns * @return Model * @throws ModelNotFoundException */ public function firstOrFail($columns = ['*']) { $result = $this->query()->first($columns); if ( ! empty($result)) return $result; throw (new ModelNotFoundException)->setModel($this->model()); } /** * @param array $columns * @return mixed */ public function all($columns = ['*']) { return $this->query()->get($columns); } /** * @param string $value * @param string $key * @return array */ public function pluck($value, $key = null) { $this->applyCriteria(); $lists = $this->model->pluck($value, $key); if (is_array($lists)) return $lists; return $lists->all(); } /** * @param string $value * @param string $key * @return array * @deprecated */ public function lists($value, $key = null) { return $this->pluck($value, $key); } /** * @param int $perPage * @param array $columns * @param string $pageName * @param null $page * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ public function paginate($perPage, $columns = ['*'], $pageName = 'page', $page = null) { $perPage = $perPage ?: $this->getDefaultPerPage(); return $this->query() ->paginate($perPage, $columns, $pageName, $page); } /** * @param mixed $id * @param array $columns * @param string|null $attribute * @return Model|null */ public function find($id, $columns = ['*'], $attribute = null) { $query = $this->query(); if (null !== $attribute && $attribute !== $query->getModel()->getKeyName()) { return $query->where($attribute, $id)->first($columns); } return $query->find($id, $columns); } /** * Returns first match or throws exception if not found * * @param int $id * @param array $columns * @return Model * @throws ModelNotFoundException */ public function findOrFail($id, $columns = ['*']) { $result = $this->query()->find($id, $columns); if ( ! empty($result)) return $result; throw (new ModelNotFoundException)->setModel($this->model()); } /** * @param string $attribute * @param mixed $value * @param array $columns * @return mixed */ public function findBy($attribute, $value, $columns = ['*']) { return $this->query() ->where($attribute, $value) ->first($columns); } /** * @param string $attribute * @param mixed $value * @param array $columns * @return mixed */ public function findAllBy($attribute, $value, $columns = ['*']) { return $this->query() ->where($attribute, $value) ->get($columns); } /** * Find a collection of models by the given query conditions. * * @param array|Arrayable $where * @param array $columns * @param bool $or * @return Collection|null */ public function findWhere($where, $columns = ['*'], $or = false) { $model = $this->query(); foreach ($where as $field => $value) { if ($value instanceof Closure) { $model = ( ! $or) ? $model->where($value) : $model->orWhere($value); } elseif (is_array($value)) { if (count($value) === 3) { list($field, $operator, $search) = $value; $model = ( ! $or) ? $model->where($field, $operator, $search) : $model->orWhere($field, $operator, $search); } elseif (count($value) === 2) { list($field, $search) = $value; $model = ( ! $or) ? $model->where($field, $search) : $model->orWhere($field, $search); } } else { $model = ( ! $or) ? $model->where($field, $value) : $model->orWhere($field, $value); } } return $model->get($columns); } // ------------------------------------------------------------------------- // Manipulation methods // ------------------------------------------------------------------------- /** * Makes a new model without persisting it * * @param array $data * @return Model * @throws \Illuminate\Database\Eloquent\MassAssignmentException */ public function make(array $data) { return $this->makeModel(false)->fill($data); } /** * Creates a model and returns it * * @param array $data * @return Model|null */ public function create(array $data) { return $this->makeModel(false)->create($data); } /** * Updates a model by id * * @param array $data * @param mixed $id * @param string $attribute * @return bool false if could not find model or not succesful in updating */ public function update(array $data, $id, $attribute = null) { $model = $this->find($id, ['*'], $attribute); if (empty($model)) return false; return $model->fill($data)->save(); } /** * Finds and fills a model by id, without persisting changes * * @param array $data * @param mixed $id * @param string $attribute * @return Model|false * @throws \Illuminate\Database\Eloquent\MassAssignmentException */ public function fill(array $data, $id, $attribute = null) { $model = $this->find($id, ['*'], $attribute); if (empty($model)) { throw (new ModelNotFoundException)->setModel($this->model()); } return $model->fill($data); } /** * Deletes a model by id * * @param mixed $id * @return boolean */ public function delete($id) { return $this->makeModel(false)->destroy($id); } // ------------------------------------------------------------------------- // With custom callback // ------------------------------------------------------------------------- /** * Applies callback to query for easier elaborate custom queries * on all() calls. * * @param Closure $callback must return query/builder compatible * @param array $columns * @return Collection * @throws \Exception */ public function allCallback(Closure $callback, $columns = ['*']) { /** @var EloquentBuilder $result */ $result = $callback( $this->query() ); $this->checkValidCustomCallback($result); return $result->get($columns); } /** * Applies callback to query for easier elaborate custom queries * on find (actually: ->first()) calls. * * @param Closure $callback must return query/builder compatible * @param array $columns * @return Collection * @throws \Exception */ public function findCallback(Closure $callback, $columns = ['*']) { /** @var EloquentBuilder $result */ $result = $callback( $this->query() ); $this->checkValidCustomCallback($result); return $result->first($columns); } /** * @param Model|EloquentBuilder|DatabaseBuilder $result * @throws InvalidArgumentException */ protected function checkValidCustomCallback($result) { if ( ! is_a($result, Model::class) && ! is_a($result, EloquentBuilder::class) && ! is_a($result, DatabaseBuilder::class) ) { throw new InvalidArgumentException('Incorrect allCustom call in repository. The callback must return a QueryBuilder/EloquentBuilder or Model object.'); } } // ------------------------------------------------------------------------- // Criteria // ------------------------------------------------------------------------- /** * Returns a collection with the default criteria for the repository. * These should be the criteria that apply for (almost) all calls * * Default set of criteria to apply to this repository * Note that this also needs all the parameters to send to the constructor * of each (and this CANNOT be solved by using the classname of as key, * since the same Criteria may be applied more than once). * * Override with your own defaults (check ExtendedRepository's refreshed, * named Criteria for examples). * * @return Collection; */ public function defaultCriteria() { return new Collection(); } /** * Builds the default criteria and replaces the criteria stack to apply with * the default collection. * * @return $this */ public function restoreDefaultCriteria() { $this->criteria = $this->defaultCriteria(); return $this; } /** * Sets criteria to empty collection * * @return $this */ public function clearCriteria() { $this->criteria = new Collection(); return $this; } /** * Sets or unsets ignoreCriteria flag. If it is set, all criteria (even * those set to apply once!) will be ignored. * * @param bool $ignore * @return $this */ public function ignoreCriteria($ignore = true) { $this->ignoreCriteria = $ignore; return $this; } /** * Returns a cloned set of all currently set criteria (not including * those to be applied once). * * @return Collection */ public function getCriteria() { return clone $this->criteria; } /** * Returns the criteria that must be applied for the next query * * @return Collection */ protected function getCriteriaToApply() { // get the standard criteria $criteriaToApply = $this->getCriteria(); // overrule them with criteria to be applied once if ( ! $this->onceCriteria->isEmpty()) { foreach ($this->onceCriteria as $onceKey => $onceCriteria) { // if there is no key, we can only add the criteria if (is_numeric($onceKey)) { $criteriaToApply->push($onceCriteria); continue; } // if there is a key, override or remove // if Null, remove criteria if (empty($onceCriteria) || is_a($onceCriteria, NullCriteria::class)) { $criteriaToApply->forget($onceKey); continue; } // otherwise, overide the criteria $criteriaToApply->put($onceKey, $onceCriteria); } } return $criteriaToApply; } /** * Applies Criteria to the model for the upcoming query * * This takes the default/standard Criteria, then overrides * them with whatever is found in the onceCriteria list * * @return $this */ public function applyCriteria() { // if we're ignoring criteria, the model must be remade without criteria if ($this->ignoreCriteria === true) { // and make sure that they are re-applied when we stop ignoring if ( ! $this->activeCriteria->isEmpty()) { $this->makeModel(); $this->activeCriteria = new Collection(); } return $this; } if ($this->areActiveCriteriaUnchanged()) return $this; // if the new Criteria are different, clear the model and apply the new Criteria $this->makeModel(); $this->markAppliedCriteriaAsActive(); // apply the collected criteria to the query foreach ($this->getCriteriaToApply() as $criteria) { if ($criteria instanceof CriteriaInterface) { $this->model = $criteria->apply($this->model, $this); } } $this->clearOnceCriteria(); return $this; } /** * Checks whether the criteria that are currently pushed * are the same as the ones that were previously applied * * @return mixed */ protected function areActiveCriteriaUnchanged() { return ( $this->onceCriteria->isEmpty() && $this->criteria == $this->activeCriteria ); } /** * Marks the active criteria so we can later check what * is currently active */ protected function markAppliedCriteriaAsActive() { $this->activeCriteria = $this->getCriteriaToApply(); } /** * After applying, removes the criteria that should only have applied once */ protected function clearOnceCriteria() { if ( ! $this->onceCriteria->isEmpty()) { $this->onceCriteria = new Collection(); } } /** * Pushes Criteria, optionally by identifying key * If a criteria already exists for the key, it is overridden * * Note that this does NOT overrule any onceCriteria, even if set by key! * * @param CriteriaInterface $criteria * @param string $key unique identifier to store criteria as * this may be used to remove and overwrite criteria * empty for normal automatic numeric key * @return $this */ public function pushCriteria(CriteriaInterface $criteria, $key = null) { // standard bosnadev behavior if (is_null($key)) { $this->criteria->push($criteria); return $this; } // set/override by key $this->criteria->put($key, $criteria); return $this; } /** * Removes criteria by key, if it exists * * @param string $key * @return $this */ public function removeCriteria($key) { $this->criteria->forget($key); return $this; } /** * Pushes Criteria, but only for the next call, resets to default afterwards * Note that this does NOT work for specific criteria exclusively, it resets * to default for ALL Criteria. * * @param CriteriaInterface $criteria * @param string $key * @return $this */ public function pushCriteriaOnce(CriteriaInterface $criteria, $key = null) { if (is_null($key)) { $this->onceCriteria->push($criteria); return $this; } // set/override by key $this->onceCriteria->put($key, $criteria); return $this; } /** * Removes Criteria, but only for the next call, resets to default afterwards * Note that this does NOT work for specific criteria exclusively, it resets * to default for ALL Criteria. * * In effect, this adds a NullCriteria to onceCriteria by key, disabling any criteria * by that key in the normal criteria list. * * @param string $key * @return $this */ public function removeCriteriaOnce($key) { // if not present in normal list, there is nothing to override if ( ! $this->criteria->has($key)) return $this; // override by key with Null-value $this->onceCriteria->put($key, new NullCriteria); return $this; } // ------------------------------------------------------------------------------ // Misc. // ------------------------------------------------------------------------------ /** * Returns default per page count. * * @return int */ protected function getDefaultPerPage() { if (is_numeric($this->perPage) && $this->perPage > 0) { return $this->perPage; } return config('repository.perPage', $this->makeModel(false)->getPerPage()); } }