Completed
Branch develop-3.0 (ae28cb)
by Mohamed
08:28
created

Updater::queueUpdate()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 31
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 31
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 13
nc 6
nop 2
1
<?php
2
3
/*
4
 * This file is part of the Tinyissue package.
5
 *
6
 * (c) Mohamed Alsharaf <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Tinyissue\Repository\Project\Issue;
13
14
use Illuminate\Support\Collection;
15
use Tinyissue\Contracts\Model\UserInterface;
16
use Tinyissue\Model\Activity;
17
use Tinyissue\Model\Message\Queue;
18
use Tinyissue\Model\Project;
19
use Tinyissue\Model\Tag;
20
use Tinyissue\Model\User;
21
use Tinyissue\Repository\RepositoryUpdater;
22
23
class Updater extends RepositoryUpdater
24
{
25
    public function __construct(Project\Issue $model)
26
    {
27
        $this->model = $model;
28
    }
29
30
    /**
31
     * Set the issue is updated by a user.
32
     *
33
     * @param int $userId
34
     *
35
     * @return Project\Issue
36
     */
37
    public function changeUpdatedBy($userId)
38
    {
39
        $this->model->updated_by = $userId;
40
        $this->model->touch();
41
42
        return $this->save();
43
    }
44
45
    /**
46
     * Reassign the issue to a new user.
47
     *
48
     * @param int|UserInterface $assignTo
49
     * @param UserInterface     $user
50
     *
51
     * @return Project\Issue
52
     */
53
    public function reassign($assignTo, UserInterface $user)
54
    {
55
        $this->setUser($user);
56
        $assignToId               = !$assignTo instanceof UserInterface ? $assignTo : $assignTo->id;
0 ignored issues
show
Bug introduced by
Accessing id on the interface Tinyissue\Contracts\Model\UserInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
57
        $this->model->assigned_to = $assignToId;
58
59
        // Add event on successful save
60
        Project\Issue::saved(function (Project\Issue $issue) {
61
            $this->queueAssign($issue, $this->user);
62
        });
63
64
        $this->save();
65
66
        $this->saveToActivities([
67
            'type_id'   => Activity::TYPE_REASSIGN_ISSUE,
68
            'parent_id' => $this->model->project->id,
69
            'user_id'   => $user->id,
0 ignored issues
show
Bug introduced by
Accessing id on the interface Tinyissue\Contracts\Model\UserInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
70
            'action_id' => $this->model->assigned_to,
71
        ]);
72
73
        return $this->model;
74
    }
75
76
    /**
77
     * Update the given issue.
78
     *
79
     * @param array $input
80
     *
81
     * @return Project\Issue
82
     */
83
    public function update(array $input = [])
84
    {
85
        $fill = array_only($input, [
86
            'title', 'body', 'assigned_to',
87
        ]);
88
        $fill['updated_by'] = $this->model->updatedBy->id;
89
90
        if (isset($input['time_quote']['lock'])) {
91
            $fill['lock_quote'] = $input['time_quote']['lock'];
92
        }
93
94
        // Only save quote if not locked or locked & user allowed to modify it
95
        if (array_key_exists('time_quote', $input) &&
96
            (!$this->model->isQuoteLocked() || $this->getLoggedUser()->can('lockQuote', $this->model))
97
        ) {
98
            $fill['time_quote'] = $input['time_quote'];
99
        }
100
101
        /* Add to activity log for assignment if changed */
102
        $assignToId = (int) array_get($input, 'assigned_to');
103
        if ($assignToId > 0 && $assignToId !== (int) $this->model->assigned_to) {
104
            $this->saveToActivities([
105
                'type_id'   => Activity::TYPE_REASSIGN_ISSUE,
106
                'parent_id' => $this->model->project->id,
107
                'user_id'   => $this->model->updatedBy->id,
108
                'action_id' => $this->model->assigned_to,
109
            ]);
110
        }
111
112
        $this->model->fill($fill);
113
114
        $this->syncTags($input, $this->model->tags()->with('parent')->get());
115
116
        // Add event on successful save
117
        Project\Issue::saved(function (Project\Issue $issue) {
118
            $this->queueUpdate($issue, $this->user);
119
        });
120
121
        return $this->save();
122
    }
