From 160c17ea8fd4a74e80bb5133489153e714c5eb9b Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Thu, 24 Oct 2024 21:43:52 +0700 Subject: [PATCH 1/6] Update MagicDto --- manual/includes/_magic-dto.md | 20 ++++++++++-- manual/index.html | 16 +++++++++- src/MagicDto.php | 52 ++++++++++++++++++++++++++++++ src/Util/PicoDateTimeUtil.php | 60 +++++++++++++++++++++++++++++++++++ tests/date.php | 26 +++++++++++++++ tests/dto.php | 14 ++++++++ tutorial.md | 20 ++++++++++-- 7 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 src/Util/PicoDateTimeUtil.php create mode 100644 tests/date.php diff --git a/manual/includes/_magic-dto.md b/manual/includes/_magic-dto.md index e48d276d..844ff318 100644 --- a/manual/includes/_magic-dto.md +++ b/manual/includes/_magic-dto.md @@ -33,6 +33,15 @@ The `MagicDto` class is designed with properties that have protected access leve #### Key Annotations +**Class Annotations** + +1. **@JSON** + + The `@JSON` annotation controls whether the JSON format should be prettified. Using `@JSON(prettify=true)` will format the output in a more readable way, while `@JSON(prettify=false)` will minimize the format + + +**Property Annotations** + 1. **@Source** The `@Source` annotation indicates the source property that maps to a specific field in the incoming data. If this annotation is omitted, MagicDto will default to using the property name that matches the class property name. This allows for flexibility in cases where the external API may use different naming conventions. @@ -48,7 +57,13 @@ protected $title; 2. **@JsonProperty** The `@JsonProperty` annotation specifies the output property name when data is serialized to JSON. If this annotation is not provided, MagicDto will serialize the property using its class property name. This ensures that data sent to third-party applications adheres to their expected format. - + +3. **@JsonFormat** + + The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be `DateTime`. It is written as @JsonFormat(pattern="Y-m-d H:i:s"). If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format. + + Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application. + ```php /** * @JsonProperty("album_title") @@ -96,7 +111,8 @@ class AlbumDtoInput extends MagicDto /** * @Source("date_release") * @JsonProperty("releaseDate") - * @var string + * @JsonFormat(pattern="Y-m-d H:i:s") + * @var DateTime */ protected $release; diff --git a/manual/index.html b/manual/index.html index a49d713c..06ebd61c 100644 --- a/manual/index.html +++ b/manual/index.html @@ -1357,6 +1357,14 @@

Features of MagicDto

Class Structure

The MagicDto class is designed with properties that have protected access levels, ensuring encapsulation while still allowing derived classes to access these properties. Each property is annotated with @var, which specifies its data type. This structured approach enhances type safety and improves code quality.

Key Annotations

+

Class Annotations

+
    +
  1. +

    @JSON

    +

    The @JSON annotation controls whether the JSON format should be prettified. Using @JSON(prettify=true) will format the output in a more readable way, while @JSON(prettify=false) will minimize the format

    +
  2. +
+

Property Annotations

  1. @Source

    @@ -1373,6 +1381,11 @@

    Key Annotations

    @JsonProperty

    The @JsonProperty annotation specifies the output property name when data is serialized to JSON. If this annotation is not provided, MagicDto will serialize the property using its class property name. This ensures that data sent to third-party applications adheres to their expected format.

  2. +
  3. +

    @JsonFormat

    +

    The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be DateTime. It is written as @JsonFormat(pattern="Y-m-d H:i:s"). If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format.

    +

    Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application.

    +
