selecting from customer_client returns flat heirarchy (php)

104 views
Skip to first unread message

Grant Horwood

unread,
Feb 28, 2023, 4:44:07 PM2/28/23
to Google Ads API and AdWords API Forum
i have a laravel project which uses the google ads api, and i am attempting to make a select from the `customer_client` table but getting unanticipated results.

of the four initial customers that i get in `runExample` by calling `getAccessibleCustomers`, one should be a manager account with several customers 'under' it.

however, when i run this code, i only see the top level customers. the heirarchy is flat.

my code is below and is a mostly copy-paste job from the example google code at:
https://developers.google.com/google-ads/api/docs/account-management/get-account-hierarchy

the main differences, i believe, is in my construction of the oauthToken and googleAdsClient.

any assistance or insight is greatly appreciated!

<?php
namespace App\Libs;

require_once(__DIR__."/../../vendor/autoload.php");

use DB;
use Exception;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

use Google\Ads\GoogleAds\Examples\Utils\ArgumentNames;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentParser;
use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder;
use Google\Ads\GoogleAds\Lib\V12\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V12\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V12\GoogleAdsException;
use Google\Ads\GoogleAds\Lib\V12\GoogleAdsServerStreamDecorator;
use Google\Ads\GoogleAds\V12\Errors\GoogleAdsError;
use Google\Ads\GoogleAds\V12\Resources\CustomerClient;
use Google\Ads\GoogleAds\V12\Services\CustomerServiceClient;
use Google\Ads\GoogleAds\V12\Services\GoogleAdsRow;
use Google\ApiCore\ApiException;

class GoogleAds {

    /**
     * The results from the example code
     */
    private static $rootCustomerClients = [];

    /**
     * Main
     */
    public function getGoogleAdsCustomersTest(bool $resetCache = false)
    {
        /**
         * Hard codes of values of OAuth2TokenBuilder and GoogleAdsClientBuilder.
         * Normally stored in db.
         */
        $clientId = "<client id>.apps.googleusercontent.com";
        $clientSecret = "<client secret>";
        $refreshToken = "<refresh token>";
        $developerToken = "<developer token>";

        /**
         * Create oauth with values, not fromFile()
         */
        $oauthToken = (new \Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder())
            ->withClientId($clientId)
            ->withClientSecret($clientSecret)
            ->withRefreshToken($refreshToken)
            ->build();


        /**
         * Create googleAdsClient with values, not fromFile()
         */
        $googleAdsClient = (new \Google\Ads\GoogleAds\Lib\V12\GoogleAdsClientBuilder())
            ->withOAuth2Credential($oauthToken)
            ->withDeveloperToken($this->developerToken)
            ->build();

        self::runExample($googleAdsClient);
    }

    /**
     * runExample
     */
    public static function runExample(GoogleAdsClient $googleAdsClient) {

        $rootCustomerIds = [];
        $rootCustomerIds = self::getAccessibleCustomers($googleAdsClient);

        $allHierarchies = [];
        $accountsWithNoInfo = [];

        // Constructs a map of account hierarchies.
        foreach ($rootCustomerIds as $rootCustomerId) {
            $customerClientToHierarchy = self::createCustomerClientToHierarchy($rootCustomerId);
            if (is_null($customerClientToHierarchy)) {
                $accountsWithNoInfo[] = $rootCustomerId;
            } else {
                $allHierarchies += $customerClientToHierarchy;
            }
        }

        // Prints the IDs of any accounts that did not produce hierarchy information.
        if (!empty($accountsWithNoInfo)) {
            print
                'Unable to retrieve information for the following accounts which are likely '
                . 'either test accounts or accounts with setup issues. Please check the logs for '
                . 'details:' . PHP_EOL;
            foreach ($accountsWithNoInfo as $accountId) {
                print $accountId . PHP_EOL;
            }
            print PHP_EOL;
        }

        // Prints the hierarchy information for all accounts for which there is hierarchy info
        // available.
        foreach ($allHierarchies as $rootCustomerId => $customerIdsToChildAccounts) {
            printf(
                "The hierarchy of customer ID %d is printed below:%s",
                $rootCustomerId,
                PHP_EOL
            );
            self::printAccountHierarchy(
                self::$rootCustomerClients[$rootCustomerId],
                $customerIdsToChildAccounts,
                0
            );
            print PHP_EOL;
        }
    }

