2016-08-19 2 views
0

Ich bin dabei, eine REST-API zu erstellen und alle Daten werden als JSON zurückgegeben. Jede Anfrage wird über eine einzige Funktion ausgeführt, die HTTP-Statuscodes setzt, Nachrichten oder Daten zurückgibt, Header setzt usw. Ich erlaube Benutzern auch, einen ?fields= Parameter hinzuzufügen, wo sie angeben können, welche Felder zurückgegeben werden sollen (zB ?fields=id,hostnames,ip_addresses) Wenn der Parameter nicht vorhanden ist, erhalten sie natürlich alle zurückgegebenen Daten. Die Funktion, die dies tut, ist auch Teil der Funktion erwähnt Earler, die die Header/Daten/Nachrichten, etc. setzt Was ich in der Lage sein soll zu ermöglichen ist der Benutzer Feldnamen mit Punktnotation angeben, so dass sie Felder von etwas angeben können anderes als ein Feld der obersten Ebene. So zum Beispiel habe ich eine Struktur wie folgt aus:PHP - Erstellen Sie ein neues Array mit der Liste der Array-Schlüssel in Punktnotation

{ 
    "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" 
       } 
      ] 
     } 
    ] 
} 

Mit Hilfe dieser Funktion kann ich Top-Level-Felder herausziehen (wobei $mData die Datenstruktur oben ist, und $sParams ist die Zeichenfolge der Felder vom Benutzer angefordert) :

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; 
} 

Hinweis: $sParams in als String kommt und ist das, was durch den Benutzer zur Verfügung gestellt wird (comma Liste der Felder, die sie sehen möchten getrennt).

So ?fields=hostnames,history zurückkehren würde:

{ 
    "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" 
       } 
      ] 
     } 
    ] 
} 

Aber wenn ich aus history vielleicht auch nur das ticket_number Feld zurückkehren wollen möchte ich den Benutzer in der Lage sein ?fields=history.ticket_number zu tun, oder wenn sie die Ticketnummer und Link wollen sie konnten tun sie dies: ?fields=history.ticket_number,history.links.link ..., die zurückkehren würde:

{ 
    "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" 
       } 
      ] 
     } 
    ] 
} 

ich viele verschiedene Array-Zugriffsmethoden für Punktnotation von Stack-Überlauf, aber sie alle brechen, wenn der Wert von 012 versucht haben,ist ein numerisches Array ... also zum Beispiel mit den Methoden, die ich bisher online gefunden habe, müsste ich etwas tun, um die gleiche Ausgabe oben zu erreichen (was offensichtlich nicht gut ist ... besonders wenn Sie Hunderte haben von Aufzeichnungen).

?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,

ich auch der Suche nach etwas war, das dynamische und rekursive war als jeder API-Endpunkt eine andere Datenstruktur (zum Beispiel zurückgibt, wenn eine Sammlung angefordert wird, es gibt einen numerischen Array mit assoziativen arrays..or gefüllt Ein Array von Objekten ... und einige dieser Objekte können Arrays (numerisch oder assoziativ) haben.

Vielen Dank im Voraus

P.S. - Es ist mir egal, ob der Code ein neues Datenarray mit den angeforderten Daten erstellt oder die Originaldaten direkt manipuliert (wie in meiner removeFields() - Funktion).

AKTUALISIERUNG: Ich habe eine PHPFiddle erstellt, die hoffentlich das Problem zeigen sollte, in das ich hineingeraten bin. http://phpfiddle.org/main/code/tw1i-qu7s

+0

Also, um klar zu sein, benötigen Sie PHP, um eine GET-Anfrage zu analysieren, die vielleicht Punkt-abgegrenzte Niveaus hat oder nicht, dann nur diese Aufzeichnungen zurückgeben? – amflare

+0

Ja, das ist richtig. Ein Benutzer kann einen optionalen 'fields'-Parameter über GET bereitstellen, der auf der Serverseite mit PHP verarbeitet wird. Wenn es bereitgestellt wird, kann es eine durch Kommas getrennte Liste von Feldern der obersten Ebene enthalten ('? Felder = Hostnamen, Datencenter, History') oder eine Punkt-begrenzte Liste von Werten, um Daten auf einer niedrigeren Ebene zu extrahieren ('? Fields = history. id, history.ticket_number') oder beide ('? fields = Hostnamen, data_centers, history.ticket_number'). Grundsätzlich könnte der Benutzer eine beliebige Kombination von verfügbaren Array-Schlüsseln bereitstellen, die jeweils durch ein Komma getrennt sind .... welches ich dann zu einem Array explodiere und verarbeite. – Brad

Antwort

1

danke für Ihre Tipps und Hilfe zu diesem Thema. Ich habe heute Morgen eine Lösung gefunden, die mit jedem Fall funktioniert, den ich bisher getestet habe. Es ist vielleicht nicht sehr elegant, aber funktioniert für das, was ich brauche. Ich verflach das Array im Wesentlichen durch Punktnotation für die Schlüssel in dem abgeflachten Array. Ich nehme dann jedes der angeforderten Felder und baue eine Regex (im Grunde ersetzt alle "." Mit einem optionalen .[digit]., um numerische Indizes zu fangen), dann jeden Feldnamen mit der Regex zu testen, halten diejenigen, die übereinstimmen. Schließlich erweitere ich das Array wieder in ein mehrdimensionales Array.

der abgeflachte Array verwandelt sich in dieses:

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 
) 

Unten sind die beiden Funktionen zum Abflachen und Expandieren der Anordnung:

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; 
} 

Beispiel:

$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 

welche Ausgänge der folgenden 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

Diese Lösung ist wirklich schlau. Ich habe ein ähnliches Problem, das mir gerade klar geworden ist, dass diese Lösung angewendet werden könnte. Wenn Sie bei der weiteren Arbeit einen Weg finden, diese zu verbessern, würden Sie gerne diese Antwort aktualisieren? – amflare

+0

Wenn ich diese Antwort verbessere, werde ich sicher sein, ein Update zu posten. – Brad

0

Statt Felder aus Klon zu entfernen wäre es auf den Feldern zu Schleife einfach sein und dann jeder von ihnen in Ihren Klon erhalten

Sie benötigen eine Funktion zum Punktnotation mit Ihrem json zu durchqueren. Wie dieser 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); 
} 

-Code ist nicht getestet, aber Sie können meinen Punkt verstehen.

+0

Danke für das Feedback. Ich habe es versucht (das ist ähnlich zu anderen Dingen, die ich auch gefunden und ausprobiert habe), aber es gibt mir immer noch nicht ganz das, was ich brauche. Das Problem liegt bei verschachtelten Arrays, die numerisch codiert sind, da sie nicht direkt oder einfach aufgerufen werden können, da der Benutzer "history.0.id, history.1.id, history.2.id", usw. statt nur einer einzigen 'history.id', um nur die' id' von jedem 'history'-Eintrag zu erhalten. Ich habe eine PHPFiddle erstellt, die hoffentlich das Problem zeigt. http://phpfiddle.org/main/code/tw1i-qu7s – Brad

0

Führen Sie einen Scheck

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 
    } 
} 

Zum Beispiel würde history.id die if auf history fangen, die else if auf die nummerierten Indizes, dann ist die if für die id.

Hoffnung, das hilft.

+0

Danke, dass Sie sich die Zeit nehmen zu antworten. Ich habe heute tatsächlich eine funktionierende Lösung gefunden. Es wurde veröffentlicht, wenn Sie neugierig sind. Danke nochmal – Brad

Verwandte Themen