Passed
Push — main ( 073c01...b49c3d )
by Dimitri
03:21
created

RouteBuilder::form()   B

Complexity

Conditions 9
Paths 25

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 27
c 2
b 0
f 0
nc 25
nop 3
dl 0
loc 44
rs 8.0555
1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Router;
13
14
use BadMethodCallException;
15
use BlitzPHP\Container\Services;
16
use BlitzPHP\Utilities\Iterable\Arr;
17
use BlitzPHP\Utilities\String\Text;
18
use Closure;
19
use InvalidArgumentException;
20
21
/**
22
 * @method void  add(string $from, array|callable|string $to, array $options = [])     Enregistre une seule route à la collection.
23
 * @method $this addPlaceholder($placeholder, ?string $pattern = null)                 Enregistre une nouvelle contrainte auprès du système.
24
 * @method $this addRedirect(string $from, string $to, int $status = 302)              Ajoute une redirection temporaire d'une route à une autre.
25
 * @method $this as(string $name)
26
 * @method void  cli(string $from, array|callable|string $to, array $options = [])     Enregistre une route qui ne sera disponible que pour les requêtes de ligne de commande.
27
 * @method $this controller(string $controller)
28
 * @method void  delete(string $from, array|callable|string $to, array $options = [])  Enregistre une route qui ne sera disponible que pour les requêtes DELETE.
29
 * @method $this domain(string $domain)
30
 * @method void  get(string $from, array|callable|string $to, array $options = [])     Enregistre une route qui ne sera disponible que pour les requêtes GET.
31
 * @method void  head(string $from, array|callable|string $to, array $options = [])    Enregistre une route qui ne sera disponible que pour les requêtes HEAD.
32
 * @method $this hostname(string $hostname)
33
 * @method $this middleware(array|string $middleware)
34
 * @method $this name(string $name)
35
 * @method $this namespace(string $namespace)
36
 * @method void  options(string $from, array|callable|string $to, array $options = []) Enregistre une route qui ne sera disponible que pour les requêtes OPTIONS.
37
 * @method void  patch(string $from, array|callable|string $to, array $options = [])   Enregistre une route qui ne sera disponible que pour les requêtes PATCH.
38
 * @method $this placeholder(string $placeholder)
39
 * @method void  post(string $from, array|callable|string $to, array $options = [])    Enregistre une route qui ne sera disponible que pour les requêtes POST.
40
 * @method $this prefix(string $prefix)
41
 * @method $this priority(int $priority)
42
 * @method void  put(string $from, array|callable|string $to, array $options = [])     Enregistre une route qui ne sera disponible que pour les requêtes PUT.
43
 * @method $this set404Override($callable = null)                                      Définit la classe/méthode qui doit être appelée si le routage ne trouver pas une correspondance.
44
 * @method $this setAutoRoute(bool $value)                                             Si TRUE, le système tentera de faire correspondre l'URI avec
45
 *                                                                                     Contrôleurs en faisant correspondre chaque segment avec des dossiers/fichiers
46
 *                                                                                     dans CONTROLLER_PATH, lorsqu'aucune correspondance n'a été trouvée pour les routes définies.
47
 * @method $this setDefaultConstraint(string $placeholder)                             Définit la contrainte par défaut à utiliser dans le système. Typiquement à utiliser avec la méthode 'ressource'.
48
 * @method $this setDefaultController(string $value)                                   Définit le contrôleur par défaut à utiliser lorsqu'aucun autre contrôleur n'a été spécifié.
49
 * @method $this setDefaultMethod(string $value)                                       Définit la méthode par défaut pour appeler le contrôleur lorsqu'aucun autre méthode a été définie dans la route.
50
 * @method $this setDefaultNamespace(string $value)                                    Définit l'espace de noms par défaut à utiliser pour les contrôleurs lorsqu'aucun autre n'a été spécifié.
51
 * @method $this setPrioritize(bool $enabled = true)                                   Activer ou désactiver le tri des routes par priorité
52
 * @method $this setTranslateURIDashes(bool $value)                                    Indique au système s'il faut convertir les tirets des chaînes URI en traits de soulignement.
53
 * @method $this subdomain(string $subdomain)
54
 */
