Completed
Push — master ( 3d3ccc...56c03c )
by Freek
03:52
created

Media::markAsConversionGenerated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
3
namespace Spatie\MediaLibrary\Models;
4
5
use DateTimeInterface;
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\HtmlString;
8
use Spatie\MediaLibrary\Helpers\File;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Contracts\Support\Htmlable;
11
use Spatie\MediaLibrary\HasMedia\HasMedia;
12
use Illuminate\Contracts\Support\Responsable;
13
use Spatie\MediaLibrary\Conversion\Conversion;
14
use Spatie\MediaLibrary\Filesystem\Filesystem;
15
use Spatie\MediaLibrary\Models\Concerns\IsSorted;
16
use Illuminate\Database\Eloquent\Relations\MorphTo;
17
use Spatie\MediaLibrary\Helpers\TemporaryDirectory;
18
use Spatie\MediaLibrary\Conversion\ConversionCollection;
19
use Spatie\MediaLibrary\ImageGenerators\FileTypes\Image;
20
use Spatie\MediaLibrary\UrlGenerator\UrlGeneratorFactory;
21
use Spatie\MediaLibrary\Models\Traits\CustomMediaProperties;
22
use Spatie\MediaLibrary\ResponsiveImages\RegisteredResponsiveImages;
23
24
class Media extends Model implements Responsable, Htmlable
25
{
26
    use IsSorted,
27
        CustomMediaProperties;
28
29
    const TYPE_OTHER = 'other';
30
31
    protected $guarded = [];
32
33
    protected $casts = [
34
        'manipulations' => 'array',
35
        'custom_properties' => 'array',
36
        'responsive_images' => 'array',
37
    ];
38
39
    public function model(): MorphTo
40
    {
41
        return $this->morphTo();
42
    }
43
44
    /*
45
     * Get the full url to a original media file.
46
    */
47
    public function getFullUrl(string $conversionName = ''): string
48
    {
49
        return url($this->getUrl($conversionName));
50
    }
51
52
    /*
53
     * Get the url to a original media file.
54
     */
55
    public function getUrl(string $conversionName = ''): string
56
    {
57
        $urlGenerator = UrlGeneratorFactory::createForMedia($this, $conversionName);
58
59
        return $urlGenerator->getUrl();
60
    }
61
62
    public function getTemporaryUrl(DateTimeInterface $expiration, string $conversionName = '', array $options = []): string
63
    {
64
        $urlGenerator = UrlGeneratorFactory::createForMedia($this, $conversionName);
65
66
        return $urlGenerator->getTemporaryUrl($expiration, $options);
67
    }
68
69
    /*
70
     * Get the path to the original media file.
71
     */
72
    public function getPath(string $conversionName = ''): string
73
    {
74
        $urlGenerator = UrlGeneratorFactory::createForMedia($this, $conversionName);
75
76
        return $urlGenerator->getPath();
77
    }
78
79
    public function getImageGenerators(): Collection
80
    {
81
        return collect(config('medialibrary.image_generators'));
82
    }
83
84
    public function getTypeAttribute(): string
85
    {
86
        $type = $this->getTypeFromExtension();
87
88
        if ($type !== self::TYPE_OTHER) {
89
            return $type;
90
        }
91
92
        return $this->getTypeFromMime();
93
    }
94
95
    public function getTypeFromExtension(): string
96
    {
97
        $imageGenerator = $this->getImageGenerators()
98
            ->map(function (string $className) {
99
                return app($className);
100
            })
101
            ->first->canHandleExtension(strtolower($this->extension));
102
103
        return $imageGenerator
104
            ? $imageGenerator->getType()
105
            : static::TYPE_OTHER;
106
    }
107
108
    public function getTypeFromMime(): string
109
    {
110
        $imageGenerator = $this->getImageGenerators()
111
            ->map(function (string $className) {
112
                return app($className);
113
            })
114
            ->first->canHandleMime($this->mime_type);
115
116
        return $imageGenerator
117
            ? $imageGenerator->getType()
118
            : static::TYPE_OTHER;
119
    }
120
121
    public function getExtensionAttribute(): string
122
    {
123
        return pathinfo($this->file_name, PATHINFO_EXTENSION);
124
    }
125
126
    public function getHumanReadableSizeAttribute(): string
127
    {
128
        return File::getHumanReadableSize($this->size);
129
    }
130
131
    public function getDiskDriverName(): string
132
    {
133
        return strtolower(config("filesystems.disks.{$this->disk}.driver"));
134
    }
135
136
    /*
137
     * Determine if the media item has a custom property with the given name.
138
     */
139
    public function hasCustomProperty(string $propertyName): bool
140
    {
141
        return array_has($this->custom_properties, $propertyName);
142
    }
143
144
    /**
145
     * Get the value of custom property with the given name.
146
     *
147
     * @param string $propertyName
148
     * @param mixed $default
149
     *
150
     * @return mixed
151
     */
152
    public function getCustomProperty(string $propertyName, $default = null)
153
    {
154
        return array_get($this->custom_properties, $propertyName, $default);
155
    }
156
157
    /**
158
     * @param string $name
159
     * @param mixed $value
160
     *
161
     * @return $this
162
     */
163
    public function setCustomProperty(string $name, $value): self
164
    {
165
        $customProperties = $this->custom_properties;
166
167
        array_set($customProperties, $name, $value);
168
169
        $this->custom_properties = $customProperties;
170
171
        return $this;
172
    }
173
174
    public function forgetCustomProperty(string $name): self
175
    {
176
        $customProperties = $this->custom_properties;
177
178
        array_forget($customProperties, $name);
179
180
        $this->custom_properties = $customProperties;
181
182
        return $this;
183
    }
184
185
    /*
186
     * Get all the names of the registered media conversions.
187
     */
188
    public function getMediaConversionNames(): array
189
    {
190
        $conversions = ConversionCollection::createForMedia($this);
191
192
        return $conversions->map(function (Conversion $conversion) {
193
            return $conversion->getName();
194
        })->toArray();
195
    }
196
197
    public function hasGeneratedConversion(string $conversionName): bool
198
    {
199
        $generatedConversions = $this->getGeneratedConversions();
200
201
        return $generatedConversions[$conversionName] ?? false;
202
    }
203
204
    public function markAsConversionGenerated(string $conversionName, bool $generated): self
205
    {
206
        $this->setCustomProperty("generated_conversions.{$conversionName}", $generated);
207
208
        return $this;
209
    }
210
211
    public function getGeneratedConversions(): Collection
212
    {
213
        return collect($this->getCustomProperty('generated_conversions', []));
214
    }
215
216
    /**
217
     * Create an HTTP response that represents the object.
218
     *
219
     * @param  \Illuminate\Http\Request $request
220
     *
221
     * @return \Illuminate\Http\Response
222
     */
223
    public function toResponse($request)
224
    {
225
        $downloadHeaders = [
226
            'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
227
            'Content-Type' => $this->mime_type,
228
            'Content-Length' => $this->size,
229
            'Content-Disposition' => 'attachment; filename="'.$this->file_name.'"',
230
            'Pragma' => 'public',
231
        ];
232
233
        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...
234
            $stream = $this->stream();
235
236
            fpassthru($stream);
237
238
            if (is_resource($stream)) {
239
                fclose($stream);
240
            }
241
        }, 200, $downloadHeaders);
