Completed
Branch develop-3.0 (4fe777)
by Mohamed
11:06
created

UpdaterRepository::deleteIssue()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 19

Duplication

Lines 15
Ratio 55.56 %

Importance

Changes 0
Metric Value
dl 15
loc 27
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 19
nc 2
nop 0
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 Tinyissue\Contracts\Repository\Project\Issue\UpdaterRepository as IssueUpdater;
15
use Tinyissue\Model\Project;
16
use Tinyissue\Model\User;
17
use Tinyissue\Repository\RepositoryUpdater;
18
19
class UpdaterRepository extends RepositoryUpdater implements IssueUpdater
20
{
21
    public function __construct(Project\Issue $model)
22
    {
23
        $this->model = $model;
24
    }
25
26
    /**
27
     * Set the issue is updated by a user.
28
     *
29
     * @param int $userId
30
     *
31
     * @return Eloquent\Model
32
     */
33
    public function changeUpdatedBy($userId)
34
    {
35
        $this->model->updated_by = $userId;
36
        $this->model->touch();
37
38
        return $this->save();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->save(); (boolean) is incompatible with the return type declared by the interface Tinyissue\Contracts\Repo...sitory::changeUpdatedBy 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...
39
    }
40
41
    /**
42
     * Reassign the issue to a new user.
43
     *
44
     * @param int|UserInterface $assignTo
45
     * @param UserInterface     $user
46
     *
47
     * @return Eloquent\Model
48
     */
49
    public function reassign($assignTo, UserInterface $user)
50
    {
51
        $assignToId               = !$assignTo instanceof UserInterface ? $assignTo : $assignTo->id;
0 ignored issues
show
Bug introduced by
The class Tinyissue\Repository\Project\Issue\UserInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
52
        $this->model->assigned_to = $assignToId;
53
54
        // Add event on successful save
55
        static::saved(function (Project\Issue $issue) use ($user) {
0 ignored issues
show
Bug introduced by
The method saved() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
56
            $this->queueAssign($issue, $user);
0 ignored issues
show
Bug introduced by
The method queueAssign() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
57
        });
58
59
        $this->save();
60
61
        return $this->model->activities()->save(new User\Activity([
62
            'type_id'   => Activity::TYPE_REASSIGN_ISSUE,
63
            'parent_id' => $this->model->project->id,
64
            'user_id'   => $user->id,
65
            'action_id' => $this->model->assigned_to,
66
        ]));
67
    }
68
69
    /**
70
     * Update the given issue.
71
     *
72
     * @param array $input
73
     *
74
     * @return Eloquent\Model
75
     */
76
    public function update(array $input)
77
    {
78
        $fill = array_only($input, [
79
            'title', 'body', 'assigned_to',
80
        ]);
81
        $fill['updated_by'] = $this->model->updatedBy->id;
82
83 View Code Duplication
        if (isset($input['time_quote']['lock'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
84
            $fill['lock_quote'] = $input['time_quote']['lock'];
85
        }
86
87
        // Only save quote if not locked or locked & user allowed to modify it
88 View Code Duplication
        if (array_key_exists('time_quote', $input) &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
89
            (!$this->model->isQuoteLocked() || $this->model->user->permission(Model\Permission::PERM_ISSUE_LOCK_QUOTE))
90
        ) {
91
            $fill['time_quote'] = $input['time_quote'];
92
        }
93
94
        /* Add to activity log for assignment if changed */
95
        $assignToId = (int) array_get($input, 'assigned_to');
96
        if ($assignToId > 0 && $assignToId !== (int) $this->assigned_to) {
0 ignored issues
show
Bug introduced by
The property assigned_to does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
97
            $this->model->activities()->save(new User\Activity([
98
                'type_id'   => Activity::TYPE_REASSIGN_ISSUE,
99
                'parent_id' => $this->model->project->id,
100
                'user_id'   => $this->model->updatedBy->id,
101
                'action_id' => $this->model->assigned_to,
102
            ]));
103
        }
104
105
        $this->model->fill($fill);
106
107
        $this->syncTags($input, $this->model->tags()->with('parent')->get());
108
109
        // Add event on successful save
110
        static::saved(function (Project\Issue $issue) {
0 ignored issues
show
Bug introduced by
The method saved() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
111
            $this->queueUpdate($issue, $issue->updatedBy);
0 ignored issues
show
Bug introduced by
The method queueUpdate() does not exist on Tinyissue\Repository\Pro...Issue\UpdaterRepository. Did you maybe mean update()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
112
        });
113
114
        return $this->save();
115
    }
116
117
    /**
118
     * Create a new issue.
119
     *
120
     * @param array $input
121
     *
122
     * @return static
123
     */
124
    public function create(array $input)
125
    {
126
        $input['project'] = Project::find((int) $input['project_id']);
127
        $this->model->setRelations($input);
128
129
        $fill = [
130
            'created_by'  => $this->model->user->id,
131
            'project_id'  => $this->model->project->id,
132
            'title'       => $input['title'],
133
            'body'        => $input['body'],
134
            'assigned_to' => (int) $this->model->project->default_assignee,
135
        ];
136
137 View Code Duplication
        if ($this->model->user->permission('issue-modify')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
138
            $fill['assigned_to'] = $input['assigned_to'];
139
            $fill['time_quote']  = $input['time_quote'];
140
        }
141
142
        $this->model->fill($fill)->save();
143
144
        // Add issue to messages queue
145
        $this->queueAdd($this, $this->user);
0 ignored issues
show
Bug introduced by
The property user does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The method queueAdd() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
146
147
        /* Add to user's activity log */
148
        $this->model->activities()->save(new User\Activity([
149
            'type_id'   => Activity::TYPE_CREATE_ISSUE,
150
            'parent_id' => $this->model->project->id,
151
            'user_id'   => $this->model->user->id,
152
        ]));
153
154
        /* Add attachments to issue */
155
        Attachment::where('upload_token', '=', $input['upload_token'])
156
            ->where('uploaded_by', '=', $this->model->user->id)
157
            ->update(['issue_id' => $this->model->id]);
158
159
        // Add default tag to newly created issue
160
        $defaultTag = app('tinyissue.settings')->getFirstStatusTagId();
161
        if ($defaultTag > 0 && empty($input['tag_status'])) {
162
            $input['tag_status'] = $defaultTag;
163
        }
164
165
        $this->syncTags($input);
166
167
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Tinyissue\Repository\Pro...Issue\UpdaterRepository) 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...
168
    }
169
170
    /**
171
     * Move the issue (comments & activities) to another project.
172
     *
173
     * @param int $projectId
174
     *
175
     * @return $this
176
     */
177
    public function changeProject($projectId)
178
    {
179
        $this->project_id = $projectId;
0 ignored issues
show
Bug introduced by
The property project_id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
180
        $this->save();
181
        $comments = $this->model->comments()->get();
182
        foreach ($comments as $comment) {
183
            $comment->model->project_id = $projectId;
184
            $comment->save();
185
        }
186
187
        $activities = $this->model->activities()->get();
188
        foreach ($activities as $activity) {
189
            $activity->model->parent_id = $projectId;
190
            $activity->save();
191
        }
192
193
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Tinyissue\Repository\Pro...Issue\UpdaterRepository) is incompatible with the return type declared by the interface Tinyissue\Contracts\Repo...pository::changeProject 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...
194
    }
195
196
    /**
197
     * Delete an issue.
198
     *
199
     * @return bool
200
     *
201
     * @throws \Exception
202
     */
203
    public function delete()
204
    {
205
        return $this->transaction('deleteIssue');
206
    }
207
208
    protected function deleteIssue()
209
    {
210
        $id          = $this->model->id;
211
        $projectId   = $this->model->project_id;
212
        $comments    = $this->model->comments;
213
        $attachments = $this->model->attachments;
214
215
        $status = $this->model->delete();
216
217 View Code Duplication
        if ($status) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
218
            $attachments->each(function (Attachment $attachment) use ($projectId) {
219
                $path = config('filesystems.disks.local.root')
220
                    . '/' . config('tinyissue.uploads_dir')
221
                    . '/' . $projectId
222
                    . '/' . $attachment->upload_token;
223
                $attachment->deleteFile($path, $attachment->filename);
224
                $attachment->delete();
225
            });
226
            $comments->each(function (Project\Issue\Comment $comment) {
227
                $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...
228
            });
229
            User\Activity::where('parent_id', '=', $projectId)->where('item_id', '=', $id)->delete();
230
            \DB::table('projects_issues_tags')->where('issue_id', '=', $id)->delete();
231
        }
232
233
        return $status;
234
    }