123
124
    /**
125
     * Create a new issue.
126
     *
127
     * @param array $input
128
     *
129
     * @return Project\Issue
130
     */
131
    public function create(array $input)
132
    {
133
        if (array_key_exists('project_id', $input)) {
134
            $this->model->setRelation('project', Project::find((int) $input['project_id']));
135
        }
136
137
        $fill = [
138
            'created_by'  => $this->model->user->id,
139
            'project_id'  => $this->model->project->id,
140
            'title'       => $input['title'],
141
            'body'        => $input['body'],
142
            'assigned_to' => (int) $this->model->project->default_assignee,
143
        ];
144
145
        if ($this->model->user->isDeveloperOrMore()) {
146
            $fill['assigned_to'] = array_get($input, 'assigned_to', $fill['assigned_to']);
147
            $fill['time_quote']  = array_get($input, 'time_quote');
148
        }
149
150
        $this->model->fill($fill)->save();
151
152
        // Add issue to messages queue
153
        $this->queueAdd($this->model, $this->user);
0 ignored issues
show
Compatibility introduced by
$this->model of type object<Illuminate\Database\Eloquent\Model> is not a sub-type of object<Tinyissue\Model\Project\Issue>. It seems like you assume a child class of the class Illuminate\Database\Eloquent\Model to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
154
155
        /* Add to user's activity log */
156
        $this->saveToActivities([
157
            'type_id'   => Activity::TYPE_CREATE_ISSUE,
158
            'parent_id' => $this->model->project->id,
159
            'user_id'   => $this->model->user->id,
160
        ]);
161
162
        /* Add attachments to issue */
163
        Project\Issue\Attachment::instance()->updateIssueToken($input['upload_token'], $this->model->user->id, $this->model->id);
0 ignored issues
show
Documentation Bug introduced by
The method updateIssueToken does not exist on object<Tinyissue\Model\Project\Issue\Attachment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
164
165
        // Add default tag to newly created issue
166
        $defaultTag = app('tinyissue.settings')->getFirstStatusTagId();
167
        if ($defaultTag > 0 && empty($input['tag_status'])) {
168
            $input['tag_status'] = $defaultTag;
169
        }
170
171
        $this->syncTags($input);
172
173
        return $this->model;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->model; (Illuminate\Database\Eloquent\Model) is incompatible with the return type of the parent method Tinyissue\Repository\RepositoryUpdater::create of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
174
    }
175
176
    /**
177
     * Move the issue (comments & activities) to another project.
178
     *
179
     * @param int $projectId
180
     *
181
     * @return Project\Issue
182
     */
183
    public function changeProject($projectId)
184
    {
185
        $this->model->project_id = $projectId;
186
        $this->save();
187
        $comments = $this->model->comments()->get();
188
        foreach ($comments as $comment) {
189
            $comment->project_id = $projectId;
190
            $comment->save();
191
        }
192
193
        $activities = $this->model->activities()->get();
194
        foreach ($activities as $activity) {
195
            $activity->parent_id = $projectId;
196
            $activity->save();
197
        }
198
199
        return $this->model;
200
    }
201
202
    /**
203
     * Delete an issue.
204
     *
205
     * @return bool
206
     *
207
     * @throws \Exception
208
     */
209
    public function delete()
210
    {
211
        return $this->transaction('deleteIssue');
212
    }
213
214
    protected function deleteIssue()
