我正在使用
facebook-php-ads-sdk
版本2.2.4(本文撰写时的最新版本)。我注意到,调用
$adAccount->getReportStats()
用隐式提取选项中断。光标是
期望
在响应中看到以下结构:
{
"paging": { "cursor": { "after": "<some_url>" } }
}
然而
/reportstats
endpoint返回如下结构的分页信息:
{
"paging": { "next": "<some_url>" }
}
几天前,我可以发誓它会如预期的那样工作,所以也许facebook的API已经改变了?
下面是一个示例:
$adAccount = new AdAccount('some_id');
// cursor is an instance of FacebookAds\Cursor.
$cursor = $adAccount->getReportStats($someFields, $someParams);
$cursor->setUseImplicitFetch(true);
foreach ($cursor as $item) {
// do stuff
}
// cursor is never advanced to next paged result.
正如您从FacebookAds\Cursor中看到的,当隐式获取设置为true时,光标只检查
paging.cursor.after|before
:
<?php
namespace FacebookAds;
use FacebookAds\Http\RequestInterface;
use FacebookAds\Http\ResponseInterface;
use FacebookAds\Object\AbstractObject;
class Cursor implements \Iterator, \Countable, \arrayaccess {
// ...
/**
* @return string|null
*/
protected function getLastRequestBefore() {
$content = $this->getLastResponse()->getContent();
return isset($content['paging']['cursors']['before'])
? $content['paging']['cursors']['before']
: null;
}
/**
* @return string|null
*/
protected function getLastRequestAfter() {
$content = $this->getLastResponse()->getContent();
return isset($content['paging']['cursors']['after'])
? $content['paging']['cursors']['after']
: null;
}
// ...
}
facebook的sdk生成的实际卷曲请求:
curl -G \
-d "data_columns=["time_start","time_stop","spend","impressions","clicks","unique_clicks","social_clicks","unique_social_clicks","cpm","unique_ctr","reach","frequency","cost_per_unique_click","cost_per_action_type","cost_per_total_action","cpp","cpc","ctr","account_id","account_name","campaign_group_id","campaign_group_name","campaign_id","campaign_name"]" \
-d "date_preset=last_90_days" \
-d "time_increment=1" \
-d "access_token=<nice_try_dude>" \
-d "appsecret_proof=<not_getting_this_either>" \
https://graph.facebook.com/v2.2/act_<account_id>/reportstats
下面是回应:
{
"data": [
{
"campaign_id": "<campaign_id>",
"date_start": "2014-12-18",
"date_stop": "2014-12-18",
"time_start": 1418878800,
"time_stop": 1418965200,
"spend": 39.39,
"impressions": 5127,
"clicks": 65,
"unique_clicks": 55,
"social_clicks": 31,
"unique_social_clicks": 27,
"cpm": 7.6828554710357,
"unique_ctr": 1.0880316518299,
"reach": 5055,
"frequency": 1.0142433234421,
"cost_per_unique_click": 0.71618181818182,
"cost_per_action_type": 0.67913793103448,
"cost_per_total_action": 0.67913793103448,
"cpp": 7.7922848664688,
"cpc": 0.606,
"ctr": 1.2677979325141,
"account_id": "<account_id>",
"account_name": "<account_name>",
"campaign_group_id": "<campaign_group_id>",
"campaign_group_name": "<campaign_group_name>",
"campaign_name": "<campaign_name>"
},
{
"..." : "x49"
}
],
"limit": 50,
"offset": 0,
"paging": {
"next": "https://graph.facebook.com/v2.2/act_<account_id>/reportstats?data_columns=%5B%22time_start%22%2C%22time_stop%22%2C%22spend%22%2C%22impressions%22%2C%22clicks%22%2C%22unique_clicks%22%2C%22social_clicks%22%2C%22unique_social_clicks%22%2C%22cpm%22%2C%22unique_ctr%22%2C%22reach%22%2C%22frequency%22%2C%22cost_per_unique_click%22%2C%22cost_per_action_type%22%2C%22cost_per_total_action%22%2C%22cpp%22%2C%22cpc%22%2C%22ctr%22%2C%22account_id%22%2C%22account_name%22%2C%22campaign_group_id%22%2C%22campaign_group_name%22%2C%22campaign_id%22%2C%22campaign_name%22%5D&date_preset=last_90_days&time_increment=1&access_token=<access_token>&appsecret_proof=<appsecret_proof>&offset=50"
}
}
同时,我使用了以下“包装器”。更好的选择?
<?php namespace PayPerClick\Market\Facebook\Data;
use FacebookAds\Cursor;
/**
* Class MyReportCursor
*
* @package PayPerClick\Market\Facebook\Data
*/
class MyReportCursor implements \Iterator, \Countable, \ArrayAccess {
/**
* @type int
*/
protected $position = 0;
/**
* @type Cursor[]
*/
protected $cursors = [];
/**
* @param Cursor $cursor
*/
public function __construct(Cursor $cursor) {
$cursor->setUseImplicitFetch(false);
$this->cursors[] = $cursor;
}
/**
* @return Cursor
*/
public function getCursor() {
return $this->cursors[ $this->position ];
}
public function current() {
return $this->getCursor()->current()->getData();
}
public function next() {
$this->getCursor()->next();
if ($this->getCursor()->key() === null) {
$this->advanceCursors();
}
}
protected function advanceCursors() {
if ($this->hasCursor($this->position+1)) {
$this->getCursor()->rewind();
$this->position++;
} else if ($this->hasNextPage()) {
$this->fetchNext();
}
}
/**
* @return bool
*/
protected function hasNextPage() {
return $this->getNextPage() !== null;
}
/**
* @return string|null
*/
protected function getNextPage() {
$content = $this->getCursor()->getLastResponse()->getContent();
return isset($content['paging']['next']) ? $content['paging']['next'] : null;
}
/**
* @param int $offset
* @return bool
*/
protected function hasCursor($offset) {
return isset($this->cursors[ $offset ]);
}
protected function fetchNext() {
parse_str(parse_url($this->getNextPage(), PHP_URL_QUERY), $previousParams);
$objectPrototype = clone $this->getCursor()->offsetGet($this->getCursor()->getIndexRight());
$request = $this->getCursor()->getLastResponse()->getRequest()->createClone();
$request->getQueryParams()->offsetSet('offset', $previousParams['offset']);
$this->getCursor()->rewind();
$this->position++;
$this->cursors[ $this->position ] = new Cursor($request->execute(), $objectPrototype);
}
public function key() {
return $this->getCursor()->key();
}
public function valid() {
return $this->getCursor()->valid();
}
public function rewind() {
$this->position = 0;
}
public function offsetExists($offset) {
return $this->getCursor()->offsetExists($offset);
}
public function offsetGet($offset) {
return $this->getCursor()->offsetGet($offset);
}
public function offsetSet($offset, $value) {
$this->getCursor()->offsetSet($offset, $value);
}
public function offsetUnset($offset) {
$this->getCursor()->offsetUnset($offset);
}
public function count() {
return array_reduce($this->cursors, function ($a, $cursor) {
return $a + $cursor->count();
}, 0);
}
}