235
236
    /**
237
     * Change the status of an issue.
238
     *
239
     * @param int           $status
240
     * @param UserInterface $user
241
     *
242
     * @return Eloquent\Model
243
     */
244 View Code Duplication
    public function changeStatus($status, UserInterface $user)
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...
245
    {
246
        if ($status == 0) {
247
            $this->closed_by = $user->id;
0 ignored issues
show
Bug introduced by
The property closed_by does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
248
            $this->closed_at = (new \DateTime())->format('Y-m-d H:i:s');
0 ignored issues
show
Bug introduced by
The property closed_at does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
249
            $activityType    = Activity::TYPE_CLOSE_ISSUE;
250
        } else {
251
            $this->closed_by = 0;
252
            $this->closed_at = null;
253
            $activityType    = Activity::TYPE_REOPEN_ISSUE;
254
        }
255
256
        /* Add to activity log */
257
        $this->activities()->save(new User\Activity([
0 ignored issues
show
Bug introduced by
The method activities() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
258
            'type_id'   => $activityType,
259
            'parent_id' => $this->project->id,
0 ignored issues
show
Bug introduced by
The property project does not seem to exist. Did you mean project_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
260
            'user_id'   => $user->id,
261
        ]));
262
263
        $this->status = $status;
0 ignored issues
show
Bug introduced by
The property status does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
264
265
        // Add event on successful save
266
        static::saved(function (Project\Issue $issue) use ($user) {
0 ignored issues
show
Bug introduced by
The method saved() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
267
            $this->queueUpdate($issue, $user);
0 ignored issues
show
Bug introduced by
The method queueUpdate() does not exist on Tinyissue\Repository\Pro...Issue\UpdaterRepository. Did you maybe mean update()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
268
        });
269
270
        return $this->save();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->save(); (boolean) is incompatible with the return type declared by the interface Tinyissue\Contracts\Repo...epository::changeStatus 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...
271
    }
