2016-08-19 4 views
0

私はREST APIを構築中です。すべてのデータがJSONとして返されています。各リクエストは、HTTPステータスコードの設定、メッセージやデータの返信、ヘッダーの設定などの機能を果たす単一のファンクションを介して行われます。?fields=パラメータを追加して、返すフィールドを指定できます(例:?fields=id,hostnames,ip_addresses)パラメータが存在しない場合は、返されたすべてのデータを取得します。これを行う関数は、ヘッダー/データ/メッセージなどを設定する関数earlerの一部でもあります。私ができるようにしたいのは、ユーザーがドット表記を使ってフィールド名を指定できるようにして、何かからフィールドを指定できるようにすることですトップレベルのフィールド以外。したがって、たとえば、私はこのような構造を持っている:PHP - ドット表記の配列キーのリストを使用して新しい配列を作成する

{ 
    "id": "8a2b449111b449409c465c66254c6fcc", 
    "hostnames": [ 
     "webapp1-sfo", 
     "webapp1-sfo.example.com" 
    ], 
    "ip_addresses": [ 
     "12.26.16.10", 
     "ee80::ae56:2dff:fd89:7868" 
    ], 
    "environment": "Production", 
    "data_center": "sfo", 
    "business_unit": "Operations", 
    "hardware_type": "Server", 
    "currently_in_maintenance": false, 
    "history": [ 
     { 
      "id": 58, 
      "time_start_utc": "2013-01-27 00:40:00", 
      "time_end_utc": "2013-01-27 01:45:00", 
      "ticket_number": "CHG123456", 
      "reason": "January production maintenance", 
      "links": [ 
       { 
        "rel": "self", 
        "link": "https://localhost/api/v1/maintenances/58" 
       } 
      ] 
     }, 
     { 
      "id": 104, 
      "time_start_utc": "2013-02-25 14:36:00", 
      "time_end_utc": "2013-02-25 18:36:00", 
      "ticket_number": "CHG456789", 
      "reason": "February production maintenance", 
      "links": [ 
       { 
        "rel": "self", 
        "link": "https://localhost/api/v1/maintenances/104" 
       } 
      ] 
     }, 
     { 
      "id": 143, 
      "time_start_utc": "2013-03-17 00:30:00", 
      "time_end_utc": "2013-03-17 01:55:00", 
      "ticket_number": "CHG789123", 
      "reason": "March production maintenance", 
      "links": [ 
       { 
        "rel": "self", 
        "link": "https://localhost/api/v1/maintenances/143" 
       } 
      ] 
     } 
    ] 
} 

この機能を使用するには、私はトップレベルのフィールドを引き出すことができます($mDataは、データ構造は上記であり、そして$sParamsは、ユーザによって要求されたフィールドの文字列です) :

private function removeFields($mData, $sParams){ 
    $clone = $mData; // Clone the original data 
    $fields = explode(',', $sParams); 

    // Remove fields not requested by the user 

    foreach($mData as $key => $value){ 
     if(!in_array((string)$key, $fields)){ 
      unset($mData[$key]); 
     } 
    } 

    // If no fields remain, restore the original data 
    // Chances are the user made a typo in the fields list 

    if(count($mData) == 0){ 
     $mData = $clone; 
    } 

    return $mData; 
} 

注:$sParamsは文字列としてで来て、使用者(彼らが見たいフィールドのカンマ区切りリスト)によって提供されるものです。

{ 
    "hostnames": [ 
     "webapp1-sfo", 
     "webapp1-sfo.example.com", 
    ], 
    "history": [ 
     { 
      "id": 58, 
      "time_start_utc": "2013-01-27 00:40:00", 
      "time_end_utc": "2013-01-27 01:45:00", 
      "ticket_number": "CHG123456", 
      "reason": "January production maintenance", 
      "links": [ 
       { 
        "rel": "self", 
        "link": "https://localhost/api/v1/maintenances/58" 
       } 
      ] 
     }, 
     { 
      "id": 104, 
      "time_start_utc": "2013-02-25 14:36:00", 
      "time_end_utc": "2013-02-25 18:36:00", 
      "ticket_number": "CHG456789", 
      "reason": "February production maintenance", 
      "links": [ 
       { 
        "rel": "self", 
        "link": "https://localhost/api/v1/maintenances/104" 
       } 
      ] 
     }, 
     { 
      "id": 143, 
      "time_start_utc": "2013-03-17 00:30:00", 
      "time_end_utc": "2013-03-17 01:55:00", 
      "ticket_number": "CHG789123", 
      "reason": "March production maintenance", 
      "links": [ 
       { 
        "rel": "self", 
        "link": "https://localhost/api/v1/maintenances/143" 
       } 
      ] 
     } 
    ] 
} 

