UploadFileBehavior::getPath()   A
last analyzed

Complexity

Conditions 5
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 3
nop 1
dl 0
loc 12
ccs 8
cts 8
cp 1
crap 5
rs 9.6111
c 0
b 0
f 0
1
<?php
2
//UploadFileBehavior.php
3
4
namespace coderius\yii2UploadFileBehavior;
5
6
use Closure;
7
use yii\base\Behavior;
8
use yii\db\ActiveRecord;
9
use yii\web\UploadedFile;
10
use yii\helpers\FileHelper;
11
use yii\base\InvalidCallException;
12
use yii\base\InvalidArgumentException;
13
use yii\base\InvalidConfigException;
14
use yii\base\NotSupportedException;
15
use yii\imagine\Image;
16
use Imagine\Image\Point;
17
use Imagine\Image\Box;
18
19
/**
20
 *  UploadFileBehavior class
21
 *  UploadFileBehavior helped create hendler for uploading files and images to server simply.
22
 *  To use UploadFileBehavior, insert the following code to your ActiveRecord class:
23
 *
24
 *  ```php
25
 * 
26
 *   namespase your/models;
27
 *
28
 *   use coderius\yii2UploadFileBehavior\UploadFileBehavior;
29
 *   use yii\imagine\Image;
30
 *    use Imagine\Image\Point;
31
 *    use Imagine\Image\Box;
32
 *
33
 *    class YourModel extends \yii\db\ActiveRecord
34
 *    {
35
 *        public $file;
36
 *
37
 *        //'img_src' - attribute to save path to file in db
38
 *        public function rules()
39
 *        {
40
 *            return [
41
 *                [['img_src'], 'safe'],
42
 *        }
43
 *
44
 *        ...
45
 *
46
 *            public function behaviors()
47
 *            {
48
 *                return [
49
 *                    //Another behaviors
50
 *                    //...
51
 *
52
 *                    'uploadFileBehavior' => [
53
 *                        'class' => UploadFileBehavior::className(),
54
 *                        'nameOfAttributeStorage' => 'img_src',
55
                    
56
                    
57
 *                        'targets' => [
58
 *                            
59
 *                            [
60
 *                                'path' => function($attributes){
61
 *                                    return \Yii::getAlias('@portfoleoPhotosPath/' . $attributes['id'] . '/big/');
62
 *                                },
63
 *                                'hendler' => function($fileTempName, $newFilePath){
64
 *                                    Image::thumbnail($fileTempName, 900, 900*2/3)
65
 *                                    ->copy()
66
 *                                    ->crop(new Point(0, 0), new Box(900, 900*2/3))
67
 *                                    ->save($newFilePath, ['quality' => 80]);
68
 *                                    sleep(1);
69
 *                                }
70
 *                            ],
71
 *                            [
72
 *                                'path' => function($attributes){
73
 *                                    return \Yii::getAlias('@portfoleoPhotosPath/' . $attributes['id'] . '/middle/');
74
 *                                },
75
 *                                'hendler' => function($fileTempName, $newFilePath){
76
 *                                    Image::thumbnail($fileTempName, 400, 400*2/3)
77
 *                                    ->save($newFilePath, ['quality' => 80]);
78
 *                                    sleep(1);
79
 *                                }
80
 *                            ],
81
 *                            [
82
 *                                'path' => function($attributes){
83
 *                                    return \Yii::getAlias('@portfoleoPhotosPath/' . $attributes['id'] . '/small/');
84
 *                                },
85
 *                                'hendler' => function($fileTempName, $newFilePath){
86
 *                                    Image::thumbnail($fileTempName, 150, 150*2/3)
87
 *                                    ->save($newFilePath, ['quality' => 80]);
88
 *                                    sleep(1);
89
 *                                }
90
 *                            ],
91
 *                        ]
92
 *                    ],
93
 *
94
 *                ];
95
 *            }
96
 *
97
 *        ...
98
 *    }    
99
 *
100
 *  ```
101
 * 
102
 * @author Sergio Coderius <[email protected]>
103
 * @since 2.0
104
 */