272
273
    /**
274
     * Sync the issue tags.
275
     *
276
     * @param array      $input
277
     * @param Collection $currentTags
278
     *
279
     * @return bool
280
     */
281
    public function syncTags(array $input, Collection $currentTags = null)
282
    {
283
        $tagIds = array_only($input, [
284
            'tag_type', 'tag_status', 'tag_resolution',
285
        ]);
286
287
        // User can edit their own role and can only change issue type
288
        if ($this->updatedBy instanceof UserInterface && $this->updatedBy->isUser()) {
0 ignored issues
show
Bug introduced by
The property updatedBy does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The class Tinyissue\Repository\Project\Issue\UserInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
289
            $currentTagIds            = $currentTags->pluck('id', 'parent.name')->toArray();
290
            $tagIds['tag_status']     = array_key_exists('status', $currentTagIds) ? $currentTagIds['status'] : 0;
291
            $tagIds['tag_resolution'] = array_key_exists('resolution', $currentTagIds) ? $currentTagIds['resolution'] : 0;
292
        }
293
294
        $tags = (new Tag())->whereIn('id', $tagIds)->get();
295
296
        $removedTags = [];
297 View Code Duplication
        if (null === $currentTags) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
298
            // Add the following tags except for open status
299
            $addedTags = $tags
300
                ->map(function (Tag $tag) {
301
                    return $tag->toShortArray();
302
                })
303
                ->toArray();
304
        } else {
305
            // Tags remove from the issue
306
            $removedTags = $currentTags
307
                ->diff($tags)
308
                ->map(function (Tag $tag) {
309
                    return $tag->toShortArray();
310
                })
311
                ->toArray();
312
313
            // Check if we are adding new tags
314
            $addedTags = $tags
315
                ->filter(function (Tag $tag) use ($currentTags) {
316
                    return $currentTags->where('id', $tag->id)->count() === 0;
317
                })
318
                ->map(function (Tag $tag) {
319
                    return $tag->toShortArray();
320
                })
321
                ->toArray();
322
323
            // No new tags to add or remove
324
            if (empty($removedTags) && empty($addedTags)) {
325
                return true;
326
            }
327
        }