    /**
     * getAccessibleCustomers
     */
    private static function getAccessibleCustomers(GoogleAdsClient $googleAdsClient): array
    {
        $accessibleCustomerIds = [];
        // Issues a request for listing all customers accessible by this authenticated Google
        // account.
        $customerServiceClient = $googleAdsClient->getCustomerServiceClient();
        $accessibleCustomers = $customerServiceClient->listAccessibleCustomers();

        print 'No manager customer ID is specified. The example will print the hierarchies of'
            . ' all accessible customer IDs:' . PHP_EOL;
        foreach ($accessibleCustomers->getResourceNames() as $customerResourceName) {
            $customer = CustomerServiceClient::parseName($customerResourceName)['customer_id'];
            print $customer . PHP_EOL;
            $accessibleCustomerIds[] = intval($customer);
        }
        print PHP_EOL;

        return $accessibleCustomerIds;
    }

    /**
     * Prints the specified account's hierarchy using recursion.
     *
     * @param CustomerClient $customerClient the customer client whose info will be printed and
     *     its child accounts will be processed if it's a manager
     * @param array $customerIdsToChildAccounts a map from customer IDs to child
     *     accounts
     * @param int $depth the current depth we are printing from in the
     *     account hierarchy
     */
    private static function printAccountHierarchy(
        CustomerClient $customerClient,
        array $customerIdsToChildAccounts,
        int $depth)
    {
        if ($depth === 0) {
            print 'Customer ID (Descriptive Name, Currency Code, Time Zone)' . PHP_EOL;
        }
        $customerId = $customerClient->getId();
        print str_repeat('-', $depth * 2);
        printf(
            " %d ('%s', '%s', '%s')%s",
            $customerId,
            $customerClient->getDescriptiveName(),
            $customerClient->getCurrencyCode(),
            $customerClient->getTimeZone(),
            PHP_EOL
        );

        // Recursively call this function for all child accounts of $customerClient.
        if (array_key_exists($customerId, $customerIdsToChildAccounts)) {
            foreach ($customerIdsToChildAccounts[$customerId] as $childAccount) {
                self::printAccountHierarchy($childAccount, $customerIdsToChildAccounts, $depth + 1);
            }
        }
    }