242
    }
243
244
    public function getResponsiveImageUrls(string $conversionName = ''): array
245
    {
246
        return $this->responsiveImages($conversionName)->getUrls();
247
    }
248
249
    public function hasResponsiveImages(string $conversionName = ''): bool
250
    {
251
        return count($this->getResponsiveImageUrls($conversionName));
252
    }
253
254
    public function getSrcset(string $conversionName = ''): string
255
    {
256
        return $this->responsiveImages($conversionName)->getSrcset();
257
    }
258
259
    public function toHtml()
260
    {
261
        return $this->img();
262
    }
263
264
    /**
265
     * @param string|array $conversion
266
     * @param array $extraAttributes
267
     *
268
     * @return string
269
     */
270
    public function img($conversion = '', array $extraAttributes = []): string
271
    {
272
        if (! (new Image())->canHandleMime($this->mime_type)) {
273
            return '';
274
        }
275
276
        if (is_array($conversion)) {
277
            $attributes = $conversion;
278
279
            $conversion = $attributes['conversion'] ?? '';
280
281
            unset($attributes['conversion']);
282
283
            $extraAttributes = array_merge($attributes, $extraAttributes);
284
        }
285
286
        $attributeString = collect($extraAttributes)
287
            ->map(function ($value, $name) {
288
                return $name.'="'.$value.'"';
289
            })->implode(' ');
290
291
        if (strlen($attributeString)) {
292
            $attributeString = ' '.$attributeString;
293
        }
294
295
        $media = $this;
296
297
        $viewName = 'image';
298
299
        $width = '';
300
301
        if ($this->hasResponsiveImages($conversion)) {
302
            $viewName = config('medialibrary.responsive_images.use_tiny_placeholders')
303
                ? 'responsiveImageWithPlaceholder'
304
                : 'responsiveImage';
305
306
            $width = $this->responsiveImages($conversion)->files->first()->width();
307
        }
308
309
        return view("medialibrary::{$viewName}", compact(
310
            'media',
311
            'conversion',
312
            'attributeString',
313
            'width'
314
        ));
315
    }
316
317
    public function move(HasMedia $model, $collectionName = 'default'): self
318
    {
319
        $newMedia = $this->copy($model, $collectionName);
320
321
        $this->delete();
322
323
        return $newMedia;
324
    }
325
326
    public function copy(HasMedia $model, $collectionName = 'default'): self
327
    {
328
        $temporaryDirectory = TemporaryDirectory::create();
329
330
        $temporaryFile = $temporaryDirectory->path($this->file_name);
331
332
        app(Filesystem::class)->copyFromMediaLibrary($this, $temporaryFile);
333
334
        $newMedia = $model
335
            ->addMedia($temporaryFile)
336
            ->usingName($this->name)
337
            ->toMediaCollection($collectionName);
338
339
        $newMedia->custom_properties = $this->custom_properties;
340
341
        $temporaryDirectory->delete();
342
343
        return $newMedia;
344
    }
345
346
    public function responsiveImages(string $conversionName = ''): RegisteredResponsiveImages
347
    {
348
        return new RegisteredResponsiveImages($this, $conversionName);
349
    }
350
351
    public function stream()
352
    {
353
        $filesystem = app(Filesystem::class);
354
355
        return $filesystem->getStream($this);
356
    }
357
358
    public function __invoke(...$arguments)
359
    {
360
        return new HtmlString($this->img(...$arguments));
361
    }
362
}
363