215
    {
216
        $id          = $this->model->id;
217
        $projectId   = $this->model->project_id;
218
        $comments    = $this->model->comments;
219
        $attachments = $this->model->attachments;
220
221
        $status = $this->model->delete();
222
223
        if ($status) {
224
            $attachments->each(function (Project\Issue\Attachment $attachment) use ($projectId) {
225
                $path = $this->getUploadStorage($projectId, $attachment->upload_token);
226
                $attachment->deleteFile($path, $attachment->filename);
0 ignored issues
show
Documentation Bug introduced by
The method deleteFile does not exist on object<Tinyissue\Model\Project\Issue\Attachment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
227
                $attachment->delete();
228
            });
229
            $comments->each(function (Project\Issue\Comment $comment) {
230
                $comment->deleteComment($this->getLoggedUser()->user());
0 ignored issues
show
Bug introduced by
The method user() does not seem to exist on object<Tinyissue\Contracts\Model\UserInterface>.

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...
Documentation Bug introduced by
The method deleteComment does not exist on object<Tinyissue\Model\Project\Issue\Comment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
231
            });
232
            User\Activity::where('parent_id', '=', $projectId)->where('item_id', '=', $id)->delete();
233
            \DB::table('projects_issues_tags')->where('issue_id', '=', $id)->delete();
234
        }
235
236
        return $status;
237
    }
238
239
    /**
240
     * Change the status of an issue.
241
     *
242
     * @param int           $status
243
     * @param UserInterface $user
244
     *
245
     * @return Project\Issue
246
     */
247
    public function changeStatus($status, UserInterface $user)
248
    {
249
        if ($status == 0) {
250
            $this->model->closed_by = $user->id;
0 ignored issues
show
Bug introduced by
Accessing id on the interface Tinyissue\Contracts\Model\UserInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
251
            $this->model->closed_at = (new \DateTime())->format('Y-m-d H:i:s');
252
            $activityType           = Activity::TYPE_CLOSE_ISSUE;
253
        } else {
254
            $this->model->closed_by = 0;
255
            $this->model->closed_at = null;
256
            $activityType           = Activity::TYPE_REOPEN_ISSUE;
257
        }
258
259
        /* Add to activity log */
260
        $this->saveToActivities([
261
            'type_id'   => $activityType,
262
            'parent_id' => $this->model->project->id,
263
            'user_id'   => $user->id,
0 ignored issues
show
Bug introduced by
Accessing id on the interface Tinyissue\Contracts\Model\UserInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
264
        ]);
265
266
        $this->model->status = $status;
267
268
        // Add event on successful save
269
        Project\Issue::saved(function (Project\Issue $issue) {
270
            $this->queueUpdate($issue, $this->user);
271
        });
272
273
        return $this->save();
274
    }
275
276
    /**
277
     * Sync the issue tags.
278
     *
279
     * @param array      $input
280
     * @param Collection $currentTags
281
     *
282
     * @return bool
283
     */
284
    public function syncTags(array $input, Collection $currentTags = null)
