Passed
Push — fix/slim ( 590b9b...bb7246 )
by Ben
08:37 queued 41s
created

AbstractMediaFieldHandler::handlePayload()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 31
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 31
rs 9.2222
cc 6
nc 6
nop 4
1
<?php declare(strict_types=1);
2
3
namespace Thinktomorrow\Chief\Media\Application;
4
5
use Illuminate\Support\Str;
6
use Illuminate\Http\Request;
7
use Thinktomorrow\AssetLibrary\Asset;
8
use Thinktomorrow\AssetLibrary\HasAsset;
9
use Thinktomorrow\Chief\Fields\Types\MediaField;
10
use Thinktomorrow\AssetLibrary\Application\AddAsset;
11
use Thinktomorrow\Chief\Media\DuplicateAssetException;
12
use Thinktomorrow\AssetLibrary\Application\DetachAsset;
13
use Thinktomorrow\AssetLibrary\Application\ReplaceAsset;
14
use Thinktomorrow\AssetLibrary\Application\AssetUploader;
15
use Thinktomorrow\AssetLibrary\Application\SortAssets;
16
17
abstract class AbstractMediaFieldHandler
18
{
19
    use ChecksExistingAssets;
20
21
    /** @var ReplaceAsset */
22
    protected $replaceAsset;
23
24
    /** @var AddAsset */
25
    protected $addAsset;
26
27
    /** @var DetachAsset */
28
    protected $detachAsset;
29
30
    /** @var AssetUploader */
31
    protected $assetUploader;
32
33
    /** @var SortAssets */
34
    protected $sortAssets;
35
36
    final public function __construct(AddAsset $addAsset, ReplaceAsset $replaceAsset, DetachAsset $detachAsset, SortAssets $sortAssets, AssetUploader $assetUploader)
37
    {
38
        $this->replaceAsset = $replaceAsset;
39
        $this->addAsset = $addAsset;
40
        $this->detachAsset = $detachAsset;
41
        $this->sortAssets = $sortAssets;
42
        $this->assetUploader = $assetUploader;
43
    }
44
45
    protected function handlePayload(HasAsset $model, MediaField $field, string $locale, $values)
46
    {
47
        foreach ($values as $key => $value) {
48
49
            $keyIsAttachedAssetId = $this->isKeyAnAttachedAssetId($model->assetRelation, $locale, $field->getKey(), $key);
0 ignored issues
show
Bug introduced by
Accessing assetRelation on the interface Thinktomorrow\AssetLibrary\HasAsset suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
50
51
            if ($this->shouldNotBeProcessed($value, $key, $keyIsAttachedAssetId)) {
52
                continue;
53
            }
54
55
            /*
56
             * when value is null, it means that the asset is queued for detachment
57
             * is key isn't an attached asset reference, we ignore it because this
58
             * means that a newly uploaded asses is deleted in the same request
59
             */
60
            if (is_null($value)) {
61
62
                if ($keyIsAttachedAssetId) {
63
                    $this->detach($model, $locale, $field->getKey(), $key);
64
                }
65
66
                continue;
67
            }
68
69
            // If key refers to an already existing asset, it is queued for replacement by a new one
70
            if ($keyIsAttachedAssetId) {
71
                $this->replace($model, $locale, $field->getKey(), $key, $value);
72
                continue;
73
            }
74
75
            $this->new($model, $locale, $field->getKey(), $value);
76
        }
77
    }
78
79
    abstract protected function new(HasAsset $model, string $locale, string $type, $value): Asset;
80
81
    protected function replace(HasAsset $model, string $locale, string $type, $currentAssetId, $value): Asset
82
    {
83
        $asset = $this->looksLikeAnAssetId($value)
84
            ? Asset::find($value)
85
            : $this->createNewAsset($model, $locale, $type, $value);
86
87
        $this->replaceAsset->handle($model, $currentAssetId, $asset->id, $type, $locale);
88
89
        return $asset;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $asset could return the type null which is incompatible with the type-hinted return Thinktomorrow\AssetLibrary\Asset. Consider adding an additional type-check to rule them out.
Loading history...
90
    }
91
92
    protected function detach(HasAsset $model, string $locale, string $type, $assetId)
93
    {
94
        $this->detachAsset->detach($model, $assetId, $type, $locale);
95
    }
96
97
    abstract protected function createNewAsset(HasAsset $model, string $locale, string $type, $value): Asset;
98
99
    protected function shouldNotBeProcessed($value, $key, bool $keyIsAttachedAssetId): bool
100
    {
101
        // If the async upload is not finished yet and the user already uploads, the slim passes an "undefined" as value.
102
        if ($value === "undefined") {
103
            return true;
104
        }
105
106
        // Passed id => id that are the same, refer to an already attached asset so skip this.
107
        if ($key == $value && $keyIsAttachedAssetId) {
108
            return true;
109
        }
110
111
        return false;
112
    }
113
114
    /**
115
     * @param HasAsset $model
116
     * @param string $locale
117
     * @param string $type
118
     * @param $assetId
119
     * @return Asset
120
     * @throws DuplicateAssetException
121
     * @throws \Spatie\MediaLibrary\Exceptions\FileCannotBeAdded
122
     */
123
    protected function newExistingAsset(HasAsset $model, string $locale, string $type, $assetId): Asset
124
    {
125
        $existingAsset = Asset::find($assetId);
126
127
        if ($model->assetRelation()->where('asset_pivots.type', $type)->where('asset_pivots.locale', $locale)->get()->contains($existingAsset)) {
128
            throw new DuplicateAssetException();
129
        }
130
131
        return $this->addAsset->add($model, $existingAsset, $type, $locale);
132
    }
133
134
    /**
135
     * @param string $filename
136
     * @return string
137
     */
138
    protected function sluggifyFilename(string $filename): string
139
    {
140
        if (false === strpos($filename, '.')) {
141
            return $filename;
142
        }
143
144
        $extension = substr($filename, strrpos($filename, '.') + 1);
145
        $filename = substr($filename, 0, strrpos($filename, '.'));
146
147
        return Str::slug($filename) . '.' . $extension;
148
    }
149
150
    protected function sort(HasAsset $model, MediaField $field, Request $request)
151
    {
152
        if ($request->has('filesOrder')) {
153
            foreach ($request->input('filesOrder') as $locale => $fileIdInput) {
154
                $fileIds = $this->getFileIdsFromInput($field->getKey(), $fileIdInput);
155
156
                if (!empty($fileIds)) {
157
                    $this->sortAssets->handle($model, $fileIds, $field->getKey(), $locale);
158
                }
159
            }
160
        }
161
    }
162
163
    /**
164
     * @param string $key
165
     * @param array $fileIdInput
166
     * @return array
167
     */
168
    private function getFileIdsFromInput(string $key, array $fileIdInput): array
169
    {
170
        $values = isset($fileIdInput[$key])
171
            ? $fileIdInput[$key]
172
            : (isset($fileIdInput['files-' . $key])
173
                ? $fileIdInput['files-' . $key]
174
                : ''
175
            );
176
177
        if (!$values) {
178
            return [];
179
        }
180
181
        return explode(',', $values);
182
    }
183
}
184