Completed
Push — master ( 3fdc21...2574b5 )
by ReliQ
01:51
created

DocumentationController::index()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ReliqArts\Docweaver\Http\Controllers;
6
7
use Illuminate\Http\RedirectResponse;
8
use Illuminate\View\View;
9
use ReliqArts\Docweaver\Contracts\ConfigProvider;
10
use ReliqArts\Docweaver\Contracts\Documentation\Provider;
11
use ReliqArts\Docweaver\Contracts\Logger;
12
use ReliqArts\Docweaver\Contracts\Product\Finder;
13
use ReliqArts\Docweaver\Models\Product;
14
use Symfony\Component\DomCrawler\Crawler;
15
16
class DocumentationController
17
{
18
    private const DEFAULT_PAGE = 'installation';
19
20
    /**
21
     * @var ConfigProvider
22
     */
23
    protected $configProvider;
24
25
    /**
26
     * The documentation repository.
27
     *
28
     * @var Provider
29
     */
30
    protected $documentationProvider;
31
32
    /**
33
     * Doc home path.
34
     *
35
     * @var
36
     */
37
    protected $documentationHome;
38
39
    /**
40
     * @var Finder
41
     */
42
    protected $productFinder;
43
44
    /**
45
     * @var Logger
46
     */
47
    protected $logger;
48
49
    /**
50
     * Create a new controller instance.
51
     *
52
     * @param ConfigProvider $configProvider
53
     * @param Logger         $logger
54
     * @param Provider       $docs
55
     * @param Finder         $productFinder
56
     */
57
    public function __construct(ConfigProvider $configProvider, Logger $logger, Provider $docs, Finder $productFinder)
58
    {
59
        $this->logger = $logger;
60
        $this->documentationProvider = $docs;
61
        $this->productFinder = $productFinder;
62
        $this->documentationHome = $configProvider->getRoutePrefix();
63
        $this->configProvider = $configProvider;
64
    }
65
66
    /**
67
     * Show the documentation home page (docs).
68
     *
69
     * @return View
70
     */
71
    public function index(): View
72
    {
73
        $templateConfig = $this->configProvider->getTemplateConfig();
74
        $title = $templateConfig->getIndexTitle();
75
        $products = $this->productFinder->listProducts();
76
77
        return view('docweaver::index', [
78
            'title' => $title,
79
            'products' => $products,
80
        ]);
81
    }
82
83
    /**
84
     * Show the index page for a product (docs/foo).
85
     *
86
     * @param mixed $productName
87
     *
88
     * @return RedirectResponse
89
     */
90
    public function productIndex(string $productName): RedirectResponse
91
    {
92
        $product = $this->productFinder->findProduct($productName);
93
94
        if (empty($product) || $product->getDefaultVersion() === Product::VERSION_UNKNOWN) {
95
            abort(404);
96
        }
97
98
        // route to default version
99
        return redirect()->route($this->configProvider->getProductPageRouteName(), [
100
            $product->getKey(),
101
            $product->getDefaultVersion(),
102
        ]);
103
    }
104
105
    /**
106
     * Show a documentation page.
107
     *
108
     * @param string $productKey
109
     * @param string $version
110
     * @param string $page
0 ignored issues
show
Documentation introduced by
Should the type for parameter $page not be null|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
111
     *
112
     * @return RedirectResponse|View
0 ignored issues
show
Documentation introduced by
Should the return type not be RedirectResponse|View|\I...\Contracts\View\Factory?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
113
     */
114
    public function show(string $productKey, string $version, string $page = null)
115
    {
116
        // ensure product exists
117
        $product = $this->productFinder->findProduct($productKey);
118
        if (empty($product)) {
119
            abort(404);
120
        }
121
122
        // get default version for product
123
        $defaultVersion = $product->getDefaultVersion();
124
        if (!$product->hasVersion($version)) {
125
            return redirect(route(
126
                $this->configProvider->getProductPageRouteName(),
127
                [$product->getKey(), $defaultVersion]
128
            ), 301);
129
        }
130
131
        // get page content
132
        $page = $page ?: self::DEFAULT_PAGE;
133
        $content = $this->documentationProvider->getPage($product, $version, $page);
0 ignored issues
show
Bug introduced by
It seems like $product defined by $this->productFinder->findProduct($productKey) on line 117 can be null; however, ReliqArts\Docweaver\Cont...ion\Provider::getPage() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
134
135
        // ensure page has content
136
        if (empty($content)) {
137
            $this->logger->warning(
138
                sprintf('Documentation page (%s) for %s has no content.', $page, $product->getName()),
139
                ['product' => $product]
140
            );
141
            abort(404);
142
        }
143
144
        $title = (new Crawler($content))->filterXPath('//h1');
145
        $section = '';
146
147
        // ensure section exists
148
        if ($this->documentationProvider->sectionExists($product, $version, $page)) {
0 ignored issues
show
Bug introduced by
It seems like $product defined by $this->productFinder->findProduct($productKey) on line 117 can be null; however, ReliqArts\Docweaver\Cont...ovider::sectionExists() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
149
            $section .= "/${page}";
150
        } elseif (!empty($page)) {
151
            // section does not exist, go to version index
152
            return redirect()->route($this->configProvider->getProductPageRouteName(), [$product->getKey(), $version]);
153
        }
154
155
        // set canonical
156
        $canonical = null;
157
        if ($this->documentationProvider->sectionExists($product, $defaultVersion, $page)) {
0 ignored issues
show
Bug introduced by
It seems like $product defined by $this->productFinder->findProduct($productKey) on line 117 can be null; however, ReliqArts\Docweaver\Cont...ovider::sectionExists() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
158
            $canonical = route(
159
                $this->configProvider->getProductPageRouteName(),
160
                [$product->getKey(), $defaultVersion, $page]
161
            );
162
        }
163
164
        return view('docweaver::page', [
165
            'canonical' => $canonical,
166
            'content' => $content,
167
            'currentProduct' => $product,
168
            'currentSection' => $section,
169
            'currentVersion' => $version,
170
            'index' => $this->documentationProvider->getPage($product, $version),
0 ignored issues
show
Bug introduced by
It seems like $product defined by $this->productFinder->findProduct($productKey) on line 117 can be null; however, ReliqArts\Docweaver\Cont...ion\Provider::getPage() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
171
            'page' => $page,
172
            'title' => count($title) ? $title->text() : null,
173
            'versions' => $product->getVersions(),
174
        ]);
175
    }
176
}
177