diff --git a/lib/Alchemy/Phrasea/Databox/DataboxBoundRepositoryFactory.php b/lib/Alchemy/Phrasea/Databox/DataboxBoundRepositoryFactory.php new file mode 100644 index 0000000000..74662001f8 --- /dev/null +++ b/lib/Alchemy/Phrasea/Databox/DataboxBoundRepositoryFactory.php @@ -0,0 +1,22 @@ +factory = $factory; + } + + /** + * @param int $databoxId + * @return object + */ + public function getRepositoryForDatabox($databoxId) + { + if (!isset($this->repositories[$databoxId])) { + $this->repositories[$databoxId] = $this->factory->createRepositoryFor($databoxId); + } + + return $this->repositories[$databoxId]; + } +} diff --git a/lib/Alchemy/Phrasea/Hydration/Hydrator.php b/lib/Alchemy/Phrasea/Hydration/Hydrator.php new file mode 100644 index 0000000000..ac248f7513 --- /dev/null +++ b/lib/Alchemy/Phrasea/Hydration/Hydrator.php @@ -0,0 +1,31 @@ +hydrator = $hydrator; + $this->prototype = $prototype; + } + + /** + * @param string|int $index + * @param array $data + * @return object + */ + public function hydrate($index, array $data) + { + if (!isset($this->entities[$index])) { + $this->entities[$index] = clone $this->prototype; + } + + $instance = $this->entities[$index]; + + $this->hydrator->hydrate($instance, $data); + + return $instance; + } + + /** + * @param array[] $data + * @return object[] + */ + public function hydrateAll(array $data) + { + Assertion::allIsArray($data); + + $instances = []; + + foreach ($data as $index => $item) { + $instances[$index] = $this->hydrate($index, $item); + } + + return $instances; + } + + public function clear() + { + $this->entities = []; + } + + public function getIterator() + { + return new \ArrayIterator($this->entities); + } + + public function offsetExists($offset) + { + return isset($this->entities[$offset]); + } + + public function offsetGet($offset) + { + return $this->entities[$offset]; + } + + public function offsetSet($offset, $value) + { + Assertion::notNull($offset); + Assertion::isArray($value); + + $this->hydrate($offset, $value); + } + + public function offsetUnset($offset) + { + unset($this->entities[$offset]); + } + + public function count() + { + return count($this->entities); + } +} diff --git a/lib/Alchemy/Phrasea/Hydration/ReflectionHydrator.php b/lib/Alchemy/Phrasea/Hydration/ReflectionHydrator.php new file mode 100644 index 0000000000..5a635e8eb6 --- /dev/null +++ b/lib/Alchemy/Phrasea/Hydration/ReflectionHydrator.php @@ -0,0 +1,130 @@ +className = $className; + $this->properties = $properties; + } + + /** + * @param array $data + * @param object $instance + * @throws \Assert\AssertionFailedException + */ + public function hydrate($instance, array $data) + { + Assertion::isInstanceOf($instance, $this->className); + + foreach ($data as $key => $value) { + $this->getReflectionProperty($key)->setValue($instance, $value); + } + } + + /** + * @param object $instance + * @return array + * @throws \Assert\AssertionFailedException + */ + public function extract($instance) + { + Assertion::isInstanceOf($instance, $this->className); + + $data = []; + + foreach ($this->getReflectionProperties() as $name => $property) { + $data[$name] = $property->getValue($instance); + } + + return $data; + } + + /** + * @return \ReflectionClass + */ + private function getReflectionClass() + { + if (null === $this->reflectionClass) { + $this->reflectionClass = new \ReflectionClass($this->className); + } + + return $this->reflectionClass; + } + + /** + * @param string $name + * @return \ReflectionProperty + * @throws \RuntimeException + */ + private function getReflectionProperty($name) + { + $this->loadReflectionProperties(); + + return $this->reflectionProperties[$name]; + } + + /** + * @return \ReflectionProperty[] + */ + private function getReflectionProperties() + { + $this->loadReflectionProperties(); + + return $this->reflectionProperties; + } + + private function loadReflectionProperties() + { + if (null !== $this->reflectionProperties) { + return; + } + + $class = $this->getReflectionClass(); + $properties = []; + + foreach ($this->properties as $name) { + $property = $class->getProperty($name); + $property->setAccessible(true); + + $properties[$name] = $property; + } + + $this->reflectionProperties = $properties; + } +} diff --git a/lib/Alchemy/Phrasea/Vocabulary/ControlProvider/ControlProviderInterface.php b/lib/Alchemy/Phrasea/Vocabulary/ControlProvider/ControlProviderInterface.php index 5bb51a04bd..557f3d2600 100644 --- a/lib/Alchemy/Phrasea/Vocabulary/ControlProvider/ControlProviderInterface.php +++ b/lib/Alchemy/Phrasea/Vocabulary/ControlProvider/ControlProviderInterface.php @@ -1,5 +1,4 @@ record = $record; $this->app = $app; - if($retrieveValues == self::RETRIEVE_VALUES) { + if ($retrieveValues === self::RETRIEVE_VALUES) { $this->retrieveValues(); } } + /** + * @return string + */ public function getQjs() { return $this->qjs; } - public function injectValues($value, $VocabularyType, $VocabularyId) + /** + * @param string $value + * @param string $vocabularyType + * @param string $vocabularyId + */ + public function injectValues($value, $vocabularyType, $vocabularyId) { $this->value = StringHelper::crlfNormalize($value); - try { - $this->VocabularyType = $VocabularyType ? Vocabulary\Controller::get($this->app, $VocabularyType) : null; - $this->VocabularyId = $VocabularyId; - } catch (\InvalidArgumentException $e) { + $this->fetchVocabulary($vocabularyType, $vocabularyId); - } - - if ($this->VocabularyType) { - /** - * Vocabulary Control has been deactivated - */ - if ( ! $this->databox_field->getVocabularyControl()) { + if ($this->vocabularyType) { + if (!$this->databox_field->getVocabularyControl()) { + // Vocabulary Control has been deactivated $this->removeVocabulary(); - } - /** - * Vocabulary Control has changed - */ - elseif ($this->databox_field->getVocabularyControl()->getType() !== $this->VocabularyType->getType()) { + } elseif ($this->databox_field->getVocabularyControl()->getType() !== $this->vocabularyType->getType()) { + // Vocabulary Control has changed $this->removeVocabulary(); - } - /** - * Current Id is not available anymore - */ - elseif ( ! $this->VocabularyType->validate($this->VocabularyId)) { + } elseif (!$this->vocabularyType->validate($this->vocabularyId)) { + // Current Id is not available anymore $this->removeVocabulary(); - } - /** - * String equivalence has changed - */ - elseif ($this->VocabularyType->getValue($this->VocabularyId) !== $this->value) { - $this->set_value($this->VocabularyType->getValue($this->VocabularyId)); + } elseif ($this->vocabularyType->getValue($this->vocabularyId) !== $this->value) { + // String equivalence has changed + $this->set_value($this->vocabularyType->getValue($this->vocabularyId)); } } - - $datas = [ - 'value' => $this->value, - 'vocabularyId' => $this->VocabularyId, - 'vocabularyType' => $this->VocabularyType ? $this->VocabularyType->getType() : null, - ]; - - $this->set_data_to_cache($datas); } protected function retrieveValues() { try { - $datas = $this->get_data_from_cache(); - - $this->value = $datas['value']; - $this->VocabularyType = $datas['vocabularyType'] ? Vocabulary\Controller::get($this->app, $datas['vocabularyType']) : null; - $this->VocabularyId = $datas['vocabularyId']; - - return $this; + $data = $this->get_data_from_cache(); + $cacheRefreshNeeded = false; } catch (\Exception $e) { + $data = $this->databox_field->get_databox()->get_connection() + ->fetchAssoc( + 'SELECT value, VocabularyType as vocabularyType, VocabularyId as vocabularyId FROM metadatas WHERE id = :id', + [':id' => $this->id] + ); + if (!is_array($data)) { + $data = [ + 'value' => null, + 'vocabularyId' => null, + 'vocabularyType' => null, + ]; + } + + $cacheRefreshNeeded = true; } - $connbas = $this->databox_field->get_databox()->get_connection(); + $this->injectValues($data['value'], $data['vocabularyType'], $data['vocabularyId']); - $sql = 'SELECT record_id, value, VocabularyType, VocabularyId FROM metadatas WHERE id = :id'; - - $stmt = $connbas->prepare($sql); - $stmt->execute([':id' => $this->id]); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - $stmt->closeCursor(); - - if($row) { - $this->injectValues($row['value'], $row['VocabularyType'], $row['VocabularyId']); - } - else { - $this->injectValues(null, null, null); + if ($cacheRefreshNeeded) { + $this->set_data_to_cache([ + 'value' => $this->value, + 'vocabularyId' => $this->vocabularyId, + 'vocabularyType' => $this->vocabularyType ? $this->vocabularyType->getType() : null, + ]); } return $this; } /** - * @return Vocabulary\ControlProvider\ControlProviderInterface + * @return ControlProviderInterface|null */ public function getVocabularyType() { - return $this->VocabularyType; + return $this->vocabularyType; } public function getVocabularyId() { - return $this->VocabularyId; + return $this->vocabularyId; } public function getId() @@ -183,7 +185,7 @@ class caption_Field_Value implements cache_cacheableInterface public function getResource() { - return $this->VocabularyType ? $this->VocabularyType->getResource($this->VocabularyId) : null; + return $this->vocabularyType ? $this->vocabularyType->getResource($this->vocabularyId) : null; } public function getDatabox_field() @@ -198,12 +200,7 @@ class caption_Field_Value implements cache_cacheableInterface public function delete() { - $connbas = $this->databox_field->get_connection(); - - $sql = 'DELETE FROM metadatas WHERE id = :id'; - $stmt = $connbas->prepare($sql); - $stmt->execute([':id' => $this->id]); - $stmt->closeCursor(); + $this->getConnection()->delete('metadatas', ['id' => $this->id]); $this->delete_data_from_cache(); $this->databox_field->delete_data_from_cache(); @@ -213,144 +210,129 @@ class caption_Field_Value implements cache_cacheableInterface return $this; } + /** + * @return $this + * @throws \Doctrine\DBAL\DBALException + */ public function removeVocabulary() { - $connbas = $this->databox_field->get_connection(); + $this->getConnection()->executeUpdate( + 'UPDATE metadatas SET VocabularyType = NULL, VocabularyId = NULL WHERE id = :meta_id', + ['meta_id' => $this->getId()] + ); - $params = [ - ':VocabType' => null - , ':VocabularyId' => null - , ':meta_id' => $this->getId() - ]; - - $sql_up = 'UPDATE metadatas' - . ' SET VocabularyType = :VocabType, VocabularyId = :VocabularyId' - . ' WHERE id = :meta_id'; - $stmt_up = $connbas->prepare($sql_up); - $stmt_up->execute($params); - $stmt_up->closeCursor(); - - $this->VocabularyId = $this->VocabularyType = null; + $this->vocabularyId = null; + $this->vocabularyType = null; $this->delete_data_from_cache(); return $this; } - public function setVocab(Vocabulary\ControlProvider\ControlProviderInterface $vocabulary, $vocab_id) + /** + * @param ControlProviderInterface $vocabulary + * @param mixed $vocab_id + * @return $this + * @throws \Doctrine\DBAL\DBALException + */ + public function setVocab(ControlProviderInterface $vocabulary, $vocab_id) { - $connbas = $this->databox_field->get_connection(); - - $params = [ - ':VocabType' => $vocabulary->getType() - , ':VocabularyId' => $vocab_id - , ':meta_id' => $this->getId() - ]; - - $sql_up = 'UPDATE metadatas' - . ' SET VocabularyType = :VocabType, VocabularyId = :VocabularyId' - . ' WHERE id = :meta_id'; - $stmt_up = $connbas->prepare($sql_up); - $stmt_up->execute($params); - $stmt_up->closeCursor(); + $this->getConnection()->executeUpdate( + 'UPDATE metadatas SET VocabularyType = :VocabType, VocabularyId = :VocabularyId WHERE id = :meta_id', + [ + 'VocabType' => $vocabulary->getType(), + 'VocabularyId' => $vocab_id, + 'meta_id' => $this->getId(), + ] + ); $this->set_value($vocabulary->getValue($vocab_id)); return $this; } + /** + * @param ControlProviderInterface|null $vocabulary + * @param mixed|null $vocabularyId + * @return $this + * @throws \Doctrine\DBAL\DBALException + */ + public function changeVocabulary(ControlProviderInterface $vocabulary = null, $vocabularyId = null) + { + if (isset($vocabulary, $vocabularyId)) { + return $this->setVocab($vocabulary, $vocabularyId); + } + + return $this->removeVocabulary(); + } + + /** + * @param string $value + * @return $this + * @throws \Doctrine\DBAL\DBALException + */ public function set_value($value) { $this->value = $value; - $connbas = $this->databox_field->get_connection(); - - $params = [ - ':meta_id' => $this->id - , ':value' => $value - ]; - - $sql_up = 'UPDATE metadatas SET value = :value WHERE id = :meta_id'; - $stmt_up = $connbas->prepare($sql_up); - $stmt_up->execute($params); - $stmt_up->closeCursor(); + $this->getConnection()->executeUpdate( + 'UPDATE metadatas SET value = :value WHERE id = :meta_id', + [ + 'meta_id' => $this->id, + 'value' => $value, + ] + ); $this->delete_data_from_cache(); - $this->update_cache_value($value); - return $this; } - /** - * - * @param array $value - * @return caption_field - */ - public function update_cache_value($value) + public static function create(Application $app, databox_field $databox_field, \record_adapter $record, $value, ControlProviderInterface $vocabulary = null, $vocabularyId = null) { - $this->record->get_caption()->delete_data_from_cache(); + $connection = $databox_field->get_connection(); - return $this; - } - - public static function create(Application $app, databox_field $databox_field, \record_adapter $record, $value, Vocabulary\ControlProvider\ControlProviderInterface $vocabulary = null, $vocabularyId = null) - { - $connbas = $databox_field->get_connection(); - - /** - * Check consistency - */ - if ( ! $databox_field->is_multi()) { + // Check consistency + if (!$databox_field->is_multi()) { try { $field = $record->get_caption()->get_field($databox_field->get_name()); $values = $field->get_values(); - $caption_field_value = array_pop($values); - /* @var $value \caption_Field_Value */ + } catch (Exception $exception) { + // Field was not found, so no values found either + $values = []; + } + if (!empty($values)) { + /** @var caption_Field_Value $caption_field_value */ + $caption_field_value = reset($values); $caption_field_value->set_value($value); - - if (! $vocabulary || ! $vocabularyId) { - $caption_field_value->removeVocabulary(); - } else { - $caption_field_value->setVocab($vocabulary, $vocabularyId); - } + $caption_field_value->changeVocabulary($vocabulary, $vocabularyId); return $caption_field_value; - } catch (\Exception $e) { - } } - $sql_ins = 'INSERT INTO metadatas (id, record_id, meta_struct_id, value, VocabularyType, VocabularyId)' - . ' VALUES (null, :record_id, :field, :value, :VocabType, :VocabId)'; - - $params = [ - ':record_id' => $record->getRecordId(), - ':field' => $databox_field->get_id(), - ':value' => $value, - ':VocabType' => $vocabulary ? $vocabulary->getType() : null, - ':VocabId' => $vocabulary ? $vocabularyId : null, + $data = [ + 'record_id' => $record->getRecordId(), + 'meta_struct_id' => $databox_field->get_id(), + 'value' => $value, + 'VocabularyType' => $vocabulary ? $vocabulary->getType() : null, + 'VocabularyId' => $vocabulary ? $vocabularyId : null, ]; - $stmt_ins = $connbas->prepare($sql_ins); - $stmt_ins->execute($params); + $connection->insert('metadatas', $data); - $stmt_ins->closeCursor(); - $meta_id = $connbas->lastInsertId(); + $meta_id = $connection->lastInsertId(); - $caption_field_value = new self($app, $databox_field, $record, $meta_id); - $caption_field_value->update_cache_value($value); + $caption_field_value = new self($app, $databox_field, $record, $meta_id, self::DONT_RETRIEVE_VALUES); + $caption_field_value->injectValues($data['value'], $data['VocabularyType'], $data['VocabularyId']); - $record->get_caption()->delete_data_from_cache(); $databox_field->delete_data_from_cache(); - $caption_field_value->delete_data_from_cache(); return $caption_field_value; } /** - * * @return string */ public function highlight_thesaurus() @@ -361,7 +343,7 @@ class caption_Field_Value implements cache_cacheableInterface $tbranch = $this->databox_field->get_tbranch(); - if (! $tbranch || ! $XPATH_thesaurus) { + if (!$tbranch || !$XPATH_thesaurus) { return $value; } @@ -390,8 +372,9 @@ class caption_Field_Value implements cache_cacheableInterface $note = 0; $note += ($node->getAttribute("lng") == $this->app['locale']) ? 4 : 0; $note += ($node->getAttribute("w") == $term_noacc) ? 2 : 0; - if($context_noacc != "") + if ($context_noacc != "") { $note += ($node->getAttribute("k") == $context_noacc) ? 1 : 0; + } if ($note > $bestnote) { $bestnode = $node; } @@ -401,22 +384,22 @@ class caption_Field_Value implements cache_cacheableInterface list($term, $context) = $this->splitTermAndContext(str_replace(["[[em]]", "[[/em]]"], ["", ""], $value)); // a value has been found in thesaurus, update value & set the query to bounce to the value $this->value = $bestnode->getAttribute('v'); - $this->qjs = $term . ($context ? '['.$context.']' : ''); + $this->qjs = $term . ($context ? '[' . $context . ']' : ''); $this->isThesaurusValue = true; } else { $this->isThesaurusValue = false; } - return $this; + return $this->value; } /** - * @return boolean + * @return bool */ public function isThesaurusValue() { if (null === $this->isThesaurusValue) { - $this->highlight_thesaurus(); + throw new LogicException('Value was not checked against thesaurus yet. Call hightlight_thesaurus() first'); } return $this->isThesaurusValue; @@ -486,14 +469,9 @@ class caption_Field_Value implements cache_cacheableInterface */ public function delete_data_from_cache($option = null) { - $this->value = $this->VocabularyId = $this->VocabularyType = null; + $this->value = $this->vocabularyId = $this->vocabularyType = null; $this->record->delete_data_from_cache(record_adapter::CACHE_TITLE); - - try { - $this->record->get_caption()->get_field($this->databox_field->get_name())->delete_data_from_cache(); - } catch (\Exception $e) { - - } + $this->record->get_caption()->delete_data_from_cache(); unset(self::$localCache[$this->get_cache_key($option)]); } @@ -507,4 +485,26 @@ class caption_Field_Value implements cache_cacheableInterface { self::$localCache = []; } + + /** + * @param string $vocabularyType + * @param mixed $vocabularyId + */ + private function fetchVocabulary($vocabularyType, $vocabularyId) + { + try { + $this->vocabularyType = $vocabularyType ? $this->app['vocabularies'][strtolower($vocabularyType)] : null; + $this->vocabularyId = $vocabularyId; + } catch (\InvalidArgumentException $e) { + // Invalid or unknown Vocabulary + } + } + + /** + * @return \Doctrine\DBAL\Connection + */ + private function getConnection() + { + return $this->databox_field->get_connection(); + } } diff --git a/lib/classes/caption/field.php b/lib/classes/caption/field.php index d15508d53a..37b2b8ab5a 100644 --- a/lib/classes/caption/field.php +++ b/lib/classes/caption/field.php @@ -40,8 +40,6 @@ class caption_field implements cache_cacheableInterface * @param databox_field $databox_field * @param record_adapter $record * @param bool $retrieveValues - * - * @return caption_field */ public function __construct(Application $app, databox_field $databox_field, \record_adapter $record, $retrieveValues = self::RETRIEVE_VALUES) { @@ -62,8 +60,6 @@ class caption_field implements cache_cacheableInterface } } } - - return $this; } /** @@ -147,27 +143,27 @@ class caption_field implements cache_cacheableInterface } /** - * @param array $values - * @param string $separator + * @param caption_Field_Value[] $values + * @param string $separator + * @param bool $highlight * @return string */ - protected static function serialize_value(Array $values, $separator, $highlight = false) + protected function serialize_value(array $values, $separator, $highlight = false) { - if (strlen($separator) > 1) + if (strlen($separator) > 1) { $separator = $separator[0]; + } - if (trim($separator) === '') + if (trim($separator) === '') { $separator = ' '; - else + } else { $separator = ' ' . $separator . ' '; + } $array_values = []; foreach ($values as $value) { - if ($highlight) - $array_values[] = $value->highlight_thesaurus(); - else - $array_values[] = $value->getValue(); + $array_values[] = $highlight ? $value->highlight_thesaurus() : $value->getValue(); } return implode($separator, $array_values); @@ -191,10 +187,10 @@ class caption_field implements cache_cacheableInterface } /** - * @param String $custom_separator - * @param Boolean $highlightTheso + * @param string|bool $custom_separator + * @param bool $highlight * - * @return mixed + * @return string */ public function get_serialized_values($custom_separator = false, $highlight = false) { @@ -205,12 +201,12 @@ class caption_field implements cache_cacheableInterface if ($this->is_multi()) { $separator = $custom_separator !== false ? $custom_separator : $this->databox_field->get_separator(); - return self::serialize_value($this->values, $separator, $highlight); + return $this->serialize_value($this->values, $separator, $highlight); } + /** @var caption_Field_Value $value */ $value = current($this->values); - /* @var $value Caption_Field_Value */ if ($highlight) { return $value->highlight_thesaurus(); } @@ -258,7 +254,6 @@ class caption_field implements cache_cacheableInterface */ public static function get_multi_values($serialized_value, $separator) { - $values = []; if (strlen($separator) == 1) { $values = explode($separator, $serialized_value); } else { @@ -360,8 +355,7 @@ class caption_field implements cache_cacheableInterface $caption_field->delete(); $record->set_metadatas([]); - unset($caption_field); - unset($record); + unset($caption_field, $record); } catch (\Exception $e) { } @@ -389,6 +383,7 @@ class caption_field implements cache_cacheableInterface * * @param string $option * @return mixed + * @throws Exception */ public function get_data_from_cache($option = null) { diff --git a/lib/classes/caption/record.php b/lib/classes/caption/record.php index 1af2cd7509..714ac8aeef 100644 --- a/lib/classes/caption/record.php +++ b/lib/classes/caption/record.php @@ -224,9 +224,14 @@ SQL; foreach ($field->get_values() as $metaId => $v) { $values[$metaId] = [ 'value' => $v->getValue(), - 'from_thesaurus' => $highlight ? $v->isThesaurusValue() : false, - 'qjs' => $v->getQjs(), + 'from_thesaurus' => false, + 'qjs' => null, ]; + if ($highlight) { + $v->highlight_thesaurus(); + $values[$metaId]['from_thesaurus'] = $v->isThesaurusValue(); + $values[$metaId]['qjs'] = $v->getQjs(); + } } $fields[$field->get_name()] = [ 'values' => $values, diff --git a/lib/classes/databox/descriptionStructure.php b/lib/classes/databox/descriptionStructure.php index 6b0fa68930..1e81488939 100644 --- a/lib/classes/databox/descriptionStructure.php +++ b/lib/classes/databox/descriptionStructure.php @@ -1,5 +1,4 @@ + * @var array|null */ - protected $cache_name_id = []; + protected $cache_name_id; /** * @param databox_field[] $fields @@ -51,6 +52,10 @@ class databox_descriptionStructure implements IteratorAggregate, Countable { $this->elements[$field->get_id()] = $field; + if (null !== $this->cache_name_id) { + $this->cache_name_id[$field->get_name()] = $field->get_id(); + } + return $this; } @@ -60,8 +65,9 @@ class databox_descriptionStructure implements IteratorAggregate, Countable */ public function remove_element(databox_field $field) { - if (isset($this->elements[$field->get_id()])) - unset($this->elements[$field->get_id()]); + if (isset($this->elements[$field->get_id()])) { + unset($this->elements[$field->get_id()], $this->cache_name_id[$field->get_name()]); + } return $this; } @@ -75,14 +81,14 @@ class databox_descriptionStructure implements IteratorAggregate, Countable } /** - * - * @param int $id + * @param int $id * @return databox_field */ public function get_element($id) { - if ( ! isset($this->elements[$id])) + if (!isset($this->elements[$id])) { throw new Exception_Databox_FieldNotFound (); + } return $this->elements[$id]; } @@ -93,23 +99,25 @@ class databox_descriptionStructure implements IteratorAggregate, Countable */ public function get_element_by_name($name) { - $name = databox_field::generateName($name); + if (null === $this->cache_name_id) { + $this->cache_name_id = []; - if (isset($this->cache_name_id[$name])) { - return $this->elements[$this->cache_name_id[$name]]; - } - - foreach ($this->elements as $id => $meta) { - if ($meta->get_name() === $name) { - $this->cache_name_id[$name] = $id; - - return $meta; + foreach ($this->elements as $id => $meta) { + $this->cache_name_id[$meta->get_name()] = $id; } } - return null; + $name = databox_field::generateName($name); + + return isset($this->cache_name_id[$name]) + ? $this->elements[$this->cache_name_id[$name]] + : null; } + /** + * @param string $label + * @return databox_field|null + */ public function get_dces_field($label) { foreach ($this->elements as $field) { @@ -125,13 +133,16 @@ class databox_descriptionStructure implements IteratorAggregate, Countable /** * @param string $id - * @return boolean + * @return bool */ public function isset_element($id) { return isset($this->elements[$id]); } + /** + * @return array + */ public function toArray() { return array_map(function (databox_field $element) { diff --git a/tests/Alchemy/Tests/Phrasea/Authentication/Provider/LinkedinTest.php b/tests/Alchemy/Tests/Phrasea/Authentication/Provider/LinkedinTest.php index 3cdf7b9417..920e80ffbf 100644 --- a/tests/Alchemy/Tests/Phrasea/Authentication/Provider/LinkedinTest.php +++ b/tests/Alchemy/Tests/Phrasea/Authentication/Provider/LinkedinTest.php @@ -16,12 +16,24 @@ class LinkedinTest extends ProviderTestCase { $state = md5(mt_rand()); + // test cases + $data = []; + + $data[] = [$this->getProvider(), $this->getRequestMock()]; + + // Second test $request = $this->getRequestMock(); $this->addQueryParameter($request, ['state' => $state]); - $provider1 = $this->getProvider(); - $provider1->setGuzzleClient($this->getGuzzleMock(401)); - $provider1->getSession()->set('linkedin.provider.state', $state); + $provider = $this->getProvider(); + $provider->setGuzzleClient($this->getGuzzleMock(401)); + $provider->getSession()->set('linkedin.provider.state', $state); + + $data[] = [$provider, $request]; + + // Third test + $request = $this->getRequestMock(); + $this->addQueryParameter($request, ['state' => $state]); $mock = $this->getMock('Guzzle\Http\ClientInterface'); @@ -67,15 +79,13 @@ class LinkedinTest extends ProviderTestCase ->method('post') ->will($this->returnValue($requestPost)); - $provider2 = $this->getProvider(); - $provider2->setGuzzleClient($mock); - $provider2->getSession()->set('linkedin.provider.state', $state); + $provider = $this->getProvider(); + $provider->setGuzzleClient($mock); + $provider->getSession()->set('linkedin.provider.state', $state); - return [ - [$this->getProvider(), $this->getRequestMock()], - [$provider1, $request], - [$provider2, $request], - ]; + $data[] = [$provider, $request]; + + return $data; } public function getProviderForLogout() diff --git a/tests/Alchemy/Tests/Phrasea/Databox/DataboxBoundRepositoryProviderTest.php b/tests/Alchemy/Tests/Phrasea/Databox/DataboxBoundRepositoryProviderTest.php new file mode 100644 index 0000000000..99859f2f08 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Databox/DataboxBoundRepositoryProviderTest.php @@ -0,0 +1,57 @@ +prophesize(DataboxBoundRepositoryFactory::class); + + $factory + ->createRepositoryFor(Argument::type('integer')) + ->will(function ($args) { + return (object)['databoxId' => $args[0]]; + }); + + $this->sut = new DataboxBoundRepositoryProvider($factory->reveal()); + } + + public function testItCreatesRepositoriesIfUnknown() + { + $repository = $this->sut->getRepositoryForDatabox(42); + + $this->assertNotNull($repository, 'Failed to create a repository'); + $this->assertSame($repository, $this->sut->getRepositoryForDatabox(42)); + } + + public function testItShouldNotCreateTwoRepositoriesPerDatabox() + { + $repository1 = $this->sut->getRepositoryForDatabox(1); + $repository2 = $this->sut->getRepositoryForDatabox(2); + + $this->assertNotNull($repository1, 'Failed to create first repository'); + $this->assertNotNull($repository2, 'Failed to create second repository'); + $this->assertNotSame($repository1, $repository2, 'Different Databoxes should have different repositories'); + + $this->assertSame($repository2, $this->sut->getRepositoryForDatabox(2), 'Second Repository should be returned'); + $this->assertSame($repository1, $this->sut->getRepositoryForDatabox(1), 'First Repository should be returned'); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Hydration/IdentityMapTest.php b/tests/Alchemy/Tests/Phrasea/Hydration/IdentityMapTest.php new file mode 100644 index 0000000000..ceeb43eea4 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Hydration/IdentityMapTest.php @@ -0,0 +1,114 @@ +prophesize(Hydrator::class); + + $hydrator->extract(Argument::type(\stdClass::class)) + ->will(function ($args) { + return (array)$args[0]; + }); + + $hydrator->hydrate(Argument::type(\stdClass::class), Argument::type('array')) + ->will(function ($args) { + + foreach ($args[1] as $property => $value) { + $args[0]->{$property} = $value; + } + }); + + $this->sut = new IdentityMap($hydrator->reveal(), (object)['foo' => null, 'bar' => null]); + } + + public function testItShouldBeArrayAccessibleTraversableAndCountable() + { + $this->assertInstanceOf(\Traversable::class, $this->sut); + $this->assertInstanceOf(\ArrayAccess::class, $this->sut); + $this->assertInstanceOf(\Countable::class, $this->sut); + } + + public function testItShouldHydrateAnInstanceWhenNotYetInMap() + { + $expected = (object)['foo' => 'foo', 'bar' => 'bar']; + + $instance = $this->sut->hydrate(42, ['foo' => 'foo', 'bar' => 'bar']); + + $this->assertEquals($expected, $instance, 'Invalid instance generated'); + + $this->assertSame($instance, $this->sut[42], 'Accessing by offset should succeed'); + } + + public function testItShouldReHydrateAnInstance() + { + $instance = $this->sut->hydrate(42, ['foo' => 'Foo', 'bar' => 'Bar']); + + $this->assertAttributeSame('Foo', 'foo', $instance); + $this->assertAttributeSame('Bar', 'bar', $instance); + + $instance2 = $this->sut->hydrate(42, ['foo' => 'new foo value', 'bar' => null]); + + $this->assertAttributeSame('new foo value', 'foo', $instance); + $this->assertAttributeSame(null, 'bar', $instance); + + $this->assertSame($instance, $instance2, 'Same instance was not rehydrated'); + + $this->assertCount(1, $this->sut); + } + + public function testItsOffsetCanBeUnset() + { + $this->sut[42] = ['foo' => 'Foo', 'bar' => 'Bar']; + + $this->assertTrue(isset($this->sut[42]), 'Offset should exists'); + + unset($this->sut[42]); + + $this->assertFalse(isset($this->sut[42]), 'Offset should not exists after unset'); + } + + public function testItHydratesAllEntities() + { + $data = [ + ['foo' => 'Foo1', 'bar' => 'Bar1'], + ['foo' => 'Foo2', 'bar' => 'Bar2'], + ]; + + $this->sut->hydrateAll($data); + + $entities = []; + + foreach ($this->sut as $key => $value) { + $entities[$key] = $value; + } + + foreach ($data as $key => $value) { + $this->assertArrayHasKey($key, $entities, 'An entity is missing'); + $this->assertEquals((object)$value, $entities[$key], 'Unexpected entity value'); + } + + $this->assertCount(2, $this->sut); + $this->sut->clear(); + $this->assertCount(0, $this->sut, 'Map was not cleared'); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Hydration/ReflectionHydratorTest.php b/tests/Alchemy/Tests/Phrasea/Hydration/ReflectionHydratorTest.php new file mode 100644 index 0000000000..a956414f94 --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Hydration/ReflectionHydratorTest.php @@ -0,0 +1,73 @@ +sut = new ReflectionHydrator(ToHydrate::class, ['foo', 'bar']); + } + + public function testItThrowsExceptionOnUnknownProperty() + { + $this->setExpectedException(\ReflectionException::class); + + $sut = new ReflectionHydrator(ToHydrate::class, ['baz']); + $sut->extract(new ToHydrate()); + } + + public function testItShouldImplementHydrator() + { + $this->assertInstanceOf(Hydrator::class, $this->sut); + } + + public function testItShouldProperlyHydrateInstance() + { + $stub = new ToHydrate(); + + $this->sut->hydrate($stub, ['foo' => 'foo modified', 'bar' => 'bar changed']); + + $this->assertEquals('foo modified', $stub->getFoo(), 'Property foo was not hydrated'); + $this->assertEquals('bar changed', $stub->getBar(), 'Property bar was not hydrated'); + } + + public function testItShouldProperlyExtractData() + { + $data = $this->sut->extract(new ToHydrate()); + + $this->assertSame(['foo' => 'foo', 'bar' => 'bar'], $data, 'Improper extraction of properties'); + } +} + +class ToHydrate +{ + private $foo = 'foo'; + private $bar = 'bar'; + + public function getFoo() + { + return $this->foo; + } + + public function getBar() + { + return $this->bar; + } +}