質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

PHP多次元連想配列の検証にあたって、「検証対象のどこがおかしいか」を示した結果を得たい

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 142

mickymouse

score 3

実現したいこと

検証対象に対して、必須キー$requiredの指定通りであることを検証する関数を作りたいです。 
(値の有無、値の型を検証したいです。)

問題

検証対象は
・ドリンクのレスポンス$response_drink
・ケーキのレスポンス$response_drink
などいくつかのパターンがあります。

この検証対象を、valid_response( 'drink', $response_drink )のように渡して検証しています。

しかし「ドリンクのレスポンスを検証」はできるのに、「ケーキのレスポンスを検証」ができなくて行き詰っています。

該当のソースコード

/*
    レスポンス
----------------------*/

// ドリンクのレスポンス
$response_drink = [
    [
        'titles' => [
              'name'       => 'mojito'
             ,'thumbnails' => 'http...'
        ]
        ,'data' => [
            'id' => [
                'a_id' => 0
            ]
        ]
        ,'subs' => ['lime','mint']
    ],
    [
        'titles' => [
              'name'       => null // ここがnullなので、検証結果にそれが示されて問題なし
             ,'thumbnails' => 'http...'
             ]
        ,'data' => [
            'id' => [
                'a_id' => 0
            ]
        ]
    ]      
];

// ケーキのレスポンス
$response_cake = [
    'cake' => [
        'items' => [
            [
                 'name'        => 'chocolate'
                ,'images'      => ['a','b']
                ,'genres'      => ['c','d']
                ,'followers'   => [
                    'total'      => 100
                ]
                ,'id'          => 11
            ],
            [
                 'name'        => null // ここがnullなのに、検証結果にそれが示されない
                ,'images'      => []
                ,'followers'   => [
                     'total'      => 200
                    ,'weekl'      => 2
                ]
                ,'id'          => 22
            ]            
        ]
    ]
]; 


/*
    検証実行
----------------------*/

// ドリンクのレスポンスを検証
// $response_drinkについて、[1]['titles']['name']がnullであることが示される
$result_drink = valid_response( 'drink', $response_drink );
var_dump($result_drink); 

// ケーキのレスポンスを検証
// $response_cakeについて、['cake']['items'][1]['name']がnullであることが示されない
$result_cake = valid_response( 'cake', $response_cake );
var_dump($result_cake);


/*
    検証する関数
----------------------*/

// $responseについて、$requiredで指定した値の型、キー有無を判定
function valid_response( $kind, $response ){

    // 種類に応じて$requiredを指定
    if ( $kind === 'cake' ) {
        $required = [
            'cake' => [
                'items' => [
                    [
                         'name'        => ''
                        ,'images'      => []
                        ,'followers'   => [
                            'total'      => 0
                        ]
                        ,'id'          => 0
                    ]
                ]
            ]
        ];    
    }
    elseif ( $kind === 'drink' ) {
        $required = [
            [
                'titles' => [
                      'name'       => 'x'
                     ,'thumbnails' => 'x'
                ]
                ,'data' => [
                    'id' => [
                        'a_id' => 0
                    ]
                ]
            ]
        ];
    }

    // 検証
    $result = [];
    foreach ( $response as $k1 => $res ) {
        foreach ( $required as $k2 => $req ) {
            $match = valid_type_arr( $res, $req );
            if( !empty($match) ){
                $result[$k1][] = $match;
            }
        }
    }
    return $result;
}

// 値の型、キー有無を判定する
function valid_type_arr( $target_info, $base_info ){
    $result = [];
    foreach( $base_info as $k => $v ){
        if( is_array($v) ){
            if( !isset($target_info[$k]) ){
                if( array_key_exists($k, $target_info) ){
                    $result[$k] = null;
                }else{
                    $result[$k] = 'undefined';
                }
            }else{
                $re = valid_type_arr( $target_info[$k], $v );
                if( !empty($re) ){
                    $result[$k] = $re;
                }
            }            
        }else{
            if( !isset($target_info[$k]) ){
                if( array_key_exists($k, $target_info) ){
                    $result[$k] = null;
                }else{
                    $result[$k] = 'undefined';
                }
            }else{
                if ( gettype($target_info[$k]) !== gettype($v) ){
                    $result[$k] = $target_info[$k];
                }
            }
        }
    }
    return $result;
}

求めている結果

上記ドリンクの方のvar_dump($result_drink);では以下を得られます。

array(1) {
    [1]=>
    array(1) {
        [0]=>
        array(1) {
            ["titles"]=>
            array(1) {
                ["name"]=>
                NULL
            }
        }
    }
}

これは、$response_drinkについて
[1]['titles']['name']nullであること
を示しており問題ございません。

一方でケーキの方のvar_dump($result_cake);では以下を得てしまいます。

array(0) {
}


しかし本来は$response_cakeについて
['cake']['items'][1]['name']nullであること
を示したいので、求める結果は次のものです。

array(1) {
    ["cake"]=>
    array(1) {
        ["items"]=>
        array(1) {
            [1]=>
            array(1) {
                ["name"]=>
                NULL
            }
        }
    }
}

試したこと

解決策の一案として、valid_response()という一つの関数に頼らずに、ケーキとドリンクの場合それぞれで検証する関数を作る方法ならできるのですが、そうではなく合わせて汎用的な関数を作りたく思っています。

また$requiredの構造を変える方法でもできるのですが、そうではなく検証対象と同じ構造で書いたまま解決できないかと考えているのが現状です。

PHPバージョン

PHPは7.2.0を使用です。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m.ts10806

    2020/07/10 12:55

    2つの配列の構造が違うのは本当にそれで問題ないのでしょうか。

    キャンセル

  • mickymouse

    2020/07/10 13:03

    ご指摘ありがとうございます。通常は自ら統一すべきですから当然の疑問かと思います。
    実はレスポンスを得る先が異なるために、構造の統一ができないという理由がございます。

    もちろん、「統一ができないならば、レスポンスを得てから自ら構造を統一し、その上で同じ構造に対して検証すれば?」というご提案もあるかと思いますが、そうなりますと構造の統一に際しても検証を挟まねばなりません。
    そのためにレスポンスを得た時点で、異なる構造に対する検証を考えており、質問のような処理を求めた次第でございます。

    また何かございましたら宜しくお願い致します。

    キャンセル

  • m.ts10806

    2020/07/10 13:07

    でしたら言うことはありません。
    「異なる構造に対する検証」は余計な分岐や手順が増え、バグの元になりますので、「合わせる」以外にないと思ってます。
    扱う情報が増えればその分また分岐が増え、テスト工数もかさみます。

    キャンセル

回答 1

0

情報が古いですが、こんな方法があるようです。

https://www.slideshare.net/hinakano/json-schema

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/11 00:42

    ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • PHPに関する質問
  • PHP多次元連想配列の検証にあたって、「検証対象のどこがおかしいか」を示した結果を得たい