Hi all,
First of all, please note that for some reason, neither 'Quote' nor the 'Code' functionality appears to be working. Also, it seems that no formatting works whatsoever, for some reason, so sorry about the mess.
I'm sure we are missing something extremely simple, but it is now eating up a lot of time, and was wondering if anyone could point us in the right direction.
What we would like to achieve is replacing the FinalURLs of a number of ExpandedTextAds using BatchJobService and SET.
What we did was take a previously functional code which uses the operator ADD and modify that to use SET. However, when doing so, we get the following error:
[0] => Google\AdsApi\AdWords\v201710\cm\RequiredError Object
(
[reason:protected] => REQUIRED
[fieldPath:protected] => operations[0].
operand.id [fieldPathElements:protected] => Array
(
[0] => Google\AdsApi\AdWords\v201710\cm\FieldPathElement Object
(
[field:protected] => operations
[index:protected] => 0
)
[1] => Google\AdsApi\AdWords\v201710\cm\FieldPathElement Object
(
[field:protected] => operand
[index:protected] =>
)
[2] => Google\AdsApi\AdWords\v201710\cm\FieldPathElement Object
(
[field:protected] => id
[index:protected] =>
)
)
[trigger:protected] =>
[errorString:protected] => RequiredError.REQUIRED
[ApiErrorType:protected] => RequiredError
Suffice to say, we are missing an id somewhere. But how and where are we missing it? There are no examples to help us down the path. Our current code is:
function updateTextAdsCriterionOperations($expandText) {
$ExpandedTextAdsOperations = [];
foreach ($expandText as $adText) {
// Create an expanded text ad.
$expandedTextAd = new ExpandedTextAd();
$expandedTextAd->setId( $adText[4] );
$expandedTextAd->setHeadlinePart1( $adText[5] );
$expandedTextAd->setHeadlinePart2( $adText[6] );
$expandedTextAd->setDescription( $adText[7] );
$expandedTextAd->setFinalUrls([ $adText[10] ]);
$expandedTextAd->setPath1( $adText[8] );
$expandedTextAd->setPath2( $adText[9] );
// $expandedTextAd->setStatus( ExpandedTextAd::PAUSED );
// Create ad group ad.
$adGroupAd = new AdGroupAd();
$adGroupAd->setAdGroupId( $adText[2] );
$adGroupAd->setAd( $expandedTextAd );
// Optional: Set additional settings.
$adGroupAd->setStatus( AdGroupAdStatus::PAUSED );
// Create ad group ad operation and add it to the list.
$operation = new AdGroupAdOperation();
$operation->setOperand( $adGroupAd );
$operation->setOperator( Operator::SET );
$ExpandedTextAdsOperations[] = $operation;
// file_put_contents("batchjob.txt", print_r($operation, true), FILE_APPEND);
}
return $ExpandedTextAdsOperations;
}
function UploadSettings(AdWordsServices $adWordsServices, AdWordsSession $session, $expAdTextAds) {
try {
$batchJobService = $adWordsServices->get($session, BatchJobService::class);
// Create a BatchJob.
$addOp = new BatchJobOperation();
$addOp->setOperator(Operator::SET);
$addOp->setOperand(new BatchJob());
$result = $batchJobService->mutate([$addOp]);
$batchJob = $result->getValue()[0];
// Get the upload URL from the new job.
$uploadUrl = $batchJob->getUploadUrl()->getUrl();
printf("Created BatchJob with ID %d, status '%s' and upload URL '%s'.<br>",
$batchJob->getId(), $batchJob->getStatus(), $uploadUrl);
// Use BatchJobs to upload all operations.
$batchJobs = new BatchJobs($session);
// Generate and upload the first set of operations.
$adGroupCriterionOperations = updateTextAdsCriterionOperations($expAdTextAds);
$batchJobUploadStatus = $batchJobs->uploadIncrementalBatchJobOperations(
$adGroupCriterionOperations,
new BatchJobUploadStatus($uploadUrl, $session)
);
printf("Uploaded %d operations for batch job with ID %d.<br>",
count($adGroupCriterionOperations), $batchJob->getId());
$batchJobs->closeIncrementalUpload($batchJobUploadStatus);
// Poll for completion of the batch job using an exponential back off.
} catch (Exception $e) {
echo "<pre>";
print_r($e);
echo "</pre>";
}
$pollAttempts = 0;
$isPending = true;
$wasCancelRequested = false;
$selector = new Selector();
$selector->setFields(
['Id', 'Status', 'DownloadUrl', 'ProcessingErrors', 'ProgressStats']);
$selector->setPredicates([
new Predicate('Id', PredicateOperator::EQUALS, [$batchJob->getId()])
]);
do {
$sleepSeconds = POLL_FREQUENCY_SECONDS * pow(2, $pollAttempts);
printf("Sleeping %d seconds...<br>", $sleepSeconds);
sleep($sleepSeconds);
$batchJob = $batchJobService->get($selector)->getEntries()[0];
printf("Batch job ID %d has status '%s'.<br>", $batchJob->getId(),
$batchJob->getStatus());
$pollAttempts++;
if ($batchJob->getStatus() !== BatchJobStatus::ACTIVE &&
$batchJob->getStatus() !== BatchJobStatus::AWAITING_FILE &&
$batchJob->getStatus() !== BatchJobStatus::CANCELING) {
$isPending = false;
}
// Optional: Cancel the job if it has not completed after polling
// MAX_POLL_ATTEMPTS times.
if ($isPending && !$wasCancelRequested
&& $pollAttempts === MAX_POLL_ATTEMPTS) {
$batchJob->setStatus(BatchJobStatus::CANCELING);
$batchJobSetOperation = new BatchJobOperation();
$batchJobSetOperation->setOperand($batchJob);
$batchJobSetOperation->setOperator(Operator::SET);
// Only request cancellation once per job.
$wasCancelRequested = true;
try {
$batchJob =
$batchJobService->mutate([$batchJobSetOperation])->getValue()[0];
printf("Requested cancellation of batch job with ID %d.<br>",
$batchJob->getId());
// Reset the poll attempt counter to wait for cancellation.
$pollAttempts = 0;
} catch (ApiException $e) {
$errors = $e->getErrors();
if ($errors !== null
&& count($errors) > 0
&& $errors[0] instanceof BatchJobError) {
$batchJobError = $errors[0];
if ($batchJobError->getReason()
=== BatchJobErrorReason::INVALID_STATE_CHANGE) {
printf(
"Attempt to cancel batch job with ID %d was rejected because"
. " the job already completed or was canceled.<br>",
$batchJob->getId()
);
continue;
}
}
throw $e;
} finally {
// Reset the poll attempt counter to wait for cancellation.
$pollAttempts = 0;
}
}
} while ($isPending && $pollAttempts <= MAX_POLL_ATTEMPTS);
if ($isPending) {
throw new UnexpectedValueException(
sprintf('Job is still pending state after polling %d times.',
MAX_POLL_ATTEMPTS));
}
if ($batchJob->getProcessingErrors() !== null) {
$i = 0;
foreach ($batchJob->getProcessingErrors() as $processingError) {
printf(
" Processing error [%d]: errorType=%s, trigger=%s, errorString=%s,"
. " fieldPath=%s, reason=%s<br>",
$i++,
$processingError->getApiErrorType(),
$processingError->getTrigger(),
$processingError->getErrorString(),
$processingError->getFieldPath(),
$processingError->getReason()
);
}
} else {
printf("No processing errors found.<br>");
}
if ($batchJob->getDownloadUrl() !== null
&& $batchJob->getDownloadUrl()->getUrl() !== null) {
$mutateResults = $batchJobs->downloadBatchJobResults(
$batchJob->getDownloadUrl()->getUrl());
printf("Downloaded results from %s:<br>",
$batchJob->getDownloadUrl()->getUrl());
if (count($mutateResults) === 0) {
printf(" No results available.<br>");
} else {
foreach ($mutateResults as $mutateResult) {
$outcome = $mutateResult->getErrorList() === null ? 'SUCCESS' : 'FAILURE';
printf(" Operation [%d] - %s<br>", $mutateResult->getIndex(),
$outcome);
if ( $outcome == "FAILURE" ){
print_r( $mutateResult->getErrorList() );
}
}
}
} else {
printf("No results available for download.<br>");
}
}
So we have "UploadSettings" called and we get the error somewhere right after or at " $result = $batchJobService->mutate([$addOp]);". The thing is, the code never even seems to get around to adding the expanded text ad parameters. Can anyone help out with what we're exactly missing?