Completed
Push — dev ( ca0385...1f683f )
by Yan
02:15
created

UrlGenerator::guardModel()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace Lincable;
4
5
use Closure;
6
use Lincable\Parsers\Parser;
7
use Illuminate\Support\Collection;
8
use Illuminate\Database\Eloquent\Model;
9
use Lincable\Contracts\Compilers\Compiler;
10
use Lincable\Exceptions\NoModelConfException;
11
12
class UrlGenerator
13
{
14
    /**
15
     * The model instance.
16
     *
17
     * @var \Illuminate\Database\Eloquent\Model
18
     */
19
    protected $model;
20
21
    /**
22
     * The parsers collection.
23
     *
24
     * @var \Illuminate\Support\Collection
25
     */
26
    protected $parsers;
27
28
    /**
29
     * The url configuration for models.
30
     *
31
     * @var \Lincable\UrlConf
32
     */
33
    protected $urlConf;
34
35
    /**
36
     * The compiler implementation.
37
     *
38
     * @var \Lincable\Contracts\Compilers\Compiler
39
     */
40
    protected $compiler;
41
42
    /**
43
     * The parsers available for the current model.
44
     *
45
     * @var \Illuminate\Support\Collection
46
     */
47
    protected $availableParsers;
48
49
    /**
50
     * The formatter resolver for dynamic parameters on model.
51
     *
52
     * @var mixed
53
     */
54
    protected $parameterResolver;
55
56
    /**
57
     * Create a new class instance.
58
     *
59
     * @param  \Lincable\Contracts\Compilers\Compiler $compiler
60
     * @param  \Illuminate\Support\Collection $parsers
61
     * @param  \Lincable\UrlConf $urlConf
62
     * @return void
63
     */
64
    public function __construct(Compiler $compiler, Collection $parsers, UrlConf $urlConf)
65
    {
66
        $this->parsers = $parsers;
67
        $this->urlConf = $urlConf;
68
        $this->compiler = $compiler;
69
    }
70
71
    /**
72
     * Set the current model to generate the url.
73
     *
74
     * @param  \Illuminate\Database\Eloquent\Model $model
75
     * @param  array $params
76
     * @return this
77
     */
78
    public function forModel(Model $model, array $params = [])
79
    {
80
        // Verify wheter we have the model fully configured on the url configuration.
81
        $this->guardModel($model);
82
83
        // Set the formatters for the model attributes.
84
        $this->setModelFormatters($params);
85
86
        return $this;
87
    }
88
89
    /**
90
     * Guard the model setting verifying wheter the model is also configured
91
     * on url configuration.
92
     *
93
     * @throws \Lincable\Exceptions\NoModelConfException
94
     *
95
     * @param  \Illuminate\Database\Eloquent\Model $model
96
     * @return void
97
     */
98
    protected function guardModel(Model $model)
99
    {
100
        // The model class name.
101
        $className = get_class($model);
102
103
        if (! $this->urlConf->has($className)) {
104
            throw new NoModelConfException("Model [{$className}] is not configured. Check your lincable url configuration.");
105
        }
106
107
        // We assume the model is configured on url conf
108
        // then we can set the model for url generation.
109
        $this->model = $model;
110
    }
111
112
    /**
113
     * Add the formatters model parameters based on url dynamic parameters.
114
     *
115
     * @param  array $params
116
     * @return void
117
     */
118
    protected function setModelFormatters(array $customParams = [])
119
    {
120
        $attributes = $this->getModel()->getAttributes();
121
122
        // Merge model attributes with the an array on custom parameters.
123
        $parameters = array_merge($attributes, $customParams);
124
125
        // Filter only the parameters been used on url.
126
        $formatterParameters = $this->filterDynamicParameters($parameters);
127
128
        // Create the formatters for the dynamic parsers.
129
        $this->injectFormatterToAvailableParsers($formatterParameters);
130
    }
131
132
    /**
133
     * Generate the url for the current model.
134
     *
135
     * @return string
136
     */
137
    public function generate()
138
    {
139
        return $this->availableParsers->reduce(function ($url, Parser $parser) {
140
141
            // Set the compiler current parser.
142
            $this->compiler->setParser($parser);
143
144
            // Return the compiled url.
145
            return $this->compiler->compile($url);
146
        }, $this->getRawUrl());
147
    }
148
149
    /**
150
     * Return the raw model url.
151
     *
152
     * @return string
153
     */
154
    public function getRawUrl()
155
    {
156
        return $this->urlConf->get(get_class($this->getModel()));
157
    }
158
159
    /**
160
     * Return the model instance.
161
     *
162
     * @throws \Exception
163
     *
164
     * @return \Illuminate\Database\Eloquent\Model
165
     */
166
    public function getModel()
167
    {
168
        if ($this->model) {
169
            return $this->model;
170
        }
171
172
        throw new \Exception("Any model related with generator");
173
    }
174
175
    /**
176
     * Return the compiler class instance.
177
     *
178
     * @return \Lincable\Contracts\Compilers\Compiler
179
     */
180
    public function getCompiler()
181
    {
182
        return $this->compiler;
183
    }
184
185
    /**
186
     * Return the collection parsers.
187
     *
188
     * @return \Illuminate\Support\Collection
189
     */
190
    public function getParsers()
191
    {
192
        return $this->parsers;
193
    }
194
195
    /**
196
     * Return the collection with available parsers for model.
197
     *
198
     * @return \Illuminate\Support\Collection
199
     */
200
    public function getAvailableParsers()
201
    {
202
        return $this->availableParsers;
203
    }
204
205
    /**
206
     * Return the model url configuration.
207
     *
208
     * @return \Lincable\UrlConf
209
     */
210
    public function getUrlConf()
211
    {
212
        return $this->urlConf;
213
    }
214
215
    /**
216
     * Set the function to resolve parameter formatter.
217
     *
218
     * @param  mixed $resolver
219
     * @return this
220
     */
221
    public function setParameterResolver($resolver)
222
    {
223
        $this->parameterResolver = $resolver;
224
        return $this;
225
    }
226
227
    /**
228
     * Set the compiler class instance.
229
     *
230
     * @param  \Lincable\Contracts\Compilers\Compiler
231
     * @return this
232
     */
233
    public function setCompiler(Compiler $compiler)
234
    {
235
        $this->compiler = $compiler;
236
        return $this;
237
    }
238
239
    /**
240
     * Return the filtered dynamic parameters to apply as a formatter
241
     * on the parsers classes.
242
     *
243
     * @param  array $parameters
244
     * @return array
245
     */
246
    protected function filterDynamicParameters(array $parameters)
247
    {
248
        $this->parseAvailableParsers();
249
250
        // Get the url configured for the model.
251
        $url = $this->getRawUrl();
252
253
        // Get all dynamic parameters on url from model parsers.
254
        $dynamics = $this->availableParsers->map(function (Parser $parser) use ($url) {
255
256
            // Set the current parser.
257
            $this->compiler->setParser($parser);
258
            
259
            return $this->compiler->parseDynamics($url);
260
        })->flatten()->all();
261
        
262
        return array_filter($parameters, function ($key) use ($dynamics) {
263
            return in_array($key, $dynamics);
264
        }, ARRAY_FILTER_USE_KEY);
265
    }
266
267
    /**
268
     * Add the formatter for the parameters on the parsers based on current model url.
269
     *
270
     * @param  array $dynamicParameters
271
     * @return void
272
     */
273
    protected function injectFormatterToAvailableParsers(array $dynamicParameters)
274
    {
275
        $this->availableParsers->each(function (Parser $parser) use ($dynamicParameters) {
276
            foreach ($dynamicParameters as $key => $value) {
277
278
                // Register the formatter for the key, and the logic function is to return
279
                $parser->addFormatter(function () use ($value, $parser) {
280
                    if ($this->parameterResolver) {
281
282
                        // Get the container instance on parser class.
283
                        $container = $parser->getContainer();
284
285
                        // Call the parameter resolver for the value returned.
286
                        $value = $container->call($this->parameterResolver, [$value]);
287
                    }
288
289
                    return $value;
290
                }, $key);
291
            }
292
        });
293
    }
294
295
    /**
296
     * Generate the available parsers for the model.
297
     *
298
     * @return void
299
     */
300
    protected function parseAvailableParsers()
301
    {
302
        // Get the url configured for the model.
303
        $url = $this->getRawUrl();
304
305
        // Get parsers that has dynamic parameters for model url.
306
        $availableParsers = $this->parsers->filter(function (Parser $parser) use ($url) {
307
308
            // Change the current compiler parser.
309
            $this->compiler->setParser($parser);
310
311
            return $this->compiler->hasDynamics($url);
312
        });
313
314
        $this->availableParsers = clone $availableParsers;
315
    }
316
}
317