Passed
Push — master ( ef80b0...df5b6f )
by Mihail
04:52
created

FormContentUpdate::getUserIdName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Apps\Model\Admin\Content;
4
5
use Apps\ActiveRecord\CommentPost;
6
use Apps\ActiveRecord\Content;
7
use Apps\ActiveRecord\ContentCategory;
8
use Apps\ActiveRecord\ContentTag;
9
use Apps\ActiveRecord\User;
10
use Ffcms\Core\App;
11
use Ffcms\Core\Arch\Model;
12
use Ffcms\Core\Helper\Crypt;
13
use Ffcms\Core\Helper\Date;
14
use Ffcms\Core\Helper\FileSystem\Directory;
15
use Ffcms\Core\Helper\FileSystem\File;
16
use Ffcms\Core\Helper\Type\Any;
17
use Ffcms\Core\Helper\Type\Integer;
18
use Ffcms\Core\Helper\Type\Str;
19
use Illuminate\Support\Collection;
20
21
/**
22
 * Class FormContentUpdate. Create and update content items business model
23
 * @package Apps\Model\Admin\Content
24
 */
25
class FormContentUpdate extends Model
26
{
27
    public $title = [];
28
    public $text = [];
29
    public $path;
30
    public $poster;
31
    public $categoryId;
32
    public $authorId;
33
    public $metaTitle;
34
    public $metaKeywords = [];
35
    public $metaDescription = [];
36
    public $display = 1;
37
    public $source;
38
    public $addRating = 0;
39
    public $createdAt;
40
    public $important;
41
42
    public $galleryFreeId;
43
44
    private $_content;
45
    private $cloneId;
46
    private $_new = false;
47
48
    /**
49
     * FormContentUpdate constructor. Pass content active record inside
50
     * @param Content|Collection $content
51
     * @param int $cloneId
52
     */
53
    public function __construct(Content $content, int $cloneId = 0)
54
    {
55
        $this->_content = $content;
56
        $this->cloneId = $cloneId;
57
        parent::__construct();
58
    }
59
60
    /**
61
     * Set model properties from active record data
62
     */
63
    public function before()
64
    {
65
        // is new item?
66
        if (!$this->_content->id) {
67
            $this->_new = true;
68
            if (!$this->galleryFreeId) {
69
                $this->galleryFreeId = '_tmp_' . Str::randomLatin(mt_rand(16, 32));
70
            }
71
72
            if (!$this->authorId) {
73
                $this->authorId = App::$User->identity()->getId();
74
            }
75
76
            if (!$this->categoryId) {
77
                $this->categoryId = 1;
78
            }
79
            if (!$this->path) {
80
                $this->path = Integer::random(8) . '-' . date('d-m-Y');
81
            }
82
83
            if ($this->cloneId > 0) {
84
                $template = Content::find($this->cloneId);
85
                if ($template) {
86
                    $this->title = $template->title;
87
                    $this->text = $template->text;
88
                    $this->metaTitle = $template->meta_title;
89
                    $this->metaDescription = $template->meta_description;
90
                    $this->metaKeywords = $template->meta_keywords;
91
                }
92
            }
93
        } else { // is edit of exist item? define available data
94
            $this->title = $this->_content->title;
95
            $this->text = $this->_content->text;
96
            $this->path = $this->_content->path;
97
            $this->poster = $this->_content->poster;
98
            $this->categoryId = $this->_content->category_id;
99
            $this->authorId = $this->_content->author_id;
100
            $this->metaTitle = $this->_content->meta_title;
101
            $this->metaKeywords = $this->_content->meta_keywords;
102
            $this->metaDescription = $this->_content->meta_description;
103
            $this->display = $this->_content->display;
104
            $this->source = $this->_content->source;
105
            $this->createdAt = Date::convertToDatetime($this->_content->created_at, Date::FORMAT_TO_HOUR);
106
            $this->galleryFreeId = $this->_content->id;
107
            $this->important = $this->_content->important;
108
        }
109
    }
110
111
    /**
112
     * Validation rules
113
     * @return array
114
     */
115
    public function rules(): array
116
    {
117
        $res = [
118
            ['title.' . App::$Request->getLanguage(), 'required'],
119
            ['text.' . App::$Request->getLanguage(), 'required'],
120
            ['text', 'used'],
121
            ['path', 'reverse_match', '/[\/\'~`\[email protected]#\$%\^&\*\(\)+=\{\}\[\]\|;:"\<\>,\?\\\]/'],
122
            [['path', 'categoryId', 'authorId', 'display', 'galleryFreeId', 'title', 'important'], 'required'],
123
            [['metaTitle', 'metaKeywords', 'metaDescription', 'poster', 'source', 'addRating', 'createdAt'], 'used'],
124
            [['addRating', 'authorId', 'display'], 'int'],
125
            [['important', 'display'], 'in', [0, 1]],
126
            ['categoryId', 'in', $this->categoryIds()],
127
            ['path', '\Apps\Model\Admin\Content\FormContentUpdate::validatePath'],
128
            ['authorId', '\App::$User::isExist']
129
        ];
130
131
        foreach (App::$Properties->get('languages') as $lang) {
132
            $res[] = ['title.' . $lang, 'length_max', 120, null, true, true];
133
            $res[] = ['keywords.' . $lang, 'length_max', 150];
134
            $res[] = ['description.' . $lang, 'length_max', 250];
135
        }
136
137
        return $res;
138
    }
139
140
    /**
141
     * Filtering attribute types
142
     * @return array
143
     */
144
    public function types(): array
145
    {
146
        return [
147
            'text' => '!secure'
148
        ];
149
    }
150
151
    /**
152
     * Form display labels
153
     * @return array
154
     */
155
    public function labels(): array
156
    {
157
        return [
158
            'title' => __('Content title'),
159
            'text' => __('Content text'),
160
            'path' => __('Path slug'),
161
            'categoryId' => __('Category'),
162
            'metaTitle' => __('Meta title'),
163
            'metaKeywords' => __('Meta keywords'),
164
            'metaDescription' => __('Meta description'),
165
            'display' => __('Public display'),
166
            'important' => __('Make important'),
167
            'createdAt' => __('Publish date'),
168
            'authorId' => __('Author identity'),
169
            'source' => __('Source URL'),
170
            'addRating' => __('Change rating'),
171
            'poster' => __('Poster')
172
        ];
173
    }
174
175
    /**
176
     * Save changes in database
177
     */
178
    public function save()
179
    {
180
        $this->_content->title = $this->title;
181
        $this->_content->text = $this->text;
182
        $this->_content->path = $this->path;
183
        $this->_content->category_id = $this->categoryId;
184
        $this->_content->author_id = $this->authorId;
185
        $this->_content->display = $this->display;
0 ignored issues
show
Documentation Bug introduced by Mihail
The property $display was declared of type boolean, but $this->display 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...
186
        $this->_content->meta_title = $this->metaTitle;
187
        $this->_content->meta_keywords = $this->metaKeywords;
188
        $this->_content->meta_description = $this->metaDescription;
189
        $this->_content->source = $this->source;
190
        $this->_content->important = (int)$this->important;
0 ignored issues
show
Documentation Bug introduced by zenn
The property $important was declared of type boolean, but (int)$this->important 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...
191
        // check if rating is changed
192
        if ((int)$this->addRating !== 0) {
193
            $this->_content->rating += (int)$this->addRating;
194
        }
195
196
        // check if date is updated
197
        if (!Str::likeEmpty($this->createdAt) && !Str::startsWith('0000', Date::convertToDatetime($this->createdAt, Date::FORMAT_SQL_TIMESTAMP))) {
0 ignored issues
show
Bug introduced by zenn
It seems like Ffcms\Core\Helper\Date::...::FORMAT_SQL_TIMESTAMP) can also be of type false; however, parameter $where of Ffcms\Core\Helper\Type\Str::startsWith() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

197
        if (!Str::likeEmpty($this->createdAt) && !Str::startsWith('0000', /** @scrutinizer ignore-type */ Date::convertToDatetime($this->createdAt, Date::FORMAT_SQL_TIMESTAMP))) {
Loading history...
198
            $this->_content->created_at = Date::convertToDatetime($this->createdAt, Date::FORMAT_SQL_TIMESTAMP);
0 ignored issues
show
Documentation Bug introduced by Mihail
It seems like Ffcms\Core\Helper\Date::...::FORMAT_SQL_TIMESTAMP) can also be of type false. However, the property $created_at is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
199
        }
200
201
        // save poster data
202
        $posterPath = '/upload/gallery/' . $this->galleryFreeId . '/orig/' . $this->poster;
203
        if (File::exist($posterPath)) {
204
            $this->_content->poster = $this->poster;
205
        }
206
207
        // get temporary gallery id
208
        $tmpGalleryId = $this->galleryFreeId;
209
210
        // save row
211
        $this->_content->save();
212
        
213
        // update tags data in special table (relation: content->content_tags = oneToMany)
214
        ContentTag::where('content_id', $this->_content->id)->delete();
215
        $insertData = [];
216
        foreach ($this->metaKeywords as $lang => $keys) {
217
            // split keywords to tag array
218
            $tags = explode(',', $keys);
219
            foreach ($tags as $tag) {
220
                // cleanup tag from white spaces
221
                $tag = trim($tag);
222
                // prepare data to insert
223
                if (Str::length($tag) > 0) {
224
                    $insertData[] = [
225
                        'content_id' => $this->_content->id,
226
                        'lang' => $lang,
227
                        'tag' => $tag
228
                    ];
229
                }
230
            }
231
        }
232
        // insert tags
233
        ContentTag::insert($insertData);
234
235
        // move files
236
        if ($tmpGalleryId !== $this->_content->id) {
237
            Directory::rename('/upload/gallery/' . $tmpGalleryId, $this->_content->id);
238
        }
239
    }
