Completed
Push — master ( 0d1833...676fbf )
by Alexander
02:03
created

UploadImageBehavior   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 206
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 67.71%

Importance

Changes 2
Bugs 1 Features 1
Metric Value
wmc 31
c 2
b 1
f 1
lcom 1
cbo 10
dl 0
loc 206
ccs 65
cts 96
cp 0.6771
rs 9.8

9 Methods

Rating   Name   Duplication   Size   Complexity  
C init() 0 24 7
A afterUpload() 0 7 2
B createThumbs() 0 15 5
A getThumbUploadPath() 0 10 3
A getThumbUploadUrl() 0 20 4
A getPlaceholderUrl() 0 14 2
A delete() 0 12 3
A getThumbFileName() 0 4 1
A generateImageThumb() 0 21 4
1
<?php
2
3
namespace mongosoft\file;
4
5
use Imagine\Image\ManipulatorInterface;
6
use Yii;
7
use yii\base\InvalidConfigException;
8
use yii\base\InvalidParamException;
9
use yii\db\BaseActiveRecord;
10
use yii\helpers\ArrayHelper;
11
use yii\helpers\FileHelper;
12
use yii\imagine\Image;
13
14
/**
15
 * UploadImageBehavior automatically uploads image, creates thumbnails and fills
16
 * the specified attribute with a value of the name of the uploaded image.
17
 *
18
 * To use UploadImageBehavior, insert the following code to your ActiveRecord class:
19
 *
20
 * ```php
21
 * use mongosoft\file\UploadImageBehavior;
22
 *
23
 * function behaviors()
24
 * {
25
 *     return [
26
 *         [
27
 *             'class' => UploadImageBehavior::className(),
28
 *             'attribute' => 'file',
29
 *             'scenarios' => ['insert', 'update'],
30
 *             'placeholder' => '@app/modules/user/assets/images/userpic.jpg',
31
 *             'path' => '@webroot/upload/{id}/images',
32
 *             'url' => '@web/upload/{id}/images',
33
 *             'thumbPath' => '@webroot/upload/{id}/images/thumb',
34
 *             'thumbUrl' => '@web/upload/{id}/images/thumb',
35
 *             'thumbs' => [
36
 *                   'thumb' => ['width' => 400, 'quality' => 90],
37
 *                   'preview' => ['width' => 200, 'height' => 200],
38
 *              ],
39
 *         ],
40
 *     ];
41
 * }
42
 * ```
43
 *
44
 * @author Alexander Mohorev <[email protected]>
45
 */
