$id, 'search_term_id' => $id, 'phrase' => trim( (string) ( $row['search_term'] ?? ( $item['phrase'] ?? '' ) ) ), 'action' => $action, 'reason' => $reason, 'ad_group_name' => (string) ( $row['ad_group_name'] ?? '' ), 'clicks_all_time' => (float) ( $row['clicks_all_time'] ?? 0 ), 'cost_all_time' => (float) ( $row['cost_all_time'] ?? 0 ), 'conversions_all_time' => (float) ( $row['conversions_all_time'] ?? 0 ), 'conversion_value_all_time' => (float) ( $row['conversion_value_all_time'] ?? 0 ), 'roas_all_time' => (float) ( $row['roas_all_time'] ?? 0 ) ]; } return $recommendations; } static public function main_view() { return \Tpl::view( 'campaign_terms/main_view', [ 'clients' => \factory\Campaigns::get_clients(), ] ); } static public function get_campaigns_list() { $client_id = (int) \S::get( 'client_id' ); echo json_encode( [ 'campaigns' => \factory\Campaigns::get_campaigns_list( $client_id, true ) ] ); exit; } static public function get_campaign_ad_groups() { $campaign_id = (int) \S::get( 'campaign_id' ); if ( $campaign_id <= 0 ) { echo json_encode( [ 'ad_groups' => [] ] ); exit; } echo json_encode( [ 'ad_groups' => \factory\Campaigns::get_campaign_ad_groups( $campaign_id ) ] ); exit; } static public function get_campaign_phrase_details() { $campaign_id = (int) \S::get( 'campaign_id' ); $ad_group_id = (int) \S::get( 'ad_group_id' ); if ( $campaign_id <= 0 ) { echo json_encode( [ 'search_terms' => [], 'negative_keywords' => [], 'keywords' => [] ] ); exit; } echo json_encode( [ 'search_terms' => \factory\Campaigns::get_campaign_search_terms( $campaign_id, $ad_group_id ), 'negative_keywords' => \factory\Campaigns::get_campaign_negative_keywords( $campaign_id, $ad_group_id ), 'keywords' => \factory\Campaigns::get_campaign_keywords( $campaign_id, $ad_group_id ) ] ); exit; } static public function add_negative_keyword() { $search_term_id = (int) \S::get( 'search_term_id' ); $match_type = strtoupper( trim( (string) \S::get( 'match_type' ) ) ); $scope = strtolower( trim( (string) \S::get( 'scope' ) ) ); $manual_keyword_text = trim( (string) \S::get( 'keyword_text' ) ); if ( $search_term_id <= 0 ) { echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Nie podano frazy do wykluczenia.', ], [ 'search_term_id' => $search_term_id, 'match_type_raw' => (string) \S::get( 'match_type' ), 'scope_raw' => (string) \S::get( 'scope' ), 'manual_keyword_text' => $manual_keyword_text ] ) ); exit; } if ( !in_array( $match_type, [ 'PHRASE', 'EXACT', 'BROAD' ], true ) ) { $match_type = 'PHRASE'; } if ( !in_array( $scope, [ 'campaign', 'ad_group' ], true ) ) { $scope = 'campaign'; } $context = \factory\Campaigns::get_search_term_context( $search_term_id ); if ( !$context ) { echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Nie znaleziono danych frazy.', ], [ 'search_term_id' => $search_term_id, 'manual_keyword_text' => $manual_keyword_text, 'scope' => $scope, 'match_type' => $match_type ] ) ); exit; } $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) ); $campaign_external_id = trim( (string) ( $context['external_campaign_id'] ?? '' ) ); $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) ); $context_keyword_text = trim( (string) ( $context['search_term'] ?? '' ) ); $keyword_text = $manual_keyword_text !== '' ? $manual_keyword_text : $context_keyword_text; $keyword_source = $manual_keyword_text !== '' ? 'manual' : 'search_term'; $missing_data = ( $customer_id === '' || $keyword_text === '' ); if ( $scope === 'campaign' && $campaign_external_id === '' ) { $missing_data = true; } if ( $scope === 'ad_group' && $ad_group_external_id === '' ) { $missing_data = true; } if ( $missing_data ) { echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Brak wymaganych danych Google Ads dla tej frazy.', ], [ 'customer_id' => $customer_id, 'campaign_external_id' => $campaign_external_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'keyword_source' => $keyword_source, 'scope' => $scope, 'context' => $context ] ) ); exit; } $api = new \services\GoogleAdsApi(); if ( !$api -> is_configured() ) { echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.', ], [ 'search_term_id' => $search_term_id, 'customer_id' => $customer_id, 'campaign_external_id' => $campaign_external_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'scope' => $scope ] ) ); exit; } if ( $scope === 'campaign' ) { $api_result = $api -> add_negative_keyword_to_campaign( $customer_id, $campaign_external_id, $keyword_text, $match_type ); } else { $api_result = $api -> add_negative_keyword_to_ad_group( $customer_id, $ad_group_external_id, $keyword_text, $match_type ); } if ( !( $api_result['success'] ?? false ) ) { $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' ); echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Nie udalo sie zapisac frazy wykluczajacej w Google Ads.', 'error' => $last_error ], [ 'customer_id' => $customer_id, 'campaign_external_id' => $campaign_external_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'keyword_source' => $keyword_source, 'match_type' => $match_type, 'scope' => $scope, 'api_result' => $api_result ] ) ); exit; } $verification = $api_result['verification'] ?? null; $verification_found = true; if ( is_array( $verification ) && array_key_exists( 'found', $verification ) ) { $verification_found = (bool) $verification['found']; } if ( !$verification_found && !( $api_result['duplicate'] ?? false ) ) { echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Google Ads API nie potwierdzilo dodania frazy po operacji create.', ], [ 'customer_id' => $customer_id, 'campaign_external_id' => $campaign_external_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'keyword_source' => $keyword_source, 'match_type' => $match_type, 'scope' => $scope, 'api_result' => $api_result, 'verification' => $verification ] ) ); exit; } \factory\Campaigns::upsert_campaign_negative_keyword( (int) $context['db_campaign_id'], $scope === 'campaign' ? null : (int) $context['db_ad_group_id'], $scope, $keyword_text, $match_type ); $scope_label = $scope === 'campaign' ? 'kampanii' : 'grupy reklam'; echo json_encode( self::with_optional_debug( [ 'success' => true, 'message' => ( $api_result['duplicate'] ?? false ) ? 'Fraza byla juz wykluczona na poziomie ' . $scope_label . '.' : 'Fraza zostala dodana do wykluczajacych na poziomie ' . $scope_label . '.', 'duplicate' => (bool) ( $api_result['duplicate'] ?? false ), 'match_type' => $match_type, 'scope' => $scope ], [ 'customer_id' => $customer_id, 'campaign_external_id' => $campaign_external_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'keyword_source' => $keyword_source, 'scope' => $scope, 'api_response' => $api_result['response'] ?? null, 'sent_operation' => $api_result['sent_operation'] ?? null, 'verification' => $api_result['verification'] ?? null ] ) ); exit; } static public function analyze_search_terms_with_ai() { $campaign_id = (int) \S::get( 'campaign_id' ); $ad_group_id = (int) \S::get( 'ad_group_id' ); $search_term_ids_raw = \S::get( 'search_term_ids' ); if ( $campaign_id <= 0 ) { echo json_encode( [ 'success' => false, 'message' => 'Wybierz kampanie.' ] ); exit; } if ( \services\GoogleAdsApi::get_setting( 'openai_enabled' ) === '0' ) { echo json_encode( [ 'success' => false, 'message' => 'OpenAI jest wylaczone. Wlacz je w Ustawieniach.' ] ); exit; } if ( !\services\OpenAiApi::is_configured() ) { echo json_encode( [ 'success' => false, 'message' => 'Klucz API OpenAI nie jest skonfigurowany. Przejdz do Ustawien.' ] ); exit; } $rows = \factory\Campaigns::get_campaign_search_terms( $campaign_id, $ad_group_id ); $ids_filter = []; if ( is_array( $search_term_ids_raw ) ) { foreach ( $search_term_ids_raw as $id_raw ) { $id = (int) $id_raw; if ( $id > 0 ) { $ids_filter[$id] = true; } } } elseif ( $search_term_ids_raw !== null && $search_term_ids_raw !== '' ) { $id = (int) $search_term_ids_raw; if ( $id > 0 ) { $ids_filter[$id] = true; } } if ( !empty( $ids_filter ) ) { $rows = array_values( array_filter( $rows, function( $row ) use ( $ids_filter ) { $id = (int) ( $row['id'] ?? 0 ); return $id > 0 && isset( $ids_filter[$id] ); } ) ); } if ( empty( $rows ) ) { echo json_encode( [ 'success' => false, 'message' => 'Brak fraz do analizy.' ] ); exit; } $rows = array_slice( $rows, 0, 150 ); $rows_by_id = []; foreach ( $rows as $row ) { $id = (int) ( $row['id'] ?? 0 ); if ( $id > 0 ) { $rows_by_id[$id] = $row; } } $campaign_name = ''; $campaign_type = ''; if ( !empty( $rows ) ) { $campaign_name = trim( (string) ( $rows[0]['campaign_name'] ?? '' ) ); $campaign_type = trim( (string) ( $rows[0]['advertising_channel_type'] ?? '' ) ); } $ad_group_name = ''; if ( $ad_group_id > 0 && !empty( $rows ) ) { $ad_group_name = trim( (string) ( $rows[0]['ad_group_name'] ?? '' ) ); } $result = \services\OpenAiApi::suggest_negative_keywords_to_exclude( $rows, [ 'campaign_name' => $campaign_name, 'campaign_type' => $campaign_type, 'ad_group_name' => $ad_group_name, 'ad_group_id' => $ad_group_id ] ); if ( ( $result['status'] ?? 'error' ) !== 'ok' ) { echo json_encode( [ 'success' => false, 'message' => (string) ( $result['message'] ?? 'Blad analizy OpenAI.' ) ] ); exit; } $raw_json = (string) ( $result['suggestion'] ?? '' ); $recommendations = self::normalize_ai_recommendations( $raw_json, $rows_by_id ); $exclude_count = count( array_filter( $recommendations, function( $item ) { return ( $item['action'] ?? '' ) === 'exclude'; } ) ); if ( empty( $recommendations ) ) { echo json_encode( [ 'success' => false, 'message' => 'Nie udalo sie sparsowac odpowiedzi AI.', 'raw' => $raw_json ] ); exit; } echo json_encode( [ 'success' => true, 'message' => 'Analiza zakonczona. Proponowane wykluczenia: ' . $exclude_count . '.', 'analyzed_count' => count( $rows ), 'exclude_count' => $exclude_count, 'recommendations' => $recommendations ] ); exit; } static private function delete_negative_keyword_row( $negative_keyword_id ) { $negative_keyword_id = (int) $negative_keyword_id; if ( $negative_keyword_id <= 0 ) { return [ 'success' => false, 'message' => 'Nie podano frazy do usuniecia.' ]; } $context = \factory\Campaigns::get_negative_keyword_context( $negative_keyword_id ); if ( !$context ) { return [ 'success' => false, 'message' => 'Nie znaleziono danych frazy wykluczajacej.' ]; } $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) ); $scope = strtolower( trim( (string) ( $context['scope'] ?? 'campaign' ) ) ); $match_type = strtoupper( trim( (string) ( $context['match_type'] ?? 'PHRASE' ) ) ); $keyword_text = trim( (string) ( $context['keyword_text'] ?? '' ) ); $campaign_external_id = trim( (string) ( $context['external_campaign_id'] ?? '' ) ); $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) ); if ( !in_array( $scope, [ 'campaign', 'ad_group' ], true ) ) { $scope = 'campaign'; } $missing_data = ( $customer_id === '' || $keyword_text === '' ); if ( $scope === 'campaign' && $campaign_external_id === '' ) { $missing_data = true; } if ( $scope === 'ad_group' && $ad_group_external_id === '' ) { $missing_data = true; } if ( $missing_data ) { return self::with_optional_debug( [ 'success' => false, 'message' => 'Brak wymaganych danych Google Ads dla tej frazy.' ], [ 'context' => $context ] ); } $api = new \services\GoogleAdsApi(); if ( !$api -> is_configured() ) { return self::with_optional_debug( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ], [ 'customer_id' => $customer_id, 'scope' => $scope, 'campaign_external_id' => $campaign_external_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type ] ); } if ( $scope === 'campaign' ) { $api_result = $api -> remove_negative_keyword_from_campaign( $customer_id, $campaign_external_id, $keyword_text, $match_type ); } else { $api_result = $api -> remove_negative_keyword_from_ad_group( $customer_id, $ad_group_external_id, $keyword_text, $match_type ); } if ( !( $api_result['success'] ?? false ) ) { $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' ); return self::with_optional_debug( [ 'success' => false, 'message' => 'Nie udalo sie usunac frazy wykluczajacej w Google Ads.', 'error' => $last_error ], [ 'customer_id' => $customer_id, 'scope' => $scope, 'campaign_external_id' => $campaign_external_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'api_result' => $api_result ] ); } \factory\Campaigns::delete_campaign_negative_keyword( $negative_keyword_id ); $removed = (int) ( $api_result['removed'] ?? 0 ); $scope_label = $scope === 'campaign' ? 'kampanii' : 'grupy reklam'; $message = $removed > 0 ? 'Fraza zostala usunieta z wykluczajacych na poziomie ' . $scope_label . '.' : 'Fraza nie byla juz obecna w Google Ads. Usunieto lokalny wpis.'; return self::with_optional_debug( [ 'success' => true, 'message' => $message, 'removed' => $removed, 'negative_keyword_id' => $negative_keyword_id ], [ 'customer_id' => $customer_id, 'scope' => $scope, 'campaign_external_id' => $campaign_external_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'api_result' => $api_result ] ); } static public function delete_negative_keyword() { $negative_keyword_id = (int) \S::get( 'negative_keyword_id' ); $result = self::delete_negative_keyword_row( $negative_keyword_id ); echo json_encode( $result ); exit; } static public function delete_negative_keywords() { $negative_keyword_ids_raw = \S::get( 'negative_keyword_ids' ); if ( !is_array( $negative_keyword_ids_raw ) ) { $negative_keyword_ids_raw = [ $negative_keyword_ids_raw ]; } $negative_keyword_ids = []; foreach ( $negative_keyword_ids_raw as $id_raw ) { $id = (int) $id_raw; if ( $id > 0 ) { $negative_keyword_ids[] = $id; } } $negative_keyword_ids = array_values( array_unique( $negative_keyword_ids ) ); if ( empty( $negative_keyword_ids ) ) { echo json_encode( [ 'success' => false, 'message' => 'Nie podano fraz do usuniecia.' ] ); exit; } $deleted_count = 0; $failed = []; $debug = []; $total_count = count( $negative_keyword_ids ); foreach ( $negative_keyword_ids as $negative_keyword_id ) { $result = self::delete_negative_keyword_row( $negative_keyword_id ); if ( $result['success'] ?? false ) { $deleted_count++; $debug[] = [ 'id' => $negative_keyword_id, 'success' => true, 'message' => (string) ( $result['message'] ?? '' ), 'debug' => $result['debug'] ?? null ]; continue; } $failed[] = [ 'id' => $negative_keyword_id, 'message' => (string) ( $result['message'] ?? 'Nieznany blad' ), 'error' => (string) ( $result['error'] ?? '' ), 'debug' => $result['debug'] ?? null ]; $debug[] = [ 'id' => $negative_keyword_id, 'success' => false, 'message' => (string) ( $result['message'] ?? 'Nieznany blad' ), 'error' => (string) ( $result['error'] ?? '' ), 'debug' => $result['debug'] ?? null ]; } $failed_count = count( $failed ); if ( $deleted_count === $total_count ) { $response = [ 'success' => true, 'message' => 'Usunieto zaznaczone frazy wykluczajace (' . $deleted_count . ').', 'deleted_count' => $deleted_count, 'failed_count' => 0 ]; if ( self::is_google_ads_debug_enabled() ) { $response['debug'] = $debug; } echo json_encode( $response ); exit; } if ( $deleted_count > 0 ) { $response = [ 'success' => true, 'partial' => true, 'message' => 'Usunieto ' . $deleted_count . ' z ' . $total_count . ' zaznaczonych fraz wykluczajacych.', 'deleted_count' => $deleted_count, 'failed_count' => $failed_count, 'failed' => $failed ]; if ( self::is_google_ads_debug_enabled() ) { $response['debug'] = $debug; } echo json_encode( $response ); exit; } $response = [ 'success' => false, 'message' => 'Nie udalo sie usunac zaznaczonych fraz wykluczajacych.', 'deleted_count' => 0, 'failed_count' => $failed_count, 'failed' => $failed ]; if ( self::is_google_ads_debug_enabled() ) { $response['debug'] = $debug; } echo json_encode( $response ); exit; } static public function update_keyword_match_type() { $keyword_id = (int) \S::get( 'keyword_id' ); $new_match_type = strtoupper( trim( (string) \S::get( 'new_match_type' ) ) ); if ( $keyword_id <= 0 ) { echo json_encode( [ 'success' => false, 'message' => 'Nie podano frazy.' ] ); exit; } if ( !in_array( $new_match_type, [ 'PHRASE', 'EXACT', 'BROAD' ], true ) ) { echo json_encode( [ 'success' => false, 'message' => 'Nieprawidlowy typ dopasowania.' ] ); exit; } $context = \factory\Campaigns::get_keyword_context( $keyword_id ); if ( !$context ) { echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono danych frazy.' ] ); exit; } $old_match_type = strtoupper( trim( (string) ( $context['match_type'] ?? '' ) ) ); if ( $old_match_type === $new_match_type ) { echo json_encode( [ 'success' => true, 'message' => 'Dopasowanie jest juz ustawione na ' . $new_match_type . '.' ] ); exit; } $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) ); $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) ); $keyword_text = trim( (string) ( $context['keyword_text'] ?? '' ) ); if ( $customer_id === '' || $ad_group_external_id === '' || $keyword_text === '' ) { echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Brak wymaganych danych Google Ads dla tej frazy.', ], [ 'context' => $context ] ) ); exit; } $api = new \services\GoogleAdsApi(); if ( !$api -> is_configured() ) { echo json_encode( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ] ); exit; } $api_result = $api -> update_keyword_match_type( $customer_id, $ad_group_external_id, $keyword_text, $old_match_type, $new_match_type ); if ( !( $api_result['success'] ?? false ) ) { $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' ); echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Nie udalo sie zmienic dopasowania frazy w Google Ads.', 'error' => $last_error ], [ 'customer_id' => $customer_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'old_match_type' => $old_match_type, 'new_match_type' => $new_match_type, 'api_result' => $api_result ] ) ); exit; } \factory\Campaigns::update_keyword_match_type( $keyword_id, $new_match_type ); echo json_encode( self::with_optional_debug( [ 'success' => true, 'message' => 'Dopasowanie zmienione z ' . $old_match_type . ' na ' . $new_match_type . '.' ], [ 'customer_id' => $customer_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'old_match_type' => $old_match_type, 'new_match_type' => $new_match_type, 'api_result' => $api_result ] ) ); exit; } static public function add_keyword() { $campaign_id = (int) \S::get( 'campaign_id' ); $ad_group_id = (int) \S::get( 'ad_group_id' ); $keyword_text = trim( (string) \S::get( 'keyword_text' ) ); $match_type = strtoupper( trim( (string) \S::get( 'match_type' ) ) ); if ( $campaign_id <= 0 || $ad_group_id <= 0 ) { echo json_encode( [ 'success' => false, 'message' => 'Wybierz kampanie i grupe reklam.' ] ); exit; } if ( $keyword_text === '' ) { echo json_encode( [ 'success' => false, 'message' => 'Wpisz fraze do dodania.' ] ); exit; } if ( !in_array( $match_type, [ 'PHRASE', 'EXACT', 'BROAD' ], true ) ) { $match_type = 'BROAD'; } global $mdb; $campaign_data = $mdb -> query( 'SELECT c.campaign_id AS external_campaign_id, cl.google_ads_customer_id FROM campaigns AS c INNER JOIN clients AS cl ON cl.id = c.client_id WHERE c.id = :campaign_id LIMIT 1', [ ':campaign_id' => $campaign_id ] ) -> fetch( \PDO::FETCH_ASSOC ); if ( !$campaign_data ) { echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono kampanii.' ] ); exit; } $ad_group_data = $mdb -> query( 'SELECT ad_group_id AS external_ad_group_id FROM campaign_ad_groups WHERE id = :ad_group_id LIMIT 1', [ ':ad_group_id' => $ad_group_id ] ) -> fetch( \PDO::FETCH_ASSOC ); if ( !$ad_group_data ) { echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono grupy reklam.' ] ); exit; } $customer_id = trim( (string) ( $campaign_data['google_ads_customer_id'] ?? '' ) ); $ad_group_external_id = trim( (string) ( $ad_group_data['external_ad_group_id'] ?? '' ) ); if ( $customer_id === '' || $ad_group_external_id === '' ) { echo json_encode( [ 'success' => false, 'message' => 'Brak wymaganych danych Google Ads.' ] ); exit; } $api = new \services\GoogleAdsApi(); if ( !$api -> is_configured() ) { echo json_encode( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ] ); exit; } $api_result = $api -> add_keyword_to_ad_group( $customer_id, $ad_group_external_id, $keyword_text, $match_type ); if ( !( $api_result['success'] ?? false ) ) { $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' ); echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Nie udalo sie dodac frazy w Google Ads.', 'error' => $last_error ], [ 'customer_id' => $customer_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'api_result' => $api_result ] ) ); exit; } \factory\Campaigns::insert_campaign_keyword( $campaign_id, $ad_group_id, $keyword_text, $match_type ); $match_labels = [ 'PHRASE' => 'do wyrazenia', 'EXACT' => 'scisle', 'BROAD' => 'przyblizone' ]; $match_label = $match_labels[$match_type] ?? $match_type; echo json_encode( self::with_optional_debug( [ 'success' => true, 'message' => ( $api_result['duplicate'] ?? false ) ? 'Fraza juz istnieje w Google Ads (dopasowanie ' . $match_label . ').' : 'Fraza zostala dodana (dopasowanie ' . $match_label . ').', 'duplicate' => (bool) ( $api_result['duplicate'] ?? false ) ], [ 'customer_id' => $customer_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'api_result' => $api_result ] ) ); exit; } static public function delete_keyword() { $keyword_id = (int) \S::get( 'keyword_id' ); if ( $keyword_id <= 0 ) { echo json_encode( [ 'success' => false, 'message' => 'Nie podano frazy do usuniecia.' ] ); exit; } $context = \factory\Campaigns::get_keyword_context( $keyword_id ); if ( !$context ) { echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono danych frazy.' ] ); exit; } $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) ); $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) ); $keyword_text = trim( (string) ( $context['keyword_text'] ?? '' ) ); $match_type = strtoupper( trim( (string) ( $context['match_type'] ?? 'BROAD' ) ) ); if ( $customer_id === '' || $ad_group_external_id === '' || $keyword_text === '' ) { echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Brak wymaganych danych Google Ads dla tej frazy.', ], [ 'context' => $context ] ) ); exit; } $api = new \services\GoogleAdsApi(); if ( !$api -> is_configured() ) { echo json_encode( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ] ); exit; } $api_result = $api -> remove_keyword_from_ad_group( $customer_id, $ad_group_external_id, $keyword_text, $match_type ); if ( !( $api_result['success'] ?? false ) ) { $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' ); echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Nie udalo sie usunac frazy w Google Ads.', 'error' => $last_error ], [ 'customer_id' => $customer_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'api_result' => $api_result ] ) ); exit; } \factory\Campaigns::delete_campaign_keyword( $keyword_id ); $removed = (int) ( $api_result['removed'] ?? 0 ); $message = $removed > 0 ? 'Fraza zostala usunieta z Google Ads.' : 'Fraza nie byla juz obecna w Google Ads. Usunieto lokalny wpis.'; echo json_encode( self::with_optional_debug( [ 'success' => true, 'message' => $message, 'removed' => $removed ], [ 'customer_id' => $customer_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'api_result' => $api_result ] ) ); exit; } /** * Wstrzymuje lub wznawia fraze kluczowa (ENABLED <-> PAUSED). */ static public function toggle_keyword_status() { $keyword_id = (int) \S::get( 'keyword_id' ); if ( $keyword_id <= 0 ) { echo json_encode( [ 'success' => false, 'message' => 'Nie podano frazy.' ] ); exit; } $context = \factory\Campaigns::get_keyword_context( $keyword_id ); if ( !$context ) { echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono danych frazy.' ] ); exit; } $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) ); $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) ); $keyword_text = trim( (string) ( $context['keyword_text'] ?? '' ) ); $match_type = strtoupper( trim( (string) ( $context['match_type'] ?? 'BROAD' ) ) ); $current_status = strtoupper( trim( (string) ( $context['status'] ?? 'ENABLED' ) ) ); if ( $customer_id === '' || $ad_group_external_id === '' || $keyword_text === '' ) { echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Brak wymaganych danych Google Ads dla tej frazy.', ], [ 'context' => $context ] ) ); exit; } $new_status = ( $current_status === 'PAUSED' ) ? 'ENABLED' : 'PAUSED'; $api = new \services\GoogleAdsApi(); if ( !$api -> is_configured() ) { echo json_encode( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ] ); exit; } $api_result = $api -> update_keyword_status( $customer_id, $ad_group_external_id, $keyword_text, $match_type, $new_status ); if ( !( $api_result['success'] ?? false ) ) { $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' ); echo json_encode( self::with_optional_debug( [ 'success' => false, 'message' => 'Nie udalo sie zmienic statusu frazy w Google Ads.', 'error' => $last_error ], [ 'customer_id' => $customer_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'new_status' => $new_status, 'api_result' => $api_result ] ) ); exit; } \factory\Campaigns::update_campaign_keyword_status( $keyword_id, $new_status ); $status_label = ( $new_status === 'PAUSED' ) ? 'wstrzymana' : 'wznowiona'; echo json_encode( self::with_optional_debug( [ 'success' => true, 'message' => 'Fraza zostala ' . $status_label . '.', 'new_status' => $new_status ], [ 'customer_id' => $customer_id, 'ad_group_external_id' => $ad_group_external_id, 'keyword_text' => $keyword_text, 'match_type' => $match_type, 'api_result' => $api_result ] ) ); exit; } }