Passed
Push — master ( 8cf58c...3a8f57 )
by Paweł
02:46
created

TagManager::setDateOfNextStatsUpdate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created for IG Monitoring.
4
 * User: jakim <[email protected]>
5
 * Date: 12.01.2018
6
 */
7
8
namespace app\components;
9
10
11
use app\components\traits\FindOrCreate;
12
use app\models\Account;
13
use app\models\AccountTag;
14
use app\models\Media;
15
use app\models\MediaTag;
16
use app\models\Tag;
17
use app\models\User;
18
use yii\base\Component;
19
use yii\db\Expression;
20
use yii\helpers\ArrayHelper;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, app\components\ArrayHelper. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
21
use yii\helpers\Inflector;
22
23
class TagManager extends Component
24
{
25
    use FindOrCreate;
26
27
    /**
28
     * @param \app\models\Tag $tag
29
     * @param int $nextUpdateInterval In hours from now.
30
     */
31
    public function markAsValid(Tag $tag, int $nextUpdateInterval = 24)
32
    {
33
        $tag->invalidation_count = 0;
34
        $tag->is_valid = 1;
0 ignored issues
show
Documentation Bug introduced by
The property $is_valid was declared of type boolean, but 1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
35
        $tag->invalidation_type_id = null;
36
37
        $this->setDateOfNextStatsUpdate($tag, $nextUpdateInterval);
38
    }
39
40
    /**
41
     * @param \app\models\Tag $tag
42
     * @param int|null $invalidationType
43
     */
44
    public function updateInvalidation(Tag $tag, ?int $invalidationType)
45
    {
46
        $tag->invalidation_count = (int)$tag->invalidation_count + 1;
47
        $tag->is_valid = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $is_valid was declared of type boolean, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
48
        $tag->invalidation_type_id = $invalidationType;
49
        $interval = 1;
50
        for ($i = 1; $i <= $tag->invalidation_count; $i++) {
51
            $interval *= $i;
52
        }
53
        $this->setDateOfNextStatsUpdate($tag, $interval);
54
    }
55
56
    /**
57
     * @param \app\models\Tag $tag
58
     * @param int $interval In hours from now.
59
     */
60
    public function setDateOfNextStatsUpdate(Tag $tag, int $interval = 24)
61
    {
62
        $tag->update_stats_after = new Expression('DATE_ADD(NOW(), INTERVAL :interval HOUR)', [
63
            'interval' => $interval,
64
        ]);
65
        $tag->save();
66
    }
67
68
    public function monitor(string $name, $proxyId = null, $proxyTagId = null): Tag
69
    {
70
        /** @var Tag $tag */
71
        $tag = $this->findOrCreate(['name' => $name], Tag::class);
72
73
        $tag->proxy_id = $proxyId;
74
        $tag->proxy_tag_id = $proxyTagId;
75
        $tag->monitoring = 1;
76
        $tag->disabled = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $disabled was declared of type boolean, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
77
78
        $tag->save();
79
80
        return $tag;
81
    }
82
83
    public function saveForMedia(Media $media, array $tags)
84
    {
85
        $this->saveTags($tags);
86
87
        $createdAt = (new \DateTime())->format('Y-m-d H:i:s');
88
        $rows = array_map(function ($id) use ($media, $createdAt) {
89
            return [
90
                $media->id,
91
                $id,
92
                $createdAt,
93
            ];
94
        }, Tag::find()
95
            ->andWhere(['name' => $tags])
96
            ->column());
97
98
        $sql = \Yii::$app->db->queryBuilder
99
            ->batchInsert(MediaTag::tableName(), ['media_id', 'tag_id', 'created_at'], $rows);
100
        $sql = str_replace('INSERT INTO ', 'INSERT IGNORE INTO ', $sql);
101
        \Yii::$app->db->createCommand($sql)
102
            ->execute();
103
    }
104
105
    /**
106
     * It deletes the previous ones and sets new ones.
107
     *
108
     * @param \app\models\Account $account
109
     * @param array|Tag[] $tags
110
     * @param int|null $userId
111
     * @throws \yii\db\Exception
112
     */
113
    public function setForAccount(Account $account, array $tags, ?int $userId = null)
114
    {
115
        $this->deleteAccountTags($account, $userId);
116
        if ($tags) {
117
            $this->saveForAccount($account, $tags, $userId);
118
        }
119
    }
120
121
    /**
122
     * Adds new ones.
123
     *
124
     * @param \app\models\Account $account
125
     * @param array|Tag[] $tags
126
     * @param int|null $userId
127
     * @throws \yii\db\Exception
128
     */
129
    public function saveForAccount(Account $account, array $tags, ?int $userId = null)
130
    {
131
        $rows = [];
132
        $createdAt = (new \DateTime())->format('Y-m-d H:i:s');
133
        $userIds = $this->getUserIds($userId);
134
        $tagIds = $this->getTagIds($tags);
135
        foreach ($userIds as $userId) {
136
            $tmp = array_map(function ($tagId) use ($account, $userId, $createdAt) {
137
                return [
138
                    $account->id,
139
                    $tagId,
140
                    $userId,
141
                    $createdAt,
142
                ];
143
            }, $tagIds);
144
            $rows = ArrayHelper::merge($rows, $tmp);
145
        }
146
147
        $sql = \Yii::$app->db->queryBuilder
148
            ->batchInsert(AccountTag::tableName(), ['account_id', 'tag_id', 'user_id', 'created_at'], $rows);
149
        $sql = str_replace('INSERT INTO ', 'INSERT IGNORE INTO ', $sql);
150
        \Yii::$app->db->createCommand($sql)
151
            ->execute();
152
    }
153
154
    /**
155
     * @param array|string[] $tags Names array
156
     * @throws \yii\db\Exception
157
     */
158
    public function saveTags(array $tags)
159
    {
160
        $createdAt = (new \DateTime())->format('Y-m-d H:i:s');
161
        $rows = array_map(function ($tag) use ($createdAt) {
162
            return [
163
                $tag,
164
                Inflector::slug($tag),
165
                $createdAt,
166
                $createdAt,
167
            ];
168
        }, $tags);
169
170
        $sql = \Yii::$app->db->getQueryBuilder()
171
            ->batchInsert(Tag::tableName(), ['name', 'slug', 'updated_at', 'created_at'], $rows);
172
        $sql = str_replace('INSERT INTO ', 'INSERT IGNORE INTO ', $sql);
173
        \Yii::$app->db->createCommand($sql)
174
            ->execute();
175
    }
176
177
    /**
178
     * @param array $tags
179
     * @return array
180
     * @throws \yii\db\Exception
181
     */
182
    private function getTagIds(array $tags): array
183
    {
184
        if (\is_string($tags['0'])) {
185
            $this->saveTags($tags);
186
        } else {
187
            $tags = ArrayHelper::getColumn($tags, 'name');
188
        }
189
190
        return Tag::find()
191
            ->andWhere(['name' => $tags])
192
            ->column();
193
    }
194
195
    /**
196
     * @param int $userId
197
     * @return array
198
     */
199
    private function getUserIds(?int $userId): array
200
    {
201
        if (!$userId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $userId of type null|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
202
            $userIds = User::find()
203
                ->andWhere(['active' => 1])
204
                ->column();
205
        } else {
206
            $userIds = (array)$userId;
207
        }
208
209
        return $userIds;
210
    }
211
212
    private function deleteAccountTags(Account $account, ?int $userId)
213
    {
214
        if ($userId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $userId of type null|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
215
            return AccountTag::deleteAll(['account_id' => $account->id, 'user_id' => $userId]);
216
        }
217
218
        return AccountTag::deleteAll(['account_id' => $account->id]);
219
    }
220
}