105
class UploadFileBehavior extends Behavior
106
{
107
    const TYPE_IMAGE = 'image';
108
    const TYPE_FILE = 'file';//not supported yet
109
    
110
    /**
111
     * Name of attribute for recording file from form to ActiveRecord. DEfault name of attribute is `file`
112
     *
113
     * @var string
114
     */
115
    public $nameOfAttributeFile = 'file';
116
117
    /**
118
     * @var string
119
     */
120
    public $nameOfAttributeStorage = 'face_img';
121
    
122
    /**
123
     * Image name for renaming file and in next saving in file system and db
124
     *
125
     * @var string
126
     */
127
    public $newFileName = false;
128
129
    /**
130
     * Configuretion array for setting target paths to folders and uploading hendlers like in example:
131
     *
132
     * [
133
     *    'path' => function($attributes){
134
     *          return \Yii::getAlias('@portfoleoPhotosPath/' . $attributes['id'] . '/middle/');
135
     *     },
136
     *     'hendler' => function($fileTempName, $newFilePath){
137
     *           Image::thumbnail($fileTempName, 400, 400*2/3)
138
     *                 ->save($newFilePath, ['quality' => 80]);
139
     *            sleep(1);
140
     *     }
141
     * ],
142
     *
143
     * @var array
144
     */
145
    public $targets;
146
147
148
    /**
149
     * 'default' scenario set by default. 
150
     * @var array the scenarios in which the behavior will be triggered
151
     */
152
    public $scenarios = [];
153
154
    /**
155
     * Flag for delete file with related item in db. Default set to true.
156
     *
157
     * @var boolean
158
     */
159
    public $deleteImageWithRecord = true;
160
    
161
    /**
162
     * File instance populated by yii\web\UploadedFile::getInstance
163
     *
164
     * @var null|UploadedFile the instance of the uploaded file.
165
     */
166
    private $fileInstance;
167
168
    /**
169
     * Undocumented variable
170
     *
171
     * @var boolean|int
172
     */
173
    private $time = false;
174
175
        /**
176
     * @inheritdoc
177
     */
178 45
    public function init()
179
    {
180 45
        parent::init();
181
182 45
        if (!class_exists(Image::class)) {
183
            throw new NotSupportedException("Yii2-imagine extension is required to use the UploadImageBehavior");
184
        }
185
186 45
        if ($this->targets == null || $this->targets == false || empty($this->targets)) {
187 9
            throw new InvalidConfigException('The "targets" property must be set.');
188
        }
189 45
        if (!is_array($this->targets)) {
0 ignored issues
show
introduced by
The condition is_array($this->targets) is always true.
Loading history...
190 3
            throw new InvalidConfigException('The "targets" property must be an array.');
191
        }
192 45
        if (empty($this->scenarios)) {
193 45
            $this->scenarios[] = 'default';
194
        }
195
196 45
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201 42
    public function attach($owner)
202
    {
203 42
        parent::attach($owner);
204 42
        $this->time = time();
205 42
        $this->setFileInstance();
206
        
207 42
    }
208
209
     /**
210
     * {@inheritdoc}
211
     */
212 42
    public function events()
213
    {
214
        return [
215 42
            ActiveRecord::EVENT_BEFORE_INSERT => 'loadFile',
216
            ActiveRecord::EVENT_BEFORE_UPDATE => 'loadFile',
217
            ActiveRecord::EVENT_AFTER_INSERT => 'afterInsertHendler',
218
            ActiveRecord::EVENT_AFTER_UPDATE => 'afterUpdateHendler',
219
            ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete'
220
        ];
221
    }
222
223 6
    protected static function supportedFileTypes(){
224
        return [
225 6
            self::TYPE_IMAGE
226
        ];
227
    }
228
229
    /**
230
     * Undocumented function
231
     *
232
     *  @return void
233
     */
234 27
    public function loadFile()
235
    {
236 27
        if($this->isFile()){
237 27
            $this->owner->file = $this->getFileInstance();//virtual attribute
238 27
            $this->owner->{$this->nameOfAttributeStorage} = $this->getNewFileName();
239
        }
240 27
    }
241
    
242
    /**
243
     * Undocumented function
244
     *
245
     * @return void
246
     */
247 27
    public function afterInsertHendler($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

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

247
    public function afterInsertHendler(/** @scrutinizer ignore-unused */ $event)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
248
    {
249 27
        $this->hendlersReducer(true);
250 18
    }
251
    
252
    /**
253
     * Undocumented function
254
     *
255
     * @return void
256
     */
257 3
    public function afterUpdateHendler($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

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

257
    public function afterUpdateHendler(/** @scrutinizer ignore-unused */ $event)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
258
    {
259 3
        $this->hendlersReducer(false);
260 3
    }          
261
    
262
    /**
263
     * Undocumented function
264
     *
265
     * @param Event $event
0 ignored issues
show
Bug introduced by
The type coderius\yii2UploadFileBehavior\Event was not found. Did you mean Event? If so, make sure to prefix the type with \.
Loading history...
266
     * @return void
267
     */
268 3
    public function afterDelete($event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

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

268
    public function afterDelete(/** @scrutinizer ignore-unused */ $event)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
269
    {
270 3
        if($this->deleteImageWithRecord){
271 3
            foreach($this->targets as $target){
272 3
                $dirPath = $this->getPath($target['path']);
273 3
                FileHelper::removeDirectory($dirPath);
274
            }    
275
            
276
        }
277 3
    } 
278
279
280
    /**
281
     * Undocumented function
282
     *
283
     * @param boolean $insert
284
     * @return void
285
     */
286 27
    protected function hendlersReducer($insert)
287
    {
288 27
        if ($this->isFile() && $this->inScenario())
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->inScenario() targeting coderius\yii2UploadFileB...eBehavior::inScenario() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
289
        {
290 27
            foreach($this->targets as $target){
291 27
                $dirPath = $this->getPath($target['path']);
292
                
293 27
                if(!$insert){
294
                    
295 3
                    FileHelper::removeDirectory($dirPath);
296
                }
297
                
298 27
                FileHelper::createDirectory($dirPath);
299 27
                $newFilePath = $dirPath . $this->getNewFileName();
300
301 27
                $this->getHendler($target['hendler'], $this->getFileInstance()->tempName, $newFilePath);
302
                
303
            }
304
        }
305 18
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
306
    }
307
308
    /**
309
     * @param string $path
310
     * @return string $dirPath
311
     */
312 30
    protected function getPath($path){
313 30
        if(is_string($path)){
0 ignored issues
show
introduced by
The condition is_string($path) is always true.
Loading history...
314 9
            $path = rtrim($path, '/') . '/';
315 9
            $dirPath = \Yii::getAlias($path);
316
        }
317 21
        elseif ($path instanceof Closure || (is_array($path) && is_callable($path))) {
318 21
            $dirPath = $path($this->owner->attributes);
319
        }else{
320 3
            throw new InvalidCallException('Param `path` mast be string instanceof Closure or callable method.');
321
        }
322
323 30
        return $dirPath;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dirPath also could return the type boolean which is incompatible with the documented return type string.
Loading history...
324
    }
325
326 27
    protected function getHendler($hendler, $tmp, $path){
327 27
        if ($hendler instanceof Closure || (is_array($hendler) && is_callable($hendler))) {
328 18
            $hendler($tmp, $path);
329 9
        }elseif(is_array($hendler) && array_key_exists('type', $hendler) && array_key_exists('config', $hendler)){
330 6
            if(!in_array($hendler['type'], self::supportedFileTypes()))
331
            {
332 3
                throw new InvalidConfigException('File type not supported: ' . $hendler['type']);
333
            }
334 3
            switch($hendler['type']) {
335 3
                case self::TYPE_IMAGE:
336 3
                    $sizeW = $hendler['config']['size']['width'];
337 3
                    $sizeH = $hendler['config']['size']['height'];
338 3
                    $quality = $hendler['config']['quality'];
339 3
                    Image::thumbnail($tmp, $sizeW, $sizeH)
340 3
                            ->save($path, ['quality' => $quality]);
341 3
                            sleep(1);
342 3
                break;
343
            }
344
345
        }else{
346 3
            throw new InvalidCallException('Param `hendler` mast be instanceof Closure ,callable method or array with allowed configs.');
347
        }
348
349 21
        return true;
350
    }
351
352
    /**
353
     * Get the instance of the uploaded file.
354
     *
355
     * @return  null|UploadedFile
356
     */ 
357 27
    protected function getFileInstance()
358
    {
359 27
        return $this->fileInstance;
360
    }
361
362
    /**
363
     * Set the instance of the uploaded file.
364
     *
365
     * @param  null|UploadedFile  $fileInstance  the instance of the uploaded file.
366
     *
367
     * @return  self
368
     */ 
369 42
    protected function setFileInstance()
370
    {
371 42
        $this->fileInstance = UploadedFile::getInstance($this->owner, $this->nameOfAttributeFile);
372 42
    }
373
374
    /**
375
     * Get image name for renaming file and in next saving in file system and db
376
     *
377
     * @return  [string]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [string] at position 0 could not be parsed: Unknown type name '[' at position 0 in [string].
Loading history...
378
     */ 
379 27
    protected function getNewFileName()
380
    {
381 27
        $file = $this->getFileInstance();
382 27
        $baseName = $file->baseName;
0 ignored issues
show
Unused Code introduced by
The assignment to $baseName is dead and can be removed.
Loading history...
383 27
        $ext = $file->extension;
0 ignored issues
show
Unused Code introduced by
The assignment to $ext is dead and can be removed.
Loading history...
384
        
385 27
         return $this->newFileName ?
386 12
            $this->newFileName . '.' . $file->extension :
387 27
            $file->baseName . '_' . $this->time . '.' . $file->extension;
388
    }
389
390
    /**
391
     * Undocumented function
392
     *
393
     * @return boolean
394
     */
395 27
    protected function isFile()
396
    {
397 27
        return  $this->getFileInstance() && $this->getFileInstance()->tempName;
398
    }
399
400
    /**
401
     * Detect if current model scenario in allowed by behavior
402
     *
403
     * @return void
404
     */
405 27
    protected function inScenario()
406
    {
407 27
        $model = $this->owner;
408 27
        return in_array($model->scenario, $this->scenarios);
0 ignored issues
show
Bug Best Practice introduced by
The expression return in_array($model->...ario, $this->scenarios) returns the type boolean which is incompatible with the documented return type void.
Loading history...
409
    }
410
411
}