をしかし、私はhistoryから多分ちょうどticket_numberフィールドを返すようにしたい場合、私は、ユーザーが?fields=history.ticket_numberを行うことができるようにしたいか、彼らはチケット番号とリンクしたい場合、彼らは可能性:

ので?fields=hostnames,historyを返します返すことになる... ?fields=history.ticket_number,history.links.link:これを行う

{ 
    "history": [ 
     { 
      "ticket_number": "CHG123456", 
      "links": [ 
       { 
        "link": "https://localhost/api/v1/maintenances/58" 
       } 
      ] 
     }, 
     { 
      "ticket_number": "CHG456789", 
      "links": [ 
       { 
        "link": "https://localhost/api/v1/maintenances/104" 
       } 
      ] 
     }, 
     { 
      "ticket_number": "CHG789123", 
      "links": [ 
       { 
        "link": "https://localhost/api/v1/maintenances/143" 
       } 
      ] 
     } 
    ] 
} 

私は、スタックオーバーフローからのドット表記のための多くの異なるアレイアクセス方法を試してみたが、それらはすべてブレーク時に012の値は数値配列なので...例えば、私が今までオンラインで見つけたメソッドを使って、上の同じ出力を達成するためにこれを行う必要があります(明らかに良くない...特に数百レコードの)。

?fields=history.0.ticket_number,history.0.links.0.link,history.1.ticket_number,history.1.links.0.link,history.2.ticket_number,history.2.links.0.link,

私はまた、各APIエンドポイントは、例えば(異なるデータ構造を返すような動的および再帰的だった何かを探していた、コレクションが要求されたときには、連想arrays..orで満たされた数値配列を返します。 jsonの話では、オブジェクトの配列...それらのオブジェクトのいくつかは、配列(数値または連想)を持つかもしれません)。

ありがとうございます。

P.S. - コードが要求されたデータを含む新しいデータ配列を作成するか、(removeFields()関数と同じように)元のデータを直接操作するかどうかは気にしません。

更新:PHPFiddleを作成しました。これは、うまくいけば、今まで実行していた問題を示すはずです。 http://phpfiddle.org/main/code/tw1i-qu7s

+0

明確にするには、ドット区切りレベルのある場合とそうでないGETリクエストを解析するPHPが必要です。 – amflare

+0

はい、正しいです。ユーザはGETを介してオプションの 'fields'パラメータを提供することができ、PHPでサーバ側で処理されます。それが提供されている場合、最下位レベルのフィールド( '?fields = hostnames、data_center、history')またはドットで区切られた値のリストをコンマで区切ったリストを含むことができます。 id、history.ticket_number')または両方( '?fields = hostnames、data_centers、history.ticket_number')を指定します。基本的に、ユーザーは使用可能な配列キーの任意の組み合わせを提供することができ、各キーはコンマで区切られています。その後、配列とプロセスに分解します。 – Brad

答えて

1

ご協力いただきありがとうございます。私は実際に今朝テストしたすべてのケースで動作するように思われる解決策を思いつきました。それは超エレガントではないかもしれませんが、私が必要とするもののために働く。私はフラットな配列のキーにドット表記法を使って配列を本質的に平坦化します。次に、要求されたフィールドのそれぞれを取得し、正規表現を作成します(数字のインデックスをキャッチするために任意の "。"をオプションの.[digit].に置き換えます)。そして正規表現を使用して各フィールド名をテストします。最後に、配列を多次元配列に再展開します。

平坦化配列が本になる:

