getDestinationPath()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
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
    public function __construct(
33
        FileManager $fileManager,
34
        LocalStorageProviderInterface $localStorageProvider,
35
        MediaFolderRepository $mediaFolderRepository,
36
        MediaItemImportRepository $mediaItemImportRepository,
37
        MediaItemRepository $mediaItemRepository
38
    ) {
39
        $this->fileManager = $fileManager;
40
        $this->localStorageProvider = $localStorageProvider;
41
        $this->mediaFolderRepository = $mediaFolderRepository;
42
        $this->mediaItemImportRepository = $mediaItemImportRepository;
43
        $this->mediaItemRepository = $mediaItemRepository;
44
    }
45
46
    public function handle(ExecuteMediaItemImport $executeMediaItemImport): void
47
    {
48
        /** @var MediaItemImport $mediaItemImport */
49
        $mediaItemImport = $executeMediaItemImport->getMediaItemImportEntity();
50
51
        if ($this->linkExistingMediaItem($mediaItemImport)) {
52
            return;
53
        }
54
55
        $this->linkNewMediaItem($mediaItemImport);
56
    }
57
58
    /**
59
     * @param MediaItemImport $mediaItemImport
60
     * @param string $destinationPath
61
     * @throws MediaImportFailed
62
     */
63
    private function download(MediaItemImport $mediaItemImport, string $destinationPath): void
64
    {
65
        $tryDownloading = true;
66
        $downloadCounter = 0;
67
        while ($tryDownloading) {
68
            $downloadCounter += 1;
69
70
            // ToDo: add try/catch here, so instead of trying once and throwing exception
71
            // we should keep in the while loop, until we find the trying is enough
72
            if (file_put_contents($destinationPath, fopen($mediaItemImport->getPath(), 'r'))) {
73
                $tryDownloading = false;
74
                continue;
75
            }
76
77
            if ($downloadCounter === 5) {
78
                throw MediaImportFailed::forPath($mediaItemImport->getPath());
79
            }
80
        }
81
    }
82
83
    private function findExistingMediaItem(MediaItemImport $mediaItemImport): ?MediaItem
84
    {
85
        /** @var MediaItemImport|null $existingMediaItemImport */
86
        $existingMediaItemImport = $this->mediaItemImportRepository->findExistingImported(
87
            $mediaItemImport->getMediaGroup(),
88
            $mediaItemImport->getPath()
89
        );
90
91
        if (!$existingMediaItemImport instanceof MediaItemImport) {
92
            return null;
93
        }
94
95
        return $existingMediaItemImport->getMediaItem();
96
    }
97
98
    private function getDestinationPath(MediaItemImport $mediaItemImport): string
99
    {
100
        // Define upload dir
101
        $uploadDir = $this->localStorageProvider->getUploadRootDir() . '/' . $this->fileManager->getNextShardingFolder();
102
103
        // Generate folder if not exists
104
        $this->fileManager->createFolder($uploadDir);
105
106
        // Generate filename which doesn't exist yet in our media library
107
        $newName = $this->fileManager->getUniqueFileName(
108
            $uploadDir,
109
            basename($mediaItemImport->getPath())
110
        );
111
112
        return $uploadDir . '/' . $newName;
113
    }
114
115
    /**
116
     * @param string $path
117
     * @return int
118
     * @throws MediaImportFailed
119
     */
120
    private function getFileSize(string $path): int
121
    {
122
        try {
123
            return filesize($path);
124
        } catch (\Exception $e) {
125
            throw MediaImportFailed::forPath($path);
126
        }
127
    }
128
129
    /**
130
     * @param string $path
131
     * @return string
132
     * @throws MediaImportFailed
133
     */
134
    private function getMd5(string $path): string
135
    {
136
        try {
137
            return md5_file($path);
138
        } catch (\Exception $e) {
139
            throw MediaImportFailed::forPath($path);
140
        }
141
    }
142
143
    private function importMedia(MediaItemImport $mediaItemImport, string $destinationPath): void
144
    {
145
        switch ($mediaItemImport->getMethod()->getMethod()) {
146
            case Method::COPY:
147
                $this->fileManager->getFilesystem()->copy($mediaItemImport->getPath(), $destinationPath);
148
                break;
149
            case Method::MOVE:
150
                $this->fileManager->rename($mediaItemImport->getPath(), $destinationPath);
151
                break;
152
            case Method::DOWNLOAD:
153
                $this->download($mediaItemImport, $destinationPath);
154
                break;
155
        }
156
    }
157
158
    /**
159
     * @param MediaItem $mediaItem
160
     * @param MediaItemImport $mediaItemImport
161
     * @return bool
162
     * @throws MediaImportFailed
163
     */
164
    private function isMatchingAlreadyExistingMediaItem(MediaItem $mediaItem, MediaItemImport $mediaItemImport): bool
165
    {
166
        $oldPath = $mediaItem->getAbsoluteWebPath();
167
        $newPath = $mediaItemImport->getPath();
168
169
        // We check if our existing MediaItem file matches the new one we received
170
        return ($this->getFileSize($oldPath) === $this->getFileSize($newPath))
171
            && ($this->getMd5($oldPath) === $this->getMd5($newPath));
172
    }
173
174
    /**
175
     * We find an eventually existing media item and if it matches exactly, we are going to use it.
176
     *
177
     * @param MediaItemImport $mediaItemImport
178
     * @return bool
179
     * @throws MediaImportFailed
180
     */
181
    private function linkExistingMediaItem(MediaItemImport $mediaItemImport): bool
182
    {
183
        /** @var MediaItem|null $existingMediaItem */
184
        $existingMediaItem = $this->findExistingMediaItem($mediaItemImport);
185
186
        // No MediaItem found
187
        if (!$existingMediaItem instanceof MediaItem) {
188
            return false;
189
        }
190
191
        // No matching MediaItem found
192
        if (!$this->isMatchingAlreadyExistingMediaItem($existingMediaItem, $mediaItemImport)) {
193
            return false;
194
        }
195
196
        // Change status because we found matching MediaItem
197
        $mediaItemImport->changeStatusToExisting($existingMediaItem);
198
199
        return true;
200
    }
201
202
    /**
203
     * We got a completely new media item, so we move it to the right place and create a MediaItem for it.
204
     *
205
     * @param MediaItemImport $mediaItemImport
206
     */
207
    private function linkNewMediaItem(MediaItemImport $mediaItemImport): void
208
    {
209
        // Create new MediaItem
210
        try {
211
            /** @var string $destinationPath */
212
            $destinationPath = $this->getDestinationPath($mediaItemImport);
213
214
            // We place the media-item in the right path
215
            $this->importMedia($mediaItemImport, $destinationPath);
216
217
            // We now create a MediaItem and change status of MediaItemImport
218
            $mediaItemImport->changeStatusToImported(
219
                $destinationPath,
220
                $this->mediaFolderRepository->findDefault(),
221
                1
222
            );
223
224
            // We add the new item
225
            $this->mediaItemRepository->add($mediaItemImport->getMediaItem());
226
        } catch (\Exception $e) {
227
            $mediaItemImport->changeStatusToError($e->getMessage());
228
        }
229
    }
230
}
231