Passed
Push — master ( 63fb8a...8c2aef )
by Jeroen
04:48
created

ExecuteMediaItemImportHandler::download()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 4
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
     * @param MediaItemImport $mediaItemImport
72
     * @param string $destinationPath
73
     * @throws MediaImportFailed
74
     */
75
    private function download(MediaItemImport $mediaItemImport, string $destinationPath)
76
    {
77
        $tryDownloading = true;
78
        $downloadCounter = 0;
79
        while ($tryDownloading) {
80
            $downloadCounter += 1;
81
82
            // ToDo: add try/catch here, so instead of trying once and throwing exception
83
            // we should keep in the while loop, until we find the trying is enough
84
            if (file_put_contents($destinationPath, fopen($mediaItemImport->getPath(), 'r'))) {
85
                $tryDownloading = false;
86
                continue;
87
            }
88
89
            if ($downloadCounter === 5) {
90
                throw MediaImportFailed::forPath($mediaItemImport->getPath());
91
            }
92
        }
93
    }
94
95
    /**
96
     * @param MediaItemImport $mediaItemImport
97
     * @return MediaItem|null
98
     */
99
    private function findExistingMediaItem(MediaItemImport $mediaItemImport)
100
    {
101
        /** @var MediaItemImport|null $existingMediaItemImport */
102
        $existingMediaItemImport = $this->mediaItemImportRepository->findExistingImported(
103
            $mediaItemImport->getMediaGroup(),
104
            $mediaItemImport->getPath()
105
        );
106
107
        if (!$existingMediaItemImport instanceof MediaItemImport) {
108
            return null;
109
        }
110
111
        return $existingMediaItemImport->getMediaItem();
112
    }
113
114
    /**
115
     * @param MediaItemImport $mediaItemImport
116
     * @return string
117
     */
118
    private function getDestinationPath(MediaItemImport $mediaItemImport): string
119
    {
120
        // Define upload dir
121
        $uploadDir = $this->localStorageProvider->getUploadRootDir() . '/' . $this->fileManager->getNextShardingFolder();
122
123
        // Generate folder if not exists
124
        $this->fileManager->createFolder($uploadDir);
125
126
        // Generate filename which doesn't exist yet in our media library
127
        $newName = $this->fileManager->getUniqueFileName(
128
            $uploadDir,
129
            basename($mediaItemImport->getPath())
130
        );
131
132
        return $uploadDir . '/' . $newName;
133
    }
134
135
    /**
136
     * @param $path
137
     * @return integer
138
     * @throws MediaImportFailed
139
     */
140
    private function getFileSize($path)
141
    {
142
        try {
143
            return filesize($path);
144
        } catch (\Exception $e) {
145
            throw MediaImportFailed::forPath($path);
146
        }
147
    }
148
149
    /**
150
     * @param string $path
151
     * @return string
152
     * @throws MediaImportFailed
153
     */
154
    private function getMd5($path)
155
    {
156
        try {
157
            return md5_file($path);
158
        } catch (\Exception $e) {
159
            throw MediaImportFailed::forPath($path);
160
        }
161
    }
162
163
    /**
164
     * @param MediaItemImport $mediaItemImport
165
     * @param string $destinationPath
166
     */
167
    private function importMedia(MediaItemImport $mediaItemImport, string $destinationPath)
168
    {
169
        switch ($mediaItemImport->getMethod()->getMethod()) {
170
            case Method::COPY:
171
                $this->fileManager->getFilesystem()->copy($mediaItemImport->getPath(), $destinationPath);
172
                break;
173
            case Method::MOVE:
174
                $this->fileManager->rename($mediaItemImport->getPath(), $destinationPath);
175
                break;
176
            case Method::DOWNLOAD:
177
                $this->download($mediaItemImport, $destinationPath);
178
                break;
179
        }
180
    }
181
182
    /**
183
     * @param MediaItemImport $mediaItemImport
184
     * @param MediaItem $mediaItem
185
     * @return bool
186
     * @throws MediaImportFailed
187
     */
188
    private function isMatchingAlreadyExistingMediaItem(MediaItemImport $mediaItemImport, MediaItem $mediaItem): bool
189
    {
190
        $oldPath = $mediaItem->getAbsoluteWebPath();
191
        $newPath = $mediaItemImport->getPath();
192
193
        // We check if our existing MediaItem file matches the new one we received
194
        if ($this->getFileSize($oldPath) === $this->getFileSize($newPath)
195
            && $this->getMd5($oldPath) === $this->getMd5($newPath)
196
        ) {
197
            return true;
198
        }
199
200
        return false;
201
    }
202
203
    /**
204
     * We find an eventually existing media item and if it matches exactly, we are going to use it.
205
     *
206
     * @param MediaItemImport $mediaItemImport
207
     * @return bool
208
     */
209
    private function linkExistingMediaItem(MediaItemImport $mediaItemImport): bool
210
    {
211
        /** @var MediaItem|null $existingMediaItem */
212
        $existingMediaItem = $this->findExistingMediaItem($mediaItemImport);
213
214
        // No MediaItem found
215
        if (!$existingMediaItem instanceof MediaItem) {
216
            return false;
217
        }
218
219
        // No matching MediaItem found
220
        if (!$this->isMatchingAlreadyExistingMediaItem($mediaItemImport, $existingMediaItem)) {
221
            return false;
222
        }
223
224
        // Change status because we found matching MediaItem
225
        $mediaItemImport->changeStatusToExisting($existingMediaItem);
226
227
        return true;
228
    }
229
230
    /**
231
     * We got a completely new media item, so we move it to the right place and create a MediaItem for it.
232
     *
233
     * @param MediaItemImport $mediaItemImport
234
     */
235
    private function linkNewMediaItem(MediaItemImport $mediaItemImport)
236
    {
237
        // Create new MediaItem
238
        try {
239
            /** @var string $destinationPath */
240
            $destinationPath = $this->getDestinationPath($mediaItemImport);
241
242
            // We place the media-item in the right path
243
            $this->importMedia($mediaItemImport, $destinationPath);
244
245
            // We now create a MediaItem and change status of MediaItemImport
246
            $mediaItemImport->changeStatusToImported(
247
                $destinationPath,
248
                $this->mediaFolderRepository->findDefault(),
249
                1
250
            );
251
252
            // We add the new item
253
            $this->mediaItemRepository->add($mediaItemImport->getMediaItem());
254
        } catch (\Exception $e) {
255
            $mediaItemImport->changeStatusToError($e->getMessage());
256
        }
257
    }
258
}
259