/**
  * @JsonProperty("album_title")
@@ -1409,7 +1422,8 @@ 

Key Annotations

/** * @Source("date_release") * @JsonProperty("releaseDate") - * @var string + * @JsonFormat(pattern="Y-m-d H:i:s") + * @var DateTime */ protected $release; diff --git a/src/MagicDto.php b/src/MagicDto.php index dab24cd5..5b04f1de 100644 --- a/src/MagicDto.php +++ b/src/MagicDto.php @@ -2,10 +2,12 @@ namespace MagicObject; +use DateTime; use MagicObject\Exceptions\InvalidAnnotationException; use MagicObject\Exceptions\InvalidQueryInputException; use MagicObject\Util\ClassUtil\PicoAnnotationParser; use MagicObject\Util\ClassUtil\PicoObjectParser; +use MagicObject\Util\PicoDateTimeUtil; use MagicObject\Util\PicoGenericObject; use ReflectionClass; use ReflectionProperty; @@ -132,6 +134,8 @@ public function value() $returnValue->$propertyName = $this->handleSelfInstance($source, $var, $propertyName); } elseif ($this->isMagicObjectInstance($objectTest)) { $returnValue->$propertyName = $this->handleMagicObject($source, $propertyName); + } elseif ($this->isDateTimeInstance($objectTest)) { + $returnValue->$propertyName = $this->formatDateTime($this->handleDateTimeObject($source, $propertyName), $this, $key); } else { $returnValue->$propertyName = $this->handleDefaultCase($source, $key, $propertyName); } @@ -140,6 +144,26 @@ public function value() return $returnValue; } + private function formatDateTime($dateTime, $class, $property) + { + if(!isset($dateTime)) + { + return null; + } + $reflexProp = new PicoAnnotationParser(get_class($class), $property, PicoAnnotationParser::PROPERTY); + $parameters = $reflexProp->getParameters(); + if(isset($parameters['JsonFormat'])) + { + $parsed = $reflexProp->parseKeyValueAsObject($parameters['JsonFormat']); + $format = isset($parsed->pattern) ? $parsed->pattern : 'Y-m-d H:i:s'; + } + else + { + $format = 'Y-m-d H:i:s'; + } + return $dateTime->format($format); + } + /** * Retrieves the documentation comment for a specified property. * @@ -245,6 +269,17 @@ private function isMagicObjectInstance($objectTest) $objectTest instanceof PicoGenericObject; } + /** + * Checks if the given object is an instance of DateTime or its derivatives. + * + * @param mixed $objectTest The object to test. + * @return bool True if it is a MagicObject instance, otherwise false. + */ + private function isDateTimeInstance($objectTest) + { + return $objectTest instanceof DateTime; + } + /** * Handles the case where the property is an instance of MagicObject. * @@ -265,6 +300,23 @@ private function handleMagicObject($source, $propertyName) } } + /** + * Handles the case where the property is an instance of DateTime. + * + * @param string|null $source The source to extract the value from. + * @param string $propertyName The name of the property. + * @return DateTime|null The handled value for the MagicObject instance. + */ + private function handleDateTimeObject($source, $propertyName) + { + if (strpos($source, "->") === false) { + $value = isset($source) ? $this->_dataSource->get($source) : $this->_dataSource->get($propertyName); + return PicoDateTimeUtil::parseDateTime($value); + } else { + return PicoDateTimeUtil::parseDateTime($this->getNestedValue($source)); + } + } + /** * Handles the default case when retrieving property values. * diff --git a/src/Util/PicoDateTimeUtil.php b/src/Util/PicoDateTimeUtil.php new file mode 100644 index 00000000..3e3fed8f --- /dev/null +++ b/src/Util/PicoDateTimeUtil.php @@ -0,0 +1,60 @@ +setTimestamp((int)$dateString); + } + + // List of formats to parse + $formats = [ + 'Y-m-d', // ISO 8601: 2024-10-24 + 'Y-m-d H:i:s', // ISO 8601: 2024-10-24 15:30:00 + 'Y-m-d\TH:i:s', // ISO 8601: 2024-10-24T15:30:00 + 'Y-m-d\TH:i:s\Z', // ISO 8601: 2024-10-24T15:30:00Z + 'D, d M Y H:i:s O', // RFC 2822: Thu, 24 Oct 2024 15:30:00 +0000 + 'd/m/Y', // Local format: 24/10/2024 + 'd F Y', // Format with month name: 24 October 2024 + 'l, d F Y' // Format with day of the week: Thursday, 24 October 2024 + ]; + + // Iterate over each format and attempt to create a DateTime object + foreach ($formats as $format) { + $dateTime = DateTime::createFromFormat($format, $dateString); + if ($dateTime !== false) { + return $dateTime; // Return the DateTime object if successful + } + } + + return null; // Return null if no formats matched + } +} diff --git a/tests/date.php b/tests/date.php new file mode 100644 index 00000000..1f50ef9b --- /dev/null +++ b/tests/date.php @@ -0,0 +1,26 @@ +format('Y-m-d H:i:s') . " => from $dateString\n"; + } else { + echo "Could not parse: $dateString\n"; + } +} \ No newline at end of file diff --git a/tests/dto.php b/tests/dto.php index 44c24b34..f097aa0a 100644 --- a/tests/dto.php +++ b/tests/dto.php @@ -191,6 +191,8 @@ class EntityAlbum extends MagicObject */ protected $asDraft; + + } /** @@ -671,6 +673,17 @@ class AlbumDto extends MagicDto * @var string */ protected $kotaDomisili; + + /** + * Release Date + * + * @JsonProperty("releaseDate") + * @JsonFormat(pattern="Y-m-d H:i:s") + * @Source("releaseDate") + * @var DateTime + */ + protected $releaseDate; + } class ProducerDto extends MagicDto @@ -702,6 +715,7 @@ class ProducerDto extends MagicDto $album->getProducer()->setName("Kamshory"); $album->getProducer()->setCity($city); $album->getProducer()->getCity()->setNamaKota("Jakarta"); +$album->setReleaseDate("2024-10-29 01:06:12"); $albumDto = new AlbumDto($album); diff --git a/tutorial.md b/tutorial.md index d7c25c0a..ae13b327 100644 --- a/tutorial.md +++ b/tutorial.md @@ -1559,6 +1559,15 @@ The `MagicDto` class is designed with properties that have protected access leve #### Key Annotations +**Class Annotations** + +1. **@JSON** + + The `@JSON` annotation controls whether the JSON format should be prettified. Using `@JSON(prettify=true)` will format the output in a more readable way, while `@JSON(prettify=false)` will minimize the format + + +**Property Annotations** + 1. **@Source** The `@Source` annotation indicates the source property that maps to a specific field in the incoming data. If this annotation is omitted, MagicDto will default to using the property name that matches the class property name. This allows for flexibility in cases where the external API may use different naming conventions. @@ -1574,7 +1583,13 @@ protected $title; 2. **@JsonProperty** The `@JsonProperty` annotation specifies the output property name when data is serialized to JSON. If this annotation is not provided, MagicDto will serialize the property using its class property name. This ensures that data sent to third-party applications adheres to their expected format. - + +3. **@JsonFormat** + + The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be `DateTime`. It is written as @JsonFormat(pattern="Y-m-d H:i:s"). If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format. + + Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application. + ```php /** * @JsonProperty("album_title") @@ -1622,7 +1637,8 @@ class AlbumDtoInput extends MagicDto /** * @Source("date_release") * @JsonProperty("releaseDate") - * @var string + * @JsonFormat(pattern="Y-m-d H:i:s") + * @var DateTime */ protected $release; From c2982424df8c81d359c240d6e33f9aad9b49d532 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Thu, 24 Oct 2024 21:55:43 +0700 Subject: [PATCH 2/6] Update docs --- manual/includes/_magic-dto.md | 26 ++++++++++++++++++++------ manual/index.html | 23 ++++++++++++++++++----- tutorial.md | 26 ++++++++++++++++++++------ 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/manual/includes/_magic-dto.md b/manual/includes/_magic-dto.md index 844ff318..713b2f1f 100644 --- a/manual/includes/_magic-dto.md +++ b/manual/includes/_magic-dto.md @@ -58,12 +58,6 @@ protected $title; The `@JsonProperty` annotation specifies the output property name when data is serialized to JSON. If this annotation is not provided, MagicDto will serialize the property using its class property name. This ensures that data sent to third-party applications adheres to their expected format. -3. **@JsonFormat** - - The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be `DateTime`. It is written as @JsonFormat(pattern="Y-m-d H:i:s"). If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format. - - Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application. - ```php /** * @JsonProperty("album_title") @@ -87,6 +81,26 @@ In this example, `@Source("album_name")` indicates that the incoming data will u To facilitate bidirectional communication, we need two different DTOs. The `@Source` annotation in the first DTO corresponds to the `@JsonProperty` annotation in the second DTO, while the `@JsonProperty` in the first DTO maps to the `@Source` in the second DTO. +3. **@JsonFormat** + + The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be `DateTime`. It is written as `@JsonFormat(pattern="Y-m-d H:i:s")`. If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format. + + Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application. + + MagicDto automatically parses input as both strings and integers. The integer is a unique timestamp, while the string date-time format must be one of the following: + + - **'Y-m-d'**, // ISO 8601: 2024-10-24 + - **'Y-m-d H:i:s'**, // ISO 8601: 2024-10-24 15:30:00 + - **'Y-m-d\TH:i:s'**, // ISO 8601: 2024-10-24T15:30:00 + - **'Y-m-d\TH:i:s\Z'**, // ISO 8601: 2024-10-24T15:30:00Z + - **'D, d M Y H:i:s O'**, // RFC 2822: Thu, 24 Oct 2024 15:30:00 +0000 + - **'d/m/Y'**, // Local format: 24/10/2024 + - **'d F Y'**, // Format with month name: 24 October 2024 + - **'l, d F Y'** // Format with day of the week: Thursday, 24 October 2024 + + + + **Example:** DTO on the Input Side diff --git a/manual/index.html b/manual/index.html index 06ebd61c..b5458169 100644 --- a/manual/index.html +++ b/manual/index.html @@ -1381,11 +1381,6 @@

