diff --git a/lib/Alchemy/Phrasea/Border/Checker/Sha256.php b/lib/Alchemy/Phrasea/Border/Checker/Sha256.php index acd0110433..4c7d1dbb10 100644 --- a/lib/Alchemy/Phrasea/Border/Checker/Sha256.php +++ b/lib/Alchemy/Phrasea/Border/Checker/Sha256.php @@ -33,9 +33,7 @@ class Sha256 extends AbstractChecker */ public function check(EntityManager $em, File $file) { - $boolean = ! count(\record_adapter::get_record_by_sha( - $this->app, $file->getCollection()->get_databox()->get_sbas_id(), $file->getSha256() - )); + $boolean = ! count($file->getCollection()->get_databox()->getRecordRepository()->findBySha256($file->getSha256())); return new Response($boolean, $this); } diff --git a/lib/Alchemy/Phrasea/Border/Checker/UUID.php b/lib/Alchemy/Phrasea/Border/Checker/UUID.php index 11ab8552cf..9e1b941ee7 100644 --- a/lib/Alchemy/Phrasea/Border/Checker/UUID.php +++ b/lib/Alchemy/Phrasea/Border/Checker/UUID.php @@ -32,9 +32,7 @@ class UUID extends AbstractChecker */ public function check(EntityManager $em, File $file) { - $boolean = ! count(\record_adapter::get_record_by_uuid( - $this->app, $file->getCollection()->get_databox(), $file->getUUID() - )); + $boolean = ! count($file->getCollection()->get_databox()->getRecordRepository()->findByUuid($file->getUUID())); return new Response($boolean, $this); } diff --git a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php index d58db4bb8d..fe40727b0c 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php @@ -17,6 +17,7 @@ use Alchemy\Phrasea\Databox\DataboxFactory; use Alchemy\Phrasea\Databox\DbalDataboxRepository; use Alchemy\Phrasea\Databox\Field\DataboxFieldFactory; use Alchemy\Phrasea\Databox\Field\DbalDataboxFieldRepository; +use Alchemy\Phrasea\Databox\Record\LegacyRecordRepository; use Silex\Application; use Silex\ServiceProviderInterface; @@ -24,6 +25,10 @@ class RepositoriesServiceProvider implements ServiceProviderInterface { public function register(Application $app) { + if (!$app instanceof PhraseaApplication) { + throw new \LogicException('Expects $app to be an instance of Phraseanet application'); + } + $app['repo.users'] = $app->share(function (PhraseaApplication $app) { return $app['orm.em']->getRepository('Phraseanet:User'); }); @@ -138,6 +143,10 @@ class RepositoriesServiceProvider implements ServiceProviderInterface $app['repo.fields.factory'] = $app->protect(function (\databox $databox) use ($app) { return new DbalDataboxFieldRepository($databox->get_connection(), new DataboxFieldFactory($app, $databox)); }); + + $app['repo.records.factory'] = $app->protect(function (\databox $databox) use ($app) { + return new LegacyRecordRepository($app, $databox); + }); } public function boot(Application $app) diff --git a/lib/Alchemy/Phrasea/Databox/Record/LegacyRecordRepository.php b/lib/Alchemy/Phrasea/Databox/Record/LegacyRecordRepository.php new file mode 100644 index 0000000000..fb2e464311 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Record/LegacyRecordRepository.php @@ -0,0 +1,146 @@ +app = $app; + $this->databox = $databox; + } + + /** + * @param mixed $record_id + * @return \record_adapter|null + */ + public function find($record_id) + { + $record = new \record_adapter($this->app, $this->databox->get_sbas_id(), $record_id, null, false); + try { + $data = $record->get_data_from_cache(); + } catch (Exception $exception) { + $data = false; + } + + if (false === $data) { + static $sql; + + if (!$sql) { + $sql = $this->createSelectBuilder()->where('record_id = :record_id')->getSQL(); + } + + $data = $this->databox->get_connection()->fetchAssoc($sql, ['record_id' => $record_id]); + } + + if (false === $data) { + return null; + } + + return $this->mapRecordFromResultRow($data, $record); + } + + /** + * @param string $sha256 + * @return \record_adapter[] + */ + public function findBySha256($sha256) + { + static $sql; + + if (!$sql) { + $sql = $this->createSelectBuilder()->where('sha256 = :sha256')->getSQL(); + } + + $result = $this->databox->get_connection()->fetchAll($sql, ['sha256' => $sha256]); + + return $this->mapRecordsFromResultSet($result); + } + + /** + * @param string $uuid + * @return \record_adapter[] + */ + public function findByUuid($uuid) + { + static $sql; + + if (!$sql) { + $sql = $this->createSelectBuilder()->where('uuid = :uuid')->getSQL(); + } + + $result = $this->databox->get_connection()->fetchAll($sql, ['uuid' => $uuid]); + + return $this->mapRecordsFromResultSet($result); + } + + /** + * @return \Doctrine\DBAL\Query\QueryBuilder + */ + private function createSelectBuilder() + { + $connection = $this->databox->get_connection(); + + return $connection->createQueryBuilder() + ->select( + 'coll_id AS collection_id', + 'record_id', + 'credate AS created', + 'uuid', + 'moddate AS updated', + 'parent_record_id AS isStory', + $connection->quoteIdentifier('type'), + 'originalname AS originalName', + 'sha256', + 'mime' + ) + ->from('record', 'r'); + } + + /** + * @param array $result + * @return \record_adapter[] + */ + private function mapRecordsFromResultSet(array $result) + { + $records = []; + + foreach ($result as $row) { + $records[] = $this->mapRecordFromResultRow($row); + } + + return $records; + } + + /** + * @param array $row + * @param \record_adapter|null $record + * @return \record_adapter + */ + private function mapRecordFromResultRow(array $row, \record_adapter $record = null) + { + if (null === $record) { + $record = new \record_adapter($this->app, $this->databox->get_sbas_id(), $row['record_id'], null, false); + } + + $record->mapFromData($row); + $record->putInCache(); + + return $record; + } +} diff --git a/lib/Alchemy/Phrasea/Databox/Record/RecordRepository.php b/lib/Alchemy/Phrasea/Databox/Record/RecordRepository.php new file mode 100644 index 0000000000..6d71b636e4 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/Record/RecordRepository.php @@ -0,0 +1,31 @@ +getCollection($app)->get_sbas_id(), $this->getSha256() - ); - - $uuidRecords = \record_adapter::get_record_by_uuid( - $app, $this->getCollection($app)->get_databox(), $this->getUuid() - ); + $repository = $this->getCollection($app)->get_databox()->getRecordRepository(); + $shaRecords = $repository->findBySha256($this->getSha256()); + $uuidRecords = $repository->findByUuid($this->getUuid()); $merged = array_merge($uuidRecords, $shaRecords); diff --git a/lib/classes/databox.php b/lib/classes/databox.php index ec24e31cdb..529459daf0 100644 --- a/lib/classes/databox.php +++ b/lib/classes/databox.php @@ -14,6 +14,7 @@ use Alchemy\Phrasea\Core\Connection\ConnectionSettings; use Alchemy\Phrasea\Core\PhraseaTokens; use Alchemy\Phrasea\Core\Thumbnail\ThumbnailedElement; use Alchemy\Phrasea\Core\Version\DataboxVersionRepository; +use Alchemy\Phrasea\Databox\Record\RecordRepository; use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Status\StatusStructure; @@ -61,6 +62,8 @@ class databox extends base implements ThumbnailedElement /** @var databox_subdefsStructure */ protected $subdef_struct; + /** @var RecordRepository */ + private $recordRepository; /** @var string[] */ private $labels = []; private $ord; @@ -102,6 +105,18 @@ class databox extends base implements ThumbnailedElement $this->loadFromRow($row); } + /** + * @return RecordRepository + */ + public function getRecordRepository() + { + if (null === $this->recordRepository) { + $this->recordRepository = $this->app['repo.records.factory']($this); + } + + return $this->recordRepository; + } + public function get_viewname() { return $this->viewname ? : $this->connectionSettings->getDatabaseName(); diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php index 38d7ec98eb..09ee186f94 100644 --- a/lib/classes/record/adapter.php +++ b/lib/classes/record/adapter.php @@ -98,17 +98,28 @@ class record_adapter implements RecordInterface, cache_cacheableInterface protected function load() { - if ($this->mapFromCache()) { - return; - } - - $row = $this->fetchDataFromDb(); - if (!$row) { + if (null === $record = $this->getDatabox()->getRecordRepository()->find($this->record_id)) { throw new Exception_Record_AdapterNotFound('Record ' . $this->record_id . ' on database ' . $this->get_sbas_id() . ' not found '); } - $this->mapFromDbData($row); - $this->putInCache(); + $this->mirror($record); + } + + /** + * @param record_adapter $record + */ + private function mirror(record_adapter $record) + { + $this->mime = $record->getMimeType(); + $this->sha256 = $record->getSha256(); + $this->original_name = $record->getOriginalName(); + $this->type = $record->getType(); + $this->isStory = $record->isStory(); + $this->uuid = $record->getUuid(); + $this->updated = $record->getUpdated(); + $this->created = $record->getCreated(); + $this->base_id = $record->getBaseId(); + $this->collection_id = $record->getCollectionId(); } /** @@ -266,7 +277,7 @@ class record_adapter implements RecordInterface, cache_cacheableInterface /** * @return string - * @deprecated use {@link getMimeType} instead. + * @deprecated use {@link self::getMimeType} instead. */ public function get_mime() { @@ -465,8 +476,14 @@ class record_adapter implements RecordInterface, cache_cacheableInterface /** * @return string + * @deprecated use {self::getSha256} instead. */ public function get_sha256() + { + return $this->getSha256(); + } + + public function getSha256() { return $this->sha256; } @@ -867,8 +884,8 @@ class record_adapter implements RecordInterface, cache_cacheableInterface } /** - * * @return int + * @deprecated use {@link self::getDatabox} instead */ public function get_sbas_id() { @@ -1335,38 +1352,20 @@ class record_adapter implements RecordInterface, cache_cacheableInterface } /** - * @param Application $app - * @param integer $sbas_id - * @param string $sha256 - * @param integer $record_id - * @return record_adapter + * @param databox $databox + * @param string $sha256 + * @param integer $record_id + * @return record_adapter[] + * @deprecated use {@link databox::getRecordRepository} instead. */ - public static function get_record_by_sha(Application $app, $sbas_id, $sha256, $record_id = null) + public static function get_record_by_sha(\databox $databox, $sha256, $record_id = null) { - $databox = $app->findDataboxById($sbas_id); - $conn = $databox->get_connection(); - - $sql = "SELECT record_id - FROM record r - WHERE sha256 IS NOT NULL - AND sha256 = :sha256"; - - $params = [':sha256' => $sha256]; + $records = $databox->getRecordRepository()->findBySha256($sha256); if (!is_null($record_id)) { - $sql .= ' AND record_id = :record_id'; - $params[':record_id'] = $record_id; - } - - $stmt = $conn->prepare($sql); - $stmt->execute($params); - $rs = $stmt->fetchAll(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - - $records = []; - - foreach ($rs as $row) { - $records[] = new record_adapter($app, $sbas_id, $row['record_id']); + $records = array_filter($records, function (record_adapter $record) use ($record_id) { + return $record->getRecordId() == $record_id; + }); } return $records; @@ -1375,34 +1374,20 @@ class record_adapter implements RecordInterface, cache_cacheableInterface /** * Search for a record on a databox by UUID * - * @param Application $app - * @param \databox $databox - * @param string $uuid - * @param int $record_id Restrict check on a record_id - * - * @return \record_adapter + * @param \databox $databox + * @param string $uuid + * @param int $record_id Restrict check on a record_id + * @return record_adapter[] + * @deprecated use {@link databox::getRecordRepository} instead. */ - public static function get_record_by_uuid(Application $app, \databox $databox, $uuid, $record_id = null) + public static function get_record_by_uuid(\databox $databox, $uuid, $record_id = null) { - $sql = "SELECT record_id FROM record r - WHERE uuid IS NOT NULL AND uuid = :uuid"; - - $params = [':uuid' => $uuid]; + $records = $databox->getRecordRepository()->findByUuid($uuid); if (!is_null($record_id)) { - $sql .= ' AND record_id = :record_id'; - $params[':record_id'] = $record_id; - } - - $stmt = $databox->get_connection()->prepare($sql); - $stmt->execute($params); - $rs = $stmt->fetchAll(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - - $records = []; - - foreach ($rs as $row) { - $records[] = new record_adapter($app, $databox->get_sbas_id(), $row['record_id']); + $records = array_filter($records, function (record_adapter $record) use ($record_id) { + return $record->getRecordId() == $record_id; + }); } return $records; @@ -1857,12 +1842,6 @@ class record_adapter implements RecordInterface, cache_cacheableInterface $this->set_original_name($originalName); } - /** {@inheritdoc} */ - public function getSha256() - { - return $this->get_sha256(); - } - /** {@inheritdoc} */ public function getId() { @@ -1893,51 +1872,19 @@ class record_adapter implements RecordInterface, cache_cacheableInterface return $this->databox->getStatusStructure(); } - /** - * Try to map data from cache. - * - * @return bool true when cache hit. - * false when cache miss. - */ - protected function mapFromCache() - { - try { - $data = $this->get_data_from_cache(); - } catch (Exception $exception) { - $data = false; - }; - - if (false === $data) { - return false; - } - - $this->mime = $data['mime']; - $this->sha256 = $data['sha256']; - $this->original_name = $data['original_name']; - $this->type = $data['type']; - $this->isStory = $data['grouping']; - $this->uuid = $data['uuid']; - $this->updated = $data['modification_date']; - $this->created = $data['creation_date']; - $this->base_id = $data['base_id']; - $this->collection_id = $data['collection_id']; - - return true; - } - - protected function putInCache() + public function putInCache() { $data = [ - 'mime' => $this->mime, - 'sha256' => $this->sha256, - 'original_name' => $this->original_name, - 'type' => $this->type, - 'grouping' => $this->isStory, - 'uuid' => $this->uuid, - 'modification_date' => $this->updated, - 'creation_date' => $this->created, - 'base_id' => $this->base_id, - 'collection_id' => $this->collection_id, + 'mime' => $this->mime, + 'sha256' => $this->sha256, + 'originalName' => $this->original_name, + 'type' => $this->type, + 'isStory' => $this->isStory, + 'uuid' => $this->uuid, + 'updated' => $this->updated->format(DATE_ISO8601), + 'created' => $this->created->format(DATE_ISO8601), + 'base_id' => $this->base_id, + 'collection_id' => $this->collection_id, ]; $this->set_data_to_cache($data); @@ -1946,48 +1893,23 @@ class record_adapter implements RecordInterface, cache_cacheableInterface /** * @param array $row */ - protected function mapFromDbData(array $row) + public function mapFromData(array $row) { - $this->collection_id = (int)$row['coll_id']; - $this->base_id = (int)phrasea::baseFromColl($this->get_sbas_id(), $this->collection_id, $this->app); - $this->created = new DateTime($row['credate']); - $this->updated = new DateTime($row['moddate']); - $this->uuid = $row['uuid']; - - $this->isStory = ($row['parent_record_id'] == '1'); - $this->type = $row['type']; - $this->original_name = $row['originalname']; - $this->sha256 = $row['sha256']; - $this->mime = $row['mime']; - } - - /** - * @return false|array - */ - protected function fetchDataFromDb() - { - static $sql; - - $connection = $this->databox->get_connection(); - if (!$sql) { - $sql = $connection->createQueryBuilder() - ->select( - 'coll_id', - 'record_id', - 'credate', - 'uuid', - 'moddate', - 'parent_record_id', - $connection->quoteIdentifier('type'), - 'originalname', - 'sha256', - 'mime' - )->from('record', 'r') - ->where('record_id = :record_id') - ->getSQL(); + if (!isset($row['base_id'])) { + $row['base_id'] = phrasea::baseFromColl($this->get_sbas_id(), $row['collection_id'], $this->app); } - return $connection->fetchAssoc($sql, [':record_id' => $this->record_id]); + $this->collection_id = (int)$row['collection_id']; + $this->base_id = (int)$row['base_id']; + $this->created = new DateTime($row['created']); + $this->updated = new DateTime($row['updated']); + $this->uuid = $row['uuid']; + + $this->isStory = ($row['isStory'] == '1'); + $this->type = $row['type']; + $this->original_name = $row['originalName']; + $this->sha256 = $row['sha256']; + $this->mime = $row['mime']; } /** diff --git a/tests/classes/record/adapterTest.php b/tests/classes/record/adapterTest.php index 0f2c853c24..ffc7b21a0f 100644 --- a/tests/classes/record/adapterTest.php +++ b/tests/classes/record/adapterTest.php @@ -483,8 +483,7 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase public function testGet_record_by_sha() { $record_1 = $this->getRecord1(); - $app = $this->getApplication(); - $tmp_records = record_adapter::get_record_by_sha($app, $record_1->get_sbas_id(), $record_1->get_sha256()); + $tmp_records = record_adapter::get_record_by_sha($record_1->getDatabox(), $record_1->get_sha256()); $this->assertTrue(is_array($tmp_records)); foreach ($tmp_records as $tmp_record) { @@ -492,7 +491,11 @@ class record_adapterTest extends \PhraseanetAuthenticatedTestCase $this->assertEquals($record_1->get_sha256(), $tmp_record->get_sha256()); } - $tmp_records = record_adapter::get_record_by_sha($app, $record_1->get_sbas_id(), $record_1->get_sha256(), $record_1->get_record_id()); + $tmp_records = record_adapter::get_record_by_sha( + $record_1->getDatabox(), + $record_1->get_sha256(), + $record_1->get_record_id() + ); $this->assertTrue(is_array($tmp_records)); $this->assertTrue(count($tmp_records) === 1);