Search code examples
phpjsonzend-framework2laminas-api-tools

How to validate nested data following Apigility way?


I have one small question about validating nested json data. For example, I have similar PATCH request as this:

{
    "awesome": "yes",
    "myObject": {
        "some_property": "Text Example value",
        "another_property": "1965"
    }
}

What is the proper and maybe correct way to set filters and validators for this nested data some_property and another_property?

Thanks a lot for answers


Solution

  • I know this answer is quite late. I stumbled on the same issue (unrelated to Apigility). After a lot of try & error I found a fully working InputFilter specification on validating nested fields / collections as well as keys named type. Adding this here for reference should others find this (hello future me).

    Nested Objects

    Already answered by Wilt, adding for completeness.

    $data = [
        'root-key' => [
            'sub-key' => 'my-value',
            'sub-key2' => 'my-other-value',
        ],
        'simple-key' => 'simple-value'
    ];
    
    'input_filter_specs' => [
        'my-filter' => [
            'root-key' => [
                'type' => InputFilter::class,
                'sub-key' => [
                    'required' => true,
                    'filters' => [ /** Add filters **/ ],
                    'validators' => [ /** Add validators **/],
                ],
                'sub-key2' => [
                    'required' => true,
                    'filters' => [ /** Add filters **/ ],
                    'validators' => [ /** Add validators **/],
                ],
            ],
            'simple-key' => [
                'required' => true,
                'filters' => [ /** Add filters **/ ],
                'validators' => [ /** Add validators **/],
            ],
        ],
    ],
    

    Collections of Objects

    For some reason the specification for validating a collection of objects is a bit different:

    $data = [
        'root-key' => [[
            'sub-key' => 'my-value',
            'sub-key2' => 'my-other-value',
        ], [
            'sub-key' => 'my-value',
            'sub-key2' => 'my-other-value',
        ]],
        'simple-key' => 'simple-value'
    ];
    
    'input_filter_specs' => [
        'my-filter' => [
            'root-key' => [
                'type' => CollectionInputFilter::class,
                'required' => true,
                'input_filter' => [
                    'sub-key' => [
                        'required' => true,
                        'filters' => [ /** Add filters **/ ],
                        'validators' => [ /** Add validators **/],
                    ],
                    'sub-key2' => [
                        'required' => true,
                        'filters' => [ /** Add filters **/ ],
                        'validators' => [ /** Add validators **/],
                    ],
                ]
            ],
            'simple-key' => [
                'required' => true,
                'filters' => [ /** Add filters **/ ],
                'validators' => [ /** Add validators **/],
            ],
        ],
    ],
    

    Bypassing the type limitation / re-using filters specifications

    Using the type key, the type of input filter can be specified (as done in the two examples before). What few do know however is that the specified filters implicitly are input filters as well and can be specified as the type too. This allows re-using specified filters within other filters and composing complex filters out of smaller ones. Simply pass the name of the specified input filter as the type.

    $data = [
        'root-key' => [
            'sub-key' => 'my-value',
            'sub-key2' => 'my-other-value',
        ],
        'simple-key' => 'simple-value'
    ];
    
    'input_filter_specs' => [
        'root-key-filter' => [
            'sub-key' => [
                'required' => true,
                'filters' => [ /** Add filters **/ ],
                'validators' => [ /** Add validators **/],
            ],
            'sub-key2' => [
                'required' => true,
                'filters' => [ /** Add filters **/ ],
                'validators' => [ /** Add validators **/],
            ],
        ],
        'my-filter' => [
            'root-key' => [
                'type' => 'root-key-filter',
            ],
            'simple-key' => [
                'required' => true,
                'filters' => [ /** Add filters **/ ],
                'validators' => [ /** Add validators **/],
            ],
        ],
    ],
    

    Doing so then allows you to use the type name in the newly created input filter:

    $data = [
        'root-key' => [
            'type' => 'my-value',
        ],
    ];
    
    'input_filter_specs' => [
        'root-key-filter' => [
            'type' => [
                'required' => true,
                'filters' => [ /** Add filters **/ ],
                'validators' => [ /** Add validators **/],
            ],
        ],
        'my-filter' => [
            'root-key' => [
                'type' => 'root-key-filter',
            ],
        ],
    ],
    

    I hope this late answer is still useful for anyone out there. Wilts answer sure was and brought me on the right track on this.