Key Annotations

@JsonProperty

The @JsonProperty annotation specifies the output property name when data is serialized to JSON. If this annotation is not provided, MagicDto will serialize the property using its class property name. This ensures that data sent to third-party applications adheres to their expected format.

-
  • -

    @JsonFormat

    -

    The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be DateTime. It is written as @JsonFormat(pattern="Y-m-d H:i:s"). If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format.

    -

    Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application.

    -
  • /**
      * @JsonProperty("album_title")
    @@ -1401,6 +1396,24 @@ 

    Key Annotations

    protected $title;

    In this example, @Source("album_name") indicates that the incoming data will use album_name, while @JsonProperty("album_title") specifies that when the data is serialized, it will be output as album_title.

    To facilitate bidirectional communication, we need two different DTOs. The @Source annotation in the first DTO corresponds to the @JsonProperty annotation in the second DTO, while the @JsonProperty in the first DTO maps to the @Source in the second DTO.

    +
      +
    1. +

      @JsonFormat

      +

      The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be DateTime. It is written as @JsonFormat(pattern="Y-m-d H:i:s"). If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format.

      +

      Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application.

      +

      MagicDto automatically parses input as both strings and integers. The integer is a unique timestamp, while the string date-time format must be one of the following:

      +
        +
      • 'Y-m-d', // ISO 8601: 2024-10-24
      • +
      • 'Y-m-d H:i:s', // ISO 8601: 2024-10-24 15:30:00
      • +
      • 'Y-m-d\TH:i:s', // ISO 8601: 2024-10-24T15:30:00
      • +
      • 'Y-m-d\TH:i:s\Z', // ISO 8601: 2024-10-24T15:30:00Z
      • +
      • 'D, d M Y H:i:s O', // RFC 2822: Thu, 24 Oct 2024 15:30:00 +0000
      • +
      • 'd/m/Y', // Local format: 24/10/2024
      • +
      • 'd F Y', // Format with month name: 24 October 2024
      • +
      • 'l, d F Y' // Format with day of the week: Thursday, 24 October 2024
      • +
      +
    2. +

    Example:

    DTO on the Input Side

    class AlbumDtoInput extends MagicDto
    diff --git a/tutorial.md b/tutorial.md
    index ae13b327..b1dd9311 100644
    --- a/tutorial.md
    +++ b/tutorial.md
    @@ -1584,12 +1584,6 @@ protected $title;
     
         The `@JsonProperty` annotation specifies the output property name when data is serialized to JSON. If this annotation is not provided, MagicDto will serialize the property using its class property name. This ensures that data sent to third-party applications adheres to their expected format.
     
    -3.  **@JsonFormat**
    -
    -    The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be `DateTime`. It is written as @JsonFormat(pattern="Y-m-d H:i:s"). If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format.
    -
    -    Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application.
    -
     ```php
     /**
      * @JsonProperty("album_title")
    @@ -1613,6 +1607,26 @@ In this example, `@Source("album_name")` indicates that the incoming data will u
     
     To facilitate bidirectional communication, we need two different DTOs. The `@Source` annotation in the first DTO corresponds to the `@JsonProperty` annotation in the second DTO, while the `@JsonProperty` in the first DTO maps to the `@Source` in the second DTO.
     
    +3.  **@JsonFormat**
    +
    +    The @JsonFormat annotation specifies the output date-time format when data is serialized to JSON. The property type must be `DateTime`. It is written as `@JsonFormat(pattern="Y-m-d H:i:s")`. If this annotation is not provided, MagicDto will serialize the property using the default format Y-m-d H:i:s. This ensures that data sent to third-party applications adheres to their expected format.
    +
    +    Format the date and time according to the conventions used in the PHP programming language. This includes utilizing the built-in date and time functions, which allow for various formatting options to display dates and times in a way that is both readable and compatible with PHP's standards. Ensure that you adhere to formats such as 'Y-m-d H:i:s' for complete timestamps or 'd/m/Y' for more localized representations, depending on the specific requirements of your application.
    +
    +    MagicDto automatically parses input as both strings and integers. The integer is a unique timestamp, while the string date-time format must be one of the following:
    +  
    +    - **'Y-m-d'**,              // ISO 8601: 2024-10-24
    +    - **'Y-m-d H:i:s'**,        // ISO 8601: 2024-10-24 15:30:00
    +    - **'Y-m-d\TH:i:s'**,       // ISO 8601: 2024-10-24T15:30:00
    +    - **'Y-m-d\TH:i:s\Z'**,     // ISO 8601: 2024-10-24T15:30:00Z
    +    - **'D, d M Y H:i:s O'**,   // RFC 2822: Thu, 24 Oct 2024 15:30:00 +0000
    +    - **'d/m/Y'**,              // Local format: 24/10/2024
    +    - **'d F Y'**,              // Format with month name: 24 October 2024
    +    - **'l, d F Y'**            // Format with day of the week: Thursday, 24 October 2024
    +
    +
    +
    +
     **Example:**
     
     DTO on the Input Side
    
    From 69aa891e001f3b13ca649f17b1d206d06df39eff Mon Sep 17 00:00:00 2001
    From: "Kamshory, MT" 
    Date: Fri, 25 Oct 2024 04:05:54 +0700
    Subject: [PATCH 3/6] Add XML to MagicDto format
    
    ---
     manual/includes/_magic-dto.md |   4 ++
     manual/index.html             |   4 ++
     src/MagicDto.php              | 109 ++++++++++++++++++++++++++++++----
     tests/dto.php                 |   3 +-
     tutorial.md                   |   4 ++
     5 files changed, 111 insertions(+), 13 deletions(-)
    
    diff --git a/manual/includes/_magic-dto.md b/manual/includes/_magic-dto.md
    index 713b2f1f..c0c75f87 100644
    --- a/manual/includes/_magic-dto.md
    +++ b/manual/includes/_magic-dto.md
    @@ -39,6 +39,10 @@ The `MagicDto` class is designed with properties that have protected access leve
         
         The `@JSON` annotation controls whether the JSON format should be prettified. Using `@JSON(prettify=true)` will format the output in a more readable way, while `@JSON(prettify=false)` will minimize the format
     
    +2.  **@XML**
    +    
    +    The `@XML` annotation controls whether the XML format should be prettified. Using `@XML(prettify=true)` will format the output in a more readable way, while `@XML(prettify=false)` will minimize the format    
    +
     
     **Property Annotations**
     
    diff --git a/manual/index.html b/manual/index.html
    index b5458169..529be077 100644
    --- a/manual/index.html
    +++ b/manual/index.html
    @@ -1363,6 +1363,10 @@ 

    Key Annotations

    @JSON

    The @JSON annotation controls whether the JSON format should be prettified. Using @JSON(prettify=true) will format the output in a more readable way, while @JSON(prettify=false) will minimize the format

    +
  • +

    @XML

    +

    The @XML annotation controls whether the XML format should be prettified. Using @XML(prettify=true) will format the output in a more readable way, while @XML(prettify=false) will minimize the format

    +
  • Property Annotations

      diff --git a/src/MagicDto.php b/src/MagicDto.php index 5b04f1de..19deb2ad 100644 --- a/src/MagicDto.php +++ b/src/MagicDto.php @@ -3,6 +3,8 @@ namespace MagicObject; use DateTime; +use DOMDocument; +use InvalidArgumentException; use MagicObject\Exceptions\InvalidAnnotationException; use MagicObject\Exceptions\InvalidQueryInputException; use MagicObject\Util\ClassUtil\PicoAnnotationParser; @@ -11,6 +13,7 @@ use MagicObject\Util\PicoGenericObject; use ReflectionClass; use ReflectionProperty; +use SimpleXMLElement; use stdClass; /** @@ -29,6 +32,7 @@ class MagicDto extends stdClass // NOSONAR { // Format constants const JSON = 'JSON'; + const XML = 'XML'; const PRETTIFY = 'prettify'; /** @@ -412,7 +416,7 @@ public function valueArrayUpperCamel() * * @return bool True if JSON output is set to be prettified; otherwise, false */ - protected function _pretty() + protected function _prettyJson() { return isset($this->_classParams[self::JSON]) && isset($this->_classParams[self::JSON][self::PRETTIFY]) @@ -420,6 +424,19 @@ protected function _pretty() ; } + /** + * Check if the XML output should be prettified + * + * @return bool True if XML output is set to be prettified; otherwise, false + */ + protected function _prettyXml() + { + return isset($this->_classParams[self::XML]) + && isset($this->_classParams[self::XML][self::PRETTIFY]) + && strcasecmp($this->_classParams[self::XML][self::PRETTIFY], 'true') == 0 + ; + } + /** * Get a list of properties * @@ -493,16 +510,16 @@ private function stringifyObject($value) /** * Magic method to convert the object to a JSON string representation. * - * This method recursively converts the object's properties into a JSON format. - * If any property is an instance of the same class, it will be stringified - * as well. The output can be formatted for readability based on the - * `_pretty()` method's return value. + * This method recursively converts the object's properties into JSON format. + * If any property is an instance of the same class, it will also be stringified. + * The output can be formatted for readability based on the JSON annotation + * of the class. * - * @return string A JSON representation of the object, possibly pretty-printed. + * @return string A JSON representation of the object, potentially formatted for readability. */ public function __toString() { - $pretty = $this->_pretty(); + $pretty = $this->_prettyJson(); $flag = $pretty ? JSON_PRETTY_PRINT : 0; $obj = clone $this; foreach($obj as $key=>$value) @@ -517,13 +534,14 @@ public function __toString() } /** - * Convert the object to a string. + * Magic method to convert the object to a JSON string representation. * - * This method returns the string representation of the object by calling - * the magic `__toString()` method. It's useful for obtaining the - * JSON representation directly as a string. + * This method recursively converts the object's properties into JSON format. + * If any property is an instance of the same class, it will also be stringified. + * The output can be formatted for readability based on the JSON annotation + * of the class. * - * @return string The string representation of the object. + * @return string A JSON representation of the object, potentially formatted for readability. */ public function toString() { @@ -559,4 +577,71 @@ public function toArray() { return json_decode((string) $this, true); } + + + /** + * Convert the object's properties to XML format. + * + * This method generates an XML representation of the object based on its properties. + * The XML structure is built from the object's properties, and the output can + * be formatted for readability based on the XML annotation of the class. + * + * @param string $root The name of the root element in the XML structure. + * @return string XML representation of the object's properties, potentially formatted for readability. + * @throws InvalidArgumentException If the JSON representation of the object is invalid. + */ + public function toXml($root = "root") { + // Decode the JSON string into an associative array + $dataArray = $this->toArray(); + + // Check if JSON was valid + if (json_last_error() !== JSON_ERROR_NONE) { + throw new InvalidArgumentException('Invalid JSON provided.'); + } + + // Create the XML structure + $xml = new SimpleXMLElement("<$root/>"); + + // Recursive function to convert array to XML + $this->arrayToXml($dataArray, $xml); + + $pretty = $this->_prettyXml(); + + if($pretty) + { + // Convert SimpleXMLElement to DOMDocument for prettifying + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + $dom->loadXML($xml->asXML()); + return $dom->saveXML(); + } + else + { + return $xml->asXML(); + } + } + + /** + * Helper function to convert an array to XML + * + * @param array $dataArray The array to convert + * @param SimpleXMLElement $xml XML element to append to + */ + function arrayToXml($dataArray, $xml) { + foreach ($dataArray as $key => $value) { + // Replace spaces and special characters in key names + $key = preg_replace('/[^a-z0-9_]/i', '_', $key); + + // If the value is an array, call this function recursively + if (is_array($value)) { + $subNode = $xml->addChild($key); + $this->arrayToXml($value, $subNode); + } else { + // If the value is not an array, just add it as a child + $xml->addChild($key, htmlspecialchars($value)); + } + } + } + } diff --git a/tests/dto.php b/tests/dto.php index f097aa0a..5fa584f3 100644 --- a/tests/dto.php +++ b/tests/dto.php @@ -627,6 +627,7 @@ class Artist extends MagicObject /** * @JSON(prettify=true) + * @XML(prettify=true) */ class AlbumDto extends MagicDto { @@ -720,4 +721,4 @@ class ProducerDto extends MagicDto $albumDto = new AlbumDto($album); -echo $albumDto; \ No newline at end of file +echo $albumDto->toXml(); \ No newline at end of file diff --git a/tutorial.md b/tutorial.md index b1dd9311..24882f36 100644 --- a/tutorial.md +++ b/tutorial.md @@ -1565,6 +1565,10 @@ The `MagicDto` class is designed with properties that have protected access leve The `@JSON` annotation controls whether the JSON format should be prettified. Using `@JSON(prettify=true)` will format the output in a more readable way, while `@JSON(prettify=false)` will minimize the format +2. **@XML** + + The `@XML` annotation controls whether the XML format should be prettified. Using `@XML(prettify=true)` will format the output in a more readable way, while `@XML(prettify=false)` will minimize the format + **Property Annotations** From 3cf27563b031c3db64ea8efb8cf4c0d19dd4bdc5 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 25 Oct 2024 05:11:03 +0700 Subject: [PATCH 4/6] Add MagicDto feature: XML support --- manual/includes/_magic-dto.md | 40 ++++++++++++++++++++++ manual/index.html | 35 +++++++++++++++++++ src/MagicDto.php | 64 ++++++++++++++++++++++++++++++++--- tests/dto.php | 31 +++++++++++++++-- tutorial.md | 40 ++++++++++++++++++++++ 5 files changed, 203 insertions(+), 7 deletions(-) diff --git a/manual/includes/_magic-dto.md b/manual/includes/_magic-dto.md index c0c75f87..7c2574ae 100644 --- a/manual/includes/_magic-dto.md +++ b/manual/includes/_magic-dto.md @@ -27,6 +27,11 @@ In modern applications, especially those that interact with third-party services - The MagicDto class utilizes PHP annotations to clarify the purpose of each property. These annotations enhance code readability and provide useful metadata for serialization. +4. **XML Support** + - MagicDto provides support for both XML input and output. This feature allows seamless integration with systems that utilize XML as their primary data format, making it easier to work with various data sources and services. + + To parse XML string, use method `MagicTdo::xmlToObject(string $xmlString)`. This method takes an XML string as input and returning it as a stdClass object. + ### Class Structure The `MagicDto` class is designed with properties that have protected access levels, ensuring encapsulation while still allowing derived classes to access these properties. Each property is annotated with `@var`, which specifies its data type. This structured approach enhances type safety and improves code quality. @@ -418,6 +423,8 @@ class AgencyDto extends MagicDto **Usage** +1. JSON Format + ```php $song = new Song(null, $database); $song->find("1234"); @@ -427,6 +434,39 @@ header("Content-type: application/json"); echo $songDto; ``` +2. XML Format + +```php +$song = new Song(null, $database); +$song->find("1234"); +$songDto = new SongDto($song); + +header("Content-type: application/xml"); +echo $songDto->toXml("root"); +``` + +3. Parse XML + +```php +$albumDto = new AlbumDto(); +$obj = $albumDto->xmlToObject($xmlString); +// $obj is stdClass +header("Content-type: application/json"); +echo json_encode($obj); +``` + +3. Load from XML + +```php +$albumDto = new AlbumDtoInput(); +$albumDto->loadXml($xmlString); +header("Content-type: application/json"); +echo $albumDto; +``` + +`loadXml` method will load data from XML to `AlbumDtoInput`. `AlbumDtoInput` is the inverse of `AlbumDto`, where values of the `@JsonProperty` and `@Source` annotations are swapped. This inversion also applies to the objects contained within it. + + #### Explanation - **@Source**: This annotation specifies the path to the property within the nested object structure. In this case, `artist->agency->name` indicates that the `agencyName` will pull data from the `name` property of the `Agency` object linked to the `Artist`. diff --git a/manual/index.html b/manual/index.html index 529be077..742f13a8 100644 --- a/manual/index.html +++ b/manual/index.html @@ -1353,6 +1353,13 @@

      Features of MagicDto

    1. The MagicDto class utilizes PHP annotations to clarify the purpose of each property. These annotations enhance code readability and provide useful metadata for serialization.
    2. +
    3. +

      XML Support

      +
        +
      • MagicDto provides support for both XML input and output. This feature allows seamless integration with systems that utilize XML as their primary data format, making it easier to work with various data sources and services.
      • +
      +

      To parse XML string, use method MagicTdo::xmlToObject(string $xmlString). This method takes an XML string as input and returning it as a stdClass object.

      +

    Class Structure

    The MagicDto class is designed with properties that have protected access levels, ensuring encapsulation while still allowing derived classes to access these properties. Each property is annotated with @var, which specifies its data type. This structured approach enhances type safety and improves code quality.

    @@ -1699,12 +1706,40 @@

    Code Implementation

    }

    Usage

    +
      +
    1. JSON Format
    2. +
    $song = new Song(null, $database);
     $song->find("1234");
     $songDto = new SongDto($song);
     
     header("Content-type: application/json");
     echo $songDto;
    +
      +
    1. XML Format
    2. +
    +
    $song = new Song(null, $database);
    +$song->find("1234");
    +$songDto = new SongDto($song);
    +
    +header("Content-type: application/xml");
    +echo $songDto->toXml("root");
    +
      +
    1. Parse XML
    2. +
    +
    $albumDto = new AlbumDto();
    +$obj = $albumDto->xmlToObject($xmlString);
    +// $obj is stdClass
    +header("Content-type: application/json");
    +echo json_encode($obj);
    +
      +
    1. Load from XML
    2. +
    +
    $albumDto = new AlbumDtoInput();
    +$albumDto->loadXml($xmlString);
    +header("Content-type: application/json");
    +echo $albumDto;
    +

    loadXml method will load data from XML to AlbumDtoInput. AlbumDtoInput is the inverse of AlbumDto, where values of the @JsonProperty and @Source annotations are swapped. This inversion also applies to the objects contained within it.

    Explanation

    • diff --git a/src/MagicDto.php b/src/MagicDto.php index 19deb2ad..4412d138 100644 --- a/src/MagicDto.php +++ b/src/MagicDto.php @@ -113,6 +113,13 @@ public function loadData($data) } return $this; } + + public function loadXml($xmlString) + { + $data = $this->xmlToObject($xmlString); + $this->loadData($data); + return $this; + } /** * Get the object values @@ -134,13 +141,15 @@ public function value() $objectTest = class_exists($var) ? new $var() : null; - if ($this->isSelfInstance($var, $objectTest)) { + if ($this->isSelfInstance($objectTest)) { $returnValue->$propertyName = $this->handleSelfInstance($source, $var, $propertyName); } elseif ($this->isMagicObjectInstance($objectTest)) { $returnValue->$propertyName = $this->handleMagicObject($source, $propertyName); } elseif ($this->isDateTimeInstance($objectTest)) { $returnValue->$propertyName = $this->formatDateTime($this->handleDateTimeObject($source, $propertyName), $this, $key); - } else { + } else if($this->_dataSource instanceof stdClass || is_object($this->_dataSource)) { + $returnValue->$propertyName = $this->handleStdClass($source, $key, $propertyName); + } else if(isset($this->_dataSource)) { $returnValue->$propertyName = $this->handleDefaultCase($source, $key, $propertyName); } } @@ -231,13 +240,12 @@ private function extractLabel($doc) /** * Checks if the given variable is a self-instance. * - * @param string $var The variable name. * @param mixed $objectTest The object to test against. * @return bool True if it's a self-instance, otherwise false. */ - private function isSelfInstance($var, $objectTest) + private function isSelfInstance($objectTest) { - return strtolower($var) != 'stdclass' && $objectTest instanceof self; + return $objectTest instanceof self; } /** @@ -338,6 +346,23 @@ private function handleDefaultCase($source, $key, $propertyName) } } + /** + * Handles the stdClass when retrieving property values. + * + * @param string|null $source The source to extract the value from. + * @param string $key The key of the property. + * @param string $propertyName The name of the property. + * @return mixed The handled default value. + */ + private function handleStdClass($source, $key, $propertyName) + { + if (strpos($source, "->") === false) { + return isset($source) && isset($this->_dataSource->{$source}) ? $this->_dataSource->{$source} : $this->_dataSource->{$key}; + } else { + return $this->getNestedValue($source); + } + } + /** * Retrieves nested values from the data source based on a specified source string. * @@ -507,6 +532,35 @@ private function stringifyObject($value) return $value->value(); } + /** + * Convert XML to an object. + * + * This function takes an XML string as input and returning it as a stdClass object. + * + * @param string $xmlString The XML string to be converted. + * @return stdClass An object representation of the XML data. + * @throws InvalidArgumentException If the XML is invalid or cannot be parsed. + */ + public function xmlToObject($xmlString) { + // Suppress errors to handle them manually + libxml_use_internal_errors(true); + + // Convert the XML string to a SimpleXMLElement + $xmlObject = simplexml_load_string($xmlString); + + // Check for errors in XML parsing + if ($xmlObject === false) { + $errors = libxml_get_errors(); + libxml_clear_errors(); + throw new InvalidArgumentException('Invalid XML provided: ' . implode(', ', array_map(function($error) { + return $error->message; + }, $errors))); + } + + // Convert SimpleXMLElement to stdClass + return json_decode(json_encode($xmlObject)); + } + /** * Magic method to convert the object to a JSON string representation. * diff --git a/tests/dto.php b/tests/dto.php index 5fa584f3..f867b460 100644 --- a/tests/dto.php +++ b/tests/dto.php @@ -718,7 +718,34 @@ class ProducerDto extends MagicDto $album->getProducer()->getCity()->setNamaKota("Jakarta"); $album->setReleaseDate("2024-10-29 01:06:12"); - +/* $albumDto = new AlbumDto($album); -echo $albumDto->toXml(); \ No newline at end of file +echo "JSON:\r\n"; +echo $albumDto."\r\n\r\n"; +echo "XML:\r\n"; +echo $albumDto->toXml(); + +$obj2 = $albumDto->xmlToObject($albumDto->toXml()); + +echo "JSON 2:\r\n"; +echo json_encode($obj2, JSON_PRETTY_PRINT)."\r\n\r\n"; +*/ + +$xml = ' + + 1234 + Album Pertama + + 5678 + Kamshory + + Kamshory + Jakarta + 2024-10-29 01:06:12 +'; + +$album2 = new AlbumDto(); +$album2->loadXml($xml); + +echo $album2; \ No newline at end of file diff --git a/tutorial.md b/tutorial.md index 24882f36..8ff4bd48 100644 --- a/tutorial.md +++ b/tutorial.md @@ -1553,6 +1553,11 @@ In modern applications, especially those that interact with third-party services - The MagicDto class utilizes PHP annotations to clarify the purpose of each property. These annotations enhance code readability and provide useful metadata for serialization. +4. **XML Support** + - MagicDto provides support for both XML input and output. This feature allows seamless integration with systems that utilize XML as their primary data format, making it easier to work with various data sources and services. + + To parse XML string, use method `MagicTdo::xmlToObject(string $xmlString)`. This method takes an XML string as input and returning it as a stdClass object. + ### Class Structure The `MagicDto` class is designed with properties that have protected access levels, ensuring encapsulation while still allowing derived classes to access these properties. Each property is annotated with `@var`, which specifies its data type. This structured approach enhances type safety and improves code quality. @@ -1944,6 +1949,8 @@ class AgencyDto extends MagicDto **Usage** +1. JSON Format + ```php $song = new Song(null, $database); $song->find("1234"); @@ -1953,6 +1960,39 @@ header("Content-type: application/json"); echo $songDto; ``` +2. XML Format + +```php +$song = new Song(null, $database); +$song->find("1234"); +$songDto = new SongDto($song); + +header("Content-type: application/xml"); +echo $songDto->toXml("root"); +``` + +3. Parse XML + +```php +$albumDto = new AlbumDto(); +$obj = $albumDto->xmlToObject($xmlString); +// $obj is stdClass +header("Content-type: application/json"); +echo json_encode($obj); +``` + +3. Load from XML + +```php +$albumDto = new AlbumDtoInput(); +$albumDto->loadXml($xmlString); +header("Content-type: application/json"); +echo $albumDto; +``` + +`loadXml` method will load data from XML to `AlbumDtoInput`. `AlbumDtoInput` is the inverse of `AlbumDto`, where values of the `@JsonProperty` and `@Source` annotations are swapped. This inversion also applies to the objects contained within it. + + #### Explanation - **@Source**: This annotation specifies the path to the property within the nested object structure. In this case, `artist->agency->name` indicates that the `agencyName` will pull data from the `name` property of the `Agency` object linked to the `Artist`. From f435dd89f77c6a20f1ee8e001604b6ba68504b55 Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 25 Oct 2024 05:14:56 +0700 Subject: [PATCH 5/6] Update MagicDto.php --- src/MagicDto.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/MagicDto.php b/src/MagicDto.php index 4412d138..f69e8d47 100644 --- a/src/MagicDto.php +++ b/src/MagicDto.php @@ -114,6 +114,18 @@ public function loadData($data) return $this; } + /** + * Loads XML data into the object. + * + * This method accepts an XML string, converts it to an object representation, + * and then loads the resulting data into the internal data source of the object. + * It processes the XML input, ensuring that only non-scalar values are handled + * appropriately. This method is useful for integrating with external XML data sources. + * + * @param string $xmlString The XML string to load into the object. + * @return self Returns the current instance for method chaining. + * @throws InvalidArgumentException If the XML string is invalid or cannot be parsed. + */ public function loadXml($xmlString) { $data = $this->xmlToObject($xmlString); From d317de971f43390db79861fb80d56b5827d874ed Mon Sep 17 00:00:00 2001 From: "Kamshory, MT" Date: Fri, 25 Oct 2024 05:20:35 +0700 Subject: [PATCH 6/6] Update dto.php --- tests/dto.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/dto.php b/tests/dto.php index f867b460..0f679fb9 100644 --- a/tests/dto.php +++ b/tests/dto.php @@ -692,7 +692,7 @@ class ProducerDto extends MagicDto /** * Producer ID * - * @Source("producerId") + * @Source("id_producer") * @JsonProperty("id_producer") * @var string */ @@ -718,7 +718,7 @@ class ProducerDto extends MagicDto $album->getProducer()->getCity()->setNamaKota("Jakarta"); $album->setReleaseDate("2024-10-29 01:06:12"); -/* + $albumDto = new AlbumDto($album); echo "JSON:\r\n"; @@ -730,7 +730,7 @@ class ProducerDto extends MagicDto echo "JSON 2:\r\n"; echo json_encode($obj2, JSON_PRETTY_PRINT)."\r\n\r\n"; -*/ + $xml = '