4 * This file is part of the TYPO3 CMS project.
6 * It is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License, either version 2
8 * of the License, or any later version.
10 * For the full copyright and license information, please read the
11 * LICENSE.txt file that was distributed with this source code.
13 * The TYPO3 project - inspiring people to share!
16 namespace TYPO3\CMS\Core\
Resource\Index
;
18 use Doctrine\DBAL\Platforms\SQLServer2012Platform
as SQLServerPlatform
;
19 use Psr\EventDispatcher\EventDispatcherInterface
;
20 use TYPO3\CMS\Core\Database\Connection
;
21 use TYPO3\CMS\Core\Database\ConnectionPool
;
22 use TYPO3\CMS\Core\Database\Query\Restriction\RootLevelRestriction
;
23 use TYPO3\CMS\Core\
Resource\Event\AfterFileMetaDataCreatedEvent
;
24 use TYPO3\CMS\Core\
Resource\Event\AfterFileMetaDataDeletedEvent
;
25 use TYPO3\CMS\Core\
Resource\Event\AfterFileMetaDataUpdatedEvent
;
26 use TYPO3\CMS\Core\
Resource\Event\EnrichFileMetaDataEvent
;
27 use TYPO3\CMS\Core\
Resource\Exception\InvalidUidException
;
28 use TYPO3\CMS\Core\
Resource\File
;
29 use TYPO3\CMS\Core\SingletonInterface
;
30 use TYPO3\CMS\Core\Type\File\ImageInfo
;
31 use TYPO3\CMS\Core\Utility\GeneralUtility
;
34 * Repository Class as an abstraction layer to sys_file_metadata
36 * Every access to table sys_file_metadata which is not handled by DataHandler
37 * has to use this Repository class
39 class MetaDataRepository
implements SingletonInterface
44 protected $tableName = 'sys_file_metadata';
47 * Internal storage for database table fields
51 protected $tableFields = [];
54 * @var EventDispatcherInterface
56 protected $eventDispatcher;
58 public function __construct(EventDispatcherInterface
$eventDispatcher)
60 $this->eventDispatcher
= $eventDispatcher;
64 * Returns array of meta-data properties
69 public function findByFile(File
$file)
71 $record = $this->findByFileUid($file->getUid());
73 // It could be possible that the meta information is freshly
74 // created and inserted into the database. If this is the case
75 // we have to take care about correct meta information for width and
76 // height in case of an image.
77 // This logic can be transferred into a custom PSR-14 event listener in the future by just using
78 // the AfterMetaDataCreated event.
79 if (!empty($record['crdate']) && (int)$record['crdate'] === $GLOBALS['EXEC_TIME']) {
80 if ($file->getType() === File
::FILETYPE_IMAGE
&& $file->getStorage()->getDriverType() === 'Local') {
81 $fileNameAndPath = $file->getForLocalProcessing(false);
83 $imageInfo = GeneralUtility
::makeInstance(ImageInfo
::class, $fileNameAndPath);
85 $additionalMetaInformation = [
86 'width' => $imageInfo->getWidth(),
87 'height' => $imageInfo->getHeight(),
90 $this->update($file->getUid(), $additionalMetaInformation);
92 $record = $this->findByFileUid($file->getUid());
99 * Retrieves metadata for file
103 * @throws InvalidUidException
105 public function findByFileUid($uid)
109 throw new InvalidUidException('Metadata can only be retrieved for indexed files. UID: "' . $uid . '"', 1381590731);
112 $queryBuilder = GeneralUtility
::makeInstance(ConnectionPool
::class)->getQueryBuilderForTable($this->tableName
);
114 $queryBuilder->getRestrictions()->add(GeneralUtility
::makeInstance(RootLevelRestriction
::class));
116 $record = $queryBuilder
118 ->from($this->tableName
)
120 $queryBuilder->expr()->eq('file', $queryBuilder->createNamedParameter($uid, \PDO
::PARAM_INT
)),
121 $queryBuilder->expr()->in('sys_language_uid', $queryBuilder->createNamedParameter([0, -1], Connection
::PARAM_INT_ARRAY
))
126 if (empty($record)) {
130 return $this->eventDispatcher
->dispatch(new EnrichFileMetaDataEvent($uid, (int)$record['uid'], $record))->getRecord();
136 * @param int $fileUid
137 * @param array $additionalFields
140 public function createMetaDataRecord($fileUid, array $additionalFields = [])
143 'file' => (int)$fileUid,
145 'crdate' => $GLOBALS['EXEC_TIME'],
146 'tstamp' => $GLOBALS['EXEC_TIME'],
147 'cruser_id' => isset($GLOBALS['BE_USER']->user
['uid']) ?
(int)$GLOBALS['BE_USER']->user
['uid'] : 0,
148 'l10n_diffsource' => ''
150 $additionalFields = array_intersect_key($additionalFields, $this->getTableFields());
151 $emptyRecord = array_merge($emptyRecord, $additionalFields);
153 $connection = GeneralUtility
::makeInstance(ConnectionPool
::class)->getConnectionForTable($this->tableName
);
157 ['l10n_diffsource' => Connection
::PARAM_LOB
]
160 $record = $emptyRecord;
161 $record['uid'] = $connection->lastInsertId($this->tableName
);
163 return $this->eventDispatcher
->dispatch(new AfterFileMetaDataCreatedEvent($fileUid, (int)$record['uid'], $record))->getRecord();
167 * Updates the metadata record in the database
169 * @param int $fileUid the file uid to update
170 * @param array $data Data to update
173 public function update($fileUid, array $data)
175 $updateRow = array_intersect_key($data, $this->getTableFields());
176 if (array_key_exists('uid', $updateRow)) {
177 unset($updateRow['uid']);
179 $row = $this->findByFileUid($fileUid);
180 if (!empty($updateRow)) {
181 $updateRow['tstamp'] = time();
182 $connection = GeneralUtility
::makeInstance(ConnectionPool
::class)->getConnectionForTable($this->tableName
);
184 if ($connection->getDatabasePlatform() instanceof SQLServerPlatform
) {
185 // mssql needs to set proper PARAM_LOB and others to update fields
186 $tableDetails = $connection->getSchemaManager()->listTableDetails($this->tableName
);
187 foreach ($updateRow as $columnName => $columnValue) {
188 $types[$columnName] = $tableDetails->getColumn($columnName)->getType()->getBindingType();
195 'uid' => (int)$row['uid']
200 $this->eventDispatcher
->dispatch(new AfterFileMetaDataUpdatedEvent($fileUid, (int)$row['uid'], array_merge($row, $updateRow)));
205 * Remove all metadata records for a certain file from the database
207 * @param int $fileUid
209 public function removeByFileUid($fileUid)
211 GeneralUtility
::makeInstance(ConnectionPool
::class)
212 ->getConnectionForTable($this->tableName
)
216 'file' => (int)$fileUid
220 $this->eventDispatcher
->dispatch(new AfterFileMetaDataDeletedEvent((int)$fileUid));
224 * Gets the fields that are available in the table
228 protected function getTableFields(): array
230 if (empty($this->tableFields
)) {
231 $this->tableFields
= GeneralUtility
::makeInstance(ConnectionPool
::class)
232 ->getConnectionForTable($this->tableName
)
234 ->listTableColumns($this->tableName
);
237 return $this->tableFields
;
241 * @return MetaDataRepository
243 public static function getInstance()
245 return GeneralUtility
::makeInstance(self
::class);