diff --git a/CHANGELOG.md b/CHANGELOG.md index be7c500..9e1818b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ChangeLog 1.3.0 (????-??-??) ------------------ +* The `Service` class adds a new `mapValueObject` method which provides basic + capabilities to map between ValueObjects and XML. * #61: You can now specify serializers for specific classes, allowing you separate the object you want to serialize from the serializer. This uses the `$classMap` property which is defined on both the `Service` and `Writer`. diff --git a/composer.json b/composer.json index 2369ebe..4c4895c 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,10 @@ "psr-4" : { "Sabre\\Xml\\" : "lib/" }, - "files": ["lib/deserializers.php"] + "files": [ + "lib/deserializers.php", + "lib/serializers.php" + ] }, "bin" : [ ], diff --git a/lib/Service.php b/lib/Service.php index 8625a00..d81775f 100644 --- a/lib/Service.php +++ b/lib/Service.php @@ -189,6 +189,30 @@ function write($rootElementName, $value, $contextUri = null) { } + /** + * Maps the given value-object to the given rootElementName within the given namespace. + * + * The $rootElement must be specified in clark notation. + * + * The $className refernces a regular php class which must provide a __construct() without arguments. + * This methods creates instances of this class which later on picks up all xml child elements as public properties. + * + * @param string $rootElementName + * @param object $className + * @param string $namespace + */ + function mapValueObject($rootElementName, $className, $namespace) { + if (!class_exists($className)) { + throw new \InvalidArgumentException('class "' . $className . '" does not exist'); + } + + $this->elementMap['{' . $namespace . '}' . $rootElementName] = function(Reader $reader) use ($className, $namespace) { + return \Sabre\Xml\Deserializer\valueObject($reader, new $className(), $namespace); + }; + $this->classMap[$className] = function(Writer $writer, $valueObject) use ($namespace) { + return \Sabre\Xml\Serializer\valueObject($writer, $valueObject, $namespace); + }; + } /** * Parses a clark-notation string, and returns the namespace and element @@ -212,6 +236,4 @@ static function parseClarkNotation($str) { ]; } - - } diff --git a/lib/deserializers.php b/lib/deserializers.php index 2bd67c6..01babf0 100644 --- a/lib/deserializers.php +++ b/lib/deserializers.php @@ -161,3 +161,31 @@ function elementList(Reader $reader, $namespace = null) { return $values; } + +/** + * @param Reader $reader + * @param object $valueObject + * @param string $namespace + * @return null|$valueObject + */ +function valueObject(Reader $reader, $valueObject, $namespace) { + if ($reader->isEmptyElement) { + $reader->next(); + return null; + } + + $reader->read(); + do { + + if ($reader->nodeType === Reader::ELEMENT && $reader->namespaceURI == $namespace) { + + $valueObject->{$reader->localName} = $reader->parseCurrentElement()['value']; + } else { + $reader->read(); + } + } while ($reader->nodeType !== Reader::END_ELEMENT); + + $reader->read(); + + return $valueObject; +} diff --git a/lib/serializers.php b/lib/serializers.php new file mode 100644 index 0000000..8a353c4 --- /dev/null +++ b/lib/serializers.php @@ -0,0 +1,22 @@ + $val) { + $writer->writeElement('{' . $namespace . '}' . $key, $val); + } +} diff --git a/tests/Sabre/Xml/ServiceTest.php b/tests/Sabre/Xml/ServiceTest.php index 287b776..2f6ba88 100644 --- a/tests/Sabre/Xml/ServiceTest.php +++ b/tests/Sabre/Xml/ServiceTest.php @@ -198,6 +198,43 @@ function testWrite() { } + function testMapValueObject() { + + $input = << + + 1234 + 99.99 + black friday deal + + 5 + + + + +XML; + + $ns = 'http://sabredav.org/ns'; + $orderService = new \Sabre\Xml\Service(); + $orderService->mapValueObject('order', 'Sabre\Xml\Order', $ns); + $orderService->mapValueObject('status', 'Sabre\Xml\OrderStatus', $ns); + $orderService->namespaceMap[$ns] = null; + + $order = $orderService->parse($input); + $expected = new Order(); + $expected->id = 1234; + $expected->amount = 99.99; + $expected->description = 'black friday deal'; + $expected->status = new OrderStatus(); + $expected->status->id = 5; + $expected->status->label = 'processed'; + + $this->assertEquals($expected, $order); + + $writtenXml = $orderService->write('{http://sabredav.org/ns}order', $order); + $this->assertEquals($input, $writtenXml); + } + function testParseClarkNotation() { $this->assertEquals([ @@ -217,3 +254,23 @@ function testParseClarkNotationFail() { } } + +/** + * asset for testMapValueObject() + * @internal + */ +class Order { + public $id; + public $amount; + public $description; + public $status; +} + +/** + * asset for testMapValueObject() + * @internal + */ +class OrderStatus { + public $id; + public $label; +}