递归调用,直到值不再在响应上


Recursive call until value is not more on response

我对此services/data/v28.0/query/?q=SELECT Id,Name,LastModifiedDate FROM Territory有一个 cURL 调用,响应如下所示:

{
  "totalSize": 6911,
  "done": false,
  "nextRecordsUrl": "/services/data/v28.0/query/01g8000002eI8dMAAS-2000",
  "records": [ ... ]
}

这意味着完整的记录集将有 6911 个项目,但在第一次请求时只返回前 2000 个项目,现在如果我稍微更改一下 cURL 调用到这个/services/data/v28.0/query/01g8000002eI8dMAAS-2000我将获得下一个 2000 个项目的第一个 6911 依此类推。

我应该调用 cURL 递归,直到响应中NULLnextRecordsUrl done = true或不再存在,如何?我需要一些建议或伪代码来说明我如何实现这一目标,因为我有点卡住了。

这显示了我应该执行的完整递归调用集(这可能是动态的,因此硬代码不起作用):

call: 
/services/data/v28.0/query/?q=SELECT Id,Name,LastModifiedDate FROM Territory
response:
{
  "totalSize": 6911,
  "done": false,
  "nextRecordsUrl": "/services/data/v28.0/query/01g8000002eI8dMAAS-2000",
  "records": [ ... ]
}
call: 
/services/data/v28.0/query/01g8000002eI8dMAAS-2000
response:
{
  "totalSize": 6911,
  "done": false,
  "nextRecordsUrl": "/services/data/v28.0/query/01g8000002eI8dMAAS-4000",
  "records": [ ... ]
}
call: 
/services/data/v28.0/query/01g8000002eI8dMAAS-4000
response:
{
  "totalSize": 6911,
  "done": false,
  "nextRecordsUrl": "/services/data/v28.0/query/01g8000002eI8dMAAS-6000",
  "records": [ ... ]
}
call: 
/services/data/v28.0/query/01g8000002eI8dMAAS-6000
response:
{
  "totalSize": 6911,
  "done": true,
  "records": [ ... ]
}

更新

我已经或多或少地弄清楚了如何实现这一目标,但现在我的问题变成了如何合并每个响应中的递归值。请参阅下面的代码:

$soqlQuery2 = "SELECT Id,Name,LastModifiedDate FROM Territory";
$soqlUrl2 = $instanceUrl.'/services/data/v28.0/query/?q='.urlencode($soqlQuery2);
$curl = curl_init($soqlUrl2);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: OAuth $veevaToken"));
$jsonResponse = curl_exec($curl);
curl_close($curl);
$soqlObj2 = json_decode($jsonResponse, true);
$this->performPaginateSOQLQuery($veevaToken, $instanceUrl, $tokenUrl, $soqlObj2['nextRecordsUrl']);

在上面的$soqlObj2['done']$soqlObj2['nextRecordsUrl']中,我将拥有第二次和递归调用所需的值。所以,我构建了这个函数:

public function performPaginateSOQLQuery($veevaToken, $instanceUrl, $tokenUrl, $nextRecordsUrl)
{
    if (isset($nextRecordsUrl) && $nextRecordsUrl !== null && $nextRecordsUrl !== "") {
        $nextRecordsUrlSOQLQuery = $instanceUrl.$nextRecordsUrl;
        $curl = curl_init($nextRecordsUrlSOQLQuery);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: OAuth $veevaToken"));
        $jsonResponse = curl_exec($curl);
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        if ($status != 200) {
            $respObj['error'] = "Error: call to token URL $tokenUrl failed with status $status, response $jsonResponse, curl_error ".curl_error(
                    $curl
                ).", curl_errno ".curl_errno($curl);
            return $respObj;
        }
        curl_close($curl);
        $nextRecordsUrlObj = json_decode($jsonResponse, true);
        while ($nextRecordsUrlObj['done'] !== true) {
            $this->performPaginateSOQLQuery($veevaToken, $instanceUrl ,$tokenUrl, $nextRecordsUrlObj['nextRecordsUrl']);
        }
        return $nextRecordsUrlObj;
    }
}

但是我需要将整个$soqlObj2与调用performPaginateSOQLQuery()每次迭代的$nextRecordsUrlObj合并,如何?有什么帮助吗?

更新 2:在第一次迭代时丢失值

我已经将我的代码更新为:

public function performPaginateSOQLQuery(
    $veevaToken,
    $instanceUrl,
    $tokenUrl,
    &$nextRecordsUrl,
    &$dataToSave = array()
) {
    if (isset($nextRecordsUrl) && $nextRecordsUrl !== null && $nextRecordsUrl !== "") {
        $nextRecordsUrlSOQLQuery = $instanceUrl.$nextRecordsUrl;
        $curl = curl_init($nextRecordsUrlSOQLQuery);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: OAuth $veevaToken"));
        $jsonResponse = curl_exec($curl);
        $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        if ($status != 200) {
            $respObj['error'] = "Error: call to token URL $tokenUrl failed with status $status, response $jsonResponse, curl_error ".curl_error(
                    $curl
                ).", curl_errno ".curl_errno($curl);
            return $respObj;
        }
        curl_close($curl);
        $nextRecordsUrlObj = json_decode($jsonResponse, true);
        echo $nextRecordsUrlObj['nextRecordsUrl'] . "'n";
        print_r($nextRecordsUrlObj);
        $dataToSave[] = $nextRecordsUrlObj;
        $i = 0;
        while ($nextRecordsUrlObj['done'] !== true) {
            echo "time ".$i;
            $i++;
            $this->performPaginateSOQLQuery(
                $veevaToken,
                $instanceUrl,
                $tokenUrl,
                $nextRecordsUrlObj['nextRecordsUrl'],
                $dataToSave
            );
        }
        return array('url' => $nextRecordsUrlObj, 'data' => $dataToSave);
    }
}

但是在迭代time 1我收到此错误:

[Symfony'Component'Debug'Exception'ContextErrorException]
  Notice: Undefined index: nextRecordsUrl

为什么?

您可以将 while 循环与布尔变量一起使用。一旦你得到的响应是最后一个(done属性是true),更改该布尔变量的值,循环应该停止。

<?php
//Assuming $response contains the response you're getting
//and that it's JSON-encoded.
$keepRequesting = true;
while($keepRequesting){
  //Your curl code over here
  // ...
  $response = json_decode($response);
  if($response->done == true) {
     $keepRequesting = false;
  }
}

关于您的更新:虽然我认为你不应该对这个问题使用递归方法,只需添加要"保存"的数据作为对该函数的引用并使用array_merge .像这样:

public function performPaginateSOQLQuery($veevaToken, $instanceUrl, $tokenUrl, $nextRecordsUrl, &$dataToSave = array()) { //1 new parameters.
    //More code here...

    $nextRecordsUrlObj = json_decode($jsonResponse, true);
    $dataToSave[] = $nextRecordsUrlObj;
    while ($nextRecordsUrlObj['done'] !== true) {
        $this->performPaginateSOQLQuery($veevaToken, $instanceUrl ,$tokenUrl, $nextRecordsUrlObj['nextRecordsUrl'], $dataToSave);
    }
    return array('url' => $nextRecordsUrlObj, 'data' => $dataToSave);

问题是你应该归还它,否则它会丢失。您必须稍微更改函数,以便它返回所需的 URL 和数据。

也许这篇文章会对你有所帮助:PHP 递归全局变量?