代码之家  ›  专栏  ›  技术社区  ›  kylehyde215

FacebookAds SDK游标无法在/reportstats端点上工作

  •  2
  • kylehyde215  · 技术社区  · 9 年前

    我正在使用 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);
        }
    }
    
    3 回复  |  直到 9 年前
        1
  •  1
  •   David Soussan    9 年前

    您看到的是“基于时间的寻呼”时的响应 https://developers.facebook.com/docs/graph-api/using-graph-api/v2.2#paging

        2
  •  1
  •   kylehyde215    9 年前

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

        3
  •  0
  •   Paweł Tomkiel    9 年前

    很可能是窃听器 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

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