以下
Array 
(
    [id] => 8a2b449111b449409c465c66254c6fcc 
    [hostnames.0] => webapp1-sfo 
    [hostnames.1] => webapp1-sfo.example.com 
    [ip_addresses.0] => 12.26.16.10 
    [ip_addresses.1] => ee80::ae56:2dff:fd89:7868 
    [environment] => Production 
    [data_center] => sfo 
    [business_unit] => Operations 
    [hardware_type] => Server 
    [currently_in_maintenance] => 
    [history.0.id] => 58 
    [history.0.time_start_utc] => 2013-01-27 00:40:00 
    [history.0.time_end_utc] => 2013-01-27 01:45:00 
    [history.0.ticket_number] => CHG123456 
    [history.0.reason] => January production maintenance 
    [history.0.links.0.rel] => self 
    [history.0.links.0.link] => https://localhost/api/v1/maintenances/58 
    [history.1.id] => 104 
    [history.1.time_start_utc] => 2013-02-25 14:36:00 
    [history.1.time_end_utc] => 2013-02-25 18:36:00 
    [history.1.ticket_number] => CHG456789 
    [history.1.reason] => February production maintenance 
    [history.1.links.0.rel] => self 
    [history.1.links.0.link] => https://localhost/api/v1/maintenances/104 
    [history.2.id] => 143 
    [history.2.time_start_utc] => 2013-03-17 00:30:00 
    [history.2.time_end_utc] => 2013-03-17 01:55:00 
    [history.2.ticket_number] => CHG789123 
    [history.2.reason] => March production maintenance 
    [history.2.links.0.rel] => self 
    [history.2.links.0.link] => https://localhost/api/v1/maintenances/143 
) 

アレイを平坦化し、拡大するための2つの機能がある:

function flattenArray($aArrayToFlatten, $sSeparator = '.', $sParentKey = NULL){ 
    if(!is_array($aArrayToFlatten)){ 
     return $aArrayToFlatten; 
    } 
    $_flattened = array(); 

    // Rewrite keys 

    foreach($aArrayToFlatten as $key => $value){ 
     if($sParentKey !== NULL){ 
      $key = $sParentKey . $sSeparator . $key; 
     } 
     $_flattened[$key] = flattenArray($value, $sSeparator, $key); 
    } 

    // Flatten 

    $flattened = array(); 
    foreach($_flattened as $key => $value){ 
     if(is_array($value)){ 
      $flattened = array_merge($flattened, $value); 
     }else{ 
      $flattened[$key] = $value; 
     } 
    } 

    return $flattened; 
} 

function expandArray($aFlattenedArray, $sSeparator = '.'){ 
    $result = array(); 
    foreach($aFlattenedArray as $key => $val){ 
     $keyParts = explode($sSeparator, $key); 
     $currentArray = &$result; 
     for($i = 0; $i < count($keyParts) - 1; $i++){ 
      if(!isset($currentArray[$keyParts[$i]])){ 
       $currentArray[$keyParts[$i]] = array(); 
      } 
      $currentArray = &$currentArray[$keyParts[$i]]; 
     } 
     $currentArray[$keyParts[count($keyParts)-1]] = $val; 
    } 

    return $result; 
} 

例:

以下を出力
$mData = json_decode('{ "id": "8a2b449111b449409c465c66254c6fcc", "hostnames": [ "webapp1-sfo", "webapp1-sfo.example.com" ], "ip_addresses": [ "12.26.16.10", "ee80::ae56:2dff:fd89:7868" ], "environment": "Production", "data_center": "sfo", "business_unit": "Operations", "hardware_type": "Server", "currently_in_maintenance": false, "history": [ { "id": 58, "time_start_utc": "2013-01-27 00:40:00", "time_end_utc": "2013-01-27 01:45:00", "ticket_number": "CHG123456", "reason": "January production maintenance", "links": [ { "rel": "self", "link": "https:\/\/localhost\/api\/v1\/maintenances\/58" } ] }, { "id": 104, "time_start_utc": "2013-02-25 14:36:00", "time_end_utc": "2013-02-25 18:36:00", "ticket_number": "CHG456789", "reason": "February production maintenance", "links": [ { "rel": "self", "link": "https:\/\/localhost\/api\/v1\/maintenances\/104" } ] }, { "id": 143, "time_start_utc": "2013-03-17 00:30:00", "time_end_utc": "2013-03-17 01:55:00", "ticket_number": "CHG789123", "reason": "March production maintenance", "links": [ { "rel": "self", "link": "https:\/\/localhost\/api\/v1\/maintenances\/143" } ] } ] }', TRUE); 

print_r($mData); // Original Data 

$fields = array("id", "hostnames", "history.id", "history.links.link"); 
$regexFields = array(); 

// Build regular expressions for each of the requested fields 