55
final class RouteBuilder
56
{
57
    /**
58
     * Les attributs à transmettre au routeur.
59
     */
60
    private array $attributes = [];
61
62
    /**
63
     * Les méthodes à transmettre dynamiquement au routeur.
64
     */
65
    private array $passthru = [
66
        'add', 'cli', 'delete', 'get', 'head', 'options', 'post', 'put', 'patch',
67
    ];
68
69
    /**
70
     * Les attributs qui peuvent être définis via cette classe.
71
     */
72
    private array $allowedAttributes = [
73
        'as', 'controller', 'domain', 'hostname', 'middlewares', 'middleware',
74
        'name', 'namespace', 'placeholder', 'prefix', 'priority', 'subdomain',
75
    ];
76
77
    /**
78
     * Les attributs qui sont des alias.
79
     */
80
    private array $aliases = [
81
        'name'        => 'as',
82
        'middlewares' => 'middleware',
83
    ];
84
85
    private array $allowedMethods = [
86
        'addPlaceholder', 'addRedirect',
87
        'set404Override', 'setAutoRoute',
88
        'setDefaultConstraint', 'setDefaultController', 'setDefaultMethod', 'setDefaultNamespace',
89
        'setTranslateURIDashes', 'setPrioritize',
90
    ];
91
92
    /**
93
     * Constructeur
94
     */
95
    public function __construct(private RouteCollection $collection)
96
    {
97
    }
98
99
    /**
100
     * Gérez dynamiquement les appels dans le registraire de routage.
101
     *
102
     * @return self
103
     *
104
     * @throws BadMethodCallException
105
     */
106
    public function __call(string $method, array $parameters = [])
107
    {
108
        if (in_array($method, $this->passthru, true)) {
109
            return $this->registerRoute($method, ...$parameters);
110
        }
111
112
        if (in_array($method, $this->allowedAttributes, true)) {
113
            if (in_array($method, ['middleware', 'middlewares'], true)) {
114
                $parameters = is_array($parameters[0]) ? $parameters[0] : $parameters;
115
116
                return $this->attribute($method, array_merge($this->attributes[$method] ?? [], $parameters));
117
            }
118
119
            return $this->attribute($method, $parameters[0]);
120
        }
121
122
        if (in_array($method, $this->allowedMethods, true)) {
123
            $collection = $this->collection->{$method}(...$parameters);
124
125
            if ($collection instanceof RouteCollection) {
126
                Services::set(RouteCollection::class, $collection);
127
                $this->collection = $collection;
128
            }
129
130
            return $this;
131
        }
132
133
        throw new BadMethodCallException(sprintf('La méthode %s::%s n\'existe pas.', self::class, $method));
134
    }
135
136
    public function configure(callable $callback)
137
    {
138
        $callback($this);
139
    }
140
141
    /**
142
     * Limite les routes à un ENVIRONNEMENT spécifié ou ils ne fonctionneront pas.
143
     */
144
    public function environment(string $env, Closure $callback): void
145
    {
146
        if ($env === config('app.environment')) {
0 ignored issues
show
introduced by
The condition $env === config('app.environment') is always false.
Loading history...
147
            $callback($this);
148
        }
149
    }
150
151
    public function form(string $from, $to, array $options = []): void
152
    {
153
        $options = $options + $this->attributes;
154
        $this->attributes = [];
155
156
        if (isset($options['unique'])) {
157
            $this->match(['get', 'post'], $from, $to, $options);
158
            return; 
159
        }
160
161
        $toGet = $toPost = $to;
162
163
        if (is_string($to)) {
164
            $parts = explode('::', $to);
165
        } else if(is_array($to)) {
166
            $parts = $to;
167
        } else {
168
            $parts = [];
169
        }
170
        
171
        if (count($parts) == 2) { // Si on a defini le controleur et la methode
172
            $controller = $parts[0];
173
            $method     = $parts[1];
174
        } else if (count($parts) == 1) {
175
            // Si on est ici, ca veut dire 2 choses. 
176
            // - Soit c'est la methode qui est definie (utilisateur d'un string)
177
            // - Soit c'est le controleur qui est defini (utilisateur d'un array)
178
                
179
            if (is_array($to)) {
180
                $controller = $parts[0];
181
                $method     = $this->collection->getDefaultMethod();
182
            } else {
183
                $controller = '';
184
                $method     = $parts[0];
185
            }
186
        }
187
188
        if (isset($controller) && isset($method)) {
189
            $toGet  = implode('::', array_filter([$controller, Text::camel('form_' . $method)]));
190
            $toPost = implode('::', array_filter([$controller, Text::camel('process_' . $method)]));
191
        }
192
193
        $this->collection->get($from, $toGet, $options);
194
        $this->collection->post($from, $toPost, $options);
195
    }
196
197
    /**
198
     * Create a route group with shared attributes.
199
     */
200
    public function group(callable $callback): void
201
    {
202
        $this->collection->group($this->attributes['prefix'] ?? '', $this->attributes, fn () => $callback($this));
203
    }
204
205
    /**
206
     * Ajoute une seule route à faire correspondre pour plusieurs verbes HTTP.
207
     *
208
     * Exemple:
209
     *  $route->match( ['get', 'post'], 'users/(:num)', 'users/$1);
210
     *
211
     * @param array|Closure|string $to
212
     */
213
    public function match(array $verbs = [], string $from = '', $to = '', array $options = []): void
214
    {
215
        $this->collection->match($verbs, $from, $to, $this->attributes + $options);
216
    }
217
218
    /**
219
     * Crée une collection de routes basées sur les verbes HTTP pour un contrôleur de présentation.
220
     *
221
     * Options possibles :
222
     * 'controller' - Personnalisez le nom du contrôleur utilisé dans la route 'to'
223
     * 'placeholder' - L'expression régulière utilisée par le routeur. La valeur par défaut est '(:any)'
224
     *
225
     * Example:
226
     *
227
     *      $route->presenter('photos');
228
     *
229
     *      // Génère les routes suivantes
230
     *      HTTP Verb | Path        | Action        | Used for...
231
     *      ----------+-------------+---------------+-----------------
232
     *      GET         /photos             index           affiche le tableau des tous les objets photo
233
     *      GET         /photos/show/{id}   show            affiche un objet photo spécifique, toutes les propriétés
234
     *      GET         /photos/new         new             affiche un formulaire pour un objet photo vide, avec les propriétés par défaut
235
     *      POST        /photos/create      create          traitement du formulaire pour une nouvelle photo
236
     *      GET         /photos/edit/{id}   edit            affiche un formulaire d'édition pour un objet photo spécifique, propriétés modifiables
237
     *      POST        /photos/update/{id} update          traitement des données du formulaire d'édition
238
     *      GET         /photos/remove/{id} remove          affiche un formulaire pour confirmer la suppression d'un objet photo spécifique
239
     *      POST        /photos/delete/{id} delete          suppression de l'objet photo spécifié
240
     *
241
     * @param string     $name    Le nom du contrôleur vers lequel router.
242
     * @param array|null $options Une liste des façons possibles de personnaliser le routage.
243
     */
244
    public function presenter(string $name, array $options = []): void
245
    {
246
        $this->collection->presenter($name, $this->attributes + $options);
247
248
		$this->attributes = [];
249
    }
250
251
    /**
252
     * Crée une collection de routes basés sur HTTP-verb pour un contrôleur.
253
     *
254
     * Options possibles :
255
     * 'controller' - Personnalisez le nom du contrôleur utilisé dans la route 'to'
256
     * 'placeholder' - L'expression régulière utilisée par le routeur. La valeur par défaut est '(:any)'
257
     * 'websafe' - - '1' si seuls les verbes HTTP GET et POST sont pris en charge
258
     *
259
     * Exemple:
260
     *
261
     *      $route->resource('photos');
262
     *
263
     *      // Genère les routes suivantes:
264
     *      HTTP Verb | Path        | Action        | Used for...
265
     *      ----------+-------------+---------------+-----------------
266
     *      GET         /photos             index           un tableau d'objets photo
267
     *      GET         /photos/new         new             un objet photo vide, avec des propriétés par défaut
268
     *      GET         /photos/{id}/edit   edit            un objet photo spécifique, propriétés modifiables
269
     *      GET         /photos/{id}        show            un objet photo spécifique, toutes les propriétés
270
     *      POST        /photos             create          un nouvel objet photo, à ajouter à la ressource
271
     *      DELETE      /photos/{id}        delete          supprime l'objet photo spécifié
272
     *      PUT/PATCH   /photos/{id}        update          propriétés de remplacement pour la photo existante
273
     *
274
     *  Si l'option 'websafe' est présente, les chemins suivants sont également disponibles :
275
     *
276
     *      POST		/photos/{id}/delete delete
277
     *      POST        /photos/{id}        update
278
     *
279
     * @param string     $name    Le nom de la ressource/du contrôleur vers lequel router.
280
     * @param array|null $options Une liste des façons possibles de personnaliser le routage.
281
     */
282
    public function resource(string $name, array $options = []): void
283
    {
284
        $this->collection->resource($name, $this->attributes + $options);
285
286
		$this->attributes = [];
287
    }
288
289
    /**
290
     * Une méthode de raccourci pour ajouter un certain nombre de routes en une seule fois.
291
     * Il ne permet pas de définir des options sur la route, ou de définir la méthode utilisée.
292
     */
293
    public function map(array $routes = [], array $options = []): void
294
    {
295
        $this->collection->map($routes, $this->attributes + $options);
296
    }
297
298
    /**
299
     * Spécifie une route qui n'affichera qu'une vue.
300
     * Ne fonctionne que pour les requêtes GET.
301
     */
302
    public function view(string $from, string $view, array $options = []): void
303
    {
304
        $this->collection->view($from, $view, $this->attributes + $options);
305
    }
306
307
    /**
308
     * Defini une valeur pour l'attribut donné
309
     *
310
     * @param mixed $value
311
     *
312
     * @throws InvalidArgumentException
313
     */
314
    private function attribute(string $key, $value): self
315
    {
316
        if (! in_array($key, $this->allowedAttributes, true)) {
317
            throw new InvalidArgumentException("L'attribute [{$key}] n'existe pas.");
318
        }
319
320
        $this->attributes[Arr::get($this->aliases, $key, $key)] = $value;
321
322
        return $this;
323
    }
324
325
    /**
326
     * Enregistre une nouvelle route avec le routeur.
327
     *
328
     * @param mixed $to
329
     */
330
    private function registerRoute(string $method, string $from, $to, array $options = []): self
331
    {
332
        $this->collection->{$method}($from, $to, $this->attributes + $options);
333
334
		$this->attributes = [];
335
336
        return $this;
337
    }
338
}
339