Completed
Push — master ( 5e61a0...2e1bfd )
by Michael
04:48 queued 02:16
created

Repository::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Mblarsen\LaravelRepository;
4
5
use BadMethodCallException;
6
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Eloquent\Collection;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Pagination\LengthAwarePaginator as PaginationLengthAwarePaginator;
11
use Illuminate\Support\Str;
12
use InvalidArgumentException;
13
use Mblarsen\LaravelRepository\Traits\Filters;
14
use Mblarsen\LaravelRepository\Traits\IncludesRelations;
15
use Mblarsen\LaravelRepository\Traits\Sorts;
16
use Mblarsen\LaravelRepository\Traits\WrapsInResource;
17
18
/**
19
 * @method allQuery($query = null): Builder
20
 * @method allResources($query = null): \Illuminate\Http\Resources\Json\ResourceCollection
21
 * @method createQuery(array $data): JsonBuilder
22
 * @method createResource(array $data): JsonResource
23
 * @method findQuery($id, $query = null): Builder
24
 * @method findResource($id, $query = null): JsonResource
25
 * @method listQuery($column = null, $query = null): Builder
26
 * @method updateQuery(Model $model, array $data): Builder
27
 * @method updateResource(Model $model, array $data): JsonResource
28
 */
29
class Repository
30
{
31
    use Filters;
32
    use IncludesRelations;
33
    use Sorts;
34
    use WrapsInResource;
35
36
    /** @var string */
37
    protected $model;
38
39
    /** @var ResourceContext $resource_context */
40
    protected $resource_context;
41
42
    /** @var string|callable $list_column */
43
    protected $default_list_column;
44
45
    /** @var bool $only_query */
46
    protected $only_query = false;
47
48 44
    public function __construct(ResourceContext $resource_context)
49
    {
50 44
        $this->resource_context = $resource_context;
51 44
        $this->register();
52 44
    }
53
54 40
    protected function register()
55
    {
56
        // Allows you to set default_list_column
57 40
    }
58
59
    /**
60
     * Creates a new repository for a model.
61
     *
62
     * @param string $model model name
63
     * @param array|ResourceContext $context
64
     */
65 39
    public static function for(string $model, $context = null): self
66
    {
67
        /** @var Repository $repository */
68 39
        $repository = resolve(static::class);
69 39
        $repository->setModel($model);
70 39
        if ($context) {
71 10
            $repository->setContext(
72 10
                is_array($context)
73 1
                    ? ArrayResourceContext::create($context)
74 10
                    : $context
75
            );
76
        }
77 39
        return $repository;
78
    }
79
80 8
    public function __call($name, $arguments)
81
    {
82 8
        if ($this->canWrapInResource($name)) {
83 5
            return $this->wrapInResource(
84 5
                call_user_func_array([&$this, Str::before($name, 'Resource')], $arguments)
85
            );
86
        }
87
88 3
        if ($this->canReturnAsQuery($name)) {
89
            try {
90 1
                $this->only_query = true;
91 1
                return call_user_func_array([&$this, Str::before($name, 'Query')], $arguments);
92
            } finally {
93 1
                $this->only_query = false;
94
            }
95
        }
96
97 2
        throw new BadMethodCallException();
98
    }
99
100 3
    private function canReturnAsQuery($name)
101
    {
102 3
        return Str::endsWith($name, 'Query') &&
103 1
            in_array(
104 1
                Str::before($name, 'Query'),
105 3
                ['all', 'find', 'list']
106
            );
107
    }
108
109 8
    private function canWrapInResource($name)
110
    {
111 8
        return (Str::endsWith($name, 'Resource') ||
112 8
            Str::endsWith($name, 'Resources')) &&
113 6
            in_array(
114 6
                Str::before($name, 'Resource'),
115 8
                ['all', 'find', 'create', 'update', 'destroy']
116
            );
117
    }
118
119
    /**
120
     * Get the currenct resource context
121
     */
122 3
    public function getContext(): ResourceContext
123
    {
124 3
        return $this->resource_context;
125
    }
126
127
    /**
128
     * Set or replace the resource context
129
     */
130 21
    public function setContext(ResourceContext $resource_context)
131
    {
132 21
        $this->resource_context = $resource_context;
133
134 21
        return $this;
135
    }
136
137
    /**
138
     * Set the model
139
     */
140 39
    public function setModel(string $model)
141
    {
142 39
        $this->model = $model;
143
144 39
        return $this;
145
    }
146
147
    /**
148
     * Return all models based on resource context and query
149
     *
150
     * The ResourceContext determines if the result is a Collection or a
151
     * LengthAwarePaginator.
152
     *
153
     * @return LengthAwarePaginator|Collection|Builder
154
     */
155 28
    public function all($query = null)
156
    {
157 28
        $query = $this->modelQuery($query);
158
159
        $this
160 28
            ->validateQurey($query)
161 27
            ->applyWith($query)
162 27
            ->applySort($query)
163 26
            ->applyFilters($query);
164
165 26
        if ($this->only_query) {
166 1
            return $query;
167
        }
168
169 25
        return $this->execute($query);
170
    }
171
172
    /**
173
     * Execute query
174
     *
175
     * @param Builder $query
176
     * @return LengthAwarePaginator|Collection
177
     */
178 25
    private function execute($query)
179
    {
180 25
        $page = $this->resource_context->page();
181 25
        $per_page = $this->resource_context->perPage();
182 25
        $should_paginate = $this->resource_context->paginate();
183
184 25
        return $should_paginate
185 3
            ? $query->paginate($per_page, ['*'], 'page', $page)
186 25
            : $query->get();
187
    }
188
189
    /**
190
     * Produces a result suitable for selects, lists, and autocomplete. All
191
     * entries that has a 'value' and a 'label' key.
192
     *
193
     * Note: if a callable is used the mapping is performed in memory, while a
194
     * string is done in the database layer.
195
     *
196
     * @param callable|string $column
197
     * @param Builder $query
198
     * @return Collection|Builder|LengthAwarePaginator
199
     */
200 8
    public function list($column = null, $query = null)
201
    {
202 8
        $query = $this->modelQuery($query);
203
204 8
        $column = $column
205 2
            ?: $this->default_list_column
206 8
            ?: $this->default_sort_by;
207
208 8
        if (is_string($column)) {
209 4
            $query->select([$query->getModel()->getKeyName() . " AS value", "$column AS label"]);
210 4
            return $this->all($query);
211
        }
212
213
        $mapper = function (Model $model) use ($column) {
214
            return [
215 3
                'value' => $model->getKey(),
216 3
                'label' => $column($model)
217
            ];
218 5
        };
219
220 5
        if (is_callable($column)) {
221 4
            $all = $this->all($query);
222
223 4
            if ($all instanceof Builder) {
224 1
                return $all;
225
            }
226 3
            if ($all instanceof Collection) {
227 2
                return $all->map($mapper);
228
            }
229 1
            if ($all instanceof LengthAwarePaginator) {
0 ignored issues
show
introduced by
$all is always a sub-type of Illuminate\Contracts\Pag...on\LengthAwarePaginator.
Loading history...
230 1
                $items = collect($all->items())->map($mapper)->toArray();
231 1
                $all = new PaginationLengthAwarePaginator(
232 1
                    $items,
233 1
                    $all->total(),
234 1
                    $all->perPage(),
235 1
                    $all->currentPage()
236
                );
237 1
                return $all;
238
            }
239
        }
240
241 1
        throw new InvalidArgumentException("'column' should be a string or callable");
242
    }
243
244
    /**
245
     * @return Model|Builder
246
     */
247 6
    public function find($id, $query = null)
248
    {
249 6
        $query = $this->modelQuery($query);
250
251
        $this
252 6
            ->validateQurey($query)
253 6
            ->applyWith($query);
254
255 6
        $query->whereId($id);
256
257 6
        if ($this->only_query) {
258 1
            return $query;
259
        }
260
261 5
        return $query->firstOrFail();
262
    }
263
264 3
    public function create(array $data): Model
265
    {
266 3
        return $this->model::create($data);
267
    }
268
269 1
    public function update(Model $model, array $data): Model
270
    {
271 1
        $model->update($data);
272
273 1
        return $model;
274
    }
275
276 1
    public function destroy(Model $model)
277
    {
278 1
        $model->delete();
279 1
    }
280
281 35
    protected function modelQuery($query = null)
282
    {
283 35
        return $query ?? $this->model::query();
284
    }
285
286 33
    protected function validateQurey(Builder $query)
287
    {
288 33
        $model = $this->model;
289 33
        $query_model = get_class($query->getModel());
290
291 33
        if ($model !== $query_model) {
292 1
            throw new InvalidArgumentException("The input query and model does not match");
293
        }
294
295 32
        return $this;
296
    }
297
}
298