MediaManager::hasLincableTrait()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Lincable;
4
5
use Exception;
6
use Illuminate\Http\File;
7
use Lincable\Eloquent\Lincable;
8
use Lincable\Http\File\FileFactory;
9
use Illuminate\Database\Eloquent\Model;
10
use League\Flysystem\FileNotFoundException;
11
use Lincable\Eloquent\Events\UploadFailure;
12
use Lincable\Eloquent\Events\UploadSuccess;
13
use Illuminate\Support\Traits\ForwardsCalls;
14
use Illuminate\Contracts\Container\Container;
15
use Lincable\Exceptions\ConflictFileUploadHttpException;
16
17
class MediaManager
18
{
19
    use ForwardsCalls;
20
21
    /**
22
     * The container implementation.
23
     *
24
     * @var \Illuminate\Contracts\Container\Container
25
     */
26
    protected $app;
27
28
    /**
29
     * The url generator instance.
30
     *
31
     * @var \Lincable\UrlGenerator
32
     */
33
    protected $urlGenerator;
34
35
    /**
36
     * The disk for file storage.
37
     *
38
     * @var \Illuminate\Contracts\Filesystem\Clooud
0 ignored issues
show
Bug introduced by
The type Illuminate\Contracts\Filesystem\Clooud was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
39
     */
40
    protected $disk;
41
42
    /**
43
     * Create a new class instance.
44
     *
45
     * @param  \Illuminate\Contracts\Container\Container  $app
46
     * @param  \Lincable\UrlGenerator  $urlGenerator
47
     * @return void
48
     */
49 37
    public function __construct(Container $app, UrlGenerator $urlGenerator)
50
    {
51 37
        $this->app = $app;
52 37
        $this->urlGenerator = $urlGenerator;
53 37
        $this->disk = $app['filesystem']->disk($this->config('disk'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->config('disk') targeting Lincable\MediaManager::config() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
54 37
    }
55
56
    /**
57
     * Return the disk storage.
58
     *
59
     * @return \Illuminate\Contracts\Filesystem\Clooud
60
     */
61 1
    public function getDisk()
62
    {
63 1
        return $this->disk;
64
    }
65
66
    /**
67
     * Return a url generator instance with the manager configuration.
68
     *
69
     * @return \Lincable\UrlGenerator
70
     */
71
    public function getUrlGenerator()
72
    {
73
        return $this->urlGenerator;
74
    }
75
76
    /**
77
     * Set the new url generator instance.
78
     *
79
     * @param  \Lincable\UrlGenerator  $urlGenerator
80
     * @return this
81
     */
82
    public function setUrlGenerator(UrlGenerator $urlGenerator)
83
    {
84
        $this->urlGenerator = $urlGenerator;
85
        return $this;
86
    }
87
88
    /**
89
     * Return the container instance.
90
     * 
91
     * @return \Illuminate\Contracts\Container\Container
92
     */
93
    public function getContainer()
94
    {
95
        return $this->app;
96
    }
97
98
    /**
99
     * Return the full url from disk for the model.
100
     *
101
     * @param  \Illuminate\Database\Eloquent\Model  $model
102
     * @return string|null
103
     */
104 4
    public function url(Model $model)
105
    {
106 4
        $this->supportLincable($model);
107
108 4
        if ($model->getRawUrl()) {
109 3
            return $this->disk->url($model->getRawUrl());
110
        }   
111 1
    }
112
113
    /**
114
     * Determine wheter the model has a valid file
115
     * on disk storage.
116
     *
117
     * @param  \Illuminate\Database\Eloquent\Model  $model
118
     * @return bool
119
     */
120 6
    public function has(Model $model)
121
    {
122 6
        $this->supportLincable($model);
123
124 6
        $url = $model->getRawUrl();
125
126 6
        return $url !== null && $this->disk->exists($url);
127
    }
128
129
    /**
130
     * Execute the upload operation, reporting correct events and exceptions.
131
     *
132
     * @param  \Illuminate\Http\File  $media
133
     * @param  \Illuminate\Database\Eloquent\Model  $model
134
     * @param  mixed|null  $callback
135
     * @param  mixed  $options
136
     * @return \Illuminate\Database\Eloquent\Model
137
     */
138 21
    public function upload(
139
        File $media, 
140
        Model $model,
141
        $options = []
142
    ) {
143 21
        $this->supportLincable($model);
144
145
        // Create the model link.        
146 21
        $link = $this->newLink($model);
147
148
        return rescue(function () use ($link, $media, $model, $options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression return rescue(function(....ion(...) { /* ... */ }) also could return the type callable which is incompatible with the documented return type Illuminate\Database\Eloquent\Model.
Loading history...
149
            // Define the arguments list and method to the media upload.
150 21
            list($arguments, $putMethod) = [[$link, $media], 'putFile'];
151
152
            // Determine if the model should overwrite the same filename.
153
            // Then the media filename is inserted into the arguments 
154
            // and we change the upload method to accept a filename. 
155 21
            if ($model->shouldOverwrite()) {
156
                $arguments[] = $model->getFileName();
157
                $putMethod = 'putFileAs';
158
            } 
159
160
            // Here we allow custom upload options that is independent from 
161
            // the method used.
162 21
            $arguments[] = $options;
163
            
164 21
            $url = $this->disk->$putMethod(...$arguments);
165
166 19
            $model->fillUrl($url);
167
168
            // Send the event that the upload has been executed with success.
169 19
            event(new UploadSuccess($model, $media));
170
            
171 19
            return $model;
172
        }, function () use ($model, $media) {
173
            // Send the event that the upload has failed.
174 2
            event(new UploadFailure($model, $media));
175
176 2
            $this->throwUploadFailureException();
177 21
        });
178
    }
179
180
     /**
181
      * Copy the file from the source model to the destiny model.
182
      *
183
      * @param  \Illuminate\Database\Eloquent\Model  $from
184
      * @param  \Illuminate\Database\Eloquent\Model  $to
185
      * @param  bool|null  $preserveName
186
      * @return \Illuminate\Database\Eloquent\Model
187
      */
188 4
    public function copy(Model $from, Model $to, bool $preserveName = null)
189
    {
190 4
        $this->supportLincable([$from, $to]);
191
192 4
        $path = $this->newLink(
193 4
            $to,
194 4
            $preserveName ? 
0 ignored issues
show
Bug introduced by
It seems like $preserveName ? $from->g.... $from->getExtension() can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $fileName of Lincable\MediaManager::newLink() does only seem to accept null|string, 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

194
            /** @scrutinizer ignore-type */ $preserveName ? 
Loading history...
195 2
                $from->getFileName() : 
196 4
                str_random(40).'.'.$from->getExtension()
0 ignored issues
show
Bug introduced by
Are you sure $from->getExtension() of type Illuminate\Database\Eloquent\Builder|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

196
                str_random(40).'.'./** @scrutinizer ignore-type */ $from->getExtension()
Loading history...
197
        );
198
        
199 4
        $this->disk->copy($from->getRawUrl(), $path);
200
201 4
        return $to->fillUrl($path);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $to->fillUrl($path) also could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Model.
Loading history...
202
    }
203
204
    /**
205
     * Move a model media to another location.
206
     *
207
     * @param  \Illuminate\Database\Eloquent\Model  $model
208
     * @param  \Illuminate\Database\Eloquent\Model|string  $path
209
     * @param  bool|null  $dryRun
210
     * @return \Illuminate\Database\Eloquent\Model
211
     */
212
    public function move(Model $model, $path, bool $dryRun = null) 
213
    {
214
        if ($path instanceof Model) {
215
            $this->supportLincable($path);
216
            $modelUrl = $path->getRawUrl();
217
218
            // Do not keep the model on.
219
            if (! $dryRun) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $dryRun of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
220
                $path->delete();
221
            }
222
223
            $path = $modelUrl;
224
            unset($modelUrl);
225
        }
226
227
        $this->disk->move($model->getRawUrl(), (string) $path);
228
    }
229
230
    /**
231
     * Download the file contents and store in a temporary directory.
232
     *
233
     * @param  \Illuminate\Database\Eloquent\Model  $model
234
     * @return \Illuminate\Http\File
235
     */
236 6
    public function get(Model $model)
237
    {
238 6
        $this->supportLincable($model);
239
240 6
        $url = $model->getRawurl();
241
242 6
        if ($url && $this->disk->has($url)) {
243 4
            $resource = $this->disk->readStream($url);
244
245 4
            return FileFactory::createTemporary($resource, $url);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Lincable\Http\Fil...porary($resource, $url) returns the type void which is incompatible with the documented return type Illuminate\Http\File.
Loading history...
Bug introduced by
Are you sure the usage of Lincable\Http\File\FileF...porary($resource, $url) targeting Lincable\Http\File\FileFactory::createTemporary() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
It seems like $url can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $originalFileName of Lincable\Http\File\FileFactory::createTemporary() does only seem to accept null|string, 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

245
            return FileFactory::createTemporary($resource, /** @scrutinizer ignore-type */ $url);
Loading history...
246
        }
247
248 2
        throw new FileNotFoundException($url);
249
    }
250
251
    /**
252
     * Create a new a link for model with a file name. 
253
     * 
254
     * @param  \Illuminate\Database\Eloquent\Model  $model
255
     * @param  string|null  $fileName
256
     * @return string
257
     */
258 25
    public function newLink(Model $model, string $fileName = null) 
259
    {
260 25
        $this->supportLincable($model);
261
262
        // Set the model instance to seed the generator and generate
263
        // the url injecting the model attributes.
264 25
        $url = str_start(
265 25
            $this->urlGenerator->forModel($model)->generate(), 
266 25
            '/'
267
        );
268
269 25
        if ($fileName) {
270 5
            return str_finish($url, '/').$fileName;
271
        }
272
273 24
        return rtrim($url, '/');
274
    }
275
276
    /**
277
     * Ensure the given model give support to lincable.
278
     * 
279
     * @param  array|\Illuminate\Database\Eloquent\Model  $model
280
     * @return void
281
     * 
282
     * @throws \Exception When model does not support Lincable
283
     */
284 31
    public function supportLincable($model)
285
    {
286 31
        if (is_array($model)) {
287 5
            foreach ($model as $class) {
288 5
                $this->supportLincable($class);
289
            }
290
291 4
            return;
292
        }
293
294 31
        $modelClass = get_class($model);
295
        
296 31
        if (! $this->hasLincableTrait($modelClass)) {
297 2
            throw new Exception("The model [$modelClass] does not support Lincable.");
298
        } 
299 30
    }
300
301
    /**
302
     * Determinse wheter a model class uses lincable trait.
303
     *
304
     * @param  string  $model
305
     * @return bool
306
     */
307 31
    protected function hasLincableTrait(string $model)
308
    {
309 31
        return in_array(Lincable::class, class_uses_recursive($model));
310
    }
311
312
    /**
313
     * Return the configuration value.
314
     *
315
     * @param  string  $key
316
     * @param  mixed|null  $default
317
     * @return void
318
     */
319 37
    protected function config(string $key, $default = null) 
320
    {
321 37
        return config("lincable.$key", $default);
322
    }
323
324
    /**
325
     * Throw a HTTP exception indicating that file could not be uploaded.
326
     * 
327
     * @return void
328
     * 
329
     * @throws \Lincable\Exceptions\ConflictFileUploadHttpException
330
     */
331 2
    protected function throwUploadFailureException()
332
    {
333 2
        throw new ConflictFileUploadHttpException('Could not store the file on disk.');
334
    }
335
336
    /**
337
     * {@inheritDoc}
338
     */
339 1
    public function __call($name, $arguments)
340
    {
341 1
        if (empty($arguments)) {
342
            static::throwBadMethodCallException($name);
343
        } 
344
345
        if (
346 1
            $arguments[0] instanceof Model && 
347 1
            $this->hasLincableTrait(get_class($arguments[0]))
348
        ) {    
349 1
            $arguments[0] = $arguments[0]->getRawUrl();
350
        }
351
352 1
        return $this->forwardCallTo($this->disk, $name, $arguments);
353
    }
354
}
355