328
329
        // Save relation
330
        $this->tags()->sync($tags->pluck('id')->all());
0 ignored issues
show
Bug introduced by
The method tags() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
331
332
        // Activity is added when new issue create with tags or updated with tags excluding the open status tag
333 View Code Duplication
        if (!empty($removedTags) || !empty($addedTags)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
334
            // Add this change to messages queue
335
            $this->queueChangeTags($this, $addedTags, $removedTags, $this->user);
0 ignored issues
show
Bug introduced by
The method queueChangeTags() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
336
337
            // Add to activity log for tags if changed
338
            $this->activities()->save(new User\Activity([
0 ignored issues
show
Bug introduced by
The method activities() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
339
                'type_id'   => Activity::TYPE_ISSUE_TAG,
340
                'parent_id' => $this->project->id,
0 ignored issues
show
Bug introduced by
The property project does not seem to exist. Did you mean project_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
341
                'user_id'   => $this->user->id,
342
                'data'      => ['added_tags' => $addedTags, 'removed_tags' => $removedTags],
343
            ]));
344
        }
345
346
        return true;
347
    }
348
349
    /**
350
     * Add tag to the issue & close issue if added tag is Closed.
351
     *
352
     * @param Tag $newTag
353
     * @param Tag $oldTag
354
     *
355
     * @return $this
356
     */
357 View Code Duplication
    public function changeKanbanTag(Tag $newTag, Tag $oldTag)
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...
358
    {
359
        //  skip if there is no change in status tags
360
        if ($oldTag->name === $newTag->name) {
361
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Tinyissue\Repository\Pro...Issue\UpdaterRepository) is incompatible with the return type declared by the interface Tinyissue\Contracts\Repo...sitory::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...
362
        }
363
364
        // Open issue
365
        $data = ['added_tags' => [], 'removed_tags' => []];
366
367
        // Remove previous status tag
368
        $this->tags()->detach($oldTag);
0 ignored issues
show
Bug introduced by
The method tags() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
369
        $data['removed_tags'][] = $oldTag->toShortArray();
370
371
        // Add new tag
372
        if (!$this->tags->contains($newTag)) {
0 ignored issues
show
Bug introduced by
The property tags does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
373
            $this->tags()->attach($newTag);
0 ignored issues
show
Bug introduced by
The method tags() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
374
375
            $data['added_tags'][] = $newTag->toShortArray();
376
        }
377
378
        if (!empty($data)) {
379
            // Add this change to messages queue
380
            $this->queueChangeTags($this, $data['added_tags'], $data['removed_tags'], $this->user);
0 ignored issues
show
Bug introduced by
The method queueChangeTags() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
381
382
            // Add to activity log for tags if changed
383
            $this->activities()->save(new User\Activity([
0 ignored issues
show
Bug introduced by
The method activities() does not seem to exist on object<Tinyissue\Reposit...ssue\UpdaterRepository>.

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...
384
                'type_id'   => Activity::TYPE_ISSUE_TAG,
385
                'parent_id' => $this->project->id,
0 ignored issues
show
Bug introduced by
The property project does not seem to exist. Did you mean project_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
386
                'user_id'   => $this->user->id,
387
                'data'      => $data,
388
            ]));
389
        }
390
391
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Tinyissue\Repository\Pro...Issue\UpdaterRepository) is incompatible with the return type declared by the interface Tinyissue\Contracts\Repo...sitory::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...
392
    }
393
}
394