46
class UploadImageBehavior extends UploadBehavior
47
{
48
    /**
49
     * @var string
50
     */
51
    public $placeholder;
52
    /**
53
     * @var boolean
54
     */
55
    public $createThumbsOnSave = true;
56
    /**
57
     * @var boolean
58
     */
59
    public $createThumbsOnRequest = false;
60
    /**
61
     * @var array the thumbnail profiles
62
     * - `width`
63
     * - `height`
64
     * - `quality`
65
     */
66
    public $thumbs = [
67
        'thumb' => ['width' => 200, 'height' => 200, 'quality' => 90],
68
    ];
69
    /**
70
     * @var string|null
71
     */
72
    public $thumbPath;
73
    /**
74
     * @var string|null
75
     */
76
    public $thumbUrl;
77
78
79
    /**
80
     * @inheritdoc
81
     */
82 3
    public function init()
83
    {
84 3
        parent::init();
85
86 3
        if ($this->createThumbsOnSave) {
87 3
            if ($this->thumbPath === null) {
88 3
                $this->thumbPath = $this->path;
89 3
            }
90 3
            if ($this->thumbUrl === null) {
91 3
                $this->thumbUrl = $this->url;
92 3
            }
93
94 3
            foreach ($this->thumbs as $config) {
95 3
                $width = ArrayHelper::getValue($config, 'width');
96 3
                $height = ArrayHelper::getValue($config, 'height');
97 3
                if ($height < 1 && $width < 1) {
98
                    throw new InvalidConfigException(sprintf(
99
                        'Length of either side of thumb cannot be 0 or negative, current size ' .
100
                            'is %sx%s', $width, $height
101
                    ));
102
                }
103 3
            }
104 3
        }
105 3
    }
106
107
    /**
108
     * @inheritdoc
109
     */
110 2
    protected function afterUpload()
111
    {
112 2
        parent::afterUpload();
113 2
        if ($this->createThumbsOnSave) {
114 2
            $this->createThumbs();
115 2
        }
116 2
    }
117
118
    /**
119
     * @throws \yii\base\InvalidParamException
120
     */
121 2
    protected function createThumbs()
122
    {
123 2
        $path = $this->getUploadPath($this->attribute);
124 2
        foreach ($this->thumbs as $profile => $config) {
125 2
            $thumbPath = $this->getThumbUploadPath($this->attribute, $profile);
126 2
            if ($thumbPath !== null) {
127 2
                if (!FileHelper::createDirectory(dirname($thumbPath))) {
128
                    throw new InvalidParamException("Directory specified in 'thumbPath' attribute doesn't exist or cannot be created.");
129
                }
130 2
                if (!is_file($thumbPath)) {
131 2
                    $this->generateImageThumb($config, $path, $thumbPath);
132 2
                }
133 2
            }
134 2
        }
135 2
    }
136
137
    /**
138
     * @param string $attribute
139
     * @param string $profile
140
     * @param boolean $old
141
     * @return string
142
     */
143 2
    public function getThumbUploadPath($attribute, $profile = 'thumb', $old = false)
144
    {
145
        /** @var BaseActiveRecord $model */
146 2
        $model = $this->owner;
147 2
        $path = $this->resolvePath($this->thumbPath);
148 2
        $attribute = ($old === true) ? $model->getOldAttribute($attribute) : $model->$attribute;
149 2
        $filename = $this->getThumbFileName($attribute, $profile);
150
151 2
        return $filename ? Yii::getAlias($path . '/' . $filename) : null;
152
    }
153
154
    /**
155
     * @param string $attribute
156
     * @param string $profile
157
     * @return string|null
158
     */
159
    public function getThumbUploadUrl($attribute, $profile = 'thumb')
160
    {
161
        /** @var BaseActiveRecord $model */
162
        $model = $this->owner;
163
        $path = $this->getUploadPath($attribute, true);
164
        if (is_file($path)) {
165
            if ($this->createThumbsOnRequest) {
166
                $this->createThumbs();
167
            }
168
            $url = $this->resolvePath($this->thumbUrl);
169
            $fileName = $model->getOldAttribute($attribute);
170
            $thumbName = $this->getThumbFileName($fileName, $profile);
171
172
            return Yii::getAlias($url . '/' . $thumbName);
173
        } elseif ($this->placeholder) {
174
            return $this->getPlaceholderUrl($profile);
175
        } else {
176
            return null;
177
        }
178
    }
179
180
    /**
181
     * @param $profile
182
     * @return string
183
     */
184
    protected function getPlaceholderUrl($profile)
185
    {
186
        list ($path, $url) = Yii::$app->assetManager->publish($this->placeholder);
187
        $filename = basename($path);
188
        $thumb = $this->getThumbFileName($filename, $profile);
189
        $thumbPath = dirname($path) . DIRECTORY_SEPARATOR . $thumb;
190
        $thumbUrl = dirname($url) . '/' . $thumb;
191
192
        if (!is_file($thumbPath)) {
193
            $this->generateImageThumb($this->thumbs[$profile], $path, $thumbPath);
194
        }
195
196
        return $thumbUrl;
197
    }
198
199
    /**
200
     * @inheritdoc
201
     */
202 1
    protected function delete($attribute, $old = false)
203
    {
204 1
        parent::delete($attribute, $old);
205
206 1
        $profiles = array_keys($this->thumbs);
207 1
        foreach ($profiles as $profile) {
208 1
            $path = $this->getThumbUploadPath($attribute, $profile, $old);
209 1
            if (is_file($path)) {
210
                unlink($path);
211
            }
212 1
        }
213 1
    }
214
215
    /**
216
     * @param $filename
217
     * @param string $profile
218
     * @return string
219
     */
220 2
    protected function getThumbFileName($filename, $profile = 'thumb')
221
    {
222 2
        return $profile . '-' . $filename;
223
    }
224
225
    /**
226
     * @param $config
227
     * @param $path
228
     * @param $thumbPath
229
     */
230 2
    protected function generateImageThumb($config, $path, $thumbPath)
231
    {
232 2
        $width = ArrayHelper::getValue($config, 'width');
233 2
        $height = ArrayHelper::getValue($config, 'height');
234 2
        $quality = ArrayHelper::getValue($config, 'quality', 100);
235 2
        $mode = ArrayHelper::getValue($config, 'mode', ManipulatorInterface::THUMBNAIL_INSET);
236
237 2
        if (!$width || !$height) {
238 2
            $image = Image::getImagine()->open($path);
239 2
            $ratio = $image->getSize()->getWidth() / $image->getSize()->getHeight();
240 2
            if ($width) {
241 2
                $height = ceil($width / $ratio);
242 2
            } else {
243
                $width = ceil($height * $ratio);
244
            }
245 2
        }
246
247
        // Fix error "PHP GD Allowed memory size exhausted".
248 2
        ini_set('memory_limit', '512M');
249 2
        Image::thumbnail($path, $width, $height, $mode)->save($thumbPath, ['quality' => $quality]);
250 2
    }
251
}
252