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-hierarchythe 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];
}
}