Passed
Pull Request — master (#115)
by Dmitriy
23:50 queued 08:56
created

ViewRenderer::withController()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
1
<?php
2
3
namespace App;
4
5
use Psr\Http\Message\ResponseInterface;
6
use Yiisoft\Aliases\Aliases;
7
use Yiisoft\Strings\Inflector;
8
use Yiisoft\View\ViewContextInterface;
9
use Yiisoft\View\WebView;
10
use Yiisoft\Yii\Web\Data\DataResponseFactoryInterface;
11
use Yiisoft\Yii\Web\User\User;
12
13
final class ViewRenderer implements ViewContextInterface
14
{
15
    protected ?string $name = null;
16
    protected DataResponseFactoryInterface $responseFactory;
17
    protected User $user;
18
19
    private Aliases $aliases;
20
    private WebView $view;
21
    private string $layout;
22
    private ?string $viewBasePath;
23
    private ?string $viewPath = null;
24
25
    public function __construct(
26
        DataResponseFactoryInterface $responseFactory,
27
        User $user,
28
        Aliases $aliases,
29
        WebView $view,
30
        string $viewBasePath,
31
        string $layout
32
    ) {
33
        $this->responseFactory = $responseFactory;
34
        $this->user = $user;
35
        $this->aliases = $aliases;
36
        $this->view = $view;
37
        $this->viewBasePath = $viewBasePath;
38
        $this->layout = $layout;
39
    }
40
41
    public function getViewPath(): string
42
    {
43
        if ($this->viewPath !== null) {
44
            return $this->viewPath;
45
        }
46
47
        return $this->aliases->get($this->viewBasePath) . '/' . $this->name;
0 ignored issues
show
Bug introduced by
It seems like $this->viewBasePath can also be of type null; however, parameter $alias of Yiisoft\Aliases\Aliases::get() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

47
        return $this->aliases->get(/** @scrutinizer ignore-type */ $this->viewBasePath) . '/' . $this->name;
Loading history...
48
    }
49
50
    public function render(string $view, array $parameters = []): ResponseInterface
51
    {
52
        $contentRenderer = fn () => $this->renderProxy($view, $parameters);
53
54
        return $this->responseFactory->createResponse($contentRenderer);
55
    }
56
57
    public function renderPartial(string $view, array $parameters = []): ResponseInterface
58
    {
59
        $content = $this->view->render($view, $parameters, $this);
60
61
        return $this->responseFactory->createResponse($content);
62
    }
63
64
    public function withController(object $controller): self
65
    {
66
        $new = clone $this;
67
        $new->name = $this->getName($controller);
68
69
        return $new;
70
    }
71
72
    public function withControllerName(string $name): self
73
    {
74
        $new = clone $this;
75
        $new->name = $name;
76
77
        return $new;
78
    }
79
80
    public function withViewPath(string $viewPath): self
81
    {
82
        $new = clone $this;
83
        $new->viewPath = $viewPath;
84
85
        return $new;
86
    }
87
88
    public function withViewBasePath(string $viewBasePath): self
89
    {
90
        $new = clone $this;
91
        $new->viewBasePath = $viewBasePath;
92
93
        return $new;
94
    }
95
96
    public function withLayout(string $layout): self
97
    {
98
        $new = clone $this;
99
        $new->layout = $layout;
100
101
        return $new;
102
    }
103
104
    private function renderProxy(string $view, array $parameters = []): string
105
    {
106
        $content = $this->view->render($view, $parameters, $this);
107
        $user = $this->user->getIdentity();
108
        $layout = $this->findLayoutFile($this->aliases->get($this->layout));
109
110
        if ($layout === null) {
0 ignored issues
show
introduced by
The condition $layout === null is always false.
Loading history...
111
            return $content;
112
        }
113
        return $this->view->renderFile(
114
            $layout,
115
            [
116
                'content' => $content,
117
                'user' => $user,
118
            ],
119
            $this
120
        );
121
    }
122
123
    private function findLayoutFile(?string $file): ?string
124
    {
125
        if ($file === null) {
126
            return null;
127
        }
128
129
        if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
130
            return $file;
131
        }
132
133
        return $file . '.' . $this->view->getDefaultExtension();
134
    }
135
136
    /**
137
     * Returns the controller name. Name should be converted to "id" case.
138
     * Method returns classname without `controller` on the ending.
139
     * If namespace is not contain `controller` or `controllers`
140
     * then returns only classname without `controller` on the ending
141
     * else returns all subnamespaces from `controller` (or `controllers`) to the end
142
     *
143
     * @return string
144
     * @example App\Controller\FooBar\BazController -> foo-bar/baz
145
     * @example App\Controllers\FooBar\BazController -> foo-bar/baz
146
     * @example Path\To\File\BlogController -> blog
147
     * @see Inflector::camel2id()
148
     */
149
    private function getName(object $controller): string
150
    {
151
        if ($this->name !== null) {
152
            return $this->name;
153
        }
154
155
        $regexp = '/((?<=controller\\\|s\\\)(?:[\w\\\]+)|(?:[a-z]+))controller/iuU';
156
        if (!preg_match($regexp, get_class($controller), $m) || empty($m[1])) {
157
            throw new \RuntimeException('Cannot detect controller name');
158
        }
159
160
        $inflector = new Inflector();
161
        $name = str_replace('\\', '/', $m[1]);
162
163
        return $this->name = $inflector->camel2id($name);
164
    }
165
}
166