1 | <?php |
2 | |
3 | namespace App\Component\Facebook; |
4 | |
5 | use DateTime; |
6 | use App\Entity\FacebookAccount; |
7 | use FacebookAds\Object\Fields\LocationFields; |
8 | use FacebookAds\Object\Values\AdsInsightsLevelValues; |
9 | use Symfony\Component\HttpFoundation\Response; |
10 | use Symfony\Contracts\HttpClient\HttpClientInterface; |
11 | |
12 | class FacebookClient |
13 | { |
14 | |
15 | private const API_URL = 'https://graph.facebook.com'; |
16 | private const API_VERSION = 'v21.0'; |
17 | |
18 | private HttpClientInterface $httpClient; |
19 | |
20 | private int $limit = 500; |
21 | |
22 | public function __construct(HttpClientInterface $httpClient) |
23 | { |
24 | $this->httpClient = $httpClient; |
25 | } |
26 | |
27 | public function setLimit(int $limit): self |
28 | { |
29 | $this->limit = $limit; |
30 | |
31 | return $this; |
32 | } |
33 | |
34 | public function getLimit(): int |
35 | { |
36 | return $this->limit; |
37 | } |
38 | |
39 | |
40 | * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface |
41 | * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface |
42 | * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface |
43 | * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface |
44 | * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface |
45 | */ |
46 | public function insights( |
47 | FacebookAccount $account, |
48 | DateTime $day, |
49 | array $fields, |
50 | string $level = AdsInsightsLevelValues::CAMPAIGN |
51 | ): array { |
52 | $response = $this->httpClient->request( |
53 | 'GET', |
54 | self::API_URL . '/' . self::API_VERSION . "/act_{$account->getFacebookAccount()}/insights", [ |
55 | 'query' => [ |
56 | 'time_range' => [ |
57 | 'since' => $day->format('Y-m-d'), |
58 | 'until' => $day->format('Y-m-d'), |
59 | ], |
60 | 'fields' => implode(',', $fields), |
61 | 'level' => $level, |
62 | 'limit' => $this->limit, |
63 | 'access_token' => $account->getAccessToken(), |
64 | ] |
65 | ]); |
66 | |
67 | if ($response->getStatusCode() !== Response::HTTP_OK) { |
68 | throw new \RuntimeException("Code: {$response->getStatusCode()}. Message: {$response->getContent()}"); |
69 | } |
70 | |
71 | return $response->toArray()['data']; |
72 | } |
73 | |
74 | |
75 | * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface |
76 | * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface |
77 | * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface |
78 | * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface |
79 | * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface |
80 | */ |
81 | public function insightCampaigns( |
82 | FacebookAccount $account, |
83 | string $campaignId, |
84 | DateTime $day, |
85 | array $fields, |
86 | array $breakdowns = [LocationFields::COUNTRY], |
87 | string $level = AdsInsightsLevelValues::ADSET |
88 | ): array { |
89 | $request = function ($after = '') use ($account, $campaignId, $day, $fields, $breakdowns, $level) { |
90 | $query = [ |
91 | 'time_range' => [ |
92 | 'since' => $day->format('Y-m-d'), |
93 | 'until' => $day->format('Y-m-d'), |
94 | ], |
95 | 'fields' => implode(',', $fields), |
96 | 'breakdowns' => implode(',', $breakdowns), |
97 | 'level' => $level, |
98 | 'limit' => $this->limit, |
99 | 'access_token' => $account->getAccessToken(), |
100 | ]; |
101 | |
102 | if (!empty($after)) { |
103 | $query = array_merge($query, [ |
104 | 'after' => $after, |
105 | ]); |
106 | } |
107 | |
108 | return $this->httpClient->request( |
109 | 'GET', |
110 | self::API_URL . '/' . self::API_VERSION . "/{$campaignId}/insights", [ |
111 | 'query' => $query |
112 | ]); |
113 | }; |
114 | |
115 | $items = []; |
116 | |
117 | $after = ''; |
118 | |
119 | while ($after !== null) { |
120 | $response = $request($after); |
121 | |
122 | if ($response->getStatusCode() !== Response::HTTP_OK) { |
123 | throw new \RuntimeException("Code: {$response->getStatusCode()}. Message: {$response->getContent()}"); |
124 | } |
125 | |
126 | if (isset($data['paging']['next'])) { |
127 | $after = $data['paging']['cursors']['after']; |
128 | } else { |
129 | $after = null; |
130 | } |
131 | |
132 | $items = [...$items, ...$response->toArray()['data']]; |
133 | } |
134 | |
135 | return $items; |
136 | } |
137 | } |