Completed
Push — master ( c2f76d...03586b )
by Freek
11:35
created

Media   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 319
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 16
dl 0
loc 319
rs 7.2769
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A model() 0 4 1
A getFullUrl() 0 4 1
A getUrl() 0 6 1
A getTemporaryUrl() 0 6 1
A getPath() 0 6 1
A getImageGenerators() 0 4 1
A getTypeAttribute() 0 10 2
A getTypeFromExtension() 0 12 2
A getTypeFromMime() 0 12 2
A getExtensionAttribute() 0 4 1
A getHumanReadableSizeAttribute() 0 4 1
A getDiskDriverName() 0 4 1
A hasCustomProperty() 0 4 1
A getCustomProperty() 0 4 1
A setCustomProperty() 0 10 1
A forgetCustomProperty() 0 10 1
A getMediaConversionNames() 0 8 1
A toResponse() 0 20 2
A getResponsiveImageUrls() 0 4 1
A hasResponsiveImages() 0 4 1
A getSrcset() 0 4 1
A toHtml() 0 4 1
B img() 0 46 6
A move() 0 8 1
A copy() 0 19 1
A responsiveImages() 0 4 1
A stream() 0 6 1
A __invoke() 0 4 1
1
<?php
2
3
namespace Spatie\MediaLibrary\Models;
4
5
use DateTimeInterface;
6
use Illuminate\Database\Eloquent\Relations\MorphTo;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\HtmlString;
9
use Spatie\MediaLibrary\Helpers\File;
10
use Illuminate\Database\Eloquent\Model;
11
use Illuminate\Contracts\Support\Htmlable;
12
use Spatie\MediaLibrary\HasMedia\HasMedia;
13
use Illuminate\Contracts\Support\Responsable;
14
use Spatie\MediaLibrary\Conversion\Conversion;
15
use Spatie\MediaLibrary\Filesystem\Filesystem;
16
use Spatie\MediaLibrary\Models\Concerns\IsSorted;
17
use Spatie\MediaLibrary\Helpers\TemporaryDirectory;
18
use Spatie\MediaLibrary\Conversion\ConversionCollection;
19
use Spatie\MediaLibrary\ImageGenerators\FileTypes\Image;
20
use Spatie\MediaLibrary\ResponsiveImages\ResponsiveImage;
21
use Spatie\MediaLibrary\Uploads\Models\TemporaryUpload;
22
use Spatie\MediaLibrary\UrlGenerator\UrlGeneratorFactory;
23
use Spatie\MediaLibrary\ResponsiveImages\ResponsiveImages;
24
use Spatie\MediaLibrary\ResponsiveImages\RegisteredResponsiveImages;
25
26
class Media extends Model implements Responsable, Htmlable
27
{
28
    use IsSorted;
29
30
    const TYPE_OTHER = 'other';
31
32
    protected $guarded = [];
33
34
    protected $casts = [
35
        'manipulations' => 'array',
36
        'custom_properties' => 'array',
37
        'responsive_images' => 'array',
38
    ];
39
40
    public function model(): MorphTo
41
    {
42
        return $this->morphTo();
43
    }
44
45
    /*
46
     * Get the full url to a original media file.
47
    */
48
    public function getFullUrl(string $conversionName = ''): string
49
    {
50
        return url($this->getUrl($conversionName));
51
    }
52
53
    /*
54
     * Get the url to a original media file.
55
     */
56
    public function getUrl(string $conversionName = ''): string
57
    {
58
        $urlGenerator = UrlGeneratorFactory::createForMedia($this, $conversionName);
59
60
        return $urlGenerator->getUrl();
61
    }
62
63
    public function getTemporaryUrl(DateTimeInterface $expiration, string $conversionName = '', array $options = []): string
64
    {
65
        $urlGenerator = UrlGeneratorFactory::createForMedia($this, $conversionName);
66
67
        return $urlGenerator->getTemporaryUrl($expiration, $options);
68
    }
69
70
    /*
71
     * Get the path to the original media file.
72
     */
73
    public function getPath(string $conversionName = ''): string
74
    {
75
        $urlGenerator = UrlGeneratorFactory::createForMedia($this, $conversionName);
76
77
        return $urlGenerator->getPath();
78
    }
79
80
    public function getImageGenerators(): Collection
81
    {
82
        return collect(config('medialibrary.image_generators'));
83
    }
84
85
    public function getTypeAttribute(): string
86
    {
87
        $type = $this->getTypeFromExtension();
88
89
        if ($type !== self::TYPE_OTHER) {
90
            return $type;
91
        }
92
93
        return $this->getTypeFromMime();
94
    }
95
96
    public function getTypeFromExtension(): string
97
    {
98
        $imageGenerator = $this->getImageGenerators()
99
            ->map(function (string $className) {
100
                return app($className);
101
            })
102
            ->first->canHandleExtension(strtolower($this->extension));
103
104
        return $imageGenerator
105
            ? $imageGenerator->getType()
106
            : static::TYPE_OTHER;
107
    }
108
109
    public function getTypeFromMime(): string
110
    {
111
        $imageGenerator = $this->getImageGenerators()
112
            ->map(function (string $className) {
113
                return app($className);
114
            })
115
            ->first->canHandleMime($this->mime_type);
116
117
        return $imageGenerator
118
            ? $imageGenerator->getType()
119
            : static::TYPE_OTHER;
120
    }
121
122
    public function getExtensionAttribute(): string
123
    {
124
        return pathinfo($this->file_name, PATHINFO_EXTENSION);
125
    }
126
127
    public function getHumanReadableSizeAttribute(): string
128
    {
129
        return File::getHumanReadableSize($this->size);
130
    }
131
132
    public function getDiskDriverName(): string
133
    {
134
        return strtolower(config("filesystems.disks.{$this->disk}.driver"));
135
    }
136
137
    /*
138
     * Determine if the media item has a custom property with the given name.
139
     */
140
    public function hasCustomProperty(string $propertyName): bool
141
    {
142
        return array_has($this->custom_properties, $propertyName);
143
    }
144
145
    /**
146
     * Get the value of custom property with the given name.
147
     *
148
     * @param string $propertyName
149
     * @param mixed $default
150
     *
151
     * @return mixed
152
     */
153
    public function getCustomProperty(string $propertyName, $default = null)
154
    {
155
        return array_get($this->custom_properties, $propertyName, $default);
156
    }
157
158
    /**
159
     * @param string $name
160
     * @param mixed $value
161
     *
162
     * @return $this
163
     */
164
    public function setCustomProperty(string $name, $value): self
165
    {
166
        $customProperties = $this->custom_properties;
167
168
        array_set($customProperties, $name, $value);
169
170
        $this->custom_properties = $customProperties;
171
172
        return $this;
173
    }
174
175
    public function forgetCustomProperty(string $name): self
176
    {
177
        $customProperties = $this->custom_properties;
178
179
        array_forget($customProperties, $name);
180
181
        $this->custom_properties = $customProperties;
182
183
        return $this;
184
    }
185
186
    /*
187
     * Get all the names of the registered media conversions.
188
     */
189
    public function getMediaConversionNames(): array
190
    {
191
        $conversions = ConversionCollection::createForMedia($this);
192
193
        return $conversions->map(function (Conversion $conversion) {
194
            return $conversion->getName();
195
        })->toArray();
196
    }
197
198
    /**
199
     * Create an HTTP response that represents the object.
200
     *
201
     * @param  \Illuminate\Http\Request $request
202
     *
203
     * @return \Illuminate\Http\Response
204
     */
205
    public function toResponse($request)
206
    {
207
        $downloadHeaders = [
208
            'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
209
            'Content-Type' => $this->mime_type,
210
            'Content-Length' => $this->size,
211
            'Content-Disposition' => 'attachment; filename="' . $this->file_name . '"',
212
            'Pragma' => 'public',
213
        ];
214
215
        return response()->stream(function () {
0 ignored issues
show
Bug Best Practice introduced by
The return type of return response()->strea...200, $downloadHeaders); (Symfony\Component\HttpFoundation\StreamedResponse) is incompatible with the return type declared by the interface Illuminate\Contracts\Sup...Responsable::toResponse of type Illuminate\Http\Response.

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...
216
            $stream = $this->stream();
217
218
            fpassthru($stream);
219
220
            if (is_resource($stream)) {
221
                fclose($stream);
222
            }
223
        }, 200, $downloadHeaders);
224
    }
