/** * Builds default context. * * @param ContextBuilderEvent $event */ public function onContextBuilder(ContextBuilderEvent $event) { $resource = $event->getResource(); if (null === $resource) { return; } $context = $event->getContext(); $prefixedShortName = sprintf('#%s', $resource->getShortName()); $attributes = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass(), $resource->getNormalizationGroups(), $resource->getDenormalizationGroups(), $resource->getValidationGroups())->getAttributes(); foreach ($attributes as $attributeName => $attribute) { if ($attribute->isIdentifier()) { continue; } $convertedName = $this->nameConverter ? $this->nameConverter->normalize($attributeName) : $attributeName; if (!($id = $attribute->getIri())) { $id = sprintf('%s/%s', $prefixedShortName, $convertedName); } if ($attribute->isNormalizationLink()) { $context[$convertedName] = ['@id' => $id, '@type' => '@id']; } else { $context[$convertedName] = $id; } } $event->setContext($context); }
/** * {@inheritdoc} */ public function getApiDocumentation() { $classes = []; $entrypointProperties = []; foreach ($this->resourceCollection as $resource) { $classMetadata = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass(), $resource->getNormalizationGroups(), $resource->getDenormalizationGroups(), $resource->getValidationGroups()); $shortName = $resource->getShortName(); $prefixedShortName = ($iri = $classMetadata->getIri()) ? $iri : '#' . $shortName; $collectionOperations = []; foreach ($resource->getCollectionOperations() as $collectionOperation) { $collectionOperations[] = $this->getHydraOperation($resource, $collectionOperation, $prefixedShortName, true); } $entrypointProperties[] = ['@type' => 'hydra:SupportedProperty', 'hydra:property' => ['@id' => sprintf('#Entrypoint/%s', lcfirst($shortName)), '@type' => 'hydra:Link', 'domain' => '#Entrypoint', 'rdfs:label' => sprintf('The collection of %s resources', $shortName), 'range' => 'hydra:PagedCollection', 'hydra:supportedOperation' => $collectionOperations], 'hydra:title' => sprintf('The collection of %s resources', $shortName), 'hydra:readable' => true, 'hydra:writable' => false]; $class = ['@id' => $prefixedShortName, '@type' => 'hydra:Class', 'rdfs:label' => $resource->getShortName(), 'hydra:title' => $resource->getShortName(), 'hydra:description' => $classMetadata->getDescription()]; if ($description = $classMetadata->getDescription()) { $class['hydra:description'] = $description; } $properties = []; foreach ($classMetadata->getAttributes() as $attributeName => $attributeMetadata) { if ($attributeMetadata->isIdentifier() && !$attributeMetadata->isWritable()) { continue; } if ($attributeMetadata->isNormalizationLink()) { $type = 'Hydra:Link'; } else { $type = 'rdf:Property'; } $property = ['@type' => 'hydra:SupportedProperty', 'hydra:property' => ['@id' => ($iri = $attributeMetadata->getIri()) ? $iri : sprintf('#%s/%s', $shortName, $attributeName), '@type' => $type, 'rdfs:label' => $attributeName, 'domain' => $prefixedShortName], 'hydra:title' => $attributeName, 'hydra:required' => $attributeMetadata->isRequired(), 'hydra:readable' => $attributeMetadata->isIdentifier() ? false : $attributeMetadata->isReadable(), 'hydra:writable' => $attributeMetadata->isWritable()]; if ($range = $this->getRange($attributeMetadata)) { $property['hydra:property']['range'] = $range; } if ($description = $attributeMetadata->getDescription()) { $property['hydra:description'] = $description; } $properties[] = $property; } $class['hydra:supportedProperty'] = $properties; $operations = []; foreach ($resource->getItemOperations() as $itemOperation) { $operations[] = $this->getHydraOperation($resource, $itemOperation, $prefixedShortName, false); } $class['hydra:supportedOperation'] = $operations; $classes[] = $class; } // Entrypoint $classes[] = ['@id' => '#Entrypoint', '@type' => 'hydra:Class', 'hydra:title' => 'The API entrypoint', 'hydra:supportedProperty' => $entrypointProperties, 'hydra:supportedOperation' => ['@type' => 'hydra:Operation', 'hydra:method' => 'GET', 'rdfs:label' => 'The API entrypoint.', 'returns' => '#EntryPoint']]; // Constraint violation $classes[] = ['@id' => '#ConstraintViolation', '@type' => 'hydra:Class', 'hydra:title' => 'A constraint violation', 'hydra:supportedProperty' => [['@type' => 'hydra:SupportedProperty', 'hydra:property' => ['@id' => '#ConstraintViolation/propertyPath', '@type' => 'rdf:Property', 'rdfs:label' => 'propertyPath', 'domain' => '#ConstraintViolation', 'range' => 'xmls:string'], 'hydra:title' => 'propertyPath', 'hydra:description' => 'The property path of the violation', 'hydra:readable' => true, 'hydra:writable' => false], ['@type' => 'hydra:SupportedProperty', 'hydra:property' => ['@id' => '#ConstraintViolation/message', '@type' => 'rdf:Property', 'rdfs:label' => 'message', 'domain' => '#ConstraintViolation', 'range' => 'xmls:string'], 'hydra:title' => 'message', 'hydra:description' => 'The message associated with the violation', 'hydra:readable' => true, 'hydra:writable' => false]]]; // Constraint violation list $classes[] = ['@id' => '#ConstraintViolationList', '@type' => 'hydra:Class', 'subClassOf' => 'hydra:Error', 'hydra:title' => 'A constraint violation list', 'hydra:supportedProperty' => [['@type' => 'hydra:SupportedProperty', 'hydra:property' => ['@id' => '#ConstraintViolationList/violation', '@type' => 'rdf:Property', 'rdfs:label' => 'violation', 'domain' => '#ConstraintViolationList', 'range' => '#ConstraintViolation'], 'hydra:title' => 'violation', 'hydra:description' => 'The violations', 'hydra:readable' => true, 'hydra:writable' => false]]]; return ['@context' => $this->getContext(), '@id' => $this->router->generate('api_hydra_vocab'), 'hydra:title' => $this->title, 'hydra:description' => $this->description, 'hydra:entrypoint' => $this->router->generate('api_json_ld_entrypoint'), 'hydra:supportedClass' => $classes]; }
/** * @param AttributeMetadataInterface $attributeMetadata * * @return mixed */ public function guess(AttributeMetadataInterface $attributeMetadata) { $value = null; $type = null; if (true === ($isDoctrine = isset($attributeMetadata->getTypes()[0]))) { $type = $attributeMetadata->getTypes()[0]; } // Guess associations if ($isDoctrine && 'object' === $type->getType() && 'DateTime' !== $type->getClass()) { $class = $type->isCollection() ? $type->getCollectionType()->getClass() : $type->getClass(); $resource = $this->resourceCollection->getResourceForEntity($class); $classMetadata = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass(), $resource->getNormalizationGroups(), $resource->getDenormalizationGroups(), $resource->getValidationGroups()); $id = $this->guess($classMetadata->getIdentifier()); $value = $this->iriConverter->getIriFromResource($resource) . '/' . $id; if ($type->isCollection()) { $value = [$value]; } } // Guess by faker if (null === $value) { try { $value = call_user_func([$this->generator, $attributeMetadata->getName()]); } catch (\InvalidArgumentException $e) { } } // Guess by field name if (null === $value) { $value = $this->guessFormat(Inflector::tableize($attributeMetadata->getName())); } // Guess by Doctrine type if (null === $value && $isDoctrine) { switch ($type->getType()) { case 'string': $value = $this->generator->sentence; break; case 'int': $value = $this->generator->numberBetween; break; case 'bool': $value = $this->generator->boolean; break; case 'object': if ('DateTime' !== $type->getClass()) { throw new \InvalidArgumentException(sprintf('Unknown Doctrine object type %s in field %s', $type->getClass(), $attributeMetadata->getName())); } $value = $this->generator->dateTime; break; } } return $this->clean($value); }
/** * Builds the JSON-LD context for the given resource. * * @param ResourceInterface|null $resource * * @return array */ public function getContext(ResourceInterface $resource = null) { $context = $this->getBaseContext(); if ($resource) { $prefixedShortName = sprintf('#%s', $resource->getShortName()); $attributes = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass(), $resource->getNormalizationGroups(), $resource->getDenormalizationGroups(), $resource->getValidationGroups())->getAttributes(); foreach ($attributes as $attributeName => $attribute) { if (!($id = $attribute->getIri())) { $id = sprintf('%s/%s', $prefixedShortName, $attributeName); } if ($attribute->isNormalizationLink()) { $context[$attributeName] = ['@id' => $id, '@type' => '@id']; } else { $context[$attributeName] = $id; } } } return $context; }
/** * {@inheritdoc} */ public function parse(Request $request) { $resource = $request->getResource(); $classMetadata = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass(), $resource->getNormalizationGroups(), $resource->getDenormalizationGroups(), $resource->getValidationGroups()); $request->addHeader('Content-Type', 'application/json'); $request->setDataMode(Request::DATA_MODE_RAW); $rawModeData = []; foreach ($classMetadata->getAttributes() as $attributeMetadata) { if (!$classMetadata->getReflectionClass()->hasProperty($attributeMetadata->getName())) { // Attribute is not a property: ignore it continue; } $groups = $this->reader->getPropertyAnnotation($classMetadata->getReflectionClass()->getProperty($attributeMetadata->getName()), 'Symfony\\Component\\Serializer\\Annotation\\Groups'); if ($attributeMetadata->isIdentifier() || !$attributeMetadata->isReadable() || !count(array_intersect($groups ? $groups->getGroups() : [], $resource->getDenormalizationGroups() ?: []))) { continue; } $rawModeData[$attributeMetadata->getName()] = $this->guesser->guess($attributeMetadata); } $request->setRawModeData($rawModeData); }
/** * {@inheritdoc} */ public function getAnnotations() { $annotations = []; $hydraDoc = $this->apiDocumentationBuilder->getApiDocumentation(); $entrypointHydraDoc = $this->getResourceHydraDoc($hydraDoc, '#Entrypoint'); /** * @var ResourceInterface */ foreach ($this->resourceCollection as $resource) { $classMetadata = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass()); $prefixedShortName = ($iri = $classMetadata->getIri()) ? $iri : '#' . $resource->getShortName(); $resourceHydraDoc = $this->getResourceHydraDoc($hydraDoc, $prefixedShortName); if ($hydraDoc) { foreach ($resource->getCollectionOperations() as $operation) { $annotations[] = $this->getApiDoc(true, $resource, $operation, $resourceHydraDoc, $entrypointHydraDoc); } foreach ($resource->getItemOperations() as $operation) { $annotations[] = $this->getApiDoc(false, $resource, $operation, $resourceHydraDoc); } } } return $annotations; }
/** * Gets metadata for the given resource with the current context. * * Fallback to the resource own groups if no context is provided. * * @param ResourceInterface $resource * @param array $context * * @return ClassMetadataInterface */ private function getMetadata(ResourceInterface $resource, array $context) { return $this->apiClassMetadataFactory->getMetadataFor($resource->getEntityClass(), isset($context['json_ld_normalization_groups']) ? $context['json_ld_normalization_groups'] : $resource->getNormalizationGroups(), isset($context['json_ld_denormalization_groups']) ? $context['json_ld_denormalization_groups'] : $resource->getDenormalizationGroups(), isset($context['json_ld_validation_groups']) ? $context['json_ld_validation_groups'] : $resource->getValidationGroups()); }
/** * @param ResourceInterface $resource * * @return AttributeMetadataInterface */ private function getIdentifierFromResource(ResourceInterface $resource) { $classMetadata = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass(), $resource->getNormalizationGroups(), $resource->getDenormalizationGroups(), $resource->getValidationGroups()); return $classMetadata->getIdentifier(); }
/** * {@inheritdoc} */ public function warmUp($cacheDir) { foreach ($this->resourceCollection as $resource) { $this->classMetadataFactory->getMetadataFor($resource->getEntityClass(), $resource->getNormalizationGroups(), $resource->getDenormalizationGroups(), $resource->getValidationGroups()); } }