240
241
    /**
242
     * Get allowed category ids as array
243
     * @return array
244
     */
245
    public function categoryIds(): ?array
246
    {
247
        $data = ContentCategory::getSortedCategories();
248
        return array_keys($data);
249
    }
250
251
    /**
252
     * Validate path filter
253
     * @return bool
254
     */
255
    public function validatePath(): bool
256
    {
257
        // try to find this item
258
        $find = Content::where('path', $this->path);
259
        // exclude self id
260
        if ($this->_content->id && Any::isInt($this->_content->id)) {
261
            $find->where('id', '!=', $this->_content->id);
262
        }
263
264
        // limit only current category id
265
        $find->where('category_id', $this->categoryId);
266
        return $find->count() < 1;
267
    }
268
269
    /**
270
     * Get users id->nick+mail list
271
     * @return array|null
272
     */
273
    public function getUserIdName(): ?array
274
    {
275
        $users = [];
276
        User::with('profile')->get()->each(function ($user) use (&$users) {
277
            /** @var User $user */
278
            $users[$user->id] = ($user->profile->nick ?? 'id' . $user->id) . ' (' . $user->email . ')';
279
        });
280
281
        return $users;
282
    }
283
284
    /**
285
     * Check if news is new
286
     * @return bool
287
     */
288
    public function isNew(): bool
289
    {
290
        return $this->_new;
291
    }
292
293
    /**
294
     * Get content comments
295
     * @return CommentPost[]|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|null
296
     */
297
    public function getComments()
298
    {
299
        if ($this->isNew()) {
300
            return null;
301
        }
302
303
        return $this->_content->commentPosts;
304
305
        /**return CommentPost::with(['user', 'user.profile'])
0 ignored issues
show
Unused Code Comprehensibility introduced by zenn
66% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
306
            ->where('app_name', 'content')
307
            ->where('app_relation_id', $this->_content->id)
308
            ->get();*/
309
    }
310
}
311