225
226
    public function getResponsiveImageUrls(string $conversionName = ''): array
227
    {
228
        return $this->responsiveImages($conversionName)->getUrls();
229
    }
230
231
    public function hasResponsiveImages(string $conversionName = ''): bool
232
    {
233
        return count($this->getResponsiveImageUrls());
234
    }
235
236
    public function getSrcset(string $conversionName = ''): string
237
    {
238
        return $this->responsiveImages($conversionName)->getSrcset();
239
    }
240
241
    public function toHtml()
242
    {
243
        return $this->img();
244
    }
245
246
    /**
247
     * @param string|array $conversion
248
     * @param array $extraAttributes
249
     *
250
     * @return string
251
     */
252
    public function img($conversion = '', array $extraAttributes = []): string
253
    {
254
        if (! (new Image())->canHandleMime($this->mime_type)) {
255
            return '';
256
        }
257
258
        if (is_array($conversion)) {
259
            $attributes = $conversion;
260
261
            $conversion = $attributes['conversion'] ?? '';
262
263
            unset($attributes['conversion']);
264
265
            $extraAttributes = array_merge($attributes, $extraAttributes);
266
        }
267
268
        $attributeString = collect($extraAttributes)
269
            ->map(function ($value, $name) {
270
                return $name . '="' . $value . '"';
271
            })->implode(' ');
272
273
        if (strlen($attributeString)) {
274
            $attributeString = ' ' . $attributeString;
275
        }
276
277
        $media = $this;
278
279
        $viewName = 'image';
280
281
        $width = '';
282
283
        if ($this->hasResponsiveImages()) {
284
            $viewName = config('medialibrary.responsive_images.use_tiny_placeholders')
285
                ? 'responsiveImageWithPlaceholder'
286
                : 'responsiveImage';
287
288
            $width = $this->responsiveImages($conversion)->files->first()->width();
289
        }
290
291
        return view("medialibrary::{$viewName}", compact(
292
            'media',
293
            'conversion',
294
            'attributeString',
295
            'width'
296
        ));
297
    }
