· 6 years ago · Nov 05, 2019, 12:12 PM
1<?php
2
3namespace App\Model\AI\Google\Ads\Tasks;
4
5use App\Model\AI\Google\Ads\Constants;
6use App\Model\GoogleAddress;
7
8
9use Google\Ads\GoogleAds\V2\Common\KeywordInfo;
10use Google\Ads\GoogleAds\V2\Enums\KeywordMatchTypeEnum\KeywordMatchType;
11use Google\Ads\GoogleAds\V2\Resources\AdGroupCriterion;
12use Google\Ads\GoogleAds\V2\Services\AdGroupCriterionOperation;
13use Google\Protobuf\StringValue;
14
15
16use GetOpt\GetOpt;
17use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder;
18use Google\Ads\GoogleAds\Lib\V2\GoogleAdsClient;
19use Google\Ads\GoogleAds\Lib\V2\GoogleAdsClientBuilder;
20use Google\Ads\GoogleAds\Lib\V2\GoogleAdsException;
21use Google\Ads\GoogleAds\Util\V2\ResourceNames;
22use Google\Ads\GoogleAds\V2\Common\PolicyViolationKey;
23use Google\Ads\GoogleAds\V2\Enums\AdGroupCriterionStatusEnum\AdGroupCriterionStatus;
24use Google\Ads\GoogleAds\V2\Errors\GoogleAdsError;
25use Google\Ads\GoogleAds\V2\Services\AdGroupCriterionServiceClient;
26use Google\ApiCore\ApiException;
27
28use Google\Ads\GoogleAds\V2\Enums\KeywordPlanNetworkEnum\KeywordPlanNetwork;
29use Google\Ads\GoogleAds\V2\Services\GenerateKeywordIdeaResult;
30use Google\Ads\GoogleAds\V2\Services\KeywordAndUrlSeed;
31use Google\Ads\GoogleAds\V2\Services\KeywordSeed;
32use Google\Ads\GoogleAds\V2\Services\UrlSeed;
33
34class AddKeywords extends Type
35{
36 private $langCode = 1000; //1000-en, 1033-sk
37 private $title = [];
38 private $locations = [];
39
40 private $excluded_keywords;
41
42 public function execute()
43 {
44 ini_set('max_execution_time', Constants\Other::MAX_EXECUTION_TIME);
45 $this->excluded_keywords = Constants\ExcludedKeywords::getExcludedKeywords();
46
47 $googleAdsClient = $this->api->getClient();
48
49 $job = $this->api->database->table(Constants\Tables::JOBS)->where('id', $this->log->job_id)->fetch();
50
51 if (!empty($job->lang))
52 $this->langCode = Constants\Google::langCode($job->lang);
53
54 $getLocations = $this->api->database->table(Constants\Tables::JOBS_LOCATIONS)
55 ->where('valid', 1)->where('deleted', 0)->where('closed', 0)->where('job_offer_id', $this->log->job_id)
56 ->fetchPairs("city", "country");
57
58 foreach ($getLocations as $key => $country) {
59
60 if (!empty($key)) {
61 $address = new GoogleAddress($key, 'en');
62 $address->setDatabase($this->api->database);
63 $location_id = $address->getGoogleLocationId();
64 }
65
66 if (empty($location_id)) {
67 $address = new GoogleAddress($country, 'en');
68 $address->setDatabase($this->api->database);
69 $location_id = $address->getGoogleLocationId();
70 $this->locations[] = $location_id;
71 } else {
72 $this->locations[] = $location_id;
73 }
74 }
75// dump($this->locations);
76
77 $this->title[] = $job->name;
78 $this->title[] = trim(strip_tags(nl2br($job->description)));
79
80 $keywords = array_unique($this->title);
81 $locationIds = array_unique($this->locations);
82 $pageUrl = "https://jobangels.com/job/" . $job->key . "/" . $job->url_title;
83
84 $customerId = Constants\Account::CUSTOMER_ID;
85
86 $keywordPlanIdeaServiceClient = $googleAdsClient->getKeywordPlanIdeaServiceClient();
87
88 // Make sure that keywords and/or page URL were specified. The request must have exactly one
89 // of urlSeed, keywordSeed, or keywordAndUrlSeed set.
90 if (empty($keywords) && is_null($pageUrl)) {
91 throw new \InvalidArgumentException(
92 'At least one of keywords or page URL is required, but neither was specified.'
93 );
94 }
95
96 // Specify the optional arguments of the request as a keywordSeed, urlSeed,
97 // or keywordAndUrlSeed.
98 $requestOptionalArgs = [];
99
100 if (empty($keywords)) {
101 // Only page URL was specified, so use a UrlSeed.
102 $requestOptionalArgs['urlSeed'] =
103 new UrlSeed(['url' => new StringValue(['value' => $pageUrl])]);
104 } elseif (is_null($pageUrl)) {
105 // Only keywords were specified, so use a KeywordSeed.
106 $requestOptionalArgs['keywordSeed'] = new KeywordSeed([
107 'keywords' => array_map(function ($keyword) {
108 return new StringValue(['value' => $keyword]);
109 }, $keywords)
110 ]);
111
112 } else {
113 // Both page URL and keywords were specified, so use a KeywordAndUrlSeed.
114 $requestOptionalArgs['keywordAndUrlSeed'] = new KeywordAndUrlSeed([
115 'url' => new StringValue(['value' => $pageUrl]),
116 'keywords' => array_map(function ($keyword) {
117 return new StringValue(['value' => $keyword]);
118 }, $keywords)
119 ]);
120
121 }
122// dump($keywords);
123 // Create a list of geo target constants based on the resource name of specified location
124 // IDs.
125 $geoTargetConstants = array_map(function ($locationId) {
126 return new StringValue(
127 ['value' => ResourceNames::forGeoTargetConstant($locationId)]
128 );
129 }, $locationIds);
130
131 // Generate keyword ideas based on the specified parameters.
132 $response = $keywordPlanIdeaServiceClient->generateKeywordIdeas(
133 $customerId,
134 // Set the language resource using the provided language ID.
135 new StringValue(['value' => ResourceNames::forLanguageConstant($this->langCode)]),
136 // Add the resource name of each location ID to the request.
137 $geoTargetConstants,
138 // Set the network. To restrict to only Google Search, change the parameter below to
139 // KeywordPlanNetwork::GOOGLE_SEARCH.
140 KeywordPlanNetwork::GOOGLE_SEARCH,
141 $requestOptionalArgs
142 );
143 $resultKeywords = [];
144 // Iterate over the results and print its detail.
145 foreach ($response->getResults() as $result) {
146 /** @var GenerateKeywordIdeaResult $result */
147 // Note that the competition printed below is enum value.
148 // For example, a value of 2 will be returned when the competition is 'LOW'.s
149 // A mapping of enum names to values can be found at KeywordPlanCompetitionLevel.php.
150 if (!is_null($result->getKeywordIdeaMetrics())) {
151 if ($result->getKeywordIdeaMetrics()->getAvgMonthlySearches()->getValue() >= 50) {
152 $resultKeywords[] = $result->getText()->getValue();
153 //$resultKeywords[$result->getText()->getValue()] = (int)$result->getKeywordIdeaMetrics()->getAvgMonthlySearches()->getValue();
154 }
155 }
156//
157// printf(
158// "Keyword idea text '%s' has %d average monthly searches and competition as %d.%s<br>",
159// $result->getText()->getValue(),
160// is_null($result->getKeywordIdeaMetrics()) ?
161// 0 : $result->getKeywordIdeaMetrics()->getAvgMonthlySearches()->getValue(),
162// is_null($result->getKeywordIdeaMetrics()) ?
163// 0 : $result->getKeywordIdeaMetrics()->getCompetition(),
164// PHP_EOL
165// );
166
167 }
168
169 if (count($resultKeywords) == 0) {
170 $this->log->update([
171 'done' => 1,
172 'done_time' => new \Datetime(),
173 'comment' => "Added keywords: 0"
174 ]);
175 // exit; // nemoze byt exit kvoli tomu, ze odstavi dalsie nasledujuce tasky
176 return;
177 }
178
179 ///
180 /// insert keywords
181
182
183 ///
184// if (count($resultKeywords) == 0) {
185// if (!is_null($result->getKeywordIdeaMetrics())) {
186// if ($result->getKeywordIdeaMetrics()->getAvgMonthlySearches()->getValue() >= 0) {
187// $resultKeywords[$result->getText()->getValue()] = (int)$result->getKeywordIdeaMetrics()->getAvgMonthlySearches()->getValue();
188// }
189// }
190// }
191
192
193 arsort($resultKeywords);
194 $tmpArray = [];
195 for ($i = 0; $i < count($resultKeywords); $i++) {
196 $approved = 1;
197 $resultKeywords[$i] = str_replace(['/', '&', '%'], ' ', $resultKeywords[$i]);
198 $resultKeywords[$i] = str_replace(array('(', ')', '|', '*', '–', '-', ','), '', $resultKeywords[$i]);
199 $resultKeywords[$i] = trim($resultKeywords[$i]);
200 if (strlen($resultKeywords[$i]) > 80) {
201 $approved = 0;
202 }
203
204// foreach ($this->excluded_keywords as $keyword) {
205// if (strpos(strtolower($resultKeywords[$i]), $keyword) !== false) {
206//// echo $resultKeywords[$i] . " -> " . $keyword . ' <b>found</b><br>';
207// $approved = 0;
208// }
209// }
210
211 if ($approved == 1) {
212 $tmpArray[] = $resultKeywords[$i];
213 }
214 }
215// $resultKeywords = array_slice($resultKeywords, 1, 10);
216 $adGroupResourseName = $this->log->parameters->google_id;
217
218 $operations = [];
219 $data = [];
220 $resultKeywords = $tmpArray;
221
222// echo "<br>--------------------PO UPRAVE----------------<br>";
223 foreach ($resultKeywords as $key => $item) {
224// echo $item . "<br>";
225 // Configures the keyword text and match type settings.
226 $keywordInfo = new KeywordInfo([
227 'text' => new StringValue(['value' => $item]),
228 'match_type' => KeywordMatchType::BROAD // rozšírenie - berie aj synonymum, t.j. developer => vývojár
229 ]);
230
231 // Constructs an ad group criterion using the keyword text info above.
232 $adGroupCriterion = new AdGroupCriterion([
233 'ad_group' => $adGroupResourseName,
234 'status' => Constants\Status::ENABLED,
235 'keyword' => $keywordInfo
236 ]);
237
238 $adGroupCriterionOperation = new AdGroupCriterionOperation();
239 $adGroupCriterionOperation->setCreate($adGroupCriterion);
240
241 $operations[] = $adGroupCriterionOperation;
242 $data[] = [
243 "group_id" => $this->log->parameters->group_id,
244 "google_id" => null, //$addedAdGroupCriterion->getResourceName(),
245 "status" => $adGroupCriterion->getStatus(),
246 "key" => $item,
247 "bid" => $this->log->parameters->bid
248 ];
249
250 }
251
252
253 $data_chunk = array_chunk($operations, 5); //moze byt maximum 5 lebo ked je > 5 tak potom nevieme zistit zakazane klucove slova; najlepsie chunk 1 ale je to velmi pomale
254 $i = 0;
255 foreach ($data_chunk as $chunk) {
256
257
258 try {
259 $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();
260 $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(
261 $customerId,
262 $chunk
263 );
264
265 printf("Added %d ad group criteria:%s", $response->getResults()->count(), PHP_EOL);
266
267 foreach ($response->getResults() as $addedAdGroupCriterion) {
268 /** @var AdGroupCriterion $addedAdGroupCriterion */
269 $data[$i++]['google_id'] = $addedAdGroupCriterion->getResourceName();
270 print $addedAdGroupCriterion->getResourceName() . "<br>";
271
272 }
273 } catch (GoogleAdsException $googleAdsException) {
274 $exemptPolicyViolationKeys = self::fetchExemptPolicyViolationKeys($googleAdsException);
275 }
276
277 if (!empty($exemptPolicyViolationKeys)) {
278 $response = self::requestExemption(
279 $customerId,
280 $adGroupCriterionServiceClient,
281 $chunk,
282 $exemptPolicyViolationKeys
283 );
284 foreach ($response->getResults() as $addedAdGroupCriterion) {
285 /** @var AdGroupCriterion $addedAdGroupCriterion */
286 $data[$i++]['google_id'] = $addedAdGroupCriterion->getResourceName();
287 print $addedAdGroupCriterion->getResourceName() . "<br>";
288
289 }
290 }
291 }
292
293 // Issues a mutate request to add the ad group criterion.
294
295 $this->api->database->table(Constants\Tables::KEYWORDS)->insert($data);
296
297
298 $this->log->update([
299 'done' => 1,
300 'done_time' => new \Datetime(),
301 'comment' => "Added keywords: " . count($operations)
302 ]);
303 $this->api->database->table(Constants\Tables::OPERATIONS)->insert([
304 'task' => 'addKeywords',
305 'count' => count($operations)
306 ]);
307 }
308
309 /**
310 * Collects all policy violation keys that can be exempted for sending a exemption request
311 * later.
312 *
313 * @param GoogleAdsClient $googleAdsClient the Google Ads API client
314 * @return PolicyViolationKey[] the exemptible policy violation keys
315 */
316 private static function fetchExemptPolicyViolationKeys(GoogleAdsException $googleAdsException)
317 {
318 $exemptPolicyViolationKeys = [];
319
320 printf("Google Ads failure details:%s", PHP_EOL);
321 foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {
322 /** @var GoogleAdsError $error */
323 printf(
324 "\t%s: %s%s",
325 $error->getErrorCode()->getErrorCode(),
326 $error->getMessage(),
327 PHP_EOL
328 );
329 if (!is_null($error->getDetails())
330 && !is_null($error->getDetails()->getPolicyViolationDetails())) {
331 $policyViolationDetails = $error->getDetails()->getPolicyViolationDetails();
332 printf("\tPolicy violation details:%s", PHP_EOL);
333 printf(
334 "\t\tExternal policy name: '%s'%s",
335 $policyViolationDetails->getExternalPolicyName(),
336 PHP_EOL
337 );
338 printf(
339 "\t\tExternal policy description: '%s'%s",
340 $policyViolationDetails->getExternalPolicyDescription(),
341 PHP_EOL
342 );
343 printf(
344 "\t\tIs exemptible? '%s'%s",
345 $policyViolationDetails->getIsExemptible() ? 'yes' : 'no',
346 PHP_EOL
347 );
348
349 if ($policyViolationDetails->getIsExemptible() &&
350 !is_null($policyViolationDetails->getKey())) {
351 $policyViolationDetailsKey = $policyViolationDetails->getKey();
352 $exemptPolicyViolationKeys[] = $policyViolationDetailsKey;
353 printf("\t\tPolicy violation key:%s", PHP_EOL);
354 printf(
355 "\t\t\tName: '%s'%s",
356 $policyViolationDetailsKey->getPolicyNameUnwrapped(),
357 PHP_EOL
358 );
359 printf(
360 "\t\t\tViolating text: '%s'%s",
361 $policyViolationDetailsKey->getViolatingTextUnwrapped(),
362 PHP_EOL
363 );
364 }
365 }
366 }
367 return $exemptPolicyViolationKeys;
368 }
369
370 /**
371 * Sends exemption requests for creating a keyword.
372 *
373 * @param int $customerId
374 * @param AdGroupCriterionServiceClient $adGroupCriterionServiceClient
375 * @param array $operations
376 * @param PolicyViolationKey[] $exemptPolicyViolationKeys
377 */
378 private static function requestExemption(
379 $customerId,
380 AdGroupCriterionServiceClient $adGroupCriterionServiceClient,
381 $operations,
382 array $exemptPolicyViolationKeys
383 )
384 {
385// dump($operations);
386// exit;
387
388 print "Try adding a keyword again by requesting exemption for its policy"
389 . " violations." . PHP_EOL;
390
391
392 foreach ($operations as $operation) {
393 $operation = $operation->setExemptPolicyViolationKeys($exemptPolicyViolationKeys);
394 }
395 $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(
396 $customerId,
397 $operations
398 );
399 return $response;
400 }
401}