285
    {
286
        $tagIds = array_only($input, [
287
            'tag_type', 'tag_status', 'tag_resolution',
288
        ]);
289
290
        // User can edit their own role and can only change issue type
291
        if ($this->model->updatedBy instanceof UserInterface && $this->model->updatedBy->isUser()) {
292
            $currentTagIds            = $currentTags->pluck('id', 'parent.name')->toArray();
0 ignored issues
show
Bug introduced by
It seems like $currentTags is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
293
            $tagIds['tag_status']     = array_key_exists('status', $currentTagIds) ? $currentTagIds['status'] : 0;
294
            $tagIds['tag_resolution'] = array_key_exists('resolution', $currentTagIds) ? $currentTagIds['resolution'] : 0;
295
        }
296
297
        $tags = (new Tag())->whereIn('id', $tagIds)->get();
0 ignored issues
show
Documentation Bug introduced by
The method whereIn does not exist on object<Tinyissue\Model\Tag>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
298
299
        $removedTags = [];
300
        if (null === $currentTags) {
301
            // Add the following tags except for open status
302
            $addedTags = $tags
303
                ->map(function (Tag $tag) {
304
                    return $tag->toShortArray();
305
                })
306
                ->toArray();
307
        } else {
308
            // Tags remove from the issue
309
            $removedTags = $currentTags
310
                ->diff($tags)
311
                ->map(function (Tag $tag) {
312
                    return $tag->toShortArray();
313
                })
314
                ->toArray();
315
316
            // Check if we are adding new tags
317
            $addedTags = $tags
318
                ->filter(function (Tag $tag) use ($currentTags) {
319
                    return $currentTags->where('id', $tag->id)->count() === 0;
320
                })
321
                ->map(function (Tag $tag) {
322
                    return $tag->toShortArray();
323
                })
324
                ->toArray();
325
326
            // No new tags to add or remove
327
            if (empty($removedTags) && empty($addedTags)) {
328
                return true;
329
            }
330
        }
331
332
        // Save relation
333
        $this->model->tags()->sync($tags->pluck('id')->all());
334
335
        // Activity is added when new issue create with tags or updated with tags excluding the open status tag
336
        if (!empty($removedTags) || !empty($addedTags)) {
337
            // Add this change to messages queue
338
            $this->queueChangeTags($this->model, $addedTags, $removedTags, $this->user);
0 ignored issues
show
Compatibility introduced by
$this->model of type object<Illuminate\Database\Eloquent\Model> is not a sub-type of object<Tinyissue\Model\Project\Issue>. It seems like you assume a child class of the class Illuminate\Database\Eloquent\Model to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
339
340
            // Add to activity log for tags if changed
341
            $this->saveToActivities([
342
                'type_id'   => Activity::TYPE_ISSUE_TAG,
343
                'parent_id' => $this->model->project->id,
344
                'user_id'   => $this->model->user->id,
345
                'data'      => ['added_tags' => $addedTags, 'removed_tags' => $removedTags],
346
            ]);
347
        }
348
349
        return true;
350
    }
351
352
    /**
353
     * Add tag to the issue & close issue if added tag is Closed.
354
     *
355
     * @param Tag $newTag
356
     * @param Tag $oldTag
357
     *
358
     * @return Project\Issue
359
     */
360
    public function changeKanbanTag(Tag $newTag, Tag $oldTag)
361
    {
362
        //  skip if there is no change in status tags
363
        if ($oldTag->name === $newTag->name) {
364
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Tinyissue\Repository\Project\Issue\Updater) is incompatible with the return type documented by Tinyissue\Repository\Pro...pdater::changeKanbanTag of type Tinyissue\Model\Project\Issue.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
365
        }
366
367
        // Open issue
368
        $data = ['added_tags' => [], 'removed_tags' => []];
369
370
        // Remove previous status tag
371
        $this->model->tags()->detach($oldTag);
372
        $data['removed_tags'][] = $oldTag->toShortArray();
373
374
        // Add new tag
375
        if (!$this->model->tags->contains($newTag)) {
376
            $this->model->tags()->attach($newTag);
377
378
            $data['added_tags'][] = $newTag->toShortArray();
379
        }
380
381
        if (!empty($data)) {
382
            // Add this change to messages queue
383
            $this->queueChangeTags($this->model, $data['added_tags'], $data['removed_tags'], $this->user);
0 ignored issues
show
Compatibility introduced by
$this->model of type object<Illuminate\Database\Eloquent\Model> is not a sub-type of object<Tinyissue\Model\Project\Issue>. It seems like you assume a child class of the class Illuminate\Database\Eloquent\Model to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
384
385
            // Add to activity log for tags if changed
386
            $this->saveToActivities([
387
                'type_id'   => Activity::TYPE_ISSUE_TAG,
388
                'parent_id' => $this->model->project->id,
389
                'user_id'   => $this->model->user->id,
390
                'data'      => $data,
391
            ]);
392
        }
393
394
        return $this->model;
395
    }
396
397
    /**
398
     * Insert update issue to message queue.
399
     *
400
     * @param Project\Issue $issue
401
     * @param UserInterface $changeBy
402
     *
403
     * @return void
404
     */
405
    public function queueUpdate(Project\Issue $issue, UserInterface $changeBy)