298
299
    public function move(HasMedia $model, $collectionName = 'default'): Media
300
    {
301
        $newMedia = $this->copy($model, $collectionName);
302
303
        $this->delete();
304
305
        return $newMedia;
306
    }
307
308
    public function copy(HasMedia $model, $collectionName = 'default'): Media
309
    {
310
        $temporaryDirectory = TemporaryDirectory::create();
311
312
        $temporaryFile = $temporaryDirectory->path($this->file_name);
313
314
        app(Filesystem::class)->copyFromMediaLibrary($this, $temporaryFile);
315
316
        $newMedia = $model
317
            ->addMedia($temporaryFile)
318
            ->usingName($this->name)
319
            ->toMediaCollection($collectionName);
320
321
        $newMedia->custom_properties = $this->custom_properties;
322
323
        $temporaryDirectory->delete();
324
325
        return $newMedia;
326
    }
327
328
    public function responsiveImages(string $conversionName = ''): RegisteredResponsiveImages
329
    {
330
        return new RegisteredResponsiveImages($this, $conversionName);
331
    }
332
333
    public function stream()
334
    {
335
        $filesystem = app(Filesystem::class);
336
337
        return $filesystem->getStream($this);
338
    }
339
340
    public function __invoke(...$arguments)
341
    {
342
        return new HtmlString($this->img(...$arguments));
343
    }
344
}
345