Passed
Push — master ( a99cdf...983daa )
by Jeroen
02:40
created

isMatchingAlreadyExistingMediaItem()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.439
c 0
b 0
f 0
cc 6
eloc 19
nc 7
nop 2
1
<?php
2
3
namespace Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\Command;
4
5
use Backend\Modules\MediaLibrary\Component\StorageProvider\LocalStorageProviderInterface;
6
use Backend\Modules\MediaLibrary\Domain\MediaFolder\MediaFolderRepository;
7
use Backend\Modules\MediaLibrary\Domain\MediaItem\MediaItem;
8
use Backend\Modules\MediaLibrary\Domain\MediaItem\MediaItemRepository;
9
use Backend\Modules\MediaLibrary\Manager\FileManager;
10
use Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\Exception\MediaImportFailed;
11
use Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\MediaItemImport;
12
use Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\MediaItemImportRepository;
13
use Backend\Modules\MediaLibraryImporter\Domain\MediaItemImport\Method;
14
15
class ExecuteMediaItemImportHandler
16
{
17
    /** @var FileManager */
18
    protected $fileManager;
19
20
    /** @var LocalStorageProviderInterface */
21
    protected $localStorageProvider;
22
23
    /** @var MediaFolderRepository */
24
    protected $mediaFolderRepository;
25
26
    /** @var MediaItemRepository */
27
    protected $mediaItemRepository;
28
29
    /** @var MediaItemImportRepository */
30
    protected $mediaItemImportRepository;
31
32
    /**
33
     * ExecuteMediaItemImportHandler constructor.
34
     *
35
     * @param FileManager $fileManager
36
     * @param LocalStorageProviderInterface $localStorageProvider
37
     * @param MediaFolderRepository $mediaFolderRepository
38
     * @param MediaItemImportRepository $mediaItemImportRepository
39
     * @param MediaItemRepository $mediaItemRepository
40
     */
41
    public function __construct(
42
        FileManager $fileManager,
43
        LocalStorageProviderInterface $localStorageProvider,
44
        MediaFolderRepository $mediaFolderRepository,
45
        MediaItemImportRepository $mediaItemImportRepository,
46
        MediaItemRepository $mediaItemRepository
47
    ) {
48
        $this->fileManager = $fileManager;
49
        $this->localStorageProvider = $localStorageProvider;
50
        $this->mediaFolderRepository = $mediaFolderRepository;
51
        $this->mediaItemImportRepository = $mediaItemImportRepository;
52
        $this->mediaItemRepository = $mediaItemRepository;
53
    }
54
55
    /**
56
     * @param ExecuteMediaItemImport $executeMediaItemImport
57
     */
58
    public function handle(ExecuteMediaItemImport $executeMediaItemImport)
59
    {
60
        /** @var MediaItemImport $mediaItemImport */
61
        $mediaItemImport = $executeMediaItemImport->getMediaItemImportEntity();
62
63
        if ($this->linkExistingMediaItem($mediaItemImport)) {
64
            return;
65
        }
66
67
        $this->linkNewMediaItem($mediaItemImport);
68
    }
69
70
    /**
71
     * We find an eventually existing media item and if it matches exactly, we are going to use it.
72
     *
73
     * @param MediaItemImport $mediaItemImport
74
     * @return bool
75
     */
76
    private function linkExistingMediaItem(MediaItemImport $mediaItemImport): bool
77
    {
78
        /** @var MediaItem|null $existingMediaItem */
79
        $existingMediaItem = $this->findExistingMediaItem($mediaItemImport);
80
81
        // Existing and matching MediaItem found
82
        if ($this->isMatchingAlreadyExistingMediaItem($mediaItemImport, $existingMediaItem)) {
83
            $mediaItemImport->changeStatusToExisting($existingMediaItem);
0 ignored issues
show
Bug introduced by
It seems like $existingMediaItem defined by $this->findExistingMediaItem($mediaItemImport) on line 79 can be null; however, Backend\Modules\MediaLib...hangeStatusToExisting() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
84
85
            return true;
86
        }
87
88
        return false;
89
    }
90
91
    /**
92
     * We got a completely new media item, so we move it to the right place and create a MediaItem for it.
93
     *
94
     * @param MediaItemImport $mediaItemImport
95
     */
96
    private function linkNewMediaItem(MediaItemImport $mediaItemImport)
97
    {
98
        // Create new MediaItem
99
        try {
100
            /** @var string $destinationPath */
101
            $destinationPath = $this->getDestinationPath($mediaItemImport);
102
103
            // We place the media-item in the right path
104
            $this->importMedia($mediaItemImport, $destinationPath);
105
106
            // We now create a MediaItem and change status of MediaItemImport
107
            $mediaItemImport->changeStatusToImported(
108
                $destinationPath,
109
                $this->mediaFolderRepository->findDefault(),
110
                1
111
            );
112
113
            // We add the new item
114
            $this->mediaItemRepository->add($mediaItemImport->getMediaItem());
115
        } catch (\Exception $e) {
116
            $mediaItemImport->changeStatusToError($e->getMessage());
117
        }
118
    }
119
120
    /**
121
     * @param MediaItemImport $mediaItemImport
122
     * @param MediaItem|null $mediaItem
123
     * @return bool
124
     * @throws MediaImportFailed
125
     */
126
    private function isMatchingAlreadyExistingMediaItem(
127
        MediaItemImport $mediaItemImport,
128
        MediaItem $mediaItem = null
129
    ): bool {
130
        if ($mediaItem === null) {
131
            return false;
132
        }
133
134
        try {
135
            $newFilesize = filesize($mediaItemImport->getPath());
136
            $newMd5 = md5_file($mediaItemImport->getPath());
137
        } catch (\Exception $e) {
138
            throw MediaImportFailed::forPath($mediaItemImport->getPath());
139
        }
140
141
        try {
142
            $oldFilesize = filesize($mediaItem->getAbsoluteWebPath());
143
            $oldMd5 = md5_file($mediaItem->getAbsoluteWebPath());
144
        } catch (\Exception $e) {
145
            throw MediaImportFailed::forPath($mediaItem->getAbsoluteWebPath());
146
        }
147
148
        // We check if our existing MediaItem file matches the new one we received
149
        if ($oldFilesize === $newFilesize && $oldMd5 === $newMd5) {
150
            return true;
151
        }
152
153
        return false;
154
    }
155
156
    /**
157
     * @param MediaItemImport $mediaItemImport
158
     * @param string $destinationPath
159
     * @throws MediaImportFailed
160
     */
161
    private function importMedia(MediaItemImport $mediaItemImport, string $destinationPath)
162
    {
163
        switch ($mediaItemImport->getMethod()->getMethod()) {
164
            case Method::COPY:
165
                $this->fileManager->getFilesystem()->copy($mediaItemImport->getPath(), $destinationPath);
166
                break;
167
            case Method::MOVE:
168
                $this->fileManager->rename($mediaItemImport->getPath(), $destinationPath);
169
                break;
170
            case Method::DOWNLOAD:
171
                $tryDownloading = true;
172
                $downloadCounter = 0;
173
                while ($tryDownloading) {
174
                    $downloadCounter += 1;
175
176
                    // ToDo: add try/catch here, so instead of trying once and throwing exception
177
                    // we should keep in the while loop, until we find the trying is enough
178
                    if (file_put_contents($destinationPath, fopen($mediaItemImport->getPath(), 'r'))) {
179
                        $tryDownloading = false;
180
                        continue;
181
                    }
182
183
                    if ($downloadCounter === 5) {
184
                        throw MediaImportFailed::forPath($mediaItemImport->getPath());
185
                    }
186
                }
187
                break;
188
        }
189
    }
190
191
    /**
192
     * @param MediaItemImport $mediaItemImport
193
     * @return MediaItem|null
194
     */
195
    private function findExistingMediaItem(MediaItemImport $mediaItemImport)
196
    {
197
        /** @var MediaItemImport|null $existingMediaItemImport */
198
        $existingMediaItemImport = $this->mediaItemImportRepository->findExistingImported(
199
            $mediaItemImport->getMediaGroup(),
200
            $mediaItemImport->getPath()
201
        );
202
203
        if (!$existingMediaItemImport instanceof MediaItemImport) {
204
            return null;
205
        }
206
207
        return $existingMediaItemImport->getMediaItem();
208
    }
209
210
    /**
211
     * @param MediaItemImport $mediaItemImport
212
     * @return string
213
     */
214
    private function getDestinationPath(MediaItemImport $mediaItemImport): string
215
    {
216
        // Define upload dir
217
        $uploadDir = $this->localStorageProvider->getUploadRootDir() . '/' . $this->fileManager->getNextShardingFolder();
218
219
        // Generate folder if not exists
220
        $this->fileManager->createFolder($uploadDir);
221
222
        // Generate filename which doesn't exist yet in our media library
223
        $newName = $this->fileManager->getUniqueFileName(
224
            $uploadDir,
225
            basename($mediaItemImport->getPath())
226
        );
227
228
        return $uploadDir . '/' . $newName;
229
    }
230
}
231