Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
23.48% covered (danger)
23.48%
31 / 132
36.36% covered (danger)
36.36%
4 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
GoogleAds
22.90% covered (danger)
22.90%
30 / 131
36.36% covered (danger)
36.36%
4 / 11
387.31
0.00% covered (danger)
0.00%
0 / 1
 getGoogleAdsCustomers
22.73% covered (danger)
22.73%
10 / 44
0.00% covered (danger)
0.00%
0 / 1
29.61
 getGoogleAdsCustomerFromCampaign
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getGoogleAdsCustomersDetails
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 getGoogleAdsCampaigns
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 getGoogleAdsConversionActions
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
6
 getAdsClient
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 getGoogleAdsConversionActionTypes
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getGoogleAdsConversionActionCategories
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getGoogleAdsConversionActionStatuses
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 submitConversionToGoogle
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 parseConfigJson
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2namespace App\Libs;
3
4// google api package does not use namespaces, so this is necessary
5require_once(__DIR__."/../../vendor/autoload.php");
6
7use DB;
8use Exception;
9use Carbon\Carbon;
10use Illuminate\Http\Request;
11use Illuminate\Support\Facades\Storage;
12use Illuminate\Support\Str;
13
14/**
15 * Models
16 */
17use App\Models\User;
18use App\Models\GoogleAdsCustomer;
19use App\Models\GoogleAdsCampaign;
20
21class 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}