    /**
     * Create heirarchy
     */
    private static function createCustomerClientToHierarchy(int $rootCustomerId): ?array {
        // Creates a GoogleAdsClient with the specified login customer ID. See
        // https://developers.google.com/google-ads/api/docs/concepts/call-structure#cid for more
        // information.
        // Generate a refreshable OAuth2 credential for authentication.

        /**
         * Hard codes of values of OAuth2TokenBuilder and GoogleAdsClientBuilder.
         * Normally stored in db.
         */
        $clientId = "<client id>.apps.googleusercontent.com";
        $clientSecret = "<client secret>";
        $refreshToken = "<refresh token>";
        $developerToken = "<developer token>";

        /**
         * Create oauth with values, not fromFile()
         */
        $oauthToken = (new \Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder())
            ->withClientId($clientId)
            ->withClientSecret($clientSecret)
            ->withRefreshToken($refreshToken)
            ->build();
        // Construct a Google Ads client configured from a properties file and the
        // OAuth2 credentials above.

        /**
         * Create googleAdsClient with values, not fromFile()
         */
        $googleAdsClient = (new \Google\Ads\GoogleAds\Lib\V12\GoogleAdsClientBuilder())
            ->withOAuth2Credential($oauthToken)
            ->withDeveloperToken($developerToken)
            ->withLoginCustomerId($rootCustomerId)
            ->build();

        // Creates the Google Ads Service client.
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        // Creates a query that retrieves all child accounts of the manager specified in search
        // calls below.
        $query = 'SELECT customer_client.client_customer, customer_client.level,'
            . ' customer_client.manager, customer_client.descriptive_name,'
            . ' customer_client.currency_code, customer_client.time_zone,'
            . ' customer_client.id FROM customer_client WHERE customer_client.level <= 1';

        $rootCustomerClient = null;
        // Adds the root customer ID to the list of IDs to be processed.
        $managerCustomerIdsToSearch = [$rootCustomerId];

        // Performs a breadth-first search algorithm to build an associative array mapping
        // managers to their child accounts ($customerIdsToChildAccounts).
        $customerIdsToChildAccounts = [];

        while (!empty($managerCustomerIdsToSearch)) {
            $customerIdToSearch = array_shift($managerCustomerIdsToSearch);
            // Issues a search request by specifying page size.
            /** @var GoogleAdsServerStreamDecorator $stream */
            try {
                $stream = $googleAdsServiceClient->searchStream( $customerIdToSearch, $query);

                // Iterates over all elements to get all customer clients under the specified customer's
                // hierarchy.
                foreach ($stream->iterateAllElements() as $googleAdsRow) {
                    /** @var GoogleAdsRow $googleAdsRow */
                    $customerClient = $googleAdsRow->getCustomerClient();

                    // Gets the CustomerClient object for the root customer in the tree.
                    if ($customerClient->getId() === $rootCustomerId) {
                        $rootCustomerClient = $customerClient;
                        self::$rootCustomerClients[$rootCustomerId] = $rootCustomerClient;
                    }

                    // The steps below map parent and children accounts. Continue here so that managers
                    // accounts exclude themselves from the list of their children accounts.
                    if ($customerClient->getId() === $customerIdToSearch) {
                        continue;
                    }

                    // For all level-1 (direct child) accounts that are a manager account, the above
                    // query will be run against them to create an associative array of managers to
                    // their child accounts for printing the hierarchy afterwards.
                    $customerIdsToChildAccounts[$customerIdToSearch][] = $customerClient;
                    // Checks if the child account is a manager itself so that it can later be processed
                    // and added to the map if it hasn't been already.
                    if ($customerClient->getManager()) {
                        // A customer can be managed by multiple managers, so to prevent visiting
                        // the same customer multiple times, we need to check if it's already in the
                        // map.
                        $alreadyVisited = array_key_exists(
                            $customerClient->getId(),
                            $customerIdsToChildAccounts
                        );
                        if (!$alreadyVisited && $customerClient->getLevel() === 1) {
                            array_push($managerCustomerIdsToSearch, $customerClient->getId());
                        }
                    }
                }
            } // try
            catch(\Exception $e) {
            }
        } // while

        return is_null($rootCustomerClient) ? null
            : [$rootCustomerClient->getId() => $customerIdsToChildAccounts];
    }

}

Google Ads API Forum Advisor

unread,
Mar 1, 2023, 3:33:51 AM3/1/23
to ghor...@fruitbat.studio, adwor...@googlegroups.com

Hi Grant,
 

Thanks for reaching out to the Google Ads API team. I hope you are doing well today.
 

With regards to your concern, to further help with your issue, could you provide a full screenshot where you can see the expected data that you want retrieve and the complete request and response logs with the request ID and request header generated on your end? If you haven't yet, logging can be enabled by navigating to the Client Libraries > Your client library (ex. Java) > Logging documentation, which you can access from this link, so that we can further check and provide precise guidance accordingly.


You may then send the requested information via the Reply privately to author option. If this option is not available, you may send the details directly to our googleadsa...@google.com alias instead


Regards,

Google Logo Google Ads API Team



ref:_00D1U1174p._5004Q2jHr95:ref
Reply all
Reply to author
Forward
0 new messages