Skip to content

Commit

Permalink
more work, need to handle quantity types better.
Browse files Browse the repository at this point in the history
  • Loading branch information
dcarbone committed Jan 29, 2025
1 parent 4cff46f commit 484d25a
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 48 deletions.
2 changes: 1 addition & 1 deletion src/Enum/XMLValueLocationUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class XMLValueLocationUtils
public static function determineDefaultLocation(Type $type, Property $property, bool $withClass): string
{
$case = match (true) {
$property->isValueProperty() => 'LOCAL_ATTRIBUTE',
$property->isValueProperty() && !$type->isQuantity() && !$type->hasQuantityParent() => 'LOCAL_ATTRIBUTE',
default => 'ELEMENT',
};
if ($withClass) {
Expand Down
21 changes: 21 additions & 0 deletions src/Version/Definition/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,27 @@ public function hasValueContainerParent(): bool
return false;
}

/**
* @return bool
*/
public function isQuantity(): bool
{
return $this->_kind === TypeKindEnum::QUANTITY;
}

/**
* @return bool
*/
public function hasQuantityParent(): bool
{
foreach($this->getParentTypes() as $parent) {
if ($parent->isQuantity()) {
return true;
}
}
return false;
}

/**
* Returns map of [$interface_name => $interface_namespace]
*
Expand Down
31 changes: 10 additions & 21 deletions src/Version/Definition/TypeDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,6 @@ public static function determineParsedTypeKinds(Config $config, Version $version
*/
public static function setContainedTypeFlag(Config $config, Version $version, Types $types): void
{
$versionName = $version->getName();

foreach ($types->getIterator() as $type) {
if ($types->isContainedType($type)) {
$type->setContainedType(true);
Expand All @@ -382,41 +380,32 @@ public static function setContainedTypeFlag(Config $config, Version $version, Ty
}

/**
* A "value container" type is any non-primitive type with a "value" property.
*
* @param \DCarbone\PHPFHIR\Config $config
* @param \DCarbone\PHPFHIR\Version\Definition\Types $types
*/
public static function setValueContainerFlag(Config $config, Types $types): void
{
static $skip = [
TypeKindEnum::PRIMITIVE,
TypeKindEnum::PHPFHIR_XHTML,
TypeKindEnum::QUANTITY,
];
// TODO: handle valueString, valueQuantity, etc. types?
foreach ($types->getIterator() as $type) {
// TODO: handle valueString, valueQuantity, etc. types?
// skip primitive types and their child types
if ($type->getKind()->isOneOf(...$skip) || $type->hasPrimitiveOrListParent()) {
// primitive types have special handling and must not be marked as "value containers"
if ($type->isPrimitiveOrListType() || $type->hasPrimitiveOrListParent()) {
continue;
}

$properties = $type->getProperties();

// only target types with a single field on them with the name "value"
if (1 !== count($properties) || !$properties->hasProperty(PHPFHIR_VALUE_PROPERTY_NAME)) {
// skip "quantity" types, as their "value" is always applied as an element
if ($type->getKind()->isOneOf(TypeKindEnum::QUANTITY)) {
continue;
}

$property = $properties->getProperty(PHPFHIR_VALUE_PROPERTY_NAME);
$propertyType = $property->getValueFHIRType();

// only target types where the "value" field is itself typed
if (null === $propertyType) {
// skip xhtml type
if (PHPFHIR_XHTML_TYPE_NAME === $type->getFHIRName()) {
continue;
}

$type->setValueContainer(true);
$type->setValueContainer($type->getProperties()->hasProperty(PHPFHIR_VALUE_PROPERTY_NAME));
}
}

Expand Down
9 changes: 5 additions & 4 deletions template/core/encoding/enum_value_xml_location.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@

<?php echo $config->getBasePHPFHIRCopyrightComment(true); ?>

enum <?php echo PHPFHIR_ENCODING_ENUM_VALUE_XML_LOCATION; ?> : string
enum <?php echo PHPFHIR_ENCODING_ENUM_VALUE_XML_LOCATION; ?>

{
case PARENT_ATTRIBUTE = 'parent_attribute';
case LOCAL_ATTRIBUTE = 'local_attribute';
case ELEMENT = 'element';
case PARENT_ATTRIBUTE;
case LOCAL_ATTRIBUTE;
case ELEMENT;
}
<?php return ob_get_clean();
6 changes: 2 additions & 4 deletions template/versions/types/class_default.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public function _getResourceType(): string
);
endif;

if ($type->isPrimitiveContainer() || $type->hasPrimitiveContainerParent()) : ?>
if ($type->isValueContainer() || $type->hasValueContainerParent()) : ?>

/* <?php echo basename(__FILE__) . ':' . __LINE__; ?> */
public function _nonValueFieldDefined(): bool
Expand Down Expand Up @@ -243,10 +243,8 @@ public function _nonValueFieldDefined(): bool
*/
public function __toString(): string
{
<?php if ($typeKind === TypeKindEnum::PRIMITIVE) : ?>
<?php if ($type->isPrimitiveOrListType() || $type->hasPrimitiveOrListParent() || $type->isValueContainer() || $type->hasValueContainerParent()) : ?>
return $this->_getFormattedValue();
<?php elseif ($typeKind->isOneOf(TypeKindEnum::LIST, TypeKindEnum::PRIMITIVE_CONTAINER)) : ?>
return (string)$this->getValue();
<?php else : ?>
return self::FHIR_TYPE_NAME;
<?php endif; ?>
Expand Down
4 changes: 2 additions & 2 deletions template/versions/types/properties/methods/default.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ public function _set<?php echo ucfirst($propertyName); ?>ValueXMLLocation(<?php
<?php endif;
endforeach;

if ($type->isPrimitiveContainer() && !$type->hasPrimitiveContainerParent()) :
if (($type->isValueContainer() && !$type->hasValueContainerParent()) || ($type->isQuantity() && !$type->hasQuantityParent())):
$valueProp = $type->getProperties()->getProperty(PHPFHIR_VALUE_PROPERTY_NAME);
?>
?>

/**
* Return the formatted value of this type's contained primitive type.
Expand Down
55 changes: 39 additions & 16 deletions template/versions/types/serialization/xml/serialize/body.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,44 +27,67 @@

ob_start();

// first, marshal attribute values
// start attribute serialization
foreach ($type->getProperties()->getIterator() as $property) :
if (!$property->isSerializableAsXMLAttribute()) {
continue;
}

$propType = $property->getValueFHIRType();

// if this is the "value" property on a primitive container, the value may be serialized to the parent's root node,
// to the local node's attributes, or as an element on the local node.
if ($type->isValueContainer() && $property->isValueProperty()) : ?>
// a "value" property may be serialized as an attribute on the containing type's parent node, an attribute on the
// containing type's node, or as a child element of the containing type's node.
//
// this defaults to being controlled by the containing node, unless the parent node explicitly passes the
// "parent_attribute" value enum into the xml serialize func of the containing type.
//
// "value" properties are the only types of properties that respect the "valueLocation" parameter on value
// container xml serialize funcs.
if ($property->isValueProperty()) :
// if this is a value container type, we need to only serialize our value as an attribute on our node
// if, and only if, the $valueLocation parameter is == "local_attribute" or is null and our internal
// location map defines it as "local_attribute"
if ($type->isValueContainer() || $type->hasValueContainerParent()) : ?>
if (isset($this-><?php echo $property->getName(); ?>)
&& <?php echo $xmlLocationEnum->getEntityName(); ?>::PARENT_ATTRIBUTE !== $valueLocation
&& (<?php echo $xmlLocationEnum->getEntityName(); ?>::LOCAL_ATTRIBUTE === $valueLocation
|| (null === $valueLocation && $this->_valueXMLLocations[self::<?php echo $property->getFieldConstantName(); ?>] === <?php echo $xmlLocationEnum->getEntityName(); ?>::LOCAL_ATTRIBUTE))) {
|| (null === $valueLocation
&& <?php echo $xmlLocationEnum->getEntityName(); ?>::LOCAL_ATTRIBUTE === $this->_valueXMLLocations[self::<?php echo $property->getFieldConstantName(); ?>]))) {
$xw->writeAttribute(self::<?php echo $property->getFieldConstantName(); ?>, $this-><?php echo $property->getName(); ?>->_getFormattedValue());
}
<?php
// if this is a primitive property type, the value may only be placed either as an attribute or element of the
// parent node.
elseif ($propType->hasPrimitiveOrListParent() || $propType->isPrimitiveOrListType()) : ?>
if (isset($this-><?php echo $property->getName(); ?>) && $this->_valueXMLLocations[self::<?php echo $property->getFieldConstantName(); ?>] === <?php echo $xmlLocationEnum->getEntityName(); ?>::PARENT_ATTRIBUTE) {
// if this is _not_ a value container type, we must only apply this attribute to the local type if and only if
// our local location map places it at "parent_attribute".
else : ?>
if (isset($this-><?php echo $property->getName(); ?>) && <?php echo $xmlLocationEnum->getEntityName(); ?>::PARENT_ATTRIBUTE === $this->_valueXMLLocations[self::<?php echo $property->getFieldConstantName(); ?>]) {
$xw->writeAttribute(self::<?php echo $property->getFieldConstantName(); ?>, $this-><?php echo $property->getName(); ?>->_getFormattedValue());
}
<?php endif;

elseif ($propType->isPrimitiveOrListType() || $propType->hasPrimitiveOrListParent()) : ?>
if (isset($this-><?php echo $property->getName(); ?>) && <?php echo $xmlLocationEnum->getEntityName(); ?>::LOCAL_ATTRIBUTE === $this->_valueXMLLocations[self::<?php echo $property->getFieldConstantName(); ?>]) {
$xw->writeAttribute(self::<?php echo $property->getFieldConstantName(); ?>, $this-><?php echo $property->getName(); ?>->_getFormattedValue());
}
<?php
// if this property is a value container, the value may be placed as an attribute on the parent node, an
// attribute on the local node, or as an element of the local node.
elseif ($propType->isValueContainer()) : ?>
if (isset($this-><?php echo $property->getName(); ?>) && $this->_valueXMLLocations[self::<?php echo $property->getFieldConstantName(); ?>] === <?php echo $xmlLocationEnum->getEntityName(); ?>::PARENT_ATTRIBUTE) {
$xw->writeAttribute(self::<?php echo $property->getFieldConstantName(); ?>, $this-><?php echo $property->getName(); ?>->getValue()?->_getFormattedValue());
elseif ($propType->isValueContainer() || $propType->hasValueContainerParent()) : ?>
if (isset($this-><?php echo $property->getName(); ?>) && <?php echo $xmlLocationEnum->getEntityName(); ?>::PARENT_ATTRIBUTE === $this->_valueXMLLocations[self::<?php echo $property->getFieldConstantName(); ?>]) {
$xw->writeAttribute(self::<?php echo $property->getFieldConstantName(); ?>, $this-><?php echo $property->getName(); ?>->_getFormattedValue());
}
<?php
else :
throw new \LogicException(sprintf(
'Cannot handle serializing type "%s" property "%s" of type "%s" as attribute.',
$type->getFHIRName(),
$property->getName(),
$propType->getFHIRName(),
));
endif;

// end attribute serialization
endforeach;

// next, marshal parent attribute & element values
if ($type->hasConcreteParent()) : ?>
parent::xmlSerialize($xw, $config<?php if ($type->hasPrimitiveContainerParent()) : ?>, $valueLocation<?php endif; ?>);
parent::xmlSerialize($xw, $config<?php if ($type->hasValueContainerParent()) : ?>, $valueLocation<?php endif; ?>);
<?php endif;

// finally, marshal local element values
Expand Down

0 comments on commit 484d25a

Please sign in to comment.