Issues (31)

src/Concerns/HasRelations.php (3 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Swis\JsonApi\Client\Concerns;
6
7
use Swis\JsonApi\Client\Collection;
8
use Swis\JsonApi\Client\Interfaces\DataInterface;
9
use Swis\JsonApi\Client\Interfaces\ManyRelationInterface;
10
use Swis\JsonApi\Client\Interfaces\OneRelationInterface;
11
use Swis\JsonApi\Client\Links;
12
use Swis\JsonApi\Client\Meta;
13
use Swis\JsonApi\Client\Relations\HasManyRelation;
14
use Swis\JsonApi\Client\Relations\HasOneRelation;
15
use Swis\JsonApi\Client\Relations\MorphToManyRelation;
16
use Swis\JsonApi\Client\Relations\MorphToRelation;
17
use Swis\JsonApi\Client\Util;
18
19
trait HasRelations
20
{
21
    /**
22
     * @var \Swis\JsonApi\Client\Interfaces\OneRelationInterface[]|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface[]
23
     */
24
    protected $relations = [];
25
26
    /**
27
     * Create a singular relation to another item.
28
     *
29
     * @template TItem of \Swis\JsonApi\Client\Interfaces\ItemInterface
30
     *
31
     * @param  class-string<TItem>  $itemClass
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<TItem> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<TItem>.
Loading history...
32
     * @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface<TItem>
33
     */
34 92
    public function hasOne(string $itemClass, ?string $name = null): OneRelationInterface
35
    {
36 92
        $name = $name ?: $this->guessRelationName();
37
38 92
        if (! array_key_exists($name, $this->relations)) {
39 92
            $this->relations[$name] = $this->newHasOne((new $itemClass)->getType());
40
        }
41
42 92
        return $this->relations[$name];
43
    }
44
45 92
    protected function newHasOne(string $type): OneRelationInterface
46
    {
47 92
        return new HasOneRelation($type);
48
    }
49
50
    /**
51
     * Create a plural relation to another item.
52
     *
53
     * @template TItem of \Swis\JsonApi\Client\Interfaces\ItemInterface
54
     *
55
     * @param  class-string<TItem>  $itemClass
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<TItem> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<TItem>.
Loading history...
56
     * @return \Swis\JsonApi\Client\Interfaces\ManyRelationInterface<TItem>
57
     */
58 72
    public function hasMany(string $itemClass, ?string $name = null): ManyRelationInterface
59
    {
60 72
        $name = $name ?: $this->guessRelationName();
61
62 72
        if (! array_key_exists($name, $this->relations)) {
63 72
            $this->relations[$name] = $this->newHasMany((new $itemClass)->getType());
64
        }
65
66 72
        return $this->relations[$name];
67
    }
68
69 72
    protected function newHasMany(string $type): ManyRelationInterface
70
    {
71 72
        return new HasManyRelation($type);
72
    }
73
74
    /**
75
     * Create a singular relation.
76
     *
77
     *
78
     * @return \Swis\JsonApi\Client\Relations\MorphToRelation
79
     */
80 168
    public function morphTo(?string $name = null): OneRelationInterface
81
    {
82 168
        $name = $name ?: $this->guessRelationName();
83
84 168
        if (! array_key_exists($name, $this->relations)) {
85 168
            $this->relations[$name] = $this->newMorphTo();
86
        }
87
88 168
        return $this->relations[$name];
89
    }
90
91 168
    protected function newMorphTo(): OneRelationInterface
92
    {
93 168
        return new MorphToRelation;
94
    }
95
96
    /**
97
     * Create a plural relation.
98
     *
99
     *
100
     * @return \Swis\JsonApi\Client\Relations\MorphToManyRelation
101
     */
102 100
    public function morphToMany(?string $name = null): ManyRelationInterface
103
    {
104 100
        $name = $name ?: $this->guessRelationName();
105
106 100
        if (! array_key_exists($name, $this->relations)) {
107 100
            $this->relations[$name] = $this->newMorphToMany();
108
        }
109
110 100
        return $this->relations[$name];
111
    }
112
113 100
    protected function newMorphToMany(): ManyRelationInterface
114
    {
115 100
        return new MorphToManyRelation;
116
    }
117
118
    /**
119
     * Guess the relationship name.
120
     */
121 196
    protected function guessRelationName(): string
122
    {
123 196
        [$one, $two, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
124
125 196
        return Util::stringSnake($caller['function']);
126
    }
127
128
    /**
129
     * @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface[]|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface[]
130
     */
131 228
    public function getRelations(): array
132
    {
133 228
        return $this->relations;
134
    }
135
136
    /**
137
     * @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface|null
138
     */
139 176
    public function getRelation(string $name)
140
    {
141 176
        return $this->relations[$name] ?? null;
142
    }
143
144
    /**
145
     * Get the relationship data (included).
146
     */
147 24
    public function getRelationValue(string $name): ?DataInterface
148
    {
149
        // If the "attribute" exists as a method on the model, we will just assume
150
        // it is a relationship and will load and return the included items in the relationship
151 24
        $method = Util::stringCamel($name);
152 24
        if (method_exists($this, $method)) {
153 4
            return $this->$method()->getAssociated();
154
        }
155
156
        // If the "attribute" exists as a relationship on the model, we will return
157
        // the included items in the relationship
158 20
        if ($this->hasRelation($name)) {
159 4
            return $this->getRelation($name)->getAssociated();
160
        }
161
162 16
        return null;
163
    }
164
165
    /**
166
     * Set the specific relationship on the model.
167
     *
168
     * @param  \Swis\JsonApi\Client\Interfaces\DataInterface|false|null  $value
169
     * @return static
170
     */
171 152
    public function setRelation(string $relation, $value = false, ?Links $links = null, ?Meta $meta = null)
172
    {
173 152
        $method = Util::stringCamel($relation);
174 152
        if (method_exists($this, $method)) {
175
            /** @var \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface $relationObject */
176 48
            $relationObject = $this->$method();
177 132
        } elseif ($value instanceof Collection) {
178 32
            $relationObject = $this->morphToMany($relation);
179
        } else {
180 124
            $relationObject = $this->morphTo($relation);
181
        }
182
183 152
        if ($value !== false) {
184 148
            $relationObject->dissociate();
185 148
            if ($value !== null) {
186 140
                $relationObject->associate($value);
0 ignored issues
show
It seems like $value can also be of type Swis\JsonApi\Client\Collection; however, parameter $included of Swis\JsonApi\Client\Inte...nInterface::associate() does only seem to accept Swis\JsonApi\Client\Interfaces\ItemInterface, 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

186
                $relationObject->associate(/** @scrutinizer ignore-type */ $value);
Loading history...
187
            }
188
        }
189 152
        $relationObject->setLinks($links);
190 152
        $relationObject->setMeta($meta);
191
192 152
        return $this;
193
    }
194
195 24
    public function hasRelation(string $name): bool
196
    {
197 24
        return array_key_exists($name, $this->relations);
198
    }
199
200
    /**
201
     * @return static
202
     */
203 4
    public function unsetRelation(string $name)
204
    {
205 4
        unset($this->relations[$name]);
206
207 4
        return $this;
208
    }
209
}
210