/** * Extracts the model's relationships, per JSON API spec. * * @param array $data * @param EntityMetadata $metadata * @return array */ protected function extractRelationships(array $data, EntityMetadata $metadata) { $flattened = []; if (!isset($data['relationships']) || !is_array($data['relationships'])) { return $flattened; } foreach ($metadata->getRelationships() as $key => $relMeta) { if (!isset($data['relationships'][$key])) { continue; } $rel = $data['relationships'][$key]; // Need to use array_key_exists over isset because 'null' is valid. Creating a key map for each relationship is too costly since every relationship needs the data check. if (false === array_key_exists('data', $rel)) { throw NormalizerException::badRequest(sprintf('The "data" member was missing from the payload for relationship "%s"', $key)); } if (empty($rel['data'])) { $flattened[$key] = $relMeta->isOne() ? null : []; continue; } if (!is_array($rel['data'])) { throw NormalizerException::badRequest(sprintf('The "data" member is not valid in the payload for relationship "%s"', $key)); } if (true === $relMeta->isOne() && true === $this->isSequentialArray($rel['data'])) { throw NormalizerException::badRequest(sprintf('The data payload for relationship "%s" is malformed. Data types of "one" must be an associative array, sequential found.', $key)); } if (true === $relMeta->isMany() && false === $this->isSequentialArray($rel['data'])) { throw NormalizerException::badRequest(sprintf('The data payload for relationship "%s" is malformed. Data types of "many" must be a sequential array, associative found.', $key)); } $flattened[$key] = $rel['data']; } return $flattened; }
/** * {@inheritDoc} */ public function handleValidate(EntityMetadata $metadata) { $persistence = $metadata->persistence; $validIdStrategies = ['object']; if (!in_array($persistence->idStrategy, $validIdStrategies)) { throw MetadataException::invalidMetadata($metadata->type, sprintf('The persistence id strategy "%s" is invalid. Valid types are "%s"', $persistence->idStrategy, implode('", "', $validIdStrategies))); } if (false === $metadata->isChildEntity() && (empty($persistence->db) || empty($persistence->collection))) { throw MetadataException::invalidMetadata($metadata->type, 'The persistence database and collection names cannot be empty.'); } if (false === $this->entityUtil->isEntityTypeValid($persistence->collection)) { throw MetadataException::invalidMetadata($metadata->type, sprintf('The entity persistence collection "%s" is invalid based on the configured name format "%s"', $persistence->collection, $this->entityUtil->getRestConfig()->getEntityFormat())); } }
protected function setMixins(Metadata\EntityMetadata $metadata, array $mixins) { foreach ($mixins as $mixinName) { $mixinMeta = $this->loadMetadataForMixin($mixinName); $metadata->addMixin($mixinMeta); } return $metadata; }
/** * Validates that a model type can be set to an owning metadata type. * * @param EntityMetadata $owningMeta The metadata the type will be added to. * @param string $typeToAdd The type to add. * @return self * @throws StoreException If the type to add is not supported. */ public function validateRelationshipSet(EntityMetadata $owningMeta, $typeToAdd) { if (true === $owningMeta->isPolymorphic()) { $canSet = in_array($typeToAdd, $owningMeta->ownedTypes); } else { $canSet = $owningMeta->type === $typeToAdd; } if (false === $canSet) { throw StoreException::badRequest(sprintf('The model type "%s" cannot be added to "%s", as it is not supported.', $typeToAdd, $owningMeta->type)); } return $this; }
/** * Determines if the model uses a particlar mixin. * * @api * @param string $name * @return bool */ public function usesMixin($name) { return $this->metadata->hasMixin($name); }
/** * Gets standard database retrieval criteria for an entity and the provided identifiers. * * @param EntityMetadata $metadata The entity to retrieve database records for. * @param string|array $identifiers The IDs to query. * @return array */ protected function getRetrieveCritiera(EntityMetadata $metadata, $identifiers) { $idKey = $this->getIdentifierKey(); $criteria[$idKey] = $this->getIdentifierCriteria($identifiers); if (empty($criteria[$idKey])) { unset($criteria[$idKey]); } if (true === $metadata->isChildEntity()) { $criteria[$this->getPolymorphicKey()] = $metadata->type; } return $criteria; }
/** * Validates entity relationships on EntityMetadata. * * @param EntityMetadata $metadata * @param MetadataFactory $mf * @return bool * @throws MetadataException */ public function validateMetadataRelationships(EntityMetadata $metadata, MetadataFactory $mf) { foreach ($metadata->getRelationships() as $key => $relationship) { if ($key != $relationship->key) { throw MetadataException::invalidMetadata($metadata->type, sprintf('Relationship key mismtach. "%s" !== "%s"', $key, $relationship->key)); } if (false === $this->isFieldKeyValid($relationship->key)) { throw MetadataException::invalidMetadata($metadata->type, sprintf('The relationship key "%s" is invalid based on the configured name format "%s"', $relationship->key, $this->config->getFieldKeyFormat())); } if (true === $metadata->isChildEntity()) { $parent = $mf->getMetadataForType($metadata->extends); if ($parent->hasRelationship($relationship->key)) { throw MetadataException::invalidMetadata($metadata->type, sprintf('Parent entity type "%s" already contains relationship field "%s"', $parent->type, $relationship->key)); } } if (false === $this->isEntityTypeValid($relationship->entityType)) { throw MetadataException::invalidMetadata($metadata->type, sprintf('The related model "%s" for relationship "%s" is invalid based on the configured name format "%s"', $relationship->entityType, $relationship->key, $this->config->getEntityFormat())); } if (false === $mf->metadataExists($relationship->entityType)) { throw MetadataException::invalidMetadata($metadata->type, sprintf('The related model "%s" for relationship "%s" does not exist.', $relationship->entityType, $relationship->key)); } if (true === $relationship->isInverse) { if ('one' === $relationship->relType) { throw MetadataException::invalidMetadata($metadata->type, 'The relationship is inverse and one, which is currently not supported.'); } if (empty($relationship->inverseField)) { throw MetadataException::invalidMetadata($metadata->type, 'The relationship is inverse, but no inverse field was specified.'); } $related = $relationship->entityType === $metadata->type ? $metadata : $mf->getMetadataForType($relationship->entityType); if (false === $related->hasRelationship($relationship->inverseField)) { throw MetadataException::invalidMetadata($metadata->type, 'The relationship is inverse, but the related model does not contain the specified inverse field.'); } $relatedRel = $related->getRelationship($relationship->inverseField); if (true === $relatedRel->isInverse) { throw MetadataException::invalidMetadata($metadata->type, 'The relationship is inverse, but the relationship it references is also inverse.'); } if ('one' !== $relatedRel->relType) { throw MetadataException::invalidMetadata($metadata->type, 'The relationship is inverse and many, but it\'s reference is not a type of one.'); } } } return true; }