Passed
Push — main ( 839ea2...8564cc )
by Sugeng
03:17
created

ModelTrait::getFilePresignedUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 9
rs 10
1
<?php
2
3
namespace diecoding\aws\s3\traits;
4
5
use Yii;
6
use yii\web\UploadedFile;
7
8
/**
9
 * Trait ModelTrait for Model
10
 * 
11
 * @package diecoding\aws\s3\traits
12
 */
13
trait ModelTrait
14
{
15
    /**
16
     * @return \diecoding\aws\s3\Service
17
     */
18
    public function getS3Component()
19
    {
20
        return Yii::$app->get('s3');
0 ignored issues
show
Bug Best Practice introduced by
The expression return Yii::app->get('s3') also could return the type mixed which is incompatible with the documented return type diecoding\aws\s3\Service.
Loading history...
21
    }
22
23
    /**
24
     * Save UploadedFile to AWS S3.
25
     * ! Important: This function uploads this model filename to keep consistency of the model.
26
     * 
27
     * @param UploadedFile $file Uploaded file to save
28
     * @param string $attribute Attribute name where the uploaded filename name will be saved
29
     * @param string $fileName Name which file will be saved. If empty will use the name from $file
30
     * @param bool $updateExtension `true` to automatically append the extension to the file name. Default is `true`
31
     * 
32
     * @return string|false Uploaded full path of filename on success or `false` in failure.
33
     */
34
    public function saveUploadedFile(UploadedFile $file, $attribute, $fileName = '', $updateExtension = true)
35
    {
36
        if ($this->hasError && !$file instanceof UploadedFile) {
0 ignored issues
show
introduced by
$file is always a sub-type of yii\web\UploadedFile.
Loading history...
37
            return false;
38
        }
39
40
        if (empty($fileName)) {
41
            $fileName = $file->name;
42
        }
43
        if ($updateExtension) {
44
            $fileName .= '.' . $file->extension;
45
        }
46
47
        $filePath = $this->getAttributePath($attribute) . $fileName;
48
49
        /** @var \Aws\ResultInterface $result */
50
        $result = $this->getS3Component()
51
            ->commands()
52
            ->upload(
53
                $filePath,
54
                $file->tempName
55
            )
56
            ->withContentType($file->type)
57
            ->execute();
58
59
        // Validate successful upload to S3
60
        if ($this->isSuccessResponseStatus($result)) {
61
            $this->{$attribute} = $fileName;
62
63
            return $fileName;
64
        }
65
66
        return false;
67
    }
68
69
    /**
70
     * Delete model file attribute from AWS S3.
71
     * 
72
     * @param string $attribute Attribute name which holds the filename
73
     * 
74
     * @return bool `true` on success or if file doesn't exist.
75
     */
76
    public function removeFile($attribute)
77
    {
78
        if (empty($this->{$attribute})) {
79
            // No file to remove
80
            return true;
81
        }
82
83
        $filePath = $this->getAttributePath($attribute) . $this->{$attribute};
84
        $result = $this->getS3Component()
85
            ->commands()
86
            ->delete($filePath)
87
            ->execute();
88
89
        // Validate successful removal from S3
90
        if ($this->isSuccessResponseStatus($result)) {
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type GuzzleHttp\Promise\PromiseInterface; however, parameter $response of diecoding\aws\s3\traits\...SuccessResponseStatus() does only seem to accept Aws\ResultInterface, 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

90
        if ($this->isSuccessResponseStatus(/** @scrutinizer ignore-type */ $result)) {
Loading history...
91
            $this->{$attribute} = null;
92
93
            return true;
94
        }
95
96
        return false;
97
    }
98
99
    /**
100
     * Retrieves the URL for a given model file attribute.
101
     * 
102
     * @param string $attribute Attribute name which holds the filename
103
     * 
104
     * @return string URL to access file
105
     */
106
    public function getFileUrl($attribute)
107
    {
108
        if (empty($this->{$attribute})) {
109
            return '';
110
        }
111
112
        return $this->getS3Component()->getUrl($this->getAttributePath($attribute) . $this->{$attribute});
113
    }
114
115
    /**
116
     * Retrieves the presigned URL for a given model file attribute.
117
     * 
118
     * @param string $attribute Attribute name which holds the filename
119
     * 
120
     * @return string Presigned URL to access file
121
     */
122
    public function getFilePresignedUrl($attribute)
123
    {
124
        if (empty($this->{$attribute})) {
125
            return '';
126
        }
127
128
        return $this->getS3Component()->getPresignedUrl(
129
            $this->getAttributePath($attribute) . $this->{$attribute},
130
            $this->getPresignedUrlDuration()
131
        );
132
    }
133
134
    /**
135
     * Retrieves the URL signature expiration.
136
     * 
137
     * @return mixed URL expiration
138
     */
139
    protected function getPresignedUrlDuration()
140
    {
141
        return '+30 minutes';
142
    }
143
144
    /**
145
     * Retrieves the base path on AWS S3 for a given attribute.
146
     * @see attributePaths()
147
     * 
148
     * @param string $attribute Attribute to get its path
149
     * 
150
     * @return string The path where all file of that attribute should be stored. Returns empty string if the attribute isn't in the list.
151
     */
152
    protected function getAttributePath($attribute)
153
    {
154
        $paths = $this->attributePaths();
155
        if (array_key_exists($attribute, $paths)) {
156
            return $paths[$attribute];
157
        }
158
159
        return '';
160
    }
161
162
    /**
163
     * List the paths on AWS S3 to each model file attribute.
164
     * It must be a Key-Value array, where Key is the attribute name and Value is the base path for the file in S3.
165
     * Override this method for saving each attribute in its own "folder".
166
     * 
167
     * @return array Key-Value of attributes and its paths.
168
     */
169
    protected function attributePaths()
170
    {
171
        return [];
172
    }
173
174
    /**
175
     * Check for valid status code from the AWS S3 response.
176
     * Success responses will be considered status codes is 2**.
177
     * Override function for custom validations.
178
     * 
179
     * @param \Aws\ResultInterface $response AWS S3 response containing the status code
180
     * 
181
     * @return bool whether this response is successful.
182
     */
183
    protected function isSuccessResponseStatus($response)
184
    {
185
        return !empty($response->get('@metadata')['statusCode']) &&
186
            $response->get('@metadata')['statusCode'] >= 200 &&
187
            $response->get('@metadata')['statusCode'] < 300;
188
    }
189
}
190