Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
23.48% |
31 / 132 |
|
36.36% |
4 / 11 |
CRAP | |
0.00% |
0 / 1 |
GoogleAds | |
22.90% |
30 / 131 |
|
36.36% |
4 / 11 |
387.31 | |
0.00% |
0 / 1 |
getGoogleAdsCustomers | |
22.73% |
10 / 44 |
|
0.00% |
0 / 1 |
29.61 | |||
getGoogleAdsCustomerFromCampaign | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getGoogleAdsCustomersDetails | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
getGoogleAdsCampaigns | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
getGoogleAdsConversionActions | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
6 | |||
getAdsClient | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
6 | |||
getGoogleAdsConversionActionTypes | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getGoogleAdsConversionActionCategories | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getGoogleAdsConversionActionStatuses | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
submitConversionToGoogle | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
parseConfigJson | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | namespace App\Libs; |
3 | |
4 | // google api package does not use namespaces, so this is necessary |
5 | require_once(__DIR__."/../../vendor/autoload.php"); |
6 | |
7 | use DB; |
8 | use Exception; |
9 | use Carbon\Carbon; |
10 | use Illuminate\Http\Request; |
11 | use Illuminate\Support\Facades\Storage; |
12 | use Illuminate\Support\Str; |
13 | |
14 | /** |
15 | * Models |
16 | */ |
17 | use App\Models\User; |
18 | use App\Models\GoogleAdsCustomer; |
19 | use App\Models\GoogleAdsCampaign; |
20 | |
21 | class GoogleAds { |
22 | |
23 | /** |
24 | * File containing the google 'config.json' |
25 | */ |
26 | private String $configJsonFile = "google-test.json"; |
27 | |
28 | /** |
29 | * Hardcoded developer token from google for accessing google ads |
30 | */ |
31 | private String $developerToken = "RTlW6it4bYRHt2n9sLRNWg"; |
32 | |
33 | /** |
34 | * Gets array of all customers for session user's google ads account |
35 | * |
36 | * @return Array |
37 | */ |
38 | public function getGoogleAdsCustomers():array |
39 | { |
40 | $max_cache_in_seconds = 86400; |
41 | //$max_cache_in_seconds = 1; |
42 | |
43 | /** |
44 | * Return cached customer_ids if they are less than max_cache_in_seconds old |
45 | */ |
46 | |
47 | // select cached customer_ids |
48 | $gacs = GoogleAdsCustomer::whereMine()->get()->toArray(); |
49 | |
50 | // if we have cached customer_ids and all of them are less than max_cache_in_seconds, return those |
51 | if ($gacs) { |
52 | $cacheChecks = array_map(fn ($gac) => (bool)(time() - date_create_from_format("Y-m-d\TH:i:s", substr($gac['created_at'], 0, 19))->getTimestamp() < $max_cache_in_seconds), $gacs); |
53 | if (in_array(true, $cacheChecks)) { |
54 | return array_map(fn ($gac) => [ |
55 | 'customer_id' => $gac['customer_id'], |
56 | 'customer_descriptive_name' => $gac['customer_descriptive_name'], |
57 | 'customer_accessible' => (bool)$gac['customer_accessible'], |
58 | 'customer_test_account' => (bool)$gac['customer_test_account'], |
59 | ], $gacs); |
60 | } |
61 | } |
62 | |
63 | /** |
64 | * Get api client for ads |
65 | */ |
66 | $googleAdsClient = $this->getAdsClient(); |
67 | |
68 | /** |
69 | * All accessible customers |
70 | */ |
71 | $customerServiceClient = $googleAdsClient->getCustomerServiceClient(); |
72 | $accessibleCustomers = $customerServiceClient->listAccessibleCustomers(); |
73 | |
74 | /** |
75 | * Delete cached customer ids |
76 | */ |
77 | $gac = GoogleAdsCustomer::whereMine()->delete(); |
78 | |
79 | /** |
80 | * Get details of each customer and cache |
81 | */ |
82 | $customers = []; |
83 | foreach ($accessibleCustomers->getResourceNames() as $resourceName) { |
84 | $customerId = preg_replace('/customers\//', '', $resourceName); |
85 | $userId = auth()->guard('api')->user()->id; |
86 | $customerDescriptiveName = null; |
87 | $customerTestAccount = false; |
88 | |
89 | /** |
90 | * Try to get the descriptive name |
91 | */ |
92 | try { |
93 | $customer = $this->getGoogleAdsCustomersDetails($customerId); |
94 | $customerDescriptiveName = $customer['customer_descriptive_name']; |
95 | $customerTestAccount = $customer['customer_test_account']; |
96 | $customerAccessible = true; |
97 | } catch (\Exception $e) { |
98 | $customerAccessible = false; |
99 | } |
100 | |
101 | /** |
102 | * Add to the return array |
103 | */ |
104 | $customers[] = [ |
105 | 'customer_id' => $customerId, |
106 | 'customer_descriptive_name' => $customerDescriptiveName, |
107 | 'customer_accessible' => $customerAccessible, |
108 | 'customer_test_account' => $customerTestAccount, |
109 | ]; |
110 | |
111 | /** |
112 | * Cache customer |
113 | */ |
114 | $gac = new GoogleAdsCustomer(); |
115 | $gac->user_id = $userId; |
116 | $gac->customer_id = (String)$customerId; |
117 | $gac->customer_descriptive_name = $customerDescriptiveName; |
118 | $gac->customer_test_account = $customerTestAccount; |
119 | $gac->customer_accessible = $customerAccessible; |
120 | $gac->save(); |
121 | |
122 | /** |
123 | * Cache campaigns for customer |
124 | */ |
125 | try { |
126 | $campaigns = $this->getGoogleAdsCampaigns($customerId); |
127 | GoogleAdsCampaign::where('customer_id', '=', $customerId)->delete(); |
128 | foreach ($campaigns as $c) { |
129 | $gac = new GoogleAdsCampaign(); |
130 | $gac->customer_id = (String)$customerId; |
131 | $gac->campaign_id = (String)$c['id']; |
132 | $gac->campaign_name = (String)$c['name']; |
133 | $gac->save(); |
134 | } |
135 | } catch (\Exception $e) { |
136 | } |
137 | } |
138 | |
139 | return $customers; |
140 | } // getGoogleAdsCustomers |
141 | |
142 | |
143 | public function getGoogleAdsCustomerFromCampaign(String $campaignId):String |
144 | { |
145 | $gcus = GoogleAdsCampaign::where('campaign_id', '=', $campaignId)->pluck('customer_id')->first(); |
146 | return strval($gcus); |
147 | } |
148 | |
149 | |
150 | /** |
151 | * Returns details on a given customer identified by $customerId |
152 | * |
153 | * @param String $customerId |
154 | * @return Array |
155 | */ |
156 | public function getGoogleAdsCustomersDetails(String $customerId):array |
157 | { |
158 | /** |
159 | * Get client for ads |
160 | */ |
161 | $googleAdsClient = $this->getAdsClient(); |
162 | $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient(); |
163 | |
164 | /** |
165 | * Query campaigns |
166 | */ |
167 | $query = 'select customer.id, customer.descriptive_name, customer.test_account from customer'; |
168 | |
169 | $stream =$googleAdsServiceClient->search($customerId, $query); |
170 | |
171 | /** |
172 | * Create returnable array |
173 | */ |
174 | $customer = []; |
175 | foreach ($stream->iterateAllElements() as $googleAdsRow) { |
176 | $customer['customer_id'] = $googleAdsRow->getCustomer()->getId(); |
177 | $customer['customer_descriptive_name'] = $googleAdsRow->getCustomer()->getDescriptiveName(); |
178 | $customer['customer_test_account'] = (bool)$googleAdsRow->getCustomer()->getTestAccount(); |
179 | } |
180 | |
181 | return $customer; |
182 | } // getGoogleAdsCustomersDetails |
183 | |
184 | |
185 | /** |
186 | * Get all campaigns for a customer |
187 | * |
188 | * @param String $customerId The id of the customer from getGoogleAdsCustomerIds() |
189 | * @return Array |
190 | */ |
191 | public function getGoogleAdsCampaigns(String $customerId):array |
192 | { |
193 | /** |
194 | * Get client for ads |
195 | */ |
196 | $googleAdsClient = $this->getAdsClient(); |
197 | $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient(); |
198 | |
199 | /** |
200 | * Query campaigns |
201 | */ |
202 | $query = 'SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id'; |
203 | $stream =$googleAdsServiceClient->search($customerId, $query); |
204 | |
205 | /** |
206 | * Create returnable array |
207 | */ |
208 | $campaigns = []; |
209 | foreach ($stream->iterateAllElements() as $googleAdsRow) { |
210 | $campaigns[] = [ |
211 | 'id' => $googleAdsRow->getCampaign()->getId(), |
212 | 'name' => $googleAdsRow->getCampaign()->getName(), |
213 | ]; |
214 | } |
215 | |
216 | return $campaigns; |
217 | } // getGoogleAdsCampaigns |
218 | |
219 | |
220 | /** |
221 | * Get all conversion actions for a customer |
222 | * |
223 | * @param String $customerId The id of the customer from getGoogleAdsCustomerIds() |
224 | * @return Array |
225 | */ |
226 | public function getGoogleAdsConversionActions(String $customerId):array |
227 | { |
228 | /** |
229 | * Get client for ads |
230 | */ |
231 | $googleAdsClient = $this->getAdsClient(); |
232 | $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient(); |
233 | |
234 | $typesLookup = $this->getGoogleAdsConversionActionTypes(); |
235 | $categoriesLookup = $this->getGoogleAdsConversionActionCategories(); |
236 | $statusesLookup = $this->getGoogleAdsConversionActionStatuses(); |
237 | |
238 | /** |
239 | * Query conversion actions |
240 | */ |
241 | $query = 'SELECT conversion_action.id, |
242 | conversion_action.name, |
243 | conversion_action.category, |
244 | conversion_action.type, |
245 | conversion_action.status |
246 | FROM conversion_action'; |
247 | $stream =$googleAdsServiceClient->search($customerId, $query); |
248 | |
249 | /** |
250 | * Create returnable array |
251 | */ |
252 | $conversionActions = []; |
253 | foreach ($stream->iterateAllElements() as $googleAdsRow) { |
254 | |
255 | // lookup up category name |
256 | $categoryId = (int)$googleAdsRow->getConversionAction()->getCategory(); |
257 | $categoryName = @array_values(array_filter($categoriesLookup, fn ($n) => $n->id == $categoryId))[0]->name; |
258 | |
259 | // lookup up type name |
260 | $typeId = (int)$googleAdsRow->getConversionAction()->getType(); |
261 | $typeName = @array_values(array_filter($typesLookup, fn ($n) => $n->id == $typeId))[0]->name; |
262 | |
263 | // lookup up status name |
264 | $statusId = (int)$googleAdsRow->getConversionAction()->getStatus(); |
265 | $statusName = @array_values(array_filter($statusesLookup, fn ($n) => $n->id == $statusId))[0]->name; |
266 | |
267 | $conversionActions[] = [ |
268 | 'id' => $googleAdsRow->getConversionAction()->getId(), |
269 | 'name' => $googleAdsRow->getConversionAction()->getName(), |
270 | 'category_id' => $categoryId, |
271 | 'category_name' => $categoryName, |
272 | 'type_id' => $typeId, |
273 | 'type_name' => $typeName, |
274 | 'status_id' => $statusId, |
275 | 'status_name' => $statusName, |
276 | ]; |
277 | } |
278 | |
279 | return $conversionActions; |
280 | } // getGoogleAdsConversionActions |
281 | |
282 | |
283 | /** |
284 | * Creates a base client for the google 'ads' sdk. |
285 | * This is different than the other google api client, for 'reasons'. |
286 | * You can only create this client if you have a session user that has |
287 | * a google access token in the db. |
288 | * |
289 | * @return \Google\Ads\GoogleAds\Lib\V10\GoogleAdsClient |
290 | * @see $this->developerToken |
291 | */ |
292 | public function getAdsClient(?String $customerId = null):\Google\Ads\GoogleAds\Lib\V10\GoogleAdsClient |
293 | { |
294 | /** |
295 | * Get access token for session user |
296 | */ |
297 | $user = User::whereMe()->first(); |
298 | $accessTokenJson = stripslashes($user->google_access_token_json); |
299 | $accessTokenObj = json_decode($accessTokenJson); |
300 | |
301 | /** |
302 | * Get config values, ie client_id and client_secret |
303 | */ |
304 | $config = $this->parseConfigJson(); |
305 | $clientId = $config->client_id; |
306 | $clientSecret = $config->client_secret; |
307 | |
308 | /** |
309 | * Create the oauth token that the 'ads' sdk needs |
310 | */ |
311 | $oauthToken = (new \Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder()) |
312 | ->withClientId($clientId) |
313 | ->withClientSecret($clientSecret) |
314 | ->withRefreshToken($accessTokenObj->refresh_token) |
315 | ->build(); |
316 | |
317 | |
318 | /** |
319 | * Create the client |
320 | * NB: developerToken |
321 | */ |
322 | $googleAdsClient = (new \Google\Ads\GoogleAds\Lib\V10\GoogleAdsClientBuilder()) |
323 | ->withOAuth2Credential($oauthToken) |
324 | ->withDeveloperToken($this->developerToken); |
325 | |
326 | if ($customerId) { |
327 | $googleAdsClient = $googleAdsClient->withLoginCustomerId((Int)$customerId); |
328 | } |
329 | |
330 | $googleAdsClient = $googleAdsClient->build(); |
331 | |
332 | return $googleAdsClient; |
333 | } // getAdsClient |
334 | |
335 | |
336 | /** |
337 | * Get all Conversion Action Types for google ads, ie for menu or lookup |
338 | * |
339 | * @return Array |
340 | */ |
341 | public function getGoogleAdsConversionActionTypes():array |
342 | { |
343 | /** |
344 | * Get up to fifty conversion action typess form the googleads sdk |
345 | */ |
346 | $returnArray = []; |
347 | foreach (range(1, 50) as $id) { |
348 | try { |
349 | $returnArray[] = (object)[ |
350 | 'id' => $id, |
351 | 'name' => \Google\Ads\GoogleAds\V10\Enums\ConversionActionTypeEnum\ConversionActionType::name($id), |
352 | ]; |
353 | } catch (\Exception $e) { |
354 | } |
355 | } |
356 | return $returnArray; |
357 | } |
358 | |
359 | |
360 | /** |
361 | * Get all Conversion Action Categories for google ads, ie for menu or lookup |
362 | * |
363 | * @return Array |
364 | */ |
365 | public function getGoogleAdsConversionActionCategories():array |
366 | { |
367 | /** |
368 | * Get up to fifty conversion action categories form the googleads sdk |
369 | */ |
370 | $returnArray = []; |
371 | foreach (range(1, 50) as $id) { |
372 | try { |
373 | $returnArray[] = (object)[ |
374 | 'id' => $id, |
375 | 'name' => \Google\Ads\GoogleAds\V10\Enums\ConversionActionCategoryEnum\ConversionActionCategory::name($id) |
376 | ]; |
377 | } catch (\Exception $e) { |
378 | } |
379 | } |
380 | return $returnArray; |
381 | } // getGoogleAdsConversionActionCategories |
382 | |
383 | |
384 | /** |
385 | * Get all Conversion Action Statuses for google ads, ie for menu or lookup |
386 | * |
387 | * @return Array |
388 | */ |
389 | public function getGoogleAdsConversionActionStatuses():array |
390 | { |
391 | /** |
392 | * Get up to fifty conversion action statuses form the googleads sdk |
393 | */ |
394 | $returnArray = []; |
395 | foreach (range(1, 50) as $id) { |
396 | try { |
397 | $returnArray[] = (object)[ |
398 | 'id' => $id, |
399 | 'name' => \Google\Ads\GoogleAds\V10\Enums\ConversionActionStatusEnum\ConversionActionStatus::name($id), |
400 | ]; |
401 | } catch (\Exception $e) { |
402 | } |
403 | } |
404 | return $returnArray; |
405 | } |
406 | |
407 | |
408 | public function submitConversionToGoogle(String $customerId, $clickConversion) |
409 | { |
410 | $googleAdsClient = $this->getAdsClient($customerId); |
411 | $conversionUploadServiceClient = $googleAdsClient->getConversionUploadServiceClient(); |
412 | |
413 | $response = $conversionUploadServiceClient->uploadClickConversions( |
414 | $customerId, |
415 | [$clickConversion], |
416 | true |
417 | ); |
418 | |
419 | if ($response->hasPartialFailureError()) { |
420 | $errorMessage = $response->getPartialFailureError()->getMessage(); |
421 | throw new \Exception($errorMessage); |
422 | } |
423 | |
424 | return $response->getResults()[0]; |
425 | } |
426 | |
427 | |
428 | /** |
429 | * Parse the 'config' file for google access into an object |
430 | * |
431 | * @return object |
432 | * @see $this->configJsonFile |
433 | */ |
434 | private function parseConfigJson():object |
435 | { |
436 | $configJson = base_path().'/'.$this->configJsonFile; |
437 | return json_decode(file_get_contents($configJson))->web; |
438 | } // parseConfigJson |
439 | } |