Passed
Push — master ( bf47d9...8e5e80 )
by Adrien
07:36
created

AbstractModel::setCreator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
ccs 2
cts 2
cp 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Model;
6
7
use Application\Acl\Acl;
8
use Cake\Chronos\Chronos;
9
use Doctrine\ORM\Event\PreUpdateEventArgs;
10
use Doctrine\ORM\Mapping as ORM;
11
use Ecodev\Felix\Api\Exception;
12
use Ecodev\Felix\Model\HasOwner;
13
use Ecodev\Felix\Model\Model;
14
use GraphQL\Doctrine\Annotation as API;
15
16
/**
17
 * Base class for all objects stored in database.
18
 *
19
 * It includes an automatic mechanism to timestamp objects with date and user.
20
 *
21
 * @ORM\MappedSuperclass
22
 * @ORM\HasLifecycleCallbacks
23
 * @ORM\Table(indexes={
24
 *     @ORM\Index(name="creation_date", columns={"creation_date"}),
25
 *     @ORM\Index(name="update_date", columns={"update_date"}),
26
 * })
27
 * @API\Filters({
28
 *     @API\Filter(field="custom", operator="Application\Api\Input\Operator\SearchOperatorType", type="string"),
29
 * })
30
 * @API\Sorting({
31
 *     "Application\Api\Input\Sorting\Owner"
32
 * })
33
 */
