递归函数直到完成值为真并将每个响应合并为一个大响应

Recursive function until done value is true and merge each response into a big one

我有这个功能:

public function syncTerritoriesSOQLQuery($veevaToken, $instanceUrl, $tokenUrl)
{
    $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);

    $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);

    $soqlObj2 = json_decode($jsonResponse, true);

    return $soqlObj2;
}

当我调用它时,我得到了这样的回复:

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

这意味着完整的记录集将有 6911 个项目,但在第一次请求时只有前 2000 个是 return,因为它是分页的。所以我需要一次又一次地调用相同的代码,直到 "done": true。在每个新请求中,我需要通过在每次调用时附加值 return 来更改 $soqlUrl2。例如:

1st time:
$soqlUrl2 = $instanceUrl.'/services/data/v28.0/query/?q='.urlencode($soqlQuery2);

2nd time:
$soqlUrl2 = $instanceUrl."/services/data/v28.0/query/01g8000002eI8dMAAS-2000";    

3rd time:
$soqlUrl2 = $instanceUrl."/services/data/v28.0/query/01g8000002eI8dMAAS-4000";

...

直到 done 得到 true 的响应。我还应该将每次迭代的整个 JSON 结果合并为一个大结果,可以说:第二次的结果应该与第一次调用的结果合并,意思是 $soqlObj2 第 3 次的结果应该与伪代码中的先前合并:

merge($soqlObj2, 1st, 2nd, 3rd, ...)

我一直在研究这个功能:

public function performPaginateSOQLQuery($veevaToken, $instanceUrl, $tokenUrl, $nextRecordsUrl, &$dataToSave = array()) {
    if ($nextRecordsUrl !== null && $nextRecordsUrl !== "") {
        $keepRequesting = true;

        while($keepRequesting){
            $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);
            $dataToSave[] = $nextRecordsUrlObj;

            echo "iteration 2", "\n";
            echo "url: ", $nextRecordsUrl, "\n";
            echo "done: ";
            var_dump($nextRecordsUrlObj['done']);
            echo "\n";

            if($nextRecordsUrlObj['done'] === true) {
                $keepRequesting = false;
            }
        }

        return array('url' => $nextRecordsUrlObj, 'data' => $dataToSave);
    }
}

我从上面的函数 syncTerritoriesSOQLQuery() 调用它如下:

$entireSoqlObj2 = $this->performPaginateSOQLQuery(
        $veevaToken,
        $instanceUrl,
        $tokenUrl,
        $soqlObj2['nextRecordsUrl']
);

但它不起作用,因为我得到了这个结果:

iteration 0
url: /services/data/v28.0/query/01g8000002eYb8LAAS-2000
done: bool(false)

iteration 2
url: /services/data/v28.0/query/01g8000002eYb8LAAS-2000
done: bool(false)

iteration 2
url: /services/data/v28.0/query/01g8000002eYb8LAAS-2000
done: bool(false)

iteration 2
url: /services/data/v28.0/query/01g8000002eYb8LAAS-2000
done: bool(false)

iteration 2
url: /services/data/v28.0/query/01g8000002eYb8LAAS-2000
done: bool(false)

正确的输出应该是这样的:

// 1st
url: /services/data/v28.0/query/01g8000002eYb8LAAS-2000
done: bool(false)

// 2st
url: /services/data/v28.0/query/01g8000002eYb8LAAS-4000
done: bool(false)

// 3rd
url: /services/data/v28.0/query/01g8000002eYb8LAAS-6000
done: bool(true)

因为totalSize是6911,有什么可以帮我修改密码的吗?我的错误在哪里?

注意: 我今天早些时候问 但我一点也不清楚所以我打开了一个包含更多信息的新问题。

你的递归函数中应该有 $soqlObj2['nextRecordsUrl'],每次都应该用 $nextRecordsUrl 更新。现在它总是只检查第一页,因此你得到第二页的 url。

另外,我不明白你为什么在 performPaginateSOQLQuery() 中有这么多代码,你只是想遍历 syncTerritoriesSOQLQuery() 直到 done,对吗?


更新:

正在更新您的 syncTerritoriesSOQLQuery() 以遍历所有页面。在我进行更改的所有地方添加评论。希望对您有所帮助。

<?php
public function syncTerritoriesSOQLQuery($veevaToken, $instanceUrl, $tokenUrl)
{
    $soqlQuery2 = "SELECT Id,Name,LastModifiedDate FROM Territory";

    // removed $instanceUrl, we'll add that inside while loop
    $instancePath = '/services/data/v28.0/query/?q='.urlencode($soqlQuery2);

    $final_output = array(); // all output will be appended to this array
    $done = False; // we set this variable to true, once `done` is true

    while( !$done ) {
        $soqlUrl2 = $instanceUrl . $instancePath;

        $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);

        $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);

        $soqlObj2 = json_decode($jsonResponse, true);

        // here, we change the $instancePath to nextRecordsUrl
        $instancePath = $soqlObj2['nextRecordsUrl'];

        //  set $done variable
        $done = $soqlObj2['done'];

        // appending the data obtained to $final_output array, you can change this string if u need.
        $final_output[] = $soqlObj2;
       }

    return $final_output;
}