Completed
Push — master ( 3b1f1a...f15f2a )
by Igor
02:55
created

UploadAction::setState()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 9
ccs 3
cts 3
cp 1
rs 9.6666
cc 2
eloc 6
nc 2
nop 1
crap 2
1
<?php
2
3
/**
4
 * @link https://github.com/rkit/filemanager-yii2
5
 * @copyright Copyright (c) 2015 Igor Romanov
6
 * @license [MIT](http://opensource.org/licenses/MIT)
7
 */
8
9
namespace rkit\filemanager\actions;
10
11
use Yii;
12
use yii\base\Action;
13
use yii\base\DynamicModel;
14
use yii\base\InvalidParamException;
15
use yii\web\UploadedFile;
16
17
class UploadAction extends Action
18
{
19
    /**
20
     * @var string $modelClass Class name of the model
21
     */
22
    public $modelClass;
23
    /**
24
     * @var string $attribute Attribute name of the model
25
     */
26
    public $attribute;
27
    /**
28
     * @var string $inputName The name of the file input field
29
     */
30
    public $inputName;
31
    /**
32
     * @var string $resultFieldId The name of the field that contains the id of the file in the response
33
     */
34
    public $resultFieldId = 'id';
35
    /**
36
     * @var string $resultFieldPath The name of the field that contains the path of the file in the response
37
     */
38
    public $resultFieldPath = 'path';
39
    /**
40
     * @var ActiveRecord $model
41
     */
42
    private $model;
43
44
    public function init()
45
    {
46
        if ($this->modelClass === null) {
47
            throw new InvalidParamException(get_class($this) . '::$modelClass must be set');
48
        }
49
50
        $this->model = new $this->modelClass;
51
    }
52
53
    public function run()
54
    {
55
        $file = UploadedFile::getInstanceByName($this->inputName);
56
57
        if (!$file) {
58
            return $this->response(
59
                ['error' => Yii::t('filemanager-yii2', 'An error occured, try again later…')]
60 28
            );
61
        }
62 28
63 1
        $rules = $this->model->getFileRules($this->attribute, true);
64
        $type = $this->model->getFileOption($this->attribute, 'type');
65
66 27
        $model = new DynamicModel(compact('file'));
67 27
        $model->addRule('file', $type, $rules)->validate();
68
69 27
        if ($model->hasErrors()) {
70
            return $this->response(['error' => $model->getFirstError('file')]);
71 27
        }
72
        return $this->upload($file);
73 27
    }
74 1
75 1
    /**
76 1
     * Upload
77
     *
78
     * @param UploadedFile $file
79 26
     * @return string JSON
80
     * @SuppressWarnings(PHPMD.ElseExpression)
81 26
     */
82 26
    private function upload($file)
83
    {
84 26
        $file = $this->createFile($this->attribute, $file->tempName, $file->name);
85 1
        if ($file) {
86
            $this->setState($file);
87 25
            $presetAfterUpload = $this->model->getFilePresetAfterUpload($this->attribute);
88
            if (count($presetAfterUpload)) {
89
                $this->applyPreset($presetAfterUpload);
90
            }
91
            $isMultiple = $this->model->getFileOption($this->attribute, 'multiple');
92
            $template = $this->model->getFileOption($this->attribute, 'template');
93
            if ($isMultiple && $template) {
94
                return $this->response(
95
                    $this->controller->renderFile($template, [
96 25
                        'file' => $file,
97
                        'model' => $this->model,
98 25
                        'attribute' => $this->attribute
99 25
                    ])
100 25
                );
101 25
            }
102 25
            return $this->response([
103 25
                $this->resultFieldId => $file->getPrimaryKey(),
104
                $this->resultFieldPath => $this->model->getFilePath($this->attribute, $file),
105 25
            ]);
106 25
        }
107 25
        return $this->response(['error' => Yii::t('filemanager-yii2', 'Error saving file')]); // @codeCoverageIgnore
108 22
    }
109 22
110 25
    /**
111 3
     * Create a file
112 3
     *
113 3
     * @param string $attribute Attribute name of the model
114 3
     * @param string $path File path
115 3
     * @param string $title File title
116 3
     * @return rkit\filemanager\models\File
117 3
     */
118
    public function createFile($attribute, $path, $title = null)
119 23
    {
120 23
        if (!file_exists($path)) {
121 23
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by rkit\filemanager\actions\UploadAction::createFile of type rkit\filemanager\actions...filemanager\models\File.

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...
122 23
        }
123
124
        $onCreateFile = $this->model->getFileOption($attribute, 'onCreateFile');
125
        $file = $onCreateFile($title, $path);
126
        if ($file->save()) {
127
            $storage = $this->model->getFileStorage($attribute);
128
            $realPath = $this->model->getFileOption($attribute, 'basePath');
129
            $templatePath = $this->model->getFileOption($attribute, 'templatePath');
130
            if ($storage->saveFile($path, $realPath . $templatePath($file))) {
131
                return $file;
132
            }
133
        } // @codeCoverageIgnore
134 22
        return false; // @codeCoverageIgnore
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by rkit\filemanager\actions\UploadAction::createFile of type rkit\filemanager\actions...filemanager\models\File.

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...
135
    }
136 22
137 22
    private function setState($file)
138 22
    {
139 22
        $state = Yii::$app->session->get(Yii::$app->fileManager->sessionName);
140
        if (!is_array($state)) {
141
            $state = [];
142
        }
143
        $state[$this->attribute][] = $file->getPrimaryKey();
144
        Yii::$app->session->set(Yii::$app->fileManager->sessionName, $state);
145
    }
146
147 27
    /**
148
     * Apply preset for file
149
     *
150
     * @param array $presetAfterUpload
151
     * @return void
152
     */
153
    private function applyPreset($presetAfterUpload)
154 27
    {
155
        foreach ($presetAfterUpload as $preset) {
156
            $this->model->thumb($this->attribute, $preset);
157
        }
158
    }
159
160
    /**
161
     * JSON Response
162
     *
163
     * @param mixed $data
164
     * @return string JSON Only for yii\web\Application, for console app returns `mixed`
165
     */
166
    private function response($data)
167
    {
168
        // @codeCoverageIgnoreStart
169
        if (!Yii::$app instanceof \yii\console\Application) {
170
            \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
171
        }
172
        // @codeCoverageIgnoreEnd
173
        return $data;
174
    }
175
}
176