foreach($fields as $dotNotatedFieldName){ 

    // Requested field has a dot in it -- it's not a top-level field 
    // It may be part of a collection (0.fieldname.levelTwo, 1.fieldName.levelTwo,...) or be a collection (fieldName.0.levelTwo, fieldName.1.levelTwo, ...) 

    if(preg_match('/\./', $dotNotatedFieldName)){ 
     $regexFields[] = "^\d*\.?" . str_replace(".", "\.\d*\.?", $dotNotatedFieldName); 

    // Requested field does not have a dot in it -- it's a top-level field 
    // It may be part of a collection (0.fieldname, 1.fieldName,...) or be a collection (fieldName.0, fieldName.1, ...) 

    }else{ 
     $regexFields[] = "^\d*\.?" . $dotNotatedFieldName . "\.?\d*"; 
    } 
} 

// Flatten the array 

$flattened = flattenArray($mData); 

// Test each flattened key against each regular expression and remove those that don't match 

foreach($flattened as $key => $value){ 
    $matchFound = FALSE; 

    foreach($regexFields as $regex){ 
     if(preg_match('/' . $regex . '/', $key)){ 
      $matchFound = TRUE; 
      break; 
     } 
    } 

    if($matchFound === FALSE){ 
     unset($flattened[$key]); 
    } 

} 

// Expand the array 

$mData = expandArray($flattened); 

print_r(json_encode($mData)); // New Data 

JSON:

{ 
    "id": "8a2b449111b449409c465c66254c6fcc", 
    "hostnames": [ 
     "webapp1-sfo", 
     "webapp1-sfo.example.com" 
    ], 
    "history": [ 
     { 
     "id": 58, 
     "links": [ 
      { 
       "link": "https://localhost/api/v1/maintenances/58" 
      } 
     ] 
     }, 
     { 
     "id": 104, 
     "links": [ 
      { 
       "link": "https://localhost/api/v1/maintenances/104" 
      } 
     ] 
     }, 
     { 
     "id": 143, 
     "links": [ 
      { 
       "link": "https://localhost/api/v1/maintenances/143" 
      } 
     ] 
     } 
    ] 
} 
+0

このソリューションは本当に賢いです。私は自分の似たような問題を抱えています。私は今、この解決策が適用可能であることを認識しました。これをさらに進めていくうちに、それを改善する方法が見つかったら、この回答を更新してもよろしいですか? – amflare

+0

この回答を改善した場合は、必ずアップデートを投稿してください。 – Brad

0

代わりに自分のクローンからフィールドを除去するには、フィールド上のループに簡単だろうと、あなたはあなたのJSONを横断するドット表記を持つための機能を必要とする、あなたのクローン

にそれらのそれぞれを取得します。このようにhttps://selvinortiz.com/blog/traversing-arrays-using-dot-notation

private function buildResponse($mData, $sParams){ 
    $fields = explode(',', $sParams['fields']); 
    $response = $mData; 

    if (count($fields > 0)){ 
     $response = []; 
     foreach($fields as $value){ 
     if (getDotValue($mData, $value)){ 
      $response[$value] = $mData.getDotValue($value); 
     } 
     } 
    } 

    return json_encode($response); 
} 

コードはテストされていませんが、私の主旨を理解することができます。

+0

フィードバックをいただきありがとうございます。私はこれを試してみました(これは私が見つけて試した他のものと似ていますが)。問題は、ユーザーが 'history.0.id、history.1.id、history.2.id'を指定する必要があるため、「直接」または「簡単に」アクセスできないため、数値的にキーが入れ子になった配列にあります。 'history'エントリの' id'だけを取得するために 'history.id'を1つだけ使用するのではなく、うまくいけば問題を示すPHPFiddleを作成しました。 http://phpfiddle.org/main/code/tw1i-qu7s – Brad

0

実行チェック例えば

if (isset($field=>$nextfield)) { //if next field level is named 
    //do magic here 
} else if (isset($field[0])) { //if next field level is indexed 
    for ($i=0; $i < count($field); $i++) { 
    //catch 'em all 
    } 
} 

history.idifhistory上をキャッチします、番号がインデックスにelse if、その後、idためif

希望に役立ちます。

+0

お返事いただきありがとうございます。私は実際に働いている解決策を今朝見つけました。あなたが好奇心を持っているなら、それは掲示されています。再度ありがとう – Brad

関連する問題