Completed
Pull Request — master (#6)
by Angel
03:38
created

Slug   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Test Coverage

Coverage 85.71%

Importance

Changes 0
Metric Value
eloc 47
dl 0
loc 172
ccs 42
cts 49
cp 0.8571
rs 10
c 0
b 0
f 0
wmc 17

9 Methods

Rating   Name   Duplication   Size   Complexity  
A ensureSlug() 0 8 4
A getResourceRecordId() 0 8 2
A defaultResourceLink() 0 3 1
A populateSlugParent() 0 12 2
A init() 0 4 2
A getSelfLink() 0 8 2
A getSlugLinks() 0 19 2
A checkAccess() 0 4 1
A getResourceLink() 0 5 1
1
<?php
2
3
namespace roaresearch\yii2\roa\behaviors;
4
5
use yii\{
6
    base\Action,
7
    base\InvalidConfigException,
8
    db\BaseActiveRecord,
9
    helpers\Url,
10
    web\NotFoundHttpException,
11
};
12
13
/**
14
 * Behavior to handle slug componentes linked as parent-child relations.
15
 *
16
 * @author Angel (Faryshta) Guevara <[email protected]>
17
 * @author Luis (Berkant) Campos <[email protected]>
18
 * @author Alejandro (Seether69) Márquez <[email protected]>
19
 */
20
class Slug extends \yii\base\Behavior
21
{
22
    /**
23
     * @var ?string name of the parent relation of the `$owner`
24
     */
25
    public ?string $parentSlugRelation;
26
27
    /**
28
     * @var string name of the resource
29
     */
30
    public string $resourceName;
31
32
    /**
33
     * @var array name of the identifier attribute
34
     */
35
    public array $idAttributes = ['id'];
36
37
    /**
38
     * @var string separator to create the route for resources with multiple id
39
     * attributes.
40
     */
41
    public string $idAttributeSeparator = '/';
42
43
    /**
44
     * @var string parentNotFoundMessage for not found exception when the parent
45
     * slug was not found
46
     */
47
    public string $parentNotFoundMessage = '"{resourceName}" not found';
48
49
    /**
50
     * @var ?ARContract parent record.
51
     */
52
    protected ?ARContract $parentSlug;
0 ignored issues
show
Bug introduced by
The type roaresearch\yii2\roa\behaviors\ARContract was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
53
54
    /**
55
     * @var string url to resource
56
     */
57
    protected string $resourceLink;
58
59
    /**
60
     * @inheritdoc
61
     */
62
    public function init()
63
    {
64
        $this->resourceName ?: throw new InvalidConfigException(
65
            $this::class . '::$resourceName must be defined.'
66
        );
67
    }
68
69
    /**
70
     * Ensures the parent record is attached to the behavior.
71
     *
72
     * @param ARContract $owner
73
     * @param bool $force whether to force finding the slug parent record
74
     * when `$parentSlugRelation` is defined
75
     */
76 14
    private function ensureSlug(ARContract $owner, bool $force = false)
77
    {
78 14
        if (null === $this->parentSlugRelation) {
79
            $this->resourceLink = $this->defaultResourceLink();
80
        } elseif ($force
81
            || $owner->isRelationPopulated($this->parentSlugRelation)
82
        ) {
83
            $this->populateSlugParent($owner);
84 14
        }
85 14
    }
86
87
    /**
88
     * @return string default resource link used at bottom level resources.
89
     */
90
    protected function defaultResourceLink(): string
91
    {
92
        return Url::to([$this->resourceName . '/'], true);
93
    }
94 14
95
    /**
96 14
     * This populates the slug to the parentSlug
97 14
     * @param BaseActiveRecord $owner
98
     */
99 9
    private function populateSlugParent(ARContract $owner)
100
    {
101 9
        $this->parentSlug = $owner->{$this->parentSlugRelation}
102
            ?: throw new NotFoundHttpException(
103 14
                strtr(
104
                    $this->parentNotFoundMessage,
105
                    ['{resourceName}' => $this->parentSlugRelation]
106
                )
107
            );
108
109 9
        $this->resourceLink = $this->parentSlug->getSelfLink()
0 ignored issues
show
Bug introduced by
The method getSelfLink() does not exist on null. ( Ignorable by Annotation )

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

109
        $this->resourceLink = $this->parentSlug->/** @scrutinizer ignore-call */ getSelfLink()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
110
            . '/' . $this->resourceName;
111 9
    }
112 9
113
    /**
114 9
     * @return string value of the owner's identifier
115
     */
116
    public function getResourceRecordId(): string
117
    {
118
        $attributeValues = [];
119
        foreach ($this->idAttribute as $attribute) {
0 ignored issues
show
Bug Best Practice introduced by
The property idAttribute does not exist on roaresearch\yii2\roa\behaviors\Slug. Since you implemented __get, consider adding a @property annotation.
Loading history...
120
            $attributeValues[] = $this->owner->$attribute;
121
        }
122
123
        return implode($this->idAttributeSeparator, $attributeValues);
124
    }
125 9
126 9
    /**
127 9
     * @return string HTTP Url to the resource list
128
     */
129
    public function getResourceLink(): string
130
    {
131
        $this->ensureSlug($this->owner, true);
0 ignored issues
show
Bug introduced by
It seems like $this->owner can also be of type null; however, parameter $owner of roaresearch\yii2\roa\behaviors\Slug::ensureSlug() does only seem to accept roaresearch\yii2\roa\behaviors\ARContract, 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

131
        $this->ensureSlug(/** @scrutinizer ignore-type */ $this->owner, true);
Loading history...
132 12
133
        return $this->resourceLink;
134 12
    }
135 12
136 12
    /**
137
     * @return string HTTP Url to self resource
138
     */
139 12
    public function getSelfLink(): string
140
    {
141
        $resourceRecordId = $this->getResourceRecordId();
142
        $resourceLink = $this->getResourceLink();
143
144
        return $resourceRecordId
145 12
            ? "$resourceLink/$resourceRecordId"
146
            : $resourceLink;
147 12
    }
148
149 12
    /**
150
     * @return array link to self resource and all the acumulated parent's links
151
     */
152
    public function getSlugLinks(): array
153
    {
154
        $this->ensureSlug($this->owner, true);
0 ignored issues
show
Bug introduced by
It seems like $this->owner can also be of type null; however, parameter $owner of roaresearch\yii2\roa\behaviors\Slug::ensureSlug() does only seem to accept roaresearch\yii2\roa\behaviors\ARContract, 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

154
        $this->ensureSlug(/** @scrutinizer ignore-type */ $this->owner, true);
Loading history...
155 12
        $selfLinks = [
156
            'self' => $this->getSelfLink(),
157 12
            $this->resourceName . '_collection' => $this->resourceLink,
158 12
        ];
159
160 12
        if (null === $this->parentSlug) {
161 12
            return $selfLinks;
162 12
        }
163
164
        $parentLinks = $this->parentSlug->getSlugLinks();
165
        $parentLinks[$this->parentSlugRelation . '_record']
166
            = $parentLinks['self'];
167
        unset($parentLinks['self']);
168 9
169
        // preserve order
170 9
        return array_merge($selfLinks, $parentLinks);
171
    }
172 9
173 9
    /**
174
     * Determines if the logged user has permission to access a resource
175 9
     * record or any of its chidren resources.
176 9
     *
177
     * When extending this method make sure to call the parent at the end.
178 6
     *
179 6
     * ```php
180 6
     * // custom logic
181 6
     * parent::checkAccess($params, $action);
182
     * ```
183
     *
184 6
     * @param  array $params
185
     * @param ?Action $action
186
     * @throws \yii\web\HttpException
187
     */
188
    public function checkAccess(array $params, ?Action $action = null): void
189
    {
190
        $this->ensureSlug($this->owner, true);
0 ignored issues
show
Bug introduced by
It seems like $this->owner can also be of type null; however, parameter $owner of roaresearch\yii2\roa\behaviors\Slug::ensureSlug() does only seem to accept roaresearch\yii2\roa\behaviors\ARContract, 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

190
        $this->ensureSlug(/** @scrutinizer ignore-type */ $this->owner, true);
Loading history...
191
        $this->parentSlug?->checkAccess($params, $action);
192 14
    }
193
}
194