Completed
Push — master ( 303c41...204eee )
by Marcel
02:19
created

GenerateDocumentation::setUserToBeImpersonated()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 14
rs 9.4285
cc 3
eloc 10
nc 3
nop 1
1
<?php
2
3
namespace Mpociot\ApiDoc\Commands;
4
5
use Illuminate\Console\Command;
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\Facades\Route;
8
use Mpociot\ApiDoc\Generators\AbstractGenerator;
9
use Mpociot\ApiDoc\Generators\DingoGenerator;
10
use Mpociot\ApiDoc\Generators\LaravelGenerator;
11
use Mpociot\Documentarian\Documentarian;
12
13
class GenerateDocumentation extends Command
14
{
15
    /**
16
     * The name and signature of the console command.
17
     *
18
     * @var string
19
     */
20
    protected $signature = 'api:generate 
21
                            {--output=public/docs : The output path for the generated documentation}
22
                            {--routePrefix= : The route prefix to use for generation}
23
                            {--routes=* : The route names to use for generation}
24
                            {--actAsUserId= : The user ID to use for API response calls}
25
                            {--router=laravel : The router to be used (Laravel or Dingo)}
26
                            {--bindings= : Route Model Bindings}
27
    ';
28
29
    /**
30
     * The console command description.
31
     *
32
     * @var string
33
     */
34
    protected $description = 'Generate your API documentation from existing Laravel routes.';
35
36
    /**
37
     * Create a new command instance.
38
     *
39
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
40
     */
41
    public function __construct()
42
    {
43
        parent::__construct();
44
    }
45
46
    /**
47
     * Execute the console command.
48
     *
49
     * @return false|null
50
     */
51
    public function handle()
52
    {
53
        if ($this->option('router') === 'laravel') {
54
            $generator = new LaravelGenerator();
55
        } else {
56
            $generator = new DingoGenerator();
57
        }
58
59
        $allowedRoutes = $this->option('routes');
60
        $routePrefix = $this->option('routePrefix');
61
62
        $this->setUserToBeImpersonated($this->option('actAsUserId'));
63
64
        if ($routePrefix === null && ! count($allowedRoutes)) {
65
            $this->error('You must provide either a route prefix or a route to generate the documentation.');
66
67
            return false;
68
        }
69
70
        if ($this->option('router') === 'laravel') {
71
            $parsedRoutes = $this->processLaravelRoutes($generator, $allowedRoutes, $routePrefix);
72
        } else {
73
            $parsedRoutes = $this->processDingoRoutes($generator, $allowedRoutes, $routePrefix);
74
        }
75
        $parsedRoutes = collect($parsedRoutes)->sortBy('resource')->groupBy('resource');
76
77
        $this->writeMarkdown($parsedRoutes);
78
    }
79
80
    /**
81
     * @param  Collection $parsedRoutes
82
     *
83
     * @return void
84
     */
85
    private function writeMarkdown($parsedRoutes)
86
    {
87
        $outputPath = $this->option('output');
88
89
        $documentarian = new Documentarian();
90
91
        $markdown = view('apidoc::documentarian')->with('parsedRoutes', $parsedRoutes->all());
1 ignored issue
show
Bug introduced by
The method with does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
92
93
        if (! is_dir($outputPath)) {
94
            $documentarian->create($outputPath);
95
        }
96
97
        file_put_contents($outputPath.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR.'index.md', $markdown);
98
99
        $this->info('Wrote index.md to: '.$outputPath);
100
101
        $this->info('Generating API HTML code');
102
103
        $documentarian->generate($outputPath);
104
105
        $this->info('Wrote HTML documentation to: '.$outputPath.'/public/index.html');
106
    }
107
108
    /**
109
     * @return array
110
     */
111
    private function getBindings()
112
    {
113
        $bindings = $this->option('bindings');
114
        if (empty($bindings)) {
115
            return [];
116
        }
117
        $bindings = explode('|', $bindings);
118
        $resultBindings = [];
119
        foreach ($bindings as $binding) {
120
            list($name, $id) = explode(',', $binding);
121
            $resultBindings[$name] = $id;
122
        }
123
        return $resultBindings;
124
    }
125
126
    /**
127
     * @param $actAs
128
     */
129
    private function setUserToBeImpersonated($actAs)
130
    {
131
        if (!empty($actAs)) {
132
            if (version_compare($this->laravel->version(), '5.2.0', '<')) {
133
                $userModel = config('auth.model');
134
                $user = $userModel::find($actAs);
135
                $this->laravel['auth']->setUser($user);
136
            } else {
137
                $userModel = config('auth.providers.users.model');
138
                $user = $userModel::find($actAs);
139
                $this->laravel['auth']->guard()->setUser($user);
140
            }
141
        }
142
    }
143
144
    /**
145
     * @return mixed
146
     */
147
    private function getRoutes()
148
    {
149
        if ($this->option('router') === 'laravel') {
150
            return Route::getRoutes();
151
        } else {
152
            return app('Dingo\Api\Routing\Router')->getRoutes()[$this->option('routePrefix')];
153
        }
154
    }
155
156
    /**
157
     * @param AbstractGenerator  $generator
158
     * @param $allowedRoutes
159
     * @param $routePrefix
160
     * @return array
161
     */
162 View Code Duplication
    private function processLaravelRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
163
    {
164
        $routes = $this->getRoutes();
165
        $bindings = $this->getBindings();
166
        $parsedRoutes = [];
167
        foreach ($routes as $route) {
168
            if (in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->getUri())) {
169
                $parsedRoutes[] = $generator->processRoute($route, $bindings);
170
                $this->info('Processed route: '.$route->getUri());
171
            }
172
        }
173
        return $parsedRoutes;
174
    }
175
176
    /**
177
     * @param AbstractGenerator $generator
178
     * @param $allowedRoutes
179
     * @param $routePrefix
180
     * @return array
181
     */
182 View Code Duplication
    private function processDingoRoutes(AbstractGenerator $generator, $allowedRoutes, $routePrefix)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
183
    {
184
        $routes = $this->getRoutes();
185
        $bindings = $this->getBindings();
186
        $parsedRoutes = [];
187
        foreach ($routes as $route) {
188
            if (empty($allowedRoutes) || in_array($route->getName(), $allowedRoutes) || str_is($routePrefix, $route->uri())) {
189
                $parsedRoutes[] = $generator->processRoute($route, $bindings);
190
                $this->info('Processed route: '.$route->uri());
191
            }
192
        }
193
        return $parsedRoutes;
194
    }
195
}
196