FacebookAds SDK游标无法处理/ reportstats端点

时间:2015-03-12 23:27:54

标签: php facebook rest facebook-graph-api facebook-php-sdk

我正在使用facebook-php-ads-sdk版本2.2.4(撰写本文时的最新版本)。我注意到调用$adAccount->getReportStats()返回的Cursor被隐藏的fetch选项打破了。光标期待在响应中看到以下结构:

{
    "paging": { "cursor": { "after": "<some_url>" } }
}

但是,/reportstats端点返回如下结构的分页信息:

{
    "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);
    }
}

3 个答案:

答案 0 :(得分:1)

您所看到的是当它是基于时间的分页时的响应&#34; https://developers.facebook.com/docs/graph-api/using-graph-api/v2.2#paging

答案 1 :(得分:1)

我向他们的github提交了一个错误。

答案 2 :(得分:0)

这可能是facebook-php-ads-sdk中的错误。即使Facebook API examples显示为:

use FacebookAds\Object\AdAccount;

$account = new AdAccount('act_<AD_ACCOUNT_ID>');

$params = array(
    'date_preset'=>'last_28_days',
    'data_columns'=>"['adgroup_id','actions','spend']",
);

$stats = $account->getReportsStats(null, $params);

foreach($stats as $stat) {
    echo $stat->impressions;
    echo $stat->actions;
}

不幸的是 - 确实是基于时间的分页所以光标数据不会从中返回。它已经在GitHub上填充了问题 - https://github.com/facebook/facebook-php-ads-sdk/issues/76

编辑:哦,是你填补了这个bug:)