406
    {
407
        // Number of changed attributes
408
        $countChanges = count($issue->getDirty());
409
410
        // Whether or not the assignee has changed
411
        $noMessageForMe = false;
412
413
        // is Closed?
414
        if (!$issue->isOpen()) {
415
            return (new Queue())->queue(Queue::CLOSE_ISSUE, $issue, $changeBy);
0 ignored issues
show
Documentation Bug introduced by
The method queue does not exist on object<Tinyissue\Model\Message\Queue>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
416
        }
417
418
        // is Reopened?
419
        if ((int) $issue->getOriginal('status') === Project\Issue::STATUS_CLOSED) {
420
            return (new Queue())->queue(Queue::REOPEN_ISSUE, $issue, $changeBy);
0 ignored issues
show
Documentation Bug introduced by
The method queue does not exist on object<Tinyissue\Model\Message\Queue>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
421
        }
422
423
        // If the assignee has changed and it is not the logged in user who made the action
424
        if ($issue->assigned_to !== $issue->getOriginal('assigned_to', $issue->assigned_to)
425
            && $changeBy->id !== $issue->assigned_to
0 ignored issues
show
Bug introduced by
Accessing id on the interface Tinyissue\Contracts\Model\UserInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
426
        ) {
427
            (new Queue())->queue(Queue::ASSIGN_ISSUE, $issue, $changeBy);
0 ignored issues
show
Documentation Bug introduced by
The method queue does not exist on object<Tinyissue\Model\Message\Queue>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
428
            $noMessageForMe = $issue->assigned_to;
429
        }
430
431
        // If the update was just for assigning user, then skip update issue
432
        if (!($countChanges === 1 && $noMessageForMe !== false)) {
433
            return (new Queue())->queue(Queue::UPDATE_ISSUE, $issue, $changeBy);
0 ignored issues
show
Documentation Bug introduced by
The method queue does not exist on object<Tinyissue\Model\Message\Queue>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
434
        }
435
    }
436
437
    /**
438
     * Insert add issue to message queue.
439
     *
440
     * @param Project\Issue $issue
441
     * @param UserInterface $changeBy
442
     *
443
     * @return void
444
     */
445
    public function queueAdd(Project\Issue $issue, UserInterface $changeBy)
446
    {
447
        return (new Queue())->queue(Queue::ADD_ISSUE, $issue, $changeBy);
0 ignored issues
show
Documentation Bug introduced by
The method queue does not exist on object<Tinyissue\Model\Message\Queue>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
448
    }
449
450
    /**
451
     * Insert assign issue to message queue.
452
     *
453
     * @param Project\Issue $issue
454
     * @param UserInterface $changeBy
455
     *
456
     * @return void
457
     */
458
    public function queueAssign(Project\Issue $issue, UserInterface $changeBy)
459
    {
460
        // If the assignee has changed and it is not the logged in user who made the action
461
        if ($issue->assigned_to > 0 && $changeBy->id !== $issue->assigned_to) {
0 ignored issues
show
Bug introduced by
Accessing id on the interface Tinyissue\Contracts\Model\UserInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
462
            return (new Queue())->queue(Queue::ASSIGN_ISSUE, $issue, $changeBy);
0 ignored issues
show
Documentation Bug introduced by
The method queue does not exist on object<Tinyissue\Model\Message\Queue>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
463
        }
464
    }
465
466
    /**
467
     * Insert issue tag changes to message queue.
468
     *
469
     * @param Project\Issue $issue
470
     * @param array         $addedTags
471
     * @param array         $removedTags
472
     * @param UserInterface $changeBy
473
     *
474
     * @return mixed
475
     */
476
    public function queueChangeTags(Project\Issue $issue, array $addedTags, array $removedTags, UserInterface $changeBy)
477
    {
478
        return (new Queue())->queueIssueTagChanges($issue, $addedTags, $removedTags, $changeBy);
0 ignored issues
show
Documentation Bug introduced by
The method queueIssueTagChanges does not exist on object<Tinyissue\Model\Message\Queue>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
479
    }
480
}
481