goetas-webservices / xsd2php

Convert XSD into PHP classes and JMS serializer definitions
MIT License
238 stars 91 forks source link

Elements in a group don't honour maxOccurs ? #110

Open SimonAnnetts opened 4 years ago

SimonAnnetts commented 4 years ago

Hi, not sure if this issue should be posted here in xsd2php or in xsd-reader, but I have the following fragment of an XSD:

    <xs:element name="Product">
        <xs:complexType>
            <xs:sequence>
                <xs:group ref="gp.record_metadata"/>
                <xs:group ref="gp.product_numbers"/>
                <xs:element minOccurs="0" ref="DescriptiveDetail"/>
                <xs:element minOccurs="0" ref="CollateralDetail"/>
                <xs:element minOccurs="0" ref="PromotionDetail"/>
                <!-- 3.0.7 -->
                <xs:element minOccurs="0" ref="ContentDetail"/>
                <xs:element minOccurs="0" ref="PublishingDetail"/>
                <xs:element minOccurs="0" ref="RelatedMaterial"/>
                <xs:element minOccurs="0" maxOccurs="unbounded" ref="ProductSupply"/>
            </xs:sequence>
            <xs:attribute name="refname">
                <xs:simpleType>
                    <xs:restriction base="xs:token">
                        <xs:enumeration value="Product"/>
                    </xs:restriction>
                </xs:simpleType>
            </xs:attribute>
            <xs:attribute name="shortname">
                <xs:simpleType>
                    <xs:restriction base="xs:token">
                        <xs:enumeration value="product"/>
                    </xs:restriction>
                </xs:simpleType>
            </xs:attribute>
            <xs:attributeGroup ref="generalAttributes"/>
        </xs:complexType>
    </xs:element>

    <xs:group name="gp.record_metadata">
        <xs:sequence>
            <xs:element ref="RecordReference"/>
            <xs:element ref="NotificationType"/>
            <xs:element minOccurs="0" maxOccurs="unbounded" ref="DeletionText"/>
            <xs:element minOccurs="0" ref="RecordSourceType"/>
            <xs:element minOccurs="0" maxOccurs="unbounded" ref="RecordSourceIdentifier"/>
            <xs:element minOccurs="0" ref="RecordSourceName"/>
        </xs:sequence>
    </xs:group>

    <xs:group name="gp.product_numbers">
        <xs:sequence>
            <xs:element maxOccurs="unbounded" ref="ProductIdentifier"/>
            <xs:element minOccurs="0" maxOccurs="unbounded" ref="Barcode"/>
        </xs:sequence>
    </xs:group>

This currently produces the following code fragment for the class Product:

namespace Onix3;

/**
 * Class representing Product
 */
class Product
{

    /**
     * @var string $refname
     */
    private $refname = null;

    /**
     * @var string $shortname
     */
    private $shortname = null;

    /**
     * @var string $datestamp
     */
    private $datestamp = null;

    /**
     * @var string $sourcetype
     */
    private $sourcetype = null;

    /**
     * @var string $sourcename
     */
    private $sourcename = null;

    /**
     * @var \Onix3\RecordReference $recordReference
     */
    private $recordReference = null;

    /**
     * @var \Onix3\NotificationType $notificationType
     */
    private $notificationType = null;

    /**
     * @var \Onix3\DeletionText $deletionText
     */
    private $deletionText = null;

    /**
     * @var \Onix3\RecordSourceType $recordSourceType
     */
    private $recordSourceType = null;

    /**
     * @var \Onix3\RecordSourceIdentifier $recordSourceIdentifier
     */
    private $recordSourceIdentifier = null;

    /**
     * @var \Onix3\RecordSourceName $recordSourceName
     */
    private $recordSourceName = null;

    /**
     * @var \Onix3\ProductIdentifier $productIdentifier
     */
    private $productIdentifier = null;

    /**
     * @var \Onix3\Barcode $barcode
     */
    private $barcode = null;

    /**
     * @var \Onix3\DescriptiveDetail $descriptiveDetail
     */
    private $descriptiveDetail = null;

    /**
     * @var \Onix3\CollateralDetail $collateralDetail
     */
    private $collateralDetail = null;

    /**
     * @var \Onix3\PromotionDetail $promotionDetail
     */
    private $promotionDetail = null;

    /**
     * @var \Onix3\ContentDetail $contentDetail
     */
    private $contentDetail = null;

    /**
     * @var \Onix3\PublishingDetail $publishingDetail
     */
    private $publishingDetail = null;

    /**
     * @var \Onix3\RelatedMaterial $relatedMaterial
     */
    private $relatedMaterial = null;

    /**
     * @var \Onix3\ProductSupply[] $productSupply
     */
    private $productSupply = [

    ];

In the XSD the group 'gp.record_metadata' specifies an element 'DeletionText' with maxOccurs="unbounded". In the code above this element is created as a single, but I would expect the output for this field to be:

    /**
     * @var \Onix3\DeletionText[] $deletionText
     */
    private $deletionText = [

    ];

and for there to be additional methods:

public function addToDeletedText ...
public function issetDeletedText($index) ...
public function unsetDeletedText($index) ...

Is this a bug, or a missing feature?

Here is the full XSD: ONIX_BookProduct_3.0_reference_strict.xsd.zip and my config:

# config.yml
xsd2php:
  namespaces:
    'http://ns.editeur.org/onix/3.0/reference': 'Onix3'
  destinations_php: 
    'Onix3': Onix3/src
  destinations_jms:
    'Onix3': Onix3/metadata  
  destinations_validation:
    'Onix3': Onix3/validation
  naming_strategy: long
  configs_jms:
    xml_cdata: false
goetas commented 4 years ago

Group max occurs are not that easy to translate from xsd to php, there might be a bug in the current implementation

SimonAnnetts commented 4 years ago

Ok, well for now I guess I'll just modify the created classes to fix the elements that should be arrays as I have a project I need to finish. I would like to try and fix this though if I get some time. I noticed that the phpunit tests don't seem to test for this scenario of an array element inside of a group. Is it likely the bug is here in xsd2php or in the xsd-reader?

goetas commented 4 years ago

i think that is both... :(

xsd-reader is easier to test sice its structure is more clear and simple, while on xsd2php could be harder