Completed
Push — master ( 331057...2da229 )
by Hamidreza
01:15
created

File::owner()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Hamidrezaniazi\Upolo\Models;
4
5
use Hamidrezaniazi\Upolo\Contracts\HasFileInterface;
6
use Hamidrezaniazi\Upolo\Filters\FileFilters;
7
use Hamidrezaniazi\Upolo\Guard;
8
use Illuminate\Contracts\Auth\Authenticatable as User;
9
use Illuminate\Database\Eloquent\Builder;
10
use Illuminate\Database\Eloquent\Model;
11
use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
use Illuminate\Database\Eloquent\Relations\MorphTo;
13
use Illuminate\Http\UploadedFile;
14
use Illuminate\Support\Facades\Storage;
15
use Illuminate\Support\Str;
16
17
/**
18
 * Class File.
19
 *
20
 * @property int id
21
 * @property string uuid
22
 * @property string path
23
 * @property string disk
24
 * @property string filename
25
 * @property string type
26
 * @property string flag
27
 * @property string mime
28
 * @property string url
29
 * @property HasFileInterface owner
30
 * @property User creator
31
 */
32
class File extends Model
33
{
34
    protected $appends = ['url'];
35
36
    /**
37
     * @return BelongsTo
38
     */
39
    public function creator(): BelongsTo
40
    {
41
        return $this->belongsTo(Guard::getGuardClassName());
42
    }
43
44
    /**
45
     * @return MorphTo
46
     */
47
    public function owner(): MorphTo
48
    {
49
        return $this->morphTo();
50
    }
51
52
    /**
53
     * Apply all relevant thread filters.
54
     *
55
     * @param Builder $query
56
     * @param FileFilters $filters
57
     * @return Builder
58
     */
59
    public function scopeFilter(Builder $query, FileFilters $filters): Builder
60
    {
61
        return $filters->apply($query);
62
    }
63
64
    /**
65
     * @param Builder $query
66
     * @param HasFileInterface $owner
67
     * @return Builder
68
     */
69
    public function scopeWhereOwnerIs(Builder $query, HasFileInterface $owner): Builder
70
    {
71
        return $query->where('owner_type', $owner->getMorphClass())->where('owner_id', $owner->getKey());
0 ignored issues
show
Bug introduced by
The method getMorphClass() does not seem to exist on object<Hamidrezaniazi\Up...racts\HasFileInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getKey() does not seem to exist on object<Hamidrezaniazi\Up...racts\HasFileInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
72
    }
73
74
    /**
75
     * @param Builder $query
76
     * @param int $ownerId
77
     * @return Builder
78
     */
79
    public function scopeWhereOwnerIdIs(Builder $query, int $ownerId): Builder
80
    {
81
        return $query->where('owner_id', $ownerId);
82
    }
83
84
    /**
85
     * @param Builder $query
86
     * @param string $ownerType
87
     * @return Builder
88
     */
89
    public function scopeWhereOwnerTypeIs(Builder $query, string $ownerType): Builder
90
    {
91
        return $query->where('owner_type', $ownerType);
92
    }
93
94
    /**
95
     * @param Builder $query
96
     * @param User $creator
97
     * @return Builder
98
     */
99
    public function scopeWhereCreatorIs(Builder $query, User $creator): Builder
100
    {
101
        return $query->where('creator_id', $creator->getKey());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Auth\Authenticatable as the method getKey() does only exist in the following implementations of said interface: Illuminate\Foundation\Auth\User.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
102
    }
103
104
    /**
105
     * @param Builder $query
106
     * @param int $creatorId
107
     * @return Builder
108
     */
109
    public function scopeWhereCreatorIdIs(Builder $query, int $creatorId): Builder
110
    {
111
        return $query->where('creator_id', $creatorId);
112
    }
113
114
    /**
115
     * @param User $creator
116
     * @param UploadedFile $uploadedFile
117
     * @param HasFileInterface $owner
118
     * @param string $disk
119
     * @param string|null $type
120
     * @param string|null $flag
121
     * @return File
122
     */
123
    public function upload(
124
        User $creator,
125
        UploadedFile $uploadedFile,
126
        ?HasFileInterface $owner = null,
127
        ?string $disk = 'public',
128
        ?string $type = null,
129
        ?string $flag = null
130
    ): self {
131
        $uuid = Str::uuid();
132
        $path = sprintf('%s/%s', $creator->getKey(), $uuid);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Auth\Authenticatable as the method getKey() does only exist in the following implementations of said interface: Illuminate\Foundation\Auth\User.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
133
        $path = $uploadedFile->store($path, $disk);
134
135
        $file = new self();
136
        $file->uuid = $uuid;
137
        $file->creator()->associate($creator);
0 ignored issues
show
Documentation introduced by
$creator is of type object<Illuminate\Contracts\Auth\Authenticatable>, but the function expects a object<Illuminate\Databa...t\Model>|integer|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
138
        $file->owner()->associate($owner);
0 ignored issues
show
Documentation introduced by
$owner is of type object<Hamidrezaniazi\Up...\HasFileInterface>|null, but the function expects a object<Illuminate\Database\Eloquent\Model>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
139
        $file->filename = $uploadedFile->getClientOriginalName();
140
        $file->mime = $uploadedFile->getClientMimeType();
141
        $file->disk = $disk;
142
        $file->type = $type;
143
        $file->flag = $flag;
144
        $file->path = $path;
145
        $file->save();
146
147
        return $file;
148
    }
149
150
    /**
151
     * @return string
152
     */
153
    public function getUrlAttribute(): string
154
    {
155
        return Storage::disk($this->disk)->url($this->path);
156
    }
157
}
158