34
abstract class AbstractModel implements HasOwner, Model
35
{
36
    /**
37
     * @ORM\Column(type="integer")
38
     * @ORM\Id
39
     * @ORM\GeneratedValue(strategy="IDENTITY")
40
     */
41
    private ?int $id = null;
42
43
    /**
44
     * @ORM\Column(type="datetime", nullable=true)
45
     */
46
    private ?Chronos $creationDate = null;
47
48
    /**
49
     * @ORM\Column(type="datetime", nullable=true)
50
     */
51
    private ?Chronos $updateDate = null;
52
53
    /**
54
     * @ORM\ManyToOne(targetEntity="User")
55
     * @ORM\JoinColumns({
56
     *     @ORM\JoinColumn(onDelete="SET NULL")
57
     * })
58
     */
59
    private ?User $creator = null;
60
61
    /**
62
     * @ORM\ManyToOne(targetEntity="User")
63
     * @ORM\JoinColumns({
64
     *     @ORM\JoinColumn(onDelete="SET NULL")
65
     * })
66
     */
67
    private ?User $owner = null;
68
69
    /**
70
     * @ORM\ManyToOne(targetEntity="User")
71
     * @ORM\JoinColumns({
72
     *     @ORM\JoinColumn(onDelete="SET NULL")
73
     * })
74
     */
75
    private ?User $updater = null;
76
77
    /**
78
     * Get id.
79
     */
80 85
    public function getId(): ?int
81
    {
82 85
        return $this->id;
83
    }
84
85
    /**
86
     * Set creation date.
87
     */
88 58
    private function setCreationDate(Chronos $creationDate): void
89
    {
90 58
        $this->creationDate = $creationDate;
91
    }
92
93
    /**
94
     * Get creation date.
95
     */
96
    public function getCreationDate(): ?Chronos
97
    {
98
        return $this->creationDate;
99
    }
100
101
    /**
102
     * Set update date.
103
     */
104 71
    private function setUpdateDate(Chronos $updateDate): void
105
    {
106 71
        $this->updateDate = $updateDate;
107
    }
108
109
    /**
110
     * Get update date.
111
     */
112
    public function getUpdateDate(): ?Chronos
113
    {
114
        return $this->updateDate;
115
    }
116
117
    /**
118
     * Set creator.
119
     */
120 58
    private function setCreator(?User $creator): void
121
    {
122 58
        $this->creator = $creator;
123
    }
124
125
    /**
126
     * Get creator.
127
     */
128 1
    public function getCreator(): ?User
129
    {
130 1
        return $this->creator;
131
    }
132
133
    /**
134
     * Set owner.
135
     */
136 77
    public function setOwner(?User $owner): void
137
    {
138 77
        if ($owner === $this->owner) {
139 29
            return;
140
        }
141
142 72
        $user = User::getCurrent();
143 72
        $isAdmin = $user && $user->getRole() === User::ROLE_ADMINISTRATOR;
144 72
        $isOwner = $user === $this->owner;
145
146 72
        if ($this->owner && !$isAdmin && !$isOwner) {
147 1
            $currentLogin = $user ? $user->getLogin() : '[anonymous]';
148 1
            $currentOwnerLogin = $this->owner->getLogin();
0 ignored issues
show
Bug introduced by
The method getLogin() 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

148
            /** @scrutinizer ignore-call */ 
149
            $currentOwnerLogin = $this->owner->getLogin();

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...
149 1
            $futureOwnerLogin = $owner ? $owner->getLogin() : '[nobody]';
150
151 1
            throw new Exception($currentLogin . ' is not allowed to change owner to ' . $futureOwnerLogin . ' because it belongs to ' . $currentOwnerLogin);
152
        }
153
154 72
        $this->owner = $owner;
155
    }
156
157
    /**
158
     * Get owner.
159
     */
160 102
    public function getOwner(): ?User
161
    {
162 102
        return $this->owner;
163
    }
164
165
    /**
166
     * Set updater.
167
     */
168 71
    private function setUpdater(?User $updater): void
169
    {
170 71
        $this->updater = $updater;
171
    }
172
173
    /**
174
     * Get updater.
175
     */
176
    public function getUpdater(): ?User
177
    {
178
        return $this->updater;
179
    }
180
181
    /**
182
     * Get default owner for creation.
183
     */
184 31
    protected function getOwnerForCreation(): ?User
185
    {
186 31
        return User::getCurrent();
187
    }
188
189
    /**
190
     * Automatically called by Doctrine when the object is saved for the first time.
191
     *
192
     * @ORM\PrePersist
193
     */
194 58
    public function timestampCreation(): void
195
    {
196 58
        $now = new Chronos();
197 58
        $user = User::getCurrent();
198 58
        $this->setCreationDate($now);
199 58
        $this->setUpdateDate($now);
200 58
        $this->setCreator($user);
201 58
        $this->setUpdater($user);
202
203 58
        if (!$this->getOwner()) {
204 36
            $this->setOwner($this->getOwnerForCreation());
205
        }
206
    }
207
208
    /**
209
     * Automatically called by Doctrine when the object is updated.
210
     *
211
     * @ORM\PreUpdate
212
     */
213 22
    public function timestampUpdate(PreUpdateEventArgs $args): void
214
    {
215
        // Skip stamping if we only recorded a login of a user
216 22
        $changeSet = $args->getEntityChangeSet();
217 22
        unset($changeSet['firstLogin'], $changeSet['lastLogin']);
218 22
        if (!$changeSet) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $changeSet of type array<mixed,array<mixed,mixed>> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
219 3
            return;
220
        }
221
222 21
        $this->setUpdateDate(new Chronos());
223 21
        $this->setUpdater(User::getCurrent());
224
    }
225
226
    /**
227
     * Get permissions on this object for the current user.
228
     *
229
     * @API\Field(type="Permissions")
230
     */
231 3
    public function getPermissions(): array
232
    {
233 3
        $acl = new Acl();
234
235
        return [
236 3
            'create' => $acl->isCurrentUserAllowed($this, 'create'),
237 3
            'read' => $acl->isCurrentUserAllowed($this, 'read'),
238 3
            'update' => $acl->isCurrentUserAllowed($this, 'update'),
239 3
            'delete' => $acl->isCurrentUserAllowed($this, 'delete'),
240
        ];
241
    }
242
}
243