Completed
Push — master ( 50f309...1f3d4e )
by Mohamed
06:23
created

CrudTagTrait::changeStatus()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 2

Importance

Changes 7
Bugs 3 Features 1
Metric Value
c 7
b 3
f 1
dl 0
loc 35
ccs 19
cts 19
cp 1
rs 8.8571
cc 2
eloc 20
nc 2
nop 2
crap 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\Model\Traits\Project\Issue;
13
14
use Illuminate\Database\Eloquent;
15
use Illuminate\Support\Collection;
16
use Tinyissue\Model;
17
use Tinyissue\Model\Activity;
18
use Tinyissue\Model\Project;
19
use Tinyissue\Model\Tag;
20
use Tinyissue\Model\Traits\Tag\DataMappingTrait;
21
use Tinyissue\Model\Traits\Tag\FilterTrait;
22
use Tinyissue\Model\User;
23
24
/**
25
 * CrudTagTrait is trait class containing the methods for adding/editing/deleting the tags of Project\Issue model.
26
 *
27
 * @author Mohamed Alsharaf <[email protected]>
28
 *
29
 * @property int $id
30
 * @property int $created_by
31
 * @property int $project_id
32
 * @property string $title
33
 * @property string $body
34
 * @property int $assigned_to
35
 * @property int $time_quote
36
 * @property int $closed_by
37
 * @property int $closed_at
38
 * @property int status
39
 * @property int $updated_at
40
 * @property int $updated_by
41
 * @property Model\Project $project
42
 * @property Model\User $user
43
 * @property Model\User $updatedBy
44
 */
45
trait CrudTagTrait
46
{
47
    use DataMappingTrait,
48
        FilterTrait;
49
50
    /**
51
     * Change the status of an issue.
52
     *
53
     * @param int $status
54
     * @param int $userId
55
     *
56
     * @return Eloquent\Model
57
     */
58 5
    public function changeStatus($status, $userId)
59
    {
60 5
        if ($status == 0) {
61 5
            $this->closed_by = $userId;
62 5
            $this->closed_at = (new \DateTime())->format('Y-m-d H:i:s');
63
64 5
            $activityType = Activity::TYPE_CLOSE_ISSUE;
65 5
            $addTagName   = Tag::STATUS_CLOSED;
66
67
            /** @var \Illuminate\Support\Collection $ids */
68 5
            $ids = $this->getTagsExceptStatus()->getRelatedIds();
0 ignored issues
show
Bug introduced by
It seems like getTagsExceptStatus() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
69
        } else {
70 1
            $activityType = Activity::TYPE_REOPEN_ISSUE;
71 1
            $removeTag    = Tag::STATUS_CLOSED;
72 1
            $addTagName   = Tag::STATUS_OPEN;
73
74
            /** @var \Illuminate\Support\Collection $ids */
75 1
            $ids = $this->getTagsExcept($removeTag)->getRelatedIds();
0 ignored issues
show
Bug introduced by
It seems like getTagsExcept() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
76
        }
77
78 5
        $ids->push((new Tag())->getTagByName($addTagName)->id);
79
80 5
        $this->tags()->sync($ids->unique()->all());
0 ignored issues
show
Bug introduced by
It seems like tags() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
81
82
        /* Add to activity log */
83 5
        $this->activities()->save(new User\Activity([
0 ignored issues
show
Bug introduced by
It seems like activities() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
84 5
            'type_id'   => $activityType,
85 5
            'parent_id' => $this->project->id,
86 5
            'user_id'   => $userId,
87
        ]));
88
89 5
        $this->status = $status;
90
91 5
        return $this->save();
0 ignored issues
show
Bug introduced by
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
92
    }
93
94
    /**
95
     * Sync the issue tags.
96
     *
97
     * @param Collection $tags
98
     * @param Collection $currentTags
99
     *
100
     * @return bool
101
     */
102 28
    public function syncTags(Collection $tags, Collection $currentTags = null)
103
    {
104 28
        $removedTags = [];
105 28
        if (null === $currentTags) {
106
            // Open status tag
107 28
            $openTag = (new Tag())->getTagByName(Tag::STATUS_OPEN);
108
109
            // Add the following tags except for open status
110
            $addedTags = $tags
111 28
                ->filter([$this, 'tagsExceptStatusOpenCallback'])
112 28
                ->map([$this, 'toArrayCallback'])
113 28
                ->toArray();
114
        } else {
115
            // Open status tag
116 1
            $openTag = $currentTags->first([$this, 'onlyStatusOpenCallback']);
117
118
            // Remove status tag
119 1
            $currentTags = $currentTags->filter([$this, 'tagsExceptStatusOpenCallback']);
120
121
            // Make sure the tags does not includes the open status
122 1
            $tags = $tags->filter([$this, 'tagsExceptStatusOpenCallback']);
123
124
            // Tags remove from the issue
125
            $removedTags = $currentTags
126 1
                ->diff($tags)
127 1
                ->map([$this, 'toArrayCallback'])
128 1
                ->toArray();
129
130
            // Check if we are adding new tags
131
            $addedTags = $tags
132
                ->filter(function (Tag $tag) use ($currentTags) {
133 1
                    return $currentTags->where('id', $tag->id)->count() === 0;
134 1
                })
135 1
                ->map([$this, 'toArrayCallback'])
136 1
                ->toArray();
137
138
            // No new tags to add or remove
139 1
            if (empty($removedTags) && empty($addedTags)) {
140 1
                return true;
141
            }
142
        }
143
144
        // Make sure open status exists
145 28
        $tags->put($openTag->id, $openTag);
146
147
        // Save relation
148 28
        $this->tags()->sync($tags->lists('id')->all());
0 ignored issues
show
Bug introduced by
It seems like tags() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
149
150
        // Activity is added when new issue create with tags or updated with tags excluding the open status tag
151 28
        if (!empty($removedTags) || !empty($addedTags)) {
152
            // Add to activity log for tags if changed
153 4
            $this->activities()->save(new User\Activity([
0 ignored issues
show
Bug introduced by
It seems like activities() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
154 4
                'type_id'   => Activity::TYPE_ISSUE_TAG,
155 4
                'parent_id' => $this->project->id,
156 4
                'user_id'   => $this->user->id,
157 4
                'data'      => ['added_tags' => $addedTags, 'removed_tags' => $removedTags],
158
            ]));
159
        }
160
161 28
        return true;
162
    }
163
164
    /**
165
     * Create new tags from a string "group:tag_name" and fetch tag from a tag id.
166
     *
167
     * @param array $tags
168
     * @param bool  $isAdmin
169
     *
170
     * @return Collection
171
     */
172 28
    protected function createTags(array $tags, $isAdmin = false)
173
    {
174 28
        $newTags = new Collection($tags);
175
176
        // Transform the user input tags into tag objects
177
        $newTags->transform(function ($tagNameOrId) use ($isAdmin) {
178 28
            if (strpos($tagNameOrId, ':') !== false && $isAdmin) {
179 1
                return (new Tag())->createTagFromString($tagNameOrId);
180
            } else {
181 28
                return Tag::find($tagNameOrId);
182
            }
183 28
        });
184
185
        // Filter out invalid tags entered by the user
186 28
        $newTags = $newTags->filter(function ($tag) {
187 28
            return $tag instanceof Tag;
188 28
        });
189
190 28
        return $newTags;
191
    }
192
193
    /**
194
     * Add tag to the issue & close issue if added tag is Closed.
195
     *
196
     * @param Tag  $newTag
197
     * @param Tag  $oldTag
198
     * @param User $user
199
     *
200
     * @return $this
201
     */
202
    public function setCurrentTag(Tag $newTag, Tag $oldTag, User $user)
203
    {
204
        if ($newTag->name === Tag::STATUS_CLOSED || $newTag === Tag::STATUS_OPEN) {
205
            $status = $newTag->name === Tag::STATUS_CLOSED ? 0 : 1;
206
            $this->changeStatus($status, $user->id);
207
        } else {
208
            $this->tags()->detach($oldTag);
0 ignored issues
show
Bug introduced by
It seems like tags() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
209
            $this->tags()->attach($newTag);
0 ignored issues
show
Bug introduced by
It seems like tags() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
210
        }
211
212
        return $this;
213
    }
214
}
215