feat: add new methods for direct product retrieval and improve offer ID handling in GoogleAdsApi class

This commit is contained in:
2026-02-22 15:23:34 +01:00
parent 95cfb7a495
commit 9fb7b20828
4 changed files with 216 additions and 86 deletions

View File

@@ -34,7 +34,9 @@
"mcp__serena__get_symbols_overview",
"mcp__serena__search_for_pattern",
"mcp__serena__read_file",
"mcp__serena__replace_content"
"mcp__serena__replace_content",
"mcp__serena__replace_symbol_body",
"mcp__serena__check_onboarding_performed"
]
},
"statusLine": {

View File

@@ -1,12 +1,6 @@
{
"ftp://host700513.hostido.net.pl@www@adspro.projectpro.pl": {
"public_html": {
"AGENTS.md": {
"type": "-",
"size": 2540,
"lmtime": 0,
"modified": false
},
"ajax.php": {
"type": "-",
"size": 1208,
@@ -71,13 +65,13 @@
"controls": {
"class.Allegro.php": {
"type": "-",
"size": 6456,
"size": 6939,
"lmtime": 0,
"modified": false
"modified": true
},
"class.Api.php": {
"type": "-",
"size": 19626,
"size": 20317,
"lmtime": 1744498273470,
"modified": true
},
@@ -101,14 +95,14 @@
},
"class.Clients.php": {
"type": "-",
"size": 11854,
"size": 12653,
"lmtime": 1771619242656,
"modified": false
"modified": true
},
"class.Cron.php": {
"type": "-",
"size": 198900,
"lmtime": 1771619004019,
"size": 182399,
"lmtime": 1771755214561,
"modified": false
},
"class.FacebookAds.php": {
@@ -117,11 +111,17 @@
"lmtime": 1771619366367,
"modified": false
},
"class.Logs.php": {
"type": "-",
"size": 4821,
"lmtime": 0,
"modified": false
},
"class.Products.php": {
"type": "-",
"size": 44261,
"lmtime": 1771440055487,
"modified": true
"size": 45248,
"lmtime": 1771757540415,
"modified": false
},
"class.Site.php": {
"type": "-",
@@ -131,9 +131,9 @@
},
"class.Users.php": {
"type": "-",
"size": 19292,
"size": 20039,
"lmtime": 1771617570626,
"modified": false
"modified": true
},
"class.XmlFiles.php": {
"type": "-",
@@ -151,8 +151,8 @@
},
"class.Campaigns.php": {
"type": "-",
"size": 11156,
"lmtime": 1771626553779,
"size": 11208,
"lmtime": 1771755241179,
"modified": false
},
"class.Clients.php": {
@@ -173,11 +173,17 @@
"lmtime": 1771619061605,
"modified": false
},
"class.Logs.php": {
"type": "-",
"size": 4819,
"lmtime": 0,
"modified": false
},
"class.Products.php": {
"type": "-",
"size": 32530,
"lmtime": 1771170224109,
"modified": true
"size": 33709,
"lmtime": 1771757529304,
"modified": false
},
"class.Users.php": {
"type": "-",
@@ -248,8 +254,8 @@
".claude": {
"settings.local.json": {
"type": "-",
"size": 730,
"lmtime": 1771617115553,
"size": 1421,
"lmtime": 1771755894778,
"modified": false
}
},
@@ -342,14 +348,14 @@
},
"style.css": {
"type": "-",
"size": 54205,
"lmtime": 1771494983015,
"size": 57679,
"lmtime": 1771757366015,
"modified": false
},
"style.css.map": {
"type": "-",
"size": 145847,
"lmtime": 1771488979745,
"size": 154096,
"lmtime": 1771757366015,
"modified": false
},
"style-old.css": {
@@ -366,8 +372,8 @@
},
"style.scss": {
"type": "-",
"size": 65819,
"lmtime": 1771494864189,
"size": 67231,
"lmtime": 1771757365508,
"modified": false
}
},
@@ -569,16 +575,28 @@
"lmtime": 0,
"modified": false
},
"022_facebook_ads_roas_all_time.sql": {
"type": "-",
"size": 478,
"lmtime": 1771616256503,
"modified": false
},
"023_logs.sql": {
"type": "-",
"size": 573,
"lmtime": 0,
"modified": false
},
"demo_data.sql": {
"type": "-",
"size": 21146,
"lmtime": 0,
"modified": true
},
"022_facebook_ads_roas_all_time.sql": {
"024_campaign_ad_groups_status.sql": {
"type": "-",
"size": 478,
"lmtime": 1771616256503,
"size": 131,
"lmtime": 1771755182086,
"modified": false
}
},
@@ -588,42 +606,30 @@
"lmtime": 1744488227849,
"modified": false
},
"temp_fb_authentication.html": {
"type": "-",
"size": 1167861,
"lmtime": 0,
"modified": false
},
"temp_fb_authorization.html": {
"type": "-",
"size": 1182404,
"lmtime": 0,
"modified": false
},
"temp_fb_get_started.html": {
"type": "-",
"size": 1160708,
"lmtime": 0,
"modified": false
},
"temp_fb_insights_async.html": {
"type": "-",
"size": 1190062,
"lmtime": 0,
"modified": false
},
"temp_fb_system_users.html": {
"type": "-",
"size": 1172574,
"lmtime": 0,
"modified": false
".serena": {
"cache": {
"typescript": {
"raw_document_symbols.pkl": {
"type": "-",
"size": 23480,
"lmtime": 1771755819379,
"modified": false
},
"document_symbols.pkl": {
"type": "-",
"size": 142667,
"lmtime": 1771755819382,
"modified": false
}
}
}
},
"templates": {
"products": {
"main_view.php": {
"type": "-",
"size": 64362,
"lmtime": 1771717398898,
"size": 68170,
"lmtime": 1771757685790,
"modified": false
},
"product_history.php": {
@@ -709,12 +715,6 @@
}
},
"tmp": {},
"TODO.md": {
"type": "-",
"size": 0,
"lmtime": 0,
"modified": false
},
"tools": {},
"upload": {},
"xml": {}

View File

@@ -107,8 +107,38 @@ class GoogleAdsApi
return false;
}
$remaining = array_fill_keys( $offer_ids, true );
// 1. Proba bezposredniego GET po skonstruowanym productId
$found = [];
$remaining_ids = [];
foreach ( $offer_ids as $oid )
{
$item = $this -> try_direct_merchant_product_get( $merchant_account_id, $oid, $access_token );
if ( $item !== null )
{
$link = trim( (string) ( $item['link'] ?? '' ) );
if ( $this -> is_valid_merchant_product_url( $link ) )
{
$found[ $oid ] = $link;
continue;
}
}
$remaining_ids[] = $oid;
}
if ( empty( $remaining_ids ) )
{
self::set_setting( 'google_merchant_last_error', null );
return $found;
}
// 2. Fallback: listowanie z case-insensitive matching
$remaining_lower = [];
foreach ( $remaining_ids as $oid )
{
$remaining_lower[ strtolower( $oid ) ] = $oid;
}
$page_token = '';
$safety_limit = 500;
@@ -157,8 +187,14 @@ class GoogleAdsApi
foreach ( $items as $item )
{
$offer_id = trim( (string) ( $item['offerId'] ?? '' ) );
if ( $offer_id === '' || !isset( $remaining[ $offer_id ] ) )
$item_offer_id = trim( (string) ( $item['offerId'] ?? '' ) );
if ( $item_offer_id === '' )
{
continue;
}
$item_offer_id_lower = strtolower( $item_offer_id );
if ( !isset( $remaining_lower[ $item_offer_id_lower ] ) )
{
continue;
}
@@ -169,11 +205,12 @@ class GoogleAdsApi
continue;
}
$found[ $offer_id ] = $link;
unset( $remaining[ $offer_id ] );
$original_key = $remaining_lower[ $item_offer_id_lower ];
$found[ $original_key ] = $link;
unset( $remaining_lower[ $item_offer_id_lower ] );
}
if ( empty( $remaining ) )
if ( empty( $remaining_lower ) )
{
break;
}
@@ -214,8 +251,36 @@ class GoogleAdsApi
return false;
}
$remaining = array_fill_keys( $offer_ids, true );
// 1. Proba bezposredniego GET po skonstruowanym productId (szybkie, pewne)
$found = [];
$remaining_ids = [];
foreach ( $offer_ids as $oid )
{
$item = $this -> try_direct_merchant_product_get( $merchant_account_id, $oid, $access_token );
if ( $item !== null )
{
$found[ $oid ] = $item;
}
else
{
$remaining_ids[] = $oid;
}
}
if ( empty( $remaining_ids ) )
{
self::set_setting( 'google_merchant_last_error', null );
return $found;
}
// 2. Fallback: listowanie produktow z case-insensitive matching
$remaining_lower = [];
foreach ( $remaining_ids as $oid )
{
$remaining_lower[ strtolower( $oid ) ] = $oid;
}
$page_token = '';
$safety_limit = 500;
@@ -264,17 +329,24 @@ class GoogleAdsApi
foreach ( $items as $item )
{
$offer_id = trim( (string) ( $item['offerId'] ?? '' ) );
if ( $offer_id === '' || !isset( $remaining[ $offer_id ] ) )
$item_offer_id = trim( (string) ( $item['offerId'] ?? '' ) );
if ( $item_offer_id === '' )
{
continue;
}
$found[ $offer_id ] = $item;
unset( $remaining[ $offer_id ] );
$item_offer_id_lower = strtolower( $item_offer_id );
if ( !isset( $remaining_lower[ $item_offer_id_lower ] ) )
{
continue;
}
$original_key = $remaining_lower[ $item_offer_id_lower ];
$found[ $original_key ] = $item;
unset( $remaining_lower[ $item_offer_id_lower ] );
}
if ( empty( $remaining ) )
if ( empty( $remaining_lower ) )
{
break;
}
@@ -290,6 +362,62 @@ class GoogleAdsApi
return $found;
}
/**
* Bezposredni GET produktu z Merchant Center po skonstruowanym productId.
* Format: online:{contentLanguage}:{feedLabel}:{offerId}
* Probuje kilka wariantow lang/country wyekstrahowanych z offer_id.
*/
private function try_direct_merchant_product_get( $merchant_account_id, $offer_id, $access_token )
{
$candidates = [];
// Ekstrakcja jezyka/kraju z Shopify-style offer_id: shopify_XX_...
if ( preg_match( '/^shopify_([a-z]{2})_/i', $offer_id, $m ) )
{
$lang = strtolower( $m[1] );
$country = strtoupper( $m[1] );
$candidates[] = 'online:' . $lang . ':' . $country . ':' . $offer_id;
}
// Domyslnie polski rynek
$candidates[] = 'online:pl:PL:' . $offer_id;
$candidates[] = 'online:en:PL:' . $offer_id;
$candidates = array_unique( $candidates );
foreach ( $candidates as $product_id )
{
$url = self::$MERCHANT_BASE_URL . '/' . rawurlencode( $merchant_account_id )
. '/products/' . rawurlencode( $product_id );
$ch = curl_init( $url );
curl_setopt_array( $ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $access_token,
'Accept: application/json'
],
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_TIMEOUT => 30,
] );
$response = curl_exec( $ch );
$http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
curl_close( $ch );
if ( $http_code === 200 && $response )
{
$item = json_decode( (string) $response, true );
if ( is_array( $item ) && !empty( $item['id'] ) )
{
return $item;
}
}
}
return null;
}
public function update_merchant_product_fields_by_offer_id( $merchant_account_id, $offer_id, $fields )
{
$merchant_account_id = preg_replace( '/\D+/', '', (string) $merchant_account_id );

View File

@@ -1517,7 +1517,7 @@ $( function()
( AI_CLAUDE_ENABLED ? '<button type="button" class="btn btn-sm btn-ai-suggest btn-ai-claude" data-field="title" data-provider="claude" title="Zaproponuj tytuł przez Claude"><i class="fa-solid fa-brain"></i> Claude</button>' : '' ) +
( AI_GEMINI_ENABLED ? '<button type="button" class="btn btn-sm btn-ai-suggest btn-ai-gemini" data-field="title" data-provider="gemini" title="Zaproponuj tytuł przez Gemini"><i class="fa-solid fa-diamond"></i> Gemini</button>' : '' ) +
'</div>' +
'<small>0/150 znaków</small>' +
'<small class="js-title-char-count">0/150 znaków</small>' +
'<div class="title-ai-alternatives" style="margin-top:8px;display:none;"></div>' +
'</div>' +
'<div class="form-group">' +
@@ -1610,7 +1610,7 @@ $( function()
var jc = this;
var $form = this.$content.find( 'form' );
var $inputField = this.$content.find( '.name' );
var $charCount = this.$content.find( 'small' ).first();
var $charCount = this.$content.find( '.js-title-char-count' );
var $description = this.$content.find( '.description' );
var $productUrl = this.$content.find( '.product-url' );
var $googleCategory = this.$content.find( '.google-category' );