谷歌创意素材
This commit is contained in:
parent
2033c3c46c
commit
d1713a5189
59
app/command/SyncGoogleAdsAsset.php
Normal file
59
app/command/SyncGoogleAdsAsset.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use app\model\GoogleAdsAsset;
|
||||
use app\model\GoogleAdsAd;
|
||||
use app\model\GoogleAdsAssetRelations;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\facade\Db;
|
||||
|
||||
class SyncGoogleAdsAsset extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('sync:google_ads_asset')
|
||||
->setDescription('Synchronize Google Ads Asset relations');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
// 获取所有素材
|
||||
$assets = GoogleAdsAsset::where('status', 1)->select();
|
||||
|
||||
foreach ($assets as $asset) {
|
||||
// 获取广告表中的所有广告
|
||||
$ads = GoogleAdsAd::where('status', 1)->select();
|
||||
|
||||
foreach ($ads as $ad) {
|
||||
// 检查广告的 metadata 是否包含素材的 resource_name
|
||||
if (isset($ad->metadata) && strpos($ad->metadata, $asset->resource_name) !== false) {
|
||||
// 插入关联记录
|
||||
$existingRelation = GoogleAdsAssetRelations::where('asset_id', $asset->asset_id)
|
||||
->where('ad_id', $ad->ad_id)
|
||||
->where('date', date('Y-m-d'))
|
||||
->find();
|
||||
|
||||
if (!$existingRelation) {
|
||||
// 如果没有现有记录,则插入新的关联
|
||||
GoogleAdsAssetRelations::create([
|
||||
'asset_id' => $asset->asset_id,
|
||||
'ad_id' => $ad->ad_id,
|
||||
'ad_group_id' => $ad->ad_group_id,
|
||||
'campaign_id' => $ad->campaign_id,
|
||||
'date' => date('Y-m-d')
|
||||
]);
|
||||
|
||||
$output->writeln("Inserted relation for Asset ID: {$asset->asset_id} and Ad ID: {$ad->ad_id}");
|
||||
} else {
|
||||
$output->writeln("Relation already exists for Asset ID: {$asset->asset_id} and Ad ID: {$ad->ad_id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('Google Ads Asset synchronization completed.');
|
||||
}
|
||||
}
|
18
app/controller/AssetController.php
Normal file
18
app/controller/AssetController.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\command\SyncGoogleAdsAsset;
|
||||
use think\facade\Console;
|
||||
use think\Request;
|
||||
|
||||
class AssetController
|
||||
{
|
||||
public function sync(Request $request)
|
||||
{
|
||||
// 执行命令行任务
|
||||
$result = Console::call('sync:google_ads_asset');
|
||||
|
||||
return json(['message' => 'Synchronization completed successfully']);
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ use support\Request;
|
||||
use app\service\GoogleAdsCampaignService;
|
||||
use app\service\GoogleAdsGroupService;
|
||||
use app\service\GoogleAdsAdService;
|
||||
use app\service\GoogleAdsAssetService;
|
||||
use app\service\GoogleAdsAccountService;
|
||||
use support\Response;
|
||||
use DI\Annotation\Inject;
|
||||
@ -35,6 +36,12 @@ class GoogleAdsController
|
||||
*/
|
||||
private $googleAdsAdService;
|
||||
|
||||
/**
|
||||
* @Inject
|
||||
* @var GoogleAdsAssetService
|
||||
*/
|
||||
private $googleAdsAssetService;
|
||||
|
||||
/**
|
||||
* @Inject
|
||||
* @var GoogleAdsAccountService
|
||||
@ -79,6 +86,14 @@ class GoogleAdsController
|
||||
return $this->getAds($options);
|
||||
}
|
||||
|
||||
public function listAssets(Request $request)
|
||||
{
|
||||
$options = $request->all();
|
||||
|
||||
// 继续处理 Google Ads API 操作
|
||||
return $this->getAssets($options);
|
||||
}
|
||||
|
||||
public function listDateDatas(Request $request)
|
||||
{
|
||||
$options = $request->all();
|
||||
@ -254,6 +269,17 @@ class GoogleAdsController
|
||||
return $this->successResponse(['groups_list' => $resourceName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* get assets
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function getAssets($options): Response
|
||||
{
|
||||
$resourceName = $this->googleAdsAssetService->runListAssets($options['customer_id']);
|
||||
// return $this->successResponse(['assets_list' => $resourceName]);
|
||||
return $this->successResponse(['assets_list' => 'succeed added']);
|
||||
}
|
||||
|
||||
/**
|
||||
* get date datas
|
||||
* @throws ApiException
|
||||
|
@ -11,6 +11,7 @@ use app\event\GoogleAdsCampaigns;
|
||||
use app\event\GoogleAdsGroups;
|
||||
use app\event\GoogleAdsAds;
|
||||
use app\event\GoogleAdsDateDatas;
|
||||
use app\event\GoogleAdsMaterials;
|
||||
use Webman\Event\Event;
|
||||
|
||||
class OAuthController
|
||||
@ -120,8 +121,10 @@ class OAuthController
|
||||
// Event::emit(GoogleAdsCampaigns::type, []);
|
||||
// dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsGroups::type . '开始');
|
||||
// Event::emit(GoogleAdsGroups::type, []);
|
||||
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAds::type . '开始');
|
||||
Event::emit(GoogleAdsAds::type, []);
|
||||
// dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAds::type . '开始');
|
||||
// Event::emit(GoogleAdsAdMaterials::type, []);
|
||||
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsMaterials::type . '开始');
|
||||
Event::emit(GoogleAdsMaterials::type, []);
|
||||
|
||||
return $this->successResponse(['data' => []]);
|
||||
|
||||
|
282
app/event/GoogleAdsAssets.php
Normal file
282
app/event/GoogleAdsAssets.php
Normal file
@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
namespace app\event;
|
||||
|
||||
use app\service\GoogleAdsAssetService;
|
||||
use app\service\GoogleOAuthService;
|
||||
use Google\ApiCore\ApiException;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use support\Db;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use DI\Annotation\Inject;
|
||||
|
||||
//use QL\QueryList;
|
||||
use support\Redis;
|
||||
|
||||
|
||||
class GoogleAdsAssets
|
||||
{
|
||||
/**
|
||||
* @Inject
|
||||
* @var GoogleOAuthService
|
||||
*/
|
||||
|
||||
private $googleOAuthService;
|
||||
|
||||
|
||||
//微博热榜地址
|
||||
// const url = 'https://library.tiktok.com/api/v1/search?region=GB&type=1&start_time=1666540800&end_time=1666627200';
|
||||
|
||||
const userAgent = 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
||||
|
||||
const type = 'googleadsassets';
|
||||
const limit = 12;
|
||||
const sort_order = 'impression,desc';
|
||||
|
||||
|
||||
// const countries = ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE","IT", "LV", "LI", "LT", "LU", "MT", "NL", "NO", "PL", "PT", "RO", "SK", "SI", "ES", "SE", "CH", "TR", "GB"] ;
|
||||
const countries = ["GB", "BE"];
|
||||
|
||||
|
||||
public function listAssets(Request $request)
|
||||
{
|
||||
$options = $request->all();
|
||||
|
||||
// 继续处理 Google Ads API 操作
|
||||
return $this->getAssets($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* get assets
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function getAssets($options)
|
||||
{
|
||||
$customers = $this->googleOAuthService->getGoogleAdCustomers([]);
|
||||
foreach ($customers as $customerId) {
|
||||
$googleAdsAssetService = new GoogleAdsAssetService($customerId);
|
||||
$resourceName = $googleAdsAssetService->runListAssets($customerId);
|
||||
}
|
||||
|
||||
// return $this->successResponse(['ads_list' => $resourceName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 每天爬取tiktok广告
|
||||
* @return void
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
try {
|
||||
|
||||
$client = new Client([
|
||||
//允许重定向
|
||||
// 'allow_redirects' => true,
|
||||
]);
|
||||
|
||||
|
||||
// 获取前两天 0 点的时间戳
|
||||
$dayBeforeYesterdayStart = strtotime('-2 days 00:00:00');
|
||||
|
||||
// 获取前一天 0 点的时间戳
|
||||
$yesterdayStart = strtotime('-1 day 00:00:00');
|
||||
$countryCache = Redis::get(self::type . 'lastCountry');
|
||||
//全部跑完跳出
|
||||
if ($countryCache == 'All') {
|
||||
dump($countryCache . '国家更新tiktok Ads 完成');
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($countryCache)) {
|
||||
$countryCache = 'GB';
|
||||
}
|
||||
|
||||
$searchIdCache = Redis::get(self::type . 'lastSearchId');
|
||||
$offsetCache = Redis::get(self::type . 'nextOffset');
|
||||
$totalCache = Redis::get(self::type . 'totalCache');
|
||||
if (!isset($searchIdCache) || empty($searchIdCache)) {
|
||||
$searchIdCache = '';
|
||||
}
|
||||
if (!isset($offsetCache) || empty($searchIdCache)) {
|
||||
$offsetCache = 0;
|
||||
}
|
||||
if (!isset($totalCache) || empty($searchIdCache)) {
|
||||
$totalCache = 0;
|
||||
}
|
||||
|
||||
//判断国家是否跑完。
|
||||
if ($totalCache > 0 && $offsetCache > ceil($totalCache / self::limit)) {
|
||||
$key = array_search($countryCache, self::countries, true);
|
||||
if (in_array($countryCache, self::countries) && isset(self::countries[$key + 1])) {
|
||||
$countryCache = self::countries[$key + 1];
|
||||
dump('更新' . $countryCache . '国家的tiktok Ads 完成');
|
||||
} else {
|
||||
$countryCache = 'All'; //赋值非正常国家的code中断定时任务
|
||||
dump($countryCache . '国家更新tiktok Ads 完成');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$start_time = $dayBeforeYesterdayStart;
|
||||
$end_time = $yesterdayStart;
|
||||
//当前国家爬取
|
||||
$currentParams = null;
|
||||
$currentParams = ['country' => $countryCache, 'search_id' => $searchIdCache, 'type' => 1, 'start_time' => $start_time, 'end_time' => $end_time];
|
||||
|
||||
|
||||
$url = 'https://library.tiktok.com/api/v1/search?region=' . $currentParams['country'] . '&type=' . $currentParams['type'] . '&start_time=' . $currentParams['start_time'] . '&end_time=' . $currentParams['end_time'];
|
||||
|
||||
$res = json_decode($client->post($url, [
|
||||
'headers' => [
|
||||
'accept' => 'application/json, text/plain, */*',
|
||||
'accept-language' => 'zh-CN,zh;q=0.9',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'cookie: _ttp=2ov8Fc4C2CaNscHJd90O9fMhlpE; _ga=GA1.1.1025820618.1731926196; FPID=FPID2.2.Bcgkp%2Fk%2Bbn5w5YeSMR9wd9VpNHJwTUpkkaEqSdCEa0w%3D.1731926196; FPAU=1.2.944915349.1731926193; FPLC=mbVyryI5aG6IVpAvhs1JsgWjA7FVA6QsCJ7VbXhM7zWoXNp4rcD0IK7FNTTf%2FuOrqeOgqEhTd4NB3hY7q3aDVTGQa3WGHqxkGte4%2BBZxsrpaHFas9kb7DPRXM12T5Q%3D%3D; _ga_TEQXTT9FE4=GS1.1.1732097542.7.0.1732097542.0.0.857840528',
|
||||
'origin' => 'https://library.tiktok.com',
|
||||
'priority' => 'u=1, i',
|
||||
'referer' => 'https://library.tiktok.com/ads?region=AT&start_time=1731945600000&end_time=1732032000000&adv_name=&adv_biz_ids=&query_type=&sort_type=last_shown_date,desc',
|
||||
'sec-ch-ua' => '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
||||
'sec-ch-ua-mobile' => '?0',
|
||||
'sec-ch-ua-platform' => '"Windows"',
|
||||
'sec-fetch-dest' => 'empty',
|
||||
'sec-fetch-mode' => 'cors',
|
||||
'sec-fetch-site' => 'same-origin',
|
||||
'user-agent' => self::userAgent,
|
||||
// 'user-agent' => 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||
],
|
||||
'json' => [
|
||||
'query' => '',
|
||||
'query_type' => '',
|
||||
'adv_biz_ids' => '',
|
||||
'order' => self::sort_order,
|
||||
'offset' => (int)$offsetCache,
|
||||
'search_id' => $currentParams['search_id'],
|
||||
'limit' => self::limit,
|
||||
],
|
||||
|
||||
])->getBody()->getContents(), true);
|
||||
// dump($res);return; //调试点
|
||||
|
||||
if ($res['search_id'] != $searchIdCache) {
|
||||
$searchIdCache = $res['search_id'];
|
||||
dump('search_id更新 ' . $searchIdCache . ' 成功');
|
||||
}
|
||||
|
||||
|
||||
if ($res['total'] == 0 || $res['code'] != 0) {
|
||||
dump('更新tiktok Ads接口响应异常:' . json_encode($res, JSON_UNESCAPED_UNICODE));
|
||||
return;
|
||||
}
|
||||
$listAdsIds = [];
|
||||
foreach ($res['data'] as $ad) {
|
||||
if ($ad['audit_status'] == 2 || empty($ad['videos'])) {
|
||||
continue; //审核不过或者没视频不采集
|
||||
}
|
||||
|
||||
$imagesJson = is_array($ad['image_urls']) ? json_encode($ad['image_urls']) : json_encode([]);
|
||||
$rejection_info = is_array($ad['rejection_info']) ? json_encode($ad['rejection_info']) : null;
|
||||
// dump($rejection_info);
|
||||
$insertData[$ad['id']] = [
|
||||
'ad_id' => $ad['id'],
|
||||
'name' => $ad['name'],
|
||||
'audit_status' => $ad['audit_status'],
|
||||
'type' => $ad['type'],
|
||||
'first_shown_date' => $ad['first_shown_date'],
|
||||
'last_shown_date' => $ad['last_shown_date'],
|
||||
'image_urls' => $imagesJson,
|
||||
'estimated_audience' => $ad['estimated_audience'],
|
||||
'spent' => $ad['spent'],
|
||||
'impression' => $ad['impression'],
|
||||
'show_mode' => $ad['show_mode'],
|
||||
'rejection_info' => $rejection_info,
|
||||
'sor_audit_status' => $ad['sor_audit_status'],
|
||||
'country_code' => $countryCache,
|
||||
];
|
||||
if (isset($ad['videos']) && !empty($ad['videos'])) {
|
||||
// 遍历 "videos" 数组
|
||||
foreach ($ad['videos'] as $video) {
|
||||
$insertData[$ad['id']]['video_url'] = $video['video_url'];
|
||||
$insertData[$ad['id']]['cover_img'] = $video['cover_img'];
|
||||
}
|
||||
}
|
||||
$listAdsIds = array_column($insertData, 'ad_id');
|
||||
|
||||
}
|
||||
// dump($insertData);return;
|
||||
|
||||
|
||||
if (empty($insertData)) return;
|
||||
|
||||
//开启事务
|
||||
Db::beginTransaction();
|
||||
|
||||
|
||||
//删除原来的旧数据
|
||||
TiktokAd::query()->whereIn('ad_id', array_keys($insertData))->delete();
|
||||
//添加新的数据
|
||||
TiktokAd::query()->insert($insertData);
|
||||
|
||||
//redis缓存
|
||||
Redis::set(self::type, json_encode($insertData, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
//redis缓存 记录更新时间
|
||||
$time = date('Y-m-d H:i:s');
|
||||
Redis::set(self::type . 'time', $time);
|
||||
Redis::set(self::type . 'lastCountry', $countryCache);
|
||||
Redis::set(self::type . 'nextOffset', ++$offsetCache); //记录下一次的offset
|
||||
Redis::set(self::type . 'totalCache', $res['total']);
|
||||
Redis::set(self::type . 'lastSearchId', $searchIdCache);
|
||||
if (!empty($listAdsIds)) {
|
||||
Redis::rPush(self::type . 'AdsIds', ...$listAdsIds);
|
||||
}
|
||||
|
||||
//提交事务
|
||||
Db::commit();
|
||||
//销毁$res
|
||||
unset($res);
|
||||
|
||||
dump(date('Y-m-d H:i:s') . '更新' . self::type . '成功');
|
||||
} catch (GuzzleException|\Exception $exception) {
|
||||
//回滚事务
|
||||
Db::rollBack();
|
||||
dump('更' . self::type . '异常:' . $exception->getMessage());
|
||||
dump($exception);
|
||||
}
|
||||
// } catch (ClientExceptionInterface $e) {
|
||||
// // 捕获 4xx 错误
|
||||
// dump( 'Client error: ' . $e->getMessage() . "\n");
|
||||
// } catch (ServerExceptionInterface $e) {
|
||||
// // 捕获 5xx 错误
|
||||
// dump('Server error: ' . $e->getMessage() . "\n");
|
||||
// } catch (TransportExceptionInterface $e) {
|
||||
// // 捕获网络传输错误
|
||||
// dump('Transport error: ' . $e->getMessage() . "\n") ;
|
||||
// } catch (\Exception $e) {
|
||||
// // 捕获所有其他错误
|
||||
// dump('General error: ' . $e->getMessage() . "\n") ;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
// 可以加入一些公共方法
|
||||
protected function successResponse($data): Response
|
||||
{
|
||||
return Json([
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function errorResponse($code, $message, $data = []): Response
|
||||
{
|
||||
return Json([
|
||||
'code' => $code,
|
||||
'msg' => $message ?: 'error',
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
}
|
@ -50,6 +50,97 @@ class Ad extends Model
|
||||
return $statusMap[$data['status']] ?? 'UNKNOWN';
|
||||
}
|
||||
|
||||
// 定义广告类型枚举常量
|
||||
const UNSPECIFIED = 0;
|
||||
const UNKNOWN = 1;
|
||||
const TEXT_AD = 2;
|
||||
const EXPANDED_TEXT_AD = 3;
|
||||
const EXPANDED_DYNAMIC_SEARCH_AD = 7;
|
||||
const HOTEL_AD = 8;
|
||||
const SHOPPING_SMART_AD = 9;
|
||||
const SHOPPING_PRODUCT_AD = 10;
|
||||
const VIDEO_AD = 12;
|
||||
const IMAGE_AD = 14;
|
||||
const RESPONSIVE_SEARCH_AD = 15;
|
||||
const LEGACY_RESPONSIVE_DISPLAY_AD = 16;
|
||||
const APP_AD = 17;
|
||||
const LEGACY_APP_INSTALL_AD = 18;
|
||||
const RESPONSIVE_DISPLAY_AD = 19;
|
||||
const LOCAL_AD = 20;
|
||||
const HTML5_UPLOAD_AD = 21;
|
||||
const DYNAMIC_HTML5_AD = 22;
|
||||
const APP_ENGAGEMENT_AD = 23;
|
||||
const SHOPPING_COMPARISON_LISTING_AD = 24;
|
||||
const VIDEO_BUMPER_AD = 25;
|
||||
const VIDEO_NON_SKIPPABLE_IN_STREAM_AD = 26;
|
||||
const VIDEO_OUTSTREAM_AD = 27;
|
||||
const VIDEO_TRUEVIEW_IN_STREAM_AD = 29;
|
||||
const VIDEO_RESPONSIVE_AD = 30;
|
||||
const SMART_CAMPAIGN_AD = 31;
|
||||
const CALL_AD = 32;
|
||||
const APP_PRE_REGISTRATION_AD = 33;
|
||||
const IN_FEED_VIDEO_AD = 34;
|
||||
const DEMAND_GEN_MULTI_ASSET_AD = 40;
|
||||
const DEMAND_GEN_CAROUSEL_AD = 41;
|
||||
const TRAVEL_AD = 37;
|
||||
const DEMAND_GEN_VIDEO_RESPONSIVE_AD = 42;
|
||||
const DEMAND_GEN_PRODUCT_AD = 39;
|
||||
|
||||
private static $valueToName = [
|
||||
self::UNSPECIFIED => 'UNSPECIFIED',
|
||||
self::UNKNOWN => 'UNKNOWN',
|
||||
self::TEXT_AD => 'TEXT_AD',
|
||||
self::EXPANDED_TEXT_AD => 'EXPANDED_TEXT_AD',
|
||||
self::EXPANDED_DYNAMIC_SEARCH_AD => 'EXPANDED_DYNAMIC_SEARCH_AD',
|
||||
self::HOTEL_AD => 'HOTEL_AD',
|
||||
self::SHOPPING_SMART_AD => 'SHOPPING_SMART_AD',
|
||||
self::SHOPPING_PRODUCT_AD => 'SHOPPING_PRODUCT_AD',
|
||||
self::VIDEO_AD => 'VIDEO_AD',
|
||||
self::IMAGE_AD => 'IMAGE_AD',
|
||||
self::RESPONSIVE_SEARCH_AD => 'RESPONSIVE_SEARCH_AD',
|
||||
self::LEGACY_RESPONSIVE_DISPLAY_AD => 'LEGACY_RESPONSIVE_DISPLAY_AD',
|
||||
self::APP_AD => 'APP_AD',
|
||||
self::LEGACY_APP_INSTALL_AD => 'LEGACY_APP_INSTALL_AD',
|
||||
self::RESPONSIVE_DISPLAY_AD => 'RESPONSIVE_DISPLAY_AD',
|
||||
self::LOCAL_AD => 'LOCAL_AD',
|
||||
self::HTML5_UPLOAD_AD => 'HTML5_UPLOAD_AD',
|
||||
self::DYNAMIC_HTML5_AD => 'DYNAMIC_HTML5_AD',
|
||||
self::APP_ENGAGEMENT_AD => 'APP_ENGAGEMENT_AD',
|
||||
self::SHOPPING_COMPARISON_LISTING_AD => 'SHOPPING_COMPARISON_LISTING_AD',
|
||||
self::VIDEO_BUMPER_AD => 'VIDEO_BUMPER_AD',
|
||||
self::VIDEO_NON_SKIPPABLE_IN_STREAM_AD => 'VIDEO_NON_SKIPPABLE_IN_STREAM_AD',
|
||||
self::VIDEO_OUTSTREAM_AD => 'VIDEO_OUTSTREAM_AD',
|
||||
self::VIDEO_TRUEVIEW_IN_STREAM_AD => 'VIDEO_TRUEVIEW_IN_STREAM_AD',
|
||||
self::VIDEO_RESPONSIVE_AD => 'VIDEO_RESPONSIVE_AD',
|
||||
self::SMART_CAMPAIGN_AD => 'SMART_CAMPAIGN_AD',
|
||||
self::CALL_AD => 'CALL_AD',
|
||||
self::APP_PRE_REGISTRATION_AD => 'APP_PRE_REGISTRATION_AD',
|
||||
self::IN_FEED_VIDEO_AD => 'IN_FEED_VIDEO_AD',
|
||||
self::DEMAND_GEN_MULTI_ASSET_AD => 'DEMAND_GEN_MULTI_ASSET_AD',
|
||||
self::DEMAND_GEN_CAROUSEL_AD => 'DEMAND_GEN_CAROUSEL_AD',
|
||||
self::TRAVEL_AD => 'TRAVEL_AD',
|
||||
self::DEMAND_GEN_VIDEO_RESPONSIVE_AD => 'DEMAND_GEN_VIDEO_RESPONSIVE_AD',
|
||||
self::DEMAND_GEN_PRODUCT_AD => 'DEMAND_GEN_PRODUCT_AD',
|
||||
];
|
||||
|
||||
// 检查广告类型是否有效
|
||||
public static function isValidAdType($adType) {
|
||||
return in_array($adType, array_keys(self::$valueToName));
|
||||
}
|
||||
|
||||
// 获取广告类型名称
|
||||
public static function getAdTypeName($adType) {
|
||||
return self::$valueToName[$adType] ?? 'UNKNOWN';
|
||||
}
|
||||
|
||||
// 在模型中使用这些常量
|
||||
public function setAdType($adType) {
|
||||
if (!self::isValidAdType($adType)) {
|
||||
throw new Exception("Invalid ad type");
|
||||
}
|
||||
$this->ad_type = $adType;
|
||||
}
|
||||
|
||||
// 更新广告状态
|
||||
// public function updateStatus($status)
|
||||
// {
|
||||
@ -84,6 +175,12 @@ class Ad extends Model
|
||||
return $this->belongsTo(AdGroup::class, 'ad_group_id', 'ad_group_id');
|
||||
}
|
||||
|
||||
// 关联到素材关系
|
||||
public function assetRelations()
|
||||
{
|
||||
return $this->hasMany(AssetRelation::class, 'ad_id', 'ad_id');
|
||||
}
|
||||
|
||||
// 关联 Customer 模型(广告属于客户)
|
||||
// public function customer()
|
||||
// {
|
||||
|
@ -38,6 +38,7 @@ class AdGroup extends Model
|
||||
const STATUS_ENABLED = 2;
|
||||
const STATUS_PAUSED = 3;
|
||||
const STATUS_REMOVED = 4;
|
||||
|
||||
// 获取广告组状态
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
@ -96,5 +97,11 @@ class AdGroup extends Model
|
||||
{
|
||||
return $this->hasMany(Ad::class, 'ad_group_id', 'ad_group_id');
|
||||
}
|
||||
|
||||
// 关联到素材关系
|
||||
public function assetRelations()
|
||||
{
|
||||
return $this->hasMany(AssetRelation::class, 'ad_group_id', 'ad_group_id');
|
||||
}
|
||||
}
|
||||
|
||||
|
56
app/model/Asset.php
Normal file
56
app/model/Asset.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class Asset extends Model
|
||||
{
|
||||
// 设置表名
|
||||
protected $table = 'bps.bps_google_ads_asset';
|
||||
|
||||
// 设置主键
|
||||
protected $pk = 'asset_id';
|
||||
|
||||
// 设置时间戳字段
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
// 定义 JSONB 类型字段
|
||||
protected $json = ['metadata'];
|
||||
|
||||
// 类型判断常量
|
||||
const TYPE_YOUTUBE_VIDEO = 2;
|
||||
const TYPE_IMAGE = 4;
|
||||
|
||||
// 获取类型
|
||||
public function getTypeTextAttr($value, $data)
|
||||
{
|
||||
$statusMap = [
|
||||
self::TYPE_YOUTUBE_VIDEO => 'YOUTUBE_VIDEO',
|
||||
self::TYPE_IMAGE => 'IMAGE'
|
||||
];
|
||||
return $statusMap[$data['asset_type']] ?? 'UNKNOWN';
|
||||
}
|
||||
|
||||
// 允许批量赋值的字段
|
||||
protected $fillable = ['customer_id', 'asset_type', 'asset_name', 'resource_name','asset_url', 'status', 'metadata'];
|
||||
|
||||
// 关联到广告、广告组和广告活动
|
||||
public function relations()
|
||||
{
|
||||
return $this->hasMany(AssetRelation::class, 'asset_id', 'asset_id');
|
||||
}
|
||||
|
||||
// 获取素材的广告、广告组和广告活动
|
||||
public function getRelations()
|
||||
{
|
||||
return $this->relations()->with(['ad', 'adGroup', 'campaign']);
|
||||
}
|
||||
|
||||
// 追加自定义字段到模型结果中
|
||||
public function appendCustomAttributes()
|
||||
{
|
||||
return ['relations'];
|
||||
}
|
||||
}
|
43
app/model/AssetRelation.php
Normal file
43
app/model/AssetRelation.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class AssetRelation extends Model
|
||||
{
|
||||
// 设置表名
|
||||
protected $table = 'bps.bps_google_ads_asset_relations';
|
||||
|
||||
// 设置主键
|
||||
protected $pk = 'asset_relation_id';
|
||||
|
||||
// 设置时间戳字段
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
// 关联到广告
|
||||
public function ad()
|
||||
{
|
||||
return $this->belongsTo(Ad::class, 'ad_id', 'ad_id');
|
||||
}
|
||||
|
||||
// 关联到广告组
|
||||
public function adGroup()
|
||||
{
|
||||
return $this->belongsTo(AdGroup::class, 'ad_group_id', 'ad_group_id');
|
||||
}
|
||||
|
||||
// 关联到广告活动
|
||||
public function campaign()
|
||||
{
|
||||
return $this->belongsTo(Campaign::class, 'campaign_id', 'campaign_id');
|
||||
}
|
||||
|
||||
// 素材关联关系
|
||||
public function asset()
|
||||
{
|
||||
return $this->belongsTo(Asset::class, 'asset_id', 'asset_id');
|
||||
}
|
||||
}
|
@ -105,4 +105,9 @@ class Campaign extends Model
|
||||
{
|
||||
return $this->hasMany(DayData::class, 'campaign_id', 'campaign_id');
|
||||
}
|
||||
// 关联到素材关系
|
||||
public function assetRelations()
|
||||
{
|
||||
return $this->hasMany(AssetRelation::class, 'campaign_id', 'campaign_id');
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ namespace app\process;
|
||||
use app\event\GoogleAdsCampaigns;
|
||||
use app\event\GoogleAdsGroups;
|
||||
use app\event\GoogleAdsAds;
|
||||
use app\event\GoogleAdsAssets;
|
||||
use app\event\GoogleAdsDateDatas;
|
||||
use Webman\Event\Event;
|
||||
use Workerman\Crontab\Crontab;
|
||||
@ -65,6 +66,13 @@ class UpdateGoogleAdsTask
|
||||
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAds::type . '开始');
|
||||
Event::emit(GoogleAdsAds::type, []);
|
||||
}
|
||||
|
||||
);
|
||||
// 每15分钟执行一次
|
||||
new Crontab('58 */15 * * * *', function () {
|
||||
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAssets::type . '开始');
|
||||
Event::emit(GoogleAdsAssets::type, []);
|
||||
}
|
||||
);
|
||||
|
||||
// 每15分钟执行一次
|
||||
|
5
app/schedule.php
Normal file
5
app/schedule.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
use think\facade\Scheduler;
|
||||
|
||||
Scheduler::call('php think sync:google_ads_asset')
|
||||
->everyDay(); // 每天运行一次
|
@ -78,7 +78,7 @@ class GoogleAdsAdService extends BaseService
|
||||
|
||||
// Creates a single shared budget to be used by the campaigns added below.
|
||||
$groupAdsResourceName = self::getAds($googleAdsClient->getGoogleAdsClient(), $customerId);
|
||||
// dump(json_encode($groupadsResourceName));
|
||||
// dump(json_encode($groupAdsResourceName));
|
||||
if (is_array($groupAdsResourceName)) {
|
||||
self::saveAds($groupAdsResourceName);
|
||||
}
|
||||
@ -88,7 +88,7 @@ class GoogleAdsAdService extends BaseService
|
||||
|
||||
|
||||
/**
|
||||
* 在数据库中保存广告系列信息
|
||||
* 在数据库中保存广告信息
|
||||
* @param $groupadsResourceName
|
||||
* @return void
|
||||
*/
|
||||
@ -97,9 +97,11 @@ class GoogleAdsAdService extends BaseService
|
||||
$tableName = 'bps_google_ads_ad';
|
||||
$tableName = getenv('DB_PG_SCHEMA') ? getenv('DB_PG_SCHEMA') . '.' . $tableName : 'public' . $tableName;
|
||||
foreach ($groupadsResourceName as $data) {
|
||||
// 构建 SQL 语句,添加 ad_type 和 metadata
|
||||
$sql = "INSERT INTO {$tableName}
|
||||
(ad_id, ad_group_id, campaign_id,customer_id, ad_name, status, resource_name)
|
||||
VALUES (:ad_id, :ad_group_id,:campaign_id, :customer_id, :ad_name, :status, :resource_name)
|
||||
(ad_id, ad_group_id, campaign_id, customer_id, ad_name, status, resource_name, ad_type, metadata)
|
||||
VALUES
|
||||
(:ad_id, :ad_group_id, :campaign_id, :customer_id, :ad_name, :status, :resource_name, :ad_type, :metadata)
|
||||
ON CONFLICT (ad_id)
|
||||
DO UPDATE SET
|
||||
ad_group_id = EXCLUDED.ad_group_id,
|
||||
@ -108,13 +110,28 @@ class GoogleAdsAdService extends BaseService
|
||||
ad_name = EXCLUDED.ad_name,
|
||||
status = EXCLUDED.status,
|
||||
resource_name = EXCLUDED.resource_name,
|
||||
ad_type = EXCLUDED.ad_type,
|
||||
metadata = EXCLUDED.metadata,
|
||||
update_at = EXCLUDED.update_at";
|
||||
// dump($sql);
|
||||
ThinkDb::execute($sql, $data);
|
||||
}
|
||||
|
||||
// 绑定数据
|
||||
$bindData = [
|
||||
'ad_id' => $data['ad_id'],
|
||||
'ad_group_id' => $data['ad_group_id'],
|
||||
'campaign_id' => $data['campaign_id'],
|
||||
'customer_id' => $data['customer_id'],
|
||||
'ad_name' => $data['ad_name'],
|
||||
'status' => $data['status'],
|
||||
'resource_name' => $data['resource_name'],
|
||||
'ad_type' => $data['ad_type'],
|
||||
'metadata' => $data['metadata'],
|
||||
];
|
||||
|
||||
// 执行 SQL 插入语句
|
||||
ThinkDb::execute($sql, $bindData);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
/**
|
||||
* Runs the example.
|
||||
*
|
||||
@ -123,7 +140,8 @@ class GoogleAdsAdService extends BaseService
|
||||
*/
|
||||
// [START get_campaigns]
|
||||
|
||||
public static function getAds(GoogleAdsClient $googleAdsClient, int $customerId)
|
||||
public
|
||||
static function getAds(GoogleAdsClient $googleAdsClient, int $customerId)
|
||||
{
|
||||
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
|
||||
// Creates a query that retrieves all groups.
|
||||
@ -136,7 +154,13 @@ class GoogleAdsAdService extends BaseService
|
||||
customer.id,
|
||||
ad_group_ad.ad.name,
|
||||
ad_group_ad.status,
|
||||
ad_group_ad.ad.resource_name
|
||||
ad_group_ad.ad.resource_name,
|
||||
ad_group_ad.ad.type,
|
||||
ad_group_ad.ad.responsive_display_ad.logo_images,
|
||||
ad_group_ad.ad.responsive_display_ad.square_logo_images,
|
||||
ad_group_ad.ad.responsive_display_ad.marketing_images,
|
||||
ad_group_ad.ad.responsive_display_ad.square_marketing_images,
|
||||
ad_group_ad.ad.responsive_display_ad.youtube_videos
|
||||
FROM ad_group_ad
|
||||
WHERE
|
||||
ad_group_ad.status != 'REMOVED' ";
|
||||
@ -154,6 +178,7 @@ class GoogleAdsAdService extends BaseService
|
||||
// $finalUrlsList = $googleAdsRow->getAdGroupAd()->getAd()->getFinalUrls();
|
||||
// 将最终的 URL 列表转换为 PHP 数组
|
||||
// $finalUrlsArray = iterator_to_array($finalUrlsList);
|
||||
$resourceName = [];
|
||||
$resourceName['ad_id'] = $googleAdsRow->getAdGroupAd()->getAd()->getId();
|
||||
$resourceName['ad_name'] = $googleAdsRow->getAdGroupAd()->getAd()->getName();
|
||||
$resourceName['ad_group_id'] = $googleAdsRow->getAdGroup()->getId();
|
||||
@ -162,13 +187,51 @@ class GoogleAdsAdService extends BaseService
|
||||
// $resourceName['final_urls'] = $finalUrlsArray;
|
||||
$resourceName['status'] = $googleAdsRow->getAdGroupAd()->getStatus();
|
||||
$resourceName['resource_name'] = $googleAdsRow->getAdGroupAd()->getAd()->getResourceName();
|
||||
$resourceName['ad_type'] = $googleAdsRow->getAdGroupAd()->getAd()->getType();
|
||||
//ad_type 19=> RESPONSIVE_DISPLAY_AD 自适应展示广告 详细看model定义
|
||||
if ($resourceName['ad_type'] === 19) {
|
||||
// 获取 squareMarketingImages 中的每个 asset
|
||||
$squareMarketingImages = $googleAdsRow->getAdGroupAd()->getAd()->getResponsiveDisplayAd()->getSquareMarketingImages();
|
||||
$squareMarketingAssets = [];
|
||||
foreach ($squareMarketingImages as $image) {
|
||||
$squareMarketingAssets[] = $image->getAsset();
|
||||
}
|
||||
// 获取 logoImages 中的每个 asset
|
||||
$logoImages = $googleAdsRow->getAdGroupAd()->getAd()->getResponsiveDisplayAd()->getLogoImages();
|
||||
$logoAssets = [];
|
||||
foreach ($logoImages as $logo) {
|
||||
$logoAssets[] = $logo->getAsset();
|
||||
}
|
||||
// 获取 marketingImages 中的每个 asset
|
||||
$marketingImages = $googleAdsRow->getAdGroupAd()->getAd()->getResponsiveDisplayAd()->getMarketingImages();
|
||||
$marketingAssets = [];
|
||||
foreach ($marketingImages as $marketing) {
|
||||
$marketingAssets[] = $marketing->getAsset();
|
||||
}
|
||||
// 获取 youtubeVideos 中的每个 asset
|
||||
$youtubeVideos = $googleAdsRow->getAdGroupAd()->getAd()->getResponsiveDisplayAd()->getYoutubeVideos();
|
||||
$youtubeAssets = [];
|
||||
foreach ($youtubeVideos as $video) {
|
||||
$youtubeAssets[] = $video->getAsset();
|
||||
}
|
||||
// 将提取的资产保存到 metadata 数组
|
||||
$resourceName['metadata']['square_marketing_images'] = $squareMarketingAssets;
|
||||
$resourceName['metadata']['logo_images'] = $logoAssets;
|
||||
$resourceName['metadata']['marketing_images'] = $marketingAssets;
|
||||
$resourceName['metadata']['youtube_videos'] = $youtubeAssets;
|
||||
|
||||
// 将 metadata 转换为 JSON 格式
|
||||
$resourceName['metadata'] = json_encode($resourceName['metadata']); // 存储为 JSONB 格式
|
||||
} else {
|
||||
$resourceName['metadata'] = null;
|
||||
}
|
||||
$resourceNames[] = $resourceName;
|
||||
|
||||
}
|
||||
return $resourceNames;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This example updates the CPC bid and status for a given ad group. To get ad groups, run
|
||||
* GetAdAds.php.
|
||||
@ -178,7 +241,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runUpdateAd($options): mixed
|
||||
public
|
||||
function runUpdateAd($options): mixed
|
||||
{
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||
@ -198,7 +262,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @param int $adGroupId the ad group ID that the ad group ad belongs to
|
||||
* @param int $adId the ID of the ad to pause
|
||||
*/
|
||||
public static function updateAd(
|
||||
public
|
||||
static function updateAd(
|
||||
GoogleAdsClient $googleAdsClient,
|
||||
int $customerId,
|
||||
int $adGroupId,
|
||||
@ -243,7 +308,8 @@ class GoogleAdsAdService extends BaseService
|
||||
/**
|
||||
* 更新广告状态
|
||||
*/
|
||||
public function updateAdStatus(int $customerId, int $adGroupId, int $adId, int $status)
|
||||
public
|
||||
function updateAdStatus(int $customerId, int $adGroupId, int $adId, int $status)
|
||||
{
|
||||
// 从数据库获取 Ad
|
||||
$ad = AdModel::find($adId);
|
||||
@ -267,7 +333,8 @@ class GoogleAdsAdService extends BaseService
|
||||
/**
|
||||
* 获取广告状态
|
||||
*/
|
||||
public function getAdStatus(int $adId)
|
||||
public
|
||||
function getAdStatus(int $adId)
|
||||
{
|
||||
// 从数据库获取 Ad
|
||||
$ad = AdModel::find($adId);
|
||||
@ -280,7 +347,6 @@ class GoogleAdsAdService extends BaseService
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 判断广告是否启用
|
||||
*/
|
||||
@ -330,7 +396,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runGetResponsiveSearchAds($options): mixed
|
||||
public
|
||||
function runGetResponsiveSearchAds($options): mixed
|
||||
{
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||
@ -351,7 +418,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @param int|null $adGroupId the ad group ID for which responsive search ads will be retrieved.
|
||||
* If `null`, returns from all ad groups
|
||||
*/
|
||||
public static function getResponsiveSearchAds(
|
||||
public
|
||||
static function getResponsiveSearchAds(
|
||||
GoogleAdsClient $googleAdsClient,
|
||||
int $customerId,
|
||||
?int $adGroupId
|
||||
@ -421,7 +489,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @param RepeatedField $assets the list of AdTextAsset objects
|
||||
* @return string the string representation of the provided list of AdTextAsset objects
|
||||
*/
|
||||
private static function convertAdTextAssetsToString(RepeatedField $assets): string
|
||||
private
|
||||
static function convertAdTextAssetsToString(RepeatedField $assets): string
|
||||
{
|
||||
$result = '';
|
||||
foreach ($assets as $asset) {
|
||||
@ -445,7 +514,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runUpdateResponsiveSearchAd($options): mixed
|
||||
public
|
||||
function runUpdateResponsiveSearchAd($options): mixed
|
||||
{
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||
@ -463,7 +533,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @param int $adId the ad ID to update
|
||||
*/
|
||||
// [START update_responsive_search_ad]
|
||||
public static function updateResponsiveSearchAd(
|
||||
public
|
||||
static function updateResponsiveSearchAd(
|
||||
GoogleAdsClient $googleAdsClient,
|
||||
int $customerId,
|
||||
int $adId
|
||||
@ -514,6 +585,7 @@ class GoogleAdsAdService extends BaseService
|
||||
);
|
||||
return $updatedAd->getResourceName();
|
||||
}
|
||||
|
||||
// [END update_responsive_search_ad]
|
||||
|
||||
|
||||
@ -525,7 +597,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runSearchForGoogleAdsFields($options): mixed
|
||||
public
|
||||
function runSearchForGoogleAdsFields($options): mixed
|
||||
{
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||
@ -541,7 +614,8 @@ class GoogleAdsAdService extends BaseService
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @param string $namePrefix the name prefix to use in the query
|
||||
*/
|
||||
public static function searchForGoogleAdsFields(GoogleAdsClient $googleAdsClient, string $namePrefix)
|
||||
public
|
||||
static function searchForGoogleAdsFields(GoogleAdsClient $googleAdsClient, string $namePrefix)
|
||||
{
|
||||
$googleAdsFieldServiceClient = $googleAdsClient->getGoogleAdsFieldServiceClient();
|
||||
// Searches for all fields whose name begins with the specified namePrefix.
|
||||
|
741
app/service/GoogleAdsAssetService.php
Normal file
741
app/service/GoogleAdsAssetService.php
Normal file
@ -0,0 +1,741 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\service\GoogleAdsClientService;
|
||||
use app\model\ThirdUserAdvertiser;
|
||||
use app\util\Helper;
|
||||
use app\util\ArgumentNames;
|
||||
use app\util\ArgumentParser;
|
||||
|
||||
use Google\Ads\GoogleAds\Lib\V18\GoogleAdsClient;
|
||||
use Google\Ads\GoogleAds\Util\FieldMasks;
|
||||
use Google\Ads\GoogleAds\Util\V18\ResourceNames;
|
||||
use Google\Ads\GoogleAds\V18\Common\ResponsiveSearchAdInfo;
|
||||
use Google\Ads\GoogleAds\V18\Enums\AdGroupAdStatusEnum\AdGroupAdStatus;
|
||||
use Google\Ads\GoogleAds\V18\Enums\GoogleAdsFieldCategoryEnum\GoogleAdsFieldCategory;
|
||||
use Google\Ads\GoogleAds\V18\Enums\GoogleAdsFieldDataTypeEnum\GoogleAdsFieldDataType;
|
||||
use Google\Ads\GoogleAds\V18\Errors\GoogleAdsError;
|
||||
use Google\Ads\GoogleAds\V18\Resources\Ad;
|
||||
use Google\Ads\GoogleAds\V18\Resources\AdGroupAd;
|
||||
use Google\Ads\GoogleAds\V18\Resources\GoogleAdsField;
|
||||
use Google\Ads\GoogleAds\V18\Services\AdGroupAdOperation;
|
||||
use Google\Ads\GoogleAds\V18\Services\AdOperation;
|
||||
use Google\Ads\GoogleAds\V18\Services\MutateAdGroupAdsRequest;
|
||||
|
||||
use Google\Ads\GoogleAds\V18\Common\AdTextAsset;
|
||||
use Google\Ads\GoogleAds\V18\Enums\ServedAssetFieldTypeEnum\ServedAssetFieldType;
|
||||
use Google\Ads\GoogleAds\V18\Services\GoogleAdsRow;
|
||||
use Google\Ads\GoogleAds\V18\Services\MutateAdsRequest;
|
||||
use Google\Ads\GoogleAds\V18\Services\SearchGoogleAdsFieldsRequest;
|
||||
use Google\Ads\GoogleAds\V18\Services\SearchGoogleAdsRequest;
|
||||
use Google\Ads\GoogleAds\V18\Services\SearchGoogleAdsStreamRequest;
|
||||
use Google\Protobuf\Internal\RepeatedField;
|
||||
use think\facade\Db as ThinkDb;
|
||||
use app\model\Ad as AdModel;
|
||||
|
||||
|
||||
use Google\ApiCore\ApiException;
|
||||
|
||||
class GoogleAdsAssetService extends BaseService
|
||||
{
|
||||
// private $googleAdsClient;
|
||||
private $customerId;
|
||||
|
||||
public function __construct($customerId = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// 从数据库动态获取 google RefreshToken
|
||||
// private function getRefreshTokenFromDatabase($advertiserId)
|
||||
// {
|
||||
// // 通过 advertiser_id 查询 ThirdUserAdvertiser,联表查询 ThirdUser 数据
|
||||
// $userAdvertiser = ThirdUserAdvertiser::with('googleUser') // 联表查询 user 关联
|
||||
// ->where('advertiser_id', $advertiserId) // 根据 advertiser_id 查询
|
||||
// ->find(); // 获取第一个结果
|
||||
//
|
||||
//// 如果找到广告主数据
|
||||
// if ($userAdvertiser && $userAdvertiser->googleUser) {
|
||||
// // 获取关联用户的 access_token
|
||||
// return $userAdvertiser->googleUser ? $userAdvertiser->googleUser->access_token : null;
|
||||
// } else {
|
||||
//// return $this->errorResponse('101', '未找到该广告主或关联的用户');
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
/* @param int $customerId the customer ID
|
||||
* @param $options
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runListAssets(int $customerId): mixed
|
||||
{
|
||||
|
||||
$googleAdsClient = new GoogleAdsClientService($customerId);
|
||||
// Creates a single shared budget to be used by the campaigns added below.
|
||||
$assetsResourceName = self::getAssets($googleAdsClient->getGoogleAdsClient(), $customerId);
|
||||
// dump(json_encode($assetsResourceName));
|
||||
if (is_array($assetsResourceName)) {
|
||||
self::saveAssets($assetsResourceName);
|
||||
}
|
||||
|
||||
return $assetsResourceName;
|
||||
}
|
||||
|
||||
|
||||
/* @param int $customerId the customer ID
|
||||
* @param $options
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runListAds(int $customerId): mixed
|
||||
{
|
||||
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($customerId);
|
||||
|
||||
// Creates a single shared budget to be used by the campaigns added below.
|
||||
$groupAdsResourceName = self::getAds($googleAdsClient->getGoogleAdsClient(), $customerId);
|
||||
// dump(json_encode($groupadsResourceName));
|
||||
if (is_array($groupAdsResourceName)) {
|
||||
self::saveAds($groupAdsResourceName);
|
||||
}
|
||||
|
||||
return $groupAdsResourceName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 在数据库中保存广告素材信息
|
||||
* @param $assetsResourceName
|
||||
* @return void
|
||||
*/
|
||||
public static function saveAssets($assetsResourceName)
|
||||
{
|
||||
$tableName = 'bps_google_ads_asset';
|
||||
$tableName = getenv('DB_PG_SCHEMA') ? getenv('DB_PG_SCHEMA') . '.' . $tableName : 'public' . $tableName;
|
||||
foreach ($assetsResourceName as $data) {
|
||||
// 修改后的插入 SQL 语句
|
||||
$sql = "INSERT INTO {$tableName}
|
||||
(asset_id, customer_id, asset_type, asset_name, resource_name, asset_url, status, metadata, create_at, update_at)
|
||||
VALUES (:asset_id, :customer_id, :asset_type, :asset_name, :resource_name, :asset_url, :status, :metadata, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (asset_id)
|
||||
DO UPDATE SET
|
||||
customer_id = EXCLUDED.customer_id,
|
||||
asset_type = EXCLUDED.asset_type,
|
||||
asset_name = EXCLUDED.asset_name,
|
||||
resource_name = EXCLUDED.resource_name,
|
||||
asset_url = EXCLUDED.asset_url,
|
||||
status = EXCLUDED.status,
|
||||
metadata = EXCLUDED.metadata,
|
||||
update_at = CURRENT_TIMESTAMP"; // update_at 使用 CURRENT_TIMESTAMP 自动更新
|
||||
|
||||
ThinkDb::execute($sql, $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Runs the example.
|
||||
*
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @param int $customerId the customer ID
|
||||
*/
|
||||
// [START get_campaigns]
|
||||
|
||||
public static function getAssets(GoogleAdsClient $googleAdsClient, int $customerId)
|
||||
{
|
||||
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
|
||||
// 查询广告素材及其关联的广告、广告组和广告系列
|
||||
// $query ="SELECT asset.id,asset.name, asset.image_asset.mime_type, asset.image_asset.full_size.url, customer.id FROM asset";
|
||||
$query = "SELECT asset.id,
|
||||
asset.name,
|
||||
asset.type,
|
||||
asset.resource_name,
|
||||
asset.source,
|
||||
asset.image_asset.full_size.url,
|
||||
asset.image_asset.mime_type,
|
||||
asset.image_asset.file_size,
|
||||
asset.image_asset.full_size.height_pixels,
|
||||
asset.image_asset.full_size.width_pixels,
|
||||
asset.youtube_video_asset.youtube_video_id,
|
||||
asset.youtube_video_asset.youtube_video_title,
|
||||
customer.id
|
||||
FROM asset
|
||||
WHERE
|
||||
asset.type IN ('IMAGE', 'YOUTUBE_VIDEO')
|
||||
LIMIT 10000";
|
||||
|
||||
// Issues a search stream request.
|
||||
/** @var GoogleAdsServerStreamDecorator $stream */
|
||||
$stream = $googleAdsServiceClient->searchStream(
|
||||
SearchGoogleAdsStreamRequest::build($customerId, $query)
|
||||
);
|
||||
$resourceNames = [];
|
||||
// Iterates over all rows in all messages and prints the requested field values for
|
||||
// the campaign in each row.
|
||||
foreach ($stream->iterateAllElements() as $googleAdsRow) {
|
||||
/** @var GoogleAdsRow $googleAdsRow */
|
||||
// 将最终的 URL 列表转换为 PHP 数组
|
||||
// $finalUrlsArray = iterator_to_array($finalUrlsList);
|
||||
$resourceName = [];
|
||||
$resourceName['asset_type'] = $googleAdsRow->getAsset()->getType();
|
||||
if ($resourceName['asset_type'] === 2) {
|
||||
$resourceName['asset_url'] = $googleAdsRow->getAsset()->getYoutubeVideoAsset()->getYoutubeVideoId();
|
||||
$resourceName['metadata'] ['youtube_video_id'] = $googleAdsRow->getAsset()->getYoutubeVideoAsset()->getYoutubeVideoId();
|
||||
$resourceName['metadata'] ['youtube_video_title'] = $googleAdsRow->getAsset()->getYoutubeVideoAsset()->getYoutubeVideoTitle();
|
||||
$resourceName['asset_name'] = $resourceName['metadata'] ['youtube_video_title'];
|
||||
$resourceName['metadata'] = json_encode($resourceName['metadata']);
|
||||
} elseif ($resourceName['asset_type'] === 4) {
|
||||
$resourceName['asset_name'] = $googleAdsRow->getAsset()->getName();
|
||||
$resourceName['asset_url'] = $googleAdsRow->getAsset()->getImageAsset()->getFullSize()->getUrl();
|
||||
$resourceName['metadata'] ['mimeType'] = $googleAdsRow->getAsset()->getImageAsset()->getMimeType();
|
||||
$resourceName['metadata'] ['filesize'] = $googleAdsRow->getAsset()->getImageAsset()->getFileSize();
|
||||
$resourceName['metadata'] ['height_pixels'] = $googleAdsRow->getAsset()->getImageAsset()->getFullSize()->getHeightPixels();
|
||||
$resourceName['metadata'] ['width_pixels'] = $googleAdsRow->getAsset()->getImageAsset()->getFullSize()->getWidthPixels();
|
||||
$resourceName['metadata'] = json_encode($resourceName['metadata']);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
$resourceName['asset_id'] = $googleAdsRow->getAsset()->getId();
|
||||
|
||||
$resourceName['status'] = 2; //未定义,先占坑
|
||||
$resourceName['resource_name'] = $googleAdsRow->getAsset()->getResourceName();
|
||||
$resourceName['customer_id'] = $googleAdsRow->getCustomer()->getId();
|
||||
|
||||
$resourceNames[] = $resourceName;
|
||||
}
|
||||
return $resourceNames;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Runs the example.
|
||||
*
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @param int $customerId the customer ID
|
||||
*/
|
||||
// [START get_campaigns]
|
||||
|
||||
public static function getAds(GoogleAdsClient $googleAdsClient, int $customerId)
|
||||
{
|
||||
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
|
||||
// Creates a query that retrieves all groups.
|
||||
|
||||
// $response = $googleAdsServiceClient->search($customerId, $query);
|
||||
$query = "SELECT
|
||||
ad_group_ad.ad.id,
|
||||
ad_group.id,
|
||||
campaign.id,
|
||||
customer.id,
|
||||
ad_group_ad.ad.name,
|
||||
ad_group_ad.status,
|
||||
ad_group_ad.ad.resource_name
|
||||
FROM ad_group_ad
|
||||
WHERE
|
||||
ad_group_ad.status != 'REMOVED' ";
|
||||
// Issues a search stream request.
|
||||
/** @var GoogleAdsServerStreamDecorator $stream */
|
||||
$stream = $googleAdsServiceClient->searchStream(
|
||||
SearchGoogleAdsStreamRequest::build($customerId, $query)
|
||||
);
|
||||
$resourceNames = [];
|
||||
// Iterates over all rows in all messages and prints the requested field values for
|
||||
// the campaign in each row.
|
||||
foreach ($stream->iterateAllElements() as $googleAdsRow) {
|
||||
/** @var GoogleAdsRow $googleAdsRow */
|
||||
// 假设 $googleAdsRow 是从 Google Ads API 中获取的对象
|
||||
// $finalUrlsList = $googleAdsRow->getAdGroupAd()->getAd()->getFinalUrls();
|
||||
// 将最终的 URL 列表转换为 PHP 数组
|
||||
// $finalUrlsArray = iterator_to_array($finalUrlsList);
|
||||
$resourceName['ad_id'] = $googleAdsRow->getAdGroupAd()->getAd()->getId();
|
||||
$resourceName['ad_name'] = $googleAdsRow->getAdGroupAd()->getAd()->getName();
|
||||
$resourceName['ad_group_id'] = $googleAdsRow->getAdGroup()->getId();
|
||||
$resourceName['campaign_id'] = $googleAdsRow->getCampaign()->getId();
|
||||
$resourceName['customer_id'] = $googleAdsRow->getCustomer()->getId();
|
||||
// $resourceName['final_urls'] = $finalUrlsArray;
|
||||
$resourceName['status'] = $googleAdsRow->getAdGroupAd()->getStatus();
|
||||
$resourceName['resource_name'] = $googleAdsRow->getAdGroupAd()->getAd()->getResourceName();
|
||||
$resourceNames[] = $resourceName;
|
||||
}
|
||||
return $resourceNames;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This example updates the CPC bid and status for a given ad group. To get ad groups, run
|
||||
* GetAdAds.php.
|
||||
*/
|
||||
/* @param int $customerId the customer ID
|
||||
* @param $options
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runUpdateAd($options): mixed
|
||||
{
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||
// Creates a single shared budget to be used by the campaigns added below.
|
||||
|
||||
$resourceNames = self::updateAd($googleAdsClient->getGoogleAdsClient(), $options['customer_id'], $options['group_id'], $options['ad_id'], $options['status']);
|
||||
|
||||
return $resourceNames;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs the updateAd example.
|
||||
*
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @param int $customerId the customer ID
|
||||
* @param int $adGroupId the ad group ID that the ad group ad belongs to
|
||||
* @param int $adId the ID of the ad to pause
|
||||
*/
|
||||
public static function updateAd(
|
||||
GoogleAdsClient $googleAdsClient,
|
||||
int $customerId,
|
||||
int $adGroupId,
|
||||
int $adId,
|
||||
int $status
|
||||
)
|
||||
{
|
||||
// Creates ad group ad resource name.
|
||||
$adGroupAdResourceName = ResourceNames::forAdGroupAd($customerId, $adGroupId, $adId);
|
||||
|
||||
// Creates an ad and sets its status to PAUSED.
|
||||
$adGroupAd = new AdGroupAd();
|
||||
$adGroupAd->setResourceName($adGroupAdResourceName);
|
||||
// $adGroupAd->setStatus(AdGroupAdStatus::PAUSED);
|
||||
$adGroupAd->setStatus($status);
|
||||
|
||||
// Constructs an operation that will pause the ad with the specified resource name,
|
||||
// using the FieldMasks utility to derive the update mask. This mask tells the Google Ads
|
||||
// API which attributes of the ad group you want to change.
|
||||
$adGroupAdOperation = new AdGroupAdOperation();
|
||||
$adGroupAdOperation->setUpdate($adGroupAd);
|
||||
$adGroupAdOperation->setUpdateMask(FieldMasks::allSetFieldsOf($adGroupAd));
|
||||
|
||||
// Issues a mutate request to pause the ad group ad.
|
||||
$adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();
|
||||
$response = $adGroupAdServiceClient->mutateAdGroupAds(MutateAdGroupAdsRequest::build(
|
||||
$customerId,
|
||||
[$adGroupAdOperation]
|
||||
));
|
||||
|
||||
// Prints the resource name of the paused ad group ad.
|
||||
/** @var AdGroupAd $pausedAdGroupAd */
|
||||
$pausedAdGroupAd = $response->getResults()[0];
|
||||
// printf(
|
||||
// "Ad group ad with resource name: '%s' is paused.%s",
|
||||
// $pausedAdGroupAd->getResourceName(),
|
||||
// PHP_EOL
|
||||
// );
|
||||
return $pausedAdGroupAd->getResourceName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新广告状态
|
||||
*/
|
||||
public function updateAdStatus(int $customerId, int $adGroupId, int $adId, int $status)
|
||||
{
|
||||
// 从数据库获取 Ad
|
||||
$ad = AdModel::find($adId);
|
||||
if (!$ad) {
|
||||
// throw new ValidateException('Ad not found');
|
||||
return false;
|
||||
}
|
||||
// 更新数据库中的状态
|
||||
// $ad->updateStatus($status);
|
||||
if ($this->modifyDbAdStatus($adId, $status)) {
|
||||
// 更新 Google Ads 上的状态
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($customerId);
|
||||
$resourceName = self::updateAd($googleAdsClient->getGoogleAdsClient(), $customerId, $adGroupId, $adId, $status);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取广告状态
|
||||
*/
|
||||
public function getAdStatus(int $adId)
|
||||
{
|
||||
// 从数据库获取 Ad
|
||||
$ad = AdModel::find($adId);
|
||||
if (!$ad) {
|
||||
// throw new ValidateException('Ad not found');
|
||||
}
|
||||
|
||||
// 返回广告状态
|
||||
return $ad->getStatusTextAttr(null, $ad->toArray());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 判断广告是否启用
|
||||
*/
|
||||
// public function isAdEnabled(int $adId)
|
||||
// {
|
||||
// $ad = Ad::find($adId);
|
||||
// if (!$ad) {
|
||||
// throw new ValidateException('Ad not found');
|
||||
// }
|
||||
//
|
||||
// return $ad->isEnabled();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 判断广告是否暂停
|
||||
*/
|
||||
// public function isAdPaused(int $adId)
|
||||
// {
|
||||
// $ad = Ad::find($adId);
|
||||
// if (!$ad) {
|
||||
// throw new ValidateException('Ad not found');
|
||||
// }
|
||||
//
|
||||
// return $ad->isPaused();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 判断广告是否停止
|
||||
*/
|
||||
// public function isAdStopped(int $adId)
|
||||
// {
|
||||
// $ad = Ad::find($adId);
|
||||
// if (!$ad) {
|
||||
// throw new ValidateException('Ad not found');
|
||||
// }
|
||||
//
|
||||
// return $ad->isStopped();
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* This example updates the CPC bid and status for a given ad group. To get ad groups, run
|
||||
* GetAdAds.php.
|
||||
*/
|
||||
/* @param int $customerId the customer ID
|
||||
* @param $options
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runGetResponsiveSearchAds($options): mixed
|
||||
{
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||
// Creates a single shared budget to be used by the campaigns added below.
|
||||
if (!isset($options['group_id'])) {
|
||||
$options['group_id'] = null;
|
||||
}
|
||||
$resourceNames = self::getResponsiveSearchAds($googleAdsClient->getGoogleAdsClient(), $options['customer_id'], $options['group_id']);
|
||||
|
||||
return $resourceNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定广告组中未移除的自适应搜索广告。
|
||||
*
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @param int $customerId the customer ID
|
||||
* @param int|null $adGroupId the ad group ID for which responsive search ads will be retrieved.
|
||||
* If `null`, returns from all ad groups
|
||||
*/
|
||||
public static function getResponsiveSearchAds(
|
||||
GoogleAdsClient $googleAdsClient,
|
||||
int $customerId,
|
||||
?int $adGroupId
|
||||
)
|
||||
{
|
||||
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
|
||||
|
||||
// Creates a query that retrieves responsive search ads.
|
||||
$query =
|
||||
'SELECT ad_group.id, '
|
||||
. 'ad_group_ad.ad.id, '
|
||||
. 'ad_group_ad.ad.responsive_search_ad.headlines, '
|
||||
. 'ad_group_ad.ad.responsive_search_ad.descriptions, '
|
||||
. 'ad_group_ad.status '
|
||||
. 'FROM ad_group_ad '
|
||||
. 'WHERE ad_group_ad.ad.type = RESPONSIVE_SEARCH_AD '
|
||||
. 'AND ad_group_ad.status != "REMOVED"';
|
||||
if (!is_null($adGroupId)) {
|
||||
$query .= " AND ad_group.id = $adGroupId";
|
||||
}
|
||||
|
||||
// Issues a search request.
|
||||
$response =
|
||||
$googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));
|
||||
|
||||
// Iterates over all rows in all pages and prints the requested field values for
|
||||
// the responsive search ad in each row.
|
||||
$isEmptyResult = true;
|
||||
$resources = [];
|
||||
foreach ($response->iterateAllElements() as $googleAdsRow) {
|
||||
/** @var GoogleAdsRow $googleAdsRow */
|
||||
$isEmptyResult = false;
|
||||
$ad = $googleAdsRow->getAdGroupAd()->getAd();
|
||||
$resource = [];
|
||||
// printf(
|
||||
// "Responsive search ad with resource name '%s' and status '%s' was found.%s",
|
||||
// $ad->getResourceName(),
|
||||
// AdGroupAdStatus::name($googleAdsRow->getAdGroupAd()->getStatus()),
|
||||
// PHP_EOL
|
||||
// );
|
||||
$resource['resource_name'] = $ad->getResourceName();
|
||||
$resource['status'] = AdGroupAdStatus::name($googleAdsRow->getAdGroupAd()->getStatus());
|
||||
$responsiveSearchAdInfo = $ad->getResponsiveSearchAd();
|
||||
// printf(
|
||||
// 'Headlines:%1$s%2$sDescriptions:%1$s%3$s%1$s',
|
||||
// PHP_EOL,
|
||||
// self::convertAdTextAssetsToString($responsiveSearchAdInfo->getHeadlines()),
|
||||
// self::convertAdTextAssetsToString($responsiveSearchAdInfo->getDescriptions())
|
||||
// )
|
||||
$resource['content'] = sprintf(
|
||||
'Headlines:%1$s%2$sDescriptions:%1$s%3$s%1$s',
|
||||
PHP_EOL,
|
||||
self::convertAdTextAssetsToString($responsiveSearchAdInfo->getHeadlines()),
|
||||
self::convertAdTextAssetsToString($responsiveSearchAdInfo->getDescriptions())
|
||||
);
|
||||
$resources[] = $resource;
|
||||
}
|
||||
return $resources;
|
||||
// if ($isEmptyResult) {
|
||||
// print 'No responsive search ads were found.' . PHP_EOL;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the list of AdTextAsset objects into a string representation.
|
||||
*
|
||||
* @param RepeatedField $assets the list of AdTextAsset objects
|
||||
* @return string the string representation of the provided list of AdTextAsset objects
|
||||
*/
|
||||
private static function convertAdTextAssetsToString(RepeatedField $assets): string
|
||||
{
|
||||
$result = '';
|
||||
foreach ($assets as $asset) {
|
||||
/** @var AdTextAsset $asset */
|
||||
$result .= sprintf(
|
||||
"\t%s pinned to %s.%s",
|
||||
$asset->getText(),
|
||||
ServedAssetFieldType::name($asset->getPinnedField()),
|
||||
PHP_EOL
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This example updates the CPC bid and status for a given ad group. To get ad groups, run
|
||||
* GetAdAds.php.
|
||||
*/
|
||||
/* @param int $customerId the customer ID
|
||||
* @param $options
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runUpdateResponsiveSearchAd($options): mixed
|
||||
{
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||
// Creates a single shared budget to be used by the campaigns added below.
|
||||
$resourceName = self::updateResponsiveSearchAd($googleAdsClient->getGoogleAdsClient(), $options['customer_id'], $options['ad_id']);
|
||||
|
||||
return $resourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* updateResponsiveSearchAd.
|
||||
*
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @param int $customerId the customer ID
|
||||
* @param int $adId the ad ID to update
|
||||
*/
|
||||
// [START update_responsive_search_ad]
|
||||
public static function updateResponsiveSearchAd(
|
||||
GoogleAdsClient $googleAdsClient,
|
||||
int $customerId,
|
||||
int $adId
|
||||
)
|
||||
{
|
||||
// Creates an ad with the specified resource name and other changes.
|
||||
$ad = new Ad([
|
||||
'resource_name' => ResourceNames::forAd($customerId, $adId),
|
||||
'responsive_search_ad' => new ResponsiveSearchAdInfo([
|
||||
// Update some properties of the responsive search ad.
|
||||
'headlines' => [
|
||||
new AdTextAsset([
|
||||
'text' => 'Cruise to Pluto #' . Helper::getShortPrintableDatetime(),
|
||||
'pinned_field' => ServedAssetFieldType::HEADLINE_1
|
||||
]),
|
||||
new AdTextAsset(['text' => 'Tickets on sale now', 'pinned_field' => ServedAssetFieldType::HEADLINE_2]),
|
||||
new AdTextAsset(['text' => 'Buy your ticket now'])
|
||||
],
|
||||
'descriptions' => [
|
||||
new AdTextAsset(['text' => 'Best space cruise ever.']),
|
||||
new AdTextAsset([
|
||||
'text' => 'The most wonderful space experience you will ever have.'])
|
||||
]
|
||||
]),
|
||||
'final_urls' => ['http://www.baidu.com'],
|
||||
'final_mobile_urls' => ['http://www.baidu.com/mobile']
|
||||
]);
|
||||
|
||||
// Constructs an operation that will update the ad, using the FieldMasks to derive the
|
||||
// update mask. This mask tells the Google Ads API which attributes of the ad you want to
|
||||
// change.
|
||||
$adOperation = new AdOperation();
|
||||
$adOperation->setUpdate($ad);
|
||||
$adOperation->setUpdateMask(FieldMasks::allSetFieldsOf($ad));
|
||||
|
||||
// Issues a mutate request to update the ad.
|
||||
$adServiceClient = $googleAdsClient->getAdServiceClient();
|
||||
$response =
|
||||
$adServiceClient->mutateAds(MutateAdsRequest::build($customerId, [$adOperation]));
|
||||
|
||||
// Prints the resource name of the updated ad.
|
||||
/** @var Ad $updatedAd */
|
||||
$updatedAd = $response->getResults()[0];
|
||||
printf(
|
||||
"Updated ad with resource name: '%s'.%s",
|
||||
$updatedAd->getResourceName(),
|
||||
PHP_EOL
|
||||
);
|
||||
return $updatedAd->getResourceName();
|
||||
}
|
||||
// [END update_responsive_search_ad]
|
||||
|
||||
|
||||
/**
|
||||
* 根据给定的前缀搜索 Google Ads 字段,检索到关于这些字段的元数据(metadata)
|
||||
*/
|
||||
/* @param int $customerId the customer ID
|
||||
* @param $options
|
||||
* @return mixed
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function runSearchForGoogleAdsFields($options): mixed
|
||||
{
|
||||
// $googleAdsClient = $this->googleAdsClient;
|
||||
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||
// Creates a single shared budget to be used by the campaigns added below.
|
||||
$googleAdsFieldData = self::searchForGoogleAdsFields($googleAdsClient->getGoogleAdsClient(), $options['name_prefix']);
|
||||
|
||||
return $googleAdsFieldData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the example SearchForGoogleAdsFields.
|
||||
*
|
||||
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||
* @param string $namePrefix the name prefix to use in the query
|
||||
*/
|
||||
public static function searchForGoogleAdsFields(GoogleAdsClient $googleAdsClient, string $namePrefix)
|
||||
{
|
||||
$googleAdsFieldServiceClient = $googleAdsClient->getGoogleAdsFieldServiceClient();
|
||||
// Searches for all fields whose name begins with the specified namePrefix.
|
||||
// A single "%" is the wildcard token in the Google Ads Query language.
|
||||
$query = "SELECT name, category, selectable, filterable, sortable, selectable_with, "
|
||||
. "data_type, is_repeated WHERE name LIKE '$namePrefix%'";
|
||||
$response = $googleAdsFieldServiceClient->searchGoogleAdsFields(
|
||||
SearchGoogleAdsFieldsRequest::build($query)
|
||||
);
|
||||
|
||||
if (iterator_count($response->getIterator()) === 0) {
|
||||
printf(
|
||||
"No GoogleAdsFields found with a name that begins with %s.%s",
|
||||
$namePrefix,
|
||||
PHP_EOL
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Iterates over all rows and prints our the metadata of each matching GoogleAdsField.
|
||||
foreach ($response->iterateAllElements() as $googleAdsField) {
|
||||
/** @var GoogleAdsField $googleAdsField */
|
||||
$fieldInfo = [
|
||||
'name' => $googleAdsField->getName(),
|
||||
'category' => GoogleAdsFieldCategory::name($googleAdsField->getCategory()),
|
||||
'data_type' => GoogleAdsFieldDataType::name($googleAdsField->getDataType()),
|
||||
'selectable' => $googleAdsField->getSelectable() ? 'true' : 'false',
|
||||
'filterable' => $googleAdsField->getFilterable() ? 'true' : 'false',
|
||||
'sortable' => $googleAdsField->getSortable() ? 'true' : 'false',
|
||||
'repeated' => $googleAdsField->getIsRepeated() ? 'true' : 'false',
|
||||
'selectable_with' => []
|
||||
];
|
||||
// Check if there are fields that are selectable with the current field
|
||||
if ($googleAdsField->getSelectableWith()->count() > 0) {
|
||||
$selectableWithFields = iterator_to_array($googleAdsField->getSelectableWith()->getIterator());
|
||||
sort($selectableWithFields); // Sort the fields alphabetically
|
||||
$fieldInfo['selectable_with'] = $selectableWithFields;
|
||||
}
|
||||
|
||||
// Add the field info to the result array
|
||||
$googleAdsFieldData[] = $fieldInfo;
|
||||
//
|
||||
// printf("%s:%s", $googleAdsField->getName(), PHP_EOL);
|
||||
// printf(
|
||||
// " %-16s: %s%s",
|
||||
// "category:",
|
||||
// GoogleAdsFieldCategory::name($googleAdsField->getCategory()),
|
||||
// PHP_EOL
|
||||
// );
|
||||
// printf(
|
||||
// " %-16s: %s%s",
|
||||
// "data type:",
|
||||
// GoogleAdsFieldDataType::name($googleAdsField->getDataType()),
|
||||
// PHP_EOL
|
||||
// );
|
||||
// printf(
|
||||
// " %-16s: %s%s",
|
||||
// "selectable:",
|
||||
// $googleAdsField->getSelectable() ? 'true' : 'false',
|
||||
// PHP_EOL
|
||||
// );
|
||||
// printf(
|
||||
// " %-16s: %s%s",
|
||||
// "filterable:",
|
||||
// $googleAdsField->getFilterable() ? 'true' : 'false',
|
||||
// PHP_EOL
|
||||
// );
|
||||
// printf(
|
||||
// " %-16s: %s%s",
|
||||
// "sortable:",
|
||||
// $googleAdsField->getSortable() ? 'true' : 'false',
|
||||
// PHP_EOL
|
||||
// );
|
||||
// printf(
|
||||
// " %-16s: %s%s",
|
||||
// "repeated:",
|
||||
// $googleAdsField->getIsRepeated() ? 'true' : 'false',
|
||||
// PHP_EOL
|
||||
// );
|
||||
//
|
||||
// if ($googleAdsField->getSelectableWith()->count() > 0) {
|
||||
// // Prints the list of fields that are selectable with the field.
|
||||
// $selectableWithFields =
|
||||
// iterator_to_array($googleAdsField->getSelectableWith()->getIterator());
|
||||
// // Sorts and then prints the list.
|
||||
// sort($selectableWithFields);
|
||||
// print ' selectable with:' . PHP_EOL;
|
||||
// foreach ($selectableWithFields as $selectableWithField) {
|
||||
// /** @var string $selectableWithField */
|
||||
// printf(" $selectableWithField%s", PHP_EOL);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return $googleAdsFieldData; // Return the result array
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -6,6 +6,7 @@ use app\event\TiktokAdsDetails;
|
||||
use app\event\GoogleAdsCampaigns;
|
||||
use app\event\GoogleAdsGroups;
|
||||
use app\event\GoogleAdsAds;
|
||||
use app\event\GoogleAdsAssets;
|
||||
use app\event\GoogleAdsDateDatas;
|
||||
|
||||
|
||||
@ -29,6 +30,9 @@ return [
|
||||
GoogleAdsAds::type => [
|
||||
[GoogleAdsAds::class, 'getAds'],
|
||||
],
|
||||
GoogleAdsAssets::type => [
|
||||
[GoogleAdsAssets::class, 'getAssets'],
|
||||
],
|
||||
|
||||
|
||||
];
|
||||
|
@ -100,6 +100,9 @@ Route::group('/googleads', function () {
|
||||
Route::group('/group', function () {
|
||||
Route::post('/list', [GoogleAdsController::class, 'listGroups']);
|
||||
});
|
||||
Route::group('/asset', function () {
|
||||
Route::post('/list', [GoogleAdsController::class, 'listAssets']);
|
||||
});
|
||||
Route::group('/ad', function () {
|
||||
Route::post('/update', [GoogleAdsController::class, 'updateAd']);
|
||||
Route::post('/list', [GoogleAdsController::class, 'listAds']);
|
||||
|
Loading…
Reference in New Issue
Block a user