Test Failed
Pull Request — master (#9)
by
unknown
07:45
created

ContextualTemplateTrait::createGenericContext()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 37
rs 9.0168
c 0
b 0
f 0
cc 5
nc 3
nop 0
1
<?php
2
3
namespace Charcoal\Cms\Support;
4
5
use InvalidArgumentException;
6
7
// From 'psr/http-message'
8
use Psr\Http\Message\UriInterface;
9
10
// From 'charcoal-core'
11
use Charcoal\Model\Model;
12
use Charcoal\Model\ModelInterface;
13
14
/**
15
 * Additional utilities for the routing.
16
 */
17
trait ContextualTemplateTrait
18
{
19
    /**
20
     * The current rendering / data context.
21
     *
22
     * @var ModelInterface|null
23
     */
24
    protected $contextObject;
25
26
    /**
27
     * The route group path (base URI).
28
     *
29
     * @var string|null
30
     */
31
    protected $routeGroup;
32
33
    /**
34
     * The route endpoint path (path URI).
35
     *
36
     * @var string|null
37
     */
38
    protected $routeEndpoint;
39
40
    /**
41
     * Track the state of context creation.
42
     *
43
     * @var boolean
44
     */
45
    protected $isCreatingContext = false;
46
47
    /**
48
     * The class name of the section model.
49
     *
50
     * A fully-qualified PHP namespace. Used for the model factory.
51
     *
52
     * @var string
53
     */
54
    protected $genericContextClass = Model::class;
55
56
    /**
57
     * Set the class name of the generic context model.
58
     *
59
     * @param  string $className The class name of the section model.
60
     * @throws InvalidArgumentException If the class name is not a string.
61
     * @return AbstractPropertyDisplay Chainable
62
     */
63
    public function setGenericContextClass($className)
64
    {
65
        if (!is_string($className)) {
66
            throw new InvalidArgumentException(
67
                'Generic context class name must be a string.'
68
            );
69
        }
70
71
        $this->genericContextClass = $className;
72
73
        return $this;
74
    }
75
76
    /**
77
     * Retrieve the class name of the generic context model.
78
     *
79
     * @return string
80
     */
81
    public function genericContextClass()
82
    {
83
        return $this->genericContextClass;
84
    }
85
86
    /**
87
     * Set the current renderable object relative to the context.
88
     *
89
     * @param  ModelInterface $context The context / view to render the template with.
90
     * @return self
91
     */
92
    public function setContextObject(ModelInterface $context)
93
    {
94
        $this->contextObject = $context;
95
96
        return $this;
97
    }
98
99
    /**
100
     * Retrieve the current object relative to the context.
101
     *
102
     * This method is meant to be reimplemented in a child template controller
103
     * to return the resolved object that the module considers "the context".
104
     *
105
     * @return ModelInterface|null
106
     */
107
    public function contextObject()
108
    {
109
        if ($this->contextObject === null) {
110
            $this->contextObject = $this->createGenericContext();
111
        }
112
113
        return $this->contextObject;
114
    }
115
116
    /**
117
     * Create a generic object relative to the context.
118
     *
119
     * @return ModelInterface|null
120
     */
121
    protected function createGenericContext()
122
    {
123
        if ($this->isCreatingContext) {
124
            return null;
125
        }
126
127
        $this->isCreatingContext = true;
128
129
        $obj = $this->modelFactory()->create($this->genericContextClass());
130
131
        $baseUrl = $this->baseUrl();
132
        if ($this->routeEndpoint) {
133
            $endpoint = $this->translator()->translation($this->routeEndpoint);
134
            foreach ($this->translator()->availableLocales() as $lang) {
135
                $uri = $baseUrl->withPath($endpoint[$lang]);
136
137
                if ($this->routeGroup) {
138
                    $uri = $uri->withBasePath($this->routeGroup[$lang]);
139
                }
140
141
                $base = $uri->getBasePath();
142
                $path = $uri->getPath();
143
                $path = $base . '/' . ltrim($path, '/');
144
145
                $endpoint[$lang] = $path;
146
            }
147
        } else {
148
            $endpoint = null;
149
        }
150
151
        $obj['url']   = $endpoint;
152
        $obj['title'] = $this->title();
153
154
        $this->isCreatingContext = false;
155
156
        return $obj;
157
    }
158
159
    /**
160
     * Retrieve the current URI of the context.
161
     *
162
     * @return UriInterface|string|null
163
     */
164 View Code Duplication
    public function currentUrl()
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...
165
    {
166
        $context = $this->contextObject();
167
168
        if ($context && isset($context['url'])) {
169
            return $context['url'];
170
        }
171
172
        return null;
173
    }
174
175
    /**
176
     * Append a path to the base URI.
177
     *
178
     * @param  string $path The base path.
179
     * @return self
180
     */
181
    public function setRouteGroup($path)
182
    {
183
        $group = $this->translator()->translation($path);
184
185
        foreach ($this->translator()->availableLocales() as $lang) {
186
            $group[$lang] = trim($group[$lang], '/');
187
        }
188
189
        $this->routeGroup = $group;
0 ignored issues
show
Documentation Bug introduced by
It seems like $group can also be of type object<Charcoal\Translator\Translation>. However, the property $routeGroup is declared as type string|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
190
191
        return $this;
192
    }
193
194
    /**
195
     * Append a path to the URI.
196
     *
197
     * @param  string $path The main path.
198
     * @return self
199
     */
200
    public function setRouteEndpoint($path)
201
    {
202
        $endpoint = $this->translator()->translation($path);
203
204
        foreach ($this->translator()->availableLocales() as $lang) {
205
            $endpoint[$lang] = trim($endpoint[$lang], '/');
206
        }
207
208
        $this->routeEndpoint = $endpoint;
0 ignored issues
show
Documentation Bug introduced by
It seems like $endpoint can also be of type object<Charcoal\Translator\Translation>. However, the property $routeEndpoint is declared as type string|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
209
210
        return $this;
211
    }
212
213
    /**
214
     * Retrieve the base URI of the project.
215
     *
216
     * @return UriInterface|null
217
     */
218
    abstract public function baseUrl();
219
220
    /**
221
     * Retrieve the title of the page (from the context).
222
     *
223
     * @return string
224
     */
225
    abstract public function title();
226
227
    /**
228
     * Retrieve the translator service.
229
     *
230
     * @see    \Charcoal\Translator\TranslatorAwareTrait
231
     * @return \Charcoal\Translator\Translator
232
     */
233
    abstract protected function translator();
234
235
    /**
236
     * Retrieve the object model factory.
237
     *
238
     * @return \Charcoal\Factory\FactoryInterface
239
     */
240
    abstract public function modelFactory();
241
}
242