Files
adsPRO/autoload/factory/class.FacebookAds.php
Jacek Pyziak b54a9a71b1 Add CLI script to fetch active Meta Ads insights for campaigns, adsets, and ads
- Implemented a new PHP script to retrieve insights for the last N days (default 30).
- Supports command-line options for token, account ID, days, API version, and output file.
- Fetches data at campaign, adset, and ad levels, with filtering for active statuses.
- Handles JSON output and optional file saving, including directory creation if necessary.
- Includes error handling for cURL requests and JSON responses.
2026-02-20 23:45:36 +01:00

1015 lines
29 KiB
PHP

<?php
namespace factory;
class FacebookAds
{
static private function clients_has_deleted_column()
{
global $mdb;
static $has_deleted = null;
if ( $has_deleted !== null )
{
return (bool) $has_deleted;
}
try
{
$stmt = $mdb -> pdo -> prepare( 'SHOW COLUMNS FROM clients LIKE :column_name' );
if ( $stmt )
{
$stmt -> bindValue( ':column_name', 'deleted', \PDO::PARAM_STR );
$stmt -> execute();
$has_deleted = $stmt -> fetch( \PDO::FETCH_ASSOC ) ? 1 : 0;
}
else
{
$has_deleted = 0;
}
}
catch ( \Throwable $e )
{
$has_deleted = 0;
}
return (bool) $has_deleted;
}
static private function sql_clients_not_deleted( $alias = '' )
{
$alias = trim( (string) $alias );
$prefix = $alias !== '' ? $alias . '.' : '';
if ( self::clients_has_deleted_column() )
{
return 'COALESCE(' . $prefix . 'deleted, 0) = 0';
}
return '1=1';
}
static public function get_clients_for_sync( $client_id = 0 )
{
global $mdb;
$client_id = (int) $client_id;
$clients_not_deleted_sql = self::sql_clients_not_deleted();
$params = [];
$where_client = '';
if ( $client_id > 0 )
{
$where_client = ' AND id = :client_id';
$params[':client_id'] = $client_id;
}
return $mdb -> query(
'SELECT *
FROM clients
WHERE ' . $clients_not_deleted_sql . '
AND COALESCE(active, 0) = 1
AND TRIM(COALESCE(facebook_ads_account_id, \'\')) <> \'\'' . $where_client . '
ORDER BY id ASC',
$params
) -> fetchAll( \PDO::FETCH_ASSOC );
}
static public function sync_active_last_days_for_client( $client, $api, $days = 30, $active_only = true )
{
$client = is_array( $client ) ? $client : [];
$client_id = (int) ( $client['id'] ?? 0 );
$account_id = \services\FacebookAdsApi::normalize_ad_account_id( $client['facebook_ads_account_id'] ?? '' );
if ( $client_id <= 0 || !$account_id )
{
return [
'success' => false,
'client_id' => $client_id,
'errors' => [ 'Brak poprawnego client_id lub facebook_ads_account_id.' ]
];
}
$days = max( 1, min( 90, (int) $days ) );
$payload = $api -> fetch_active_insights_last_days( $account_id, $days, $active_only );
if ( $payload === false )
{
$last_error = trim( (string) \services\FacebookAdsApi::get_setting( 'facebook_ads_last_error' ) );
return [
'success' => false,
'client_id' => $client_id,
'account_id' => $account_id,
'errors' => [ $last_error !== '' ? $last_error : 'Nie udalo sie pobrac danych z Meta API.' ]
];
}
$save_result = self::save_snapshot( $client_id, $payload );
$save_result['client_id'] = $client_id;
$save_result['account_id'] = $account_id;
$save_result['days'] = $days;
$save_result['meta'] = $payload['meta'] ?? [];
return $save_result;
}
static public function sync_active_date_for_client( $client, $api, $sync_date, $active_only = true )
{
$client = is_array( $client ) ? $client : [];
$client_id = (int) ( $client['id'] ?? 0 );
$account_id = \services\FacebookAdsApi::normalize_ad_account_id( $client['facebook_ads_account_id'] ?? '' );
if ( $client_id <= 0 || !$account_id )
{
return [
'success' => false,
'client_id' => $client_id,
'errors' => [ 'Brak poprawnego client_id lub facebook_ads_account_id.' ]
];
}
$sync_timestamp = strtotime( (string) $sync_date );
if ( !$sync_timestamp )
{
return [
'success' => false,
'client_id' => $client_id,
'account_id' => $account_id,
'errors' => [ 'Niepoprawna data synchronizacji Facebook Ads.' ]
];
}
$sync_date = date( 'Y-m-d', $sync_timestamp );
$payload = $api -> fetch_active_insights_for_date( $account_id, $sync_date, $active_only );
if ( $payload === false )
{
$last_error = trim( (string) \services\FacebookAdsApi::get_setting( 'facebook_ads_last_error' ) );
return [
'success' => false,
'client_id' => $client_id,
'account_id' => $account_id,
'sync_date' => $sync_date,
'errors' => [ $last_error !== '' ? $last_error : 'Nie udalo sie pobrac danych z Meta API.' ]
];
}
$save_result = self::save_snapshot( $client_id, $payload );
$save_result['client_id'] = $client_id;
$save_result['account_id'] = $account_id;
$save_result['days'] = 1;
$save_result['sync_date'] = $sync_date;
$save_result['meta'] = $payload['meta'] ?? [];
return $save_result;
}
static public function sync_date_range_for_client( $client, $api, $since, $until, $active_only = true )
{
$client = is_array( $client ) ? $client : [];
$client_id = (int) ( $client['id'] ?? 0 );
$account_id = \services\FacebookAdsApi::normalize_ad_account_id( $client['facebook_ads_account_id'] ?? '' );
if ( $client_id <= 0 || !$account_id )
{
return [
'success' => false,
'client_id' => $client_id,
'errors' => [ 'Brak poprawnego client_id lub facebook_ads_account_id.' ]
];
}
$since_ts = strtotime( (string) $since );
$until_ts = strtotime( (string) $until );
if ( !$since_ts || !$until_ts || $since_ts > $until_ts )
{
return [
'success' => false,
'client_id' => $client_id,
'account_id' => $account_id,
'errors' => [ 'Niepoprawny zakres dat synchronizacji Facebook Ads.' ]
];
}
$since = date( 'Y-m-d', $since_ts );
$until = date( 'Y-m-d', $until_ts );
$days = (int) floor( ( $until_ts - $since_ts ) / 86400 ) + 1;
$payload = $api -> fetch_active_insights_for_range( $account_id, $since, $until, $active_only );
if ( $payload === false )
{
$last_error = trim( (string) \services\FacebookAdsApi::get_setting( 'facebook_ads_last_error' ) );
return [
'success' => false,
'client_id' => $client_id,
'account_id' => $account_id,
'since' => $since,
'until' => $until,
'errors' => [ $last_error !== '' ? $last_error : 'Nie udalo sie pobrac danych z Meta API.' ]
];
}
foreach ( [ 'campaign', 'adset', 'ad' ] as $level )
{
if ( is_array( $payload[ $level ] ?? null ) )
{
foreach ( $payload[ $level ] as &$row )
{
$row['date_start'] = $until;
}
unset( $row );
}
}
$all_time_raw = $api -> fetch_campaigns_all_time( $account_id, $active_only );
$all_time_error = '';
if ( $all_time_raw === false )
{
$all_time_error = (string) \services\FacebookAdsApi::get_setting( 'facebook_ads_last_error' );
}
$all_time_map = is_array( $all_time_raw ) ? $all_time_raw : [];
$save_result = self::save_snapshot( $client_id, $payload, $all_time_map );
$save_result['client_id'] = $client_id;
$save_result['account_id'] = $account_id;
$save_result['days'] = $days;
$save_result['since'] = $since;
$save_result['until'] = $until;
$save_result['meta'] = $payload['meta'] ?? [];
$save_result['all_time_debug'] = [
'raw_type' => is_array( $all_time_raw ) ? 'array' : ( $all_time_raw === false ? 'false' : gettype( $all_time_raw ) ),
'campaigns_count' => count( $all_time_map ),
'error' => $all_time_error,
'data' => array_slice( $all_time_map, 0, 5, true )
];
return $save_result;
}
static private function save_snapshot( $client_id, $payload, $all_time_map = [] )
{
global $mdb;
$stats = [
'success' => false,
'campaigns_synced' => 0,
'campaign_history_synced' => 0,
'ad_sets_synced' => 0,
'ad_set_history_synced' => 0,
'ads_synced' => 0,
'ad_history_synced' => 0,
'errors' => []
];
$campaign_map = [];
$ad_set_map = [];
$ad_map = [];
$campaign_rows = is_array( $payload['campaign'] ?? null ) ? $payload['campaign'] : [];
$ad_set_rows = is_array( $payload['adset'] ?? null ) ? $payload['adset'] : [];
$ad_rows = is_array( $payload['ad'] ?? null ) ? $payload['ad'] : [];
try
{
$started_transaction = false;
if ( method_exists( $mdb -> pdo, 'beginTransaction' ) && !( $mdb -> pdo -> inTransaction() ) )
{
$mdb -> pdo -> beginTransaction();
$started_transaction = true;
}
foreach ( $campaign_rows as $row )
{
$campaign_db_id = self::upsert_campaign( $client_id, $row );
if ( $campaign_db_id <= 0 )
{
continue;
}
$campaign_external_id = self::normalize_external_id( $row['campaign_id'] ?? '' );
if ( $campaign_external_id !== null )
{
$campaign_map[ $campaign_external_id ] = $campaign_db_id;
}
$roas_at = (float) ( $all_time_map[ $campaign_external_id ]['roas_all_time'] ?? 0 );
if ( self::upsert_campaign_history( $campaign_db_id, $row, $roas_at ) )
{
$stats['campaign_history_synced']++;
}
}
foreach ( $ad_set_rows as $row )
{
$campaign_db_id = self::resolve_campaign_db_id( $client_id, $row, $campaign_map );
$ad_set_db_id = self::upsert_ad_set( $client_id, $campaign_db_id, $row );
if ( $ad_set_db_id <= 0 )
{
continue;
}
$ad_set_external_id = self::normalize_external_id( $row['adset_id'] ?? '' );
if ( $ad_set_external_id !== null )
{
$ad_set_map[ $ad_set_external_id ] = $ad_set_db_id;
}
if ( self::upsert_ad_set_history( $ad_set_db_id, $row ) )
{
$stats['ad_set_history_synced']++;
}
}
foreach ( $ad_rows as $row )
{
$campaign_db_id = self::resolve_campaign_db_id( $client_id, $row, $campaign_map );
$ad_set_db_id = self::resolve_ad_set_db_id( $client_id, $campaign_db_id, $row, $ad_set_map );
$ad_db_id = self::upsert_ad( $client_id, $campaign_db_id, $ad_set_db_id, $row );
if ( $ad_db_id <= 0 )
{
continue;
}
$ad_external_id = self::normalize_external_id( $row['ad_id'] ?? '' );
if ( $ad_external_id !== null )
{
$ad_map[ $ad_external_id ] = $ad_db_id;
}
if ( self::upsert_ad_history( $ad_db_id, $row ) )
{
$stats['ad_history_synced']++;
}
}
$stats['campaigns_synced'] = count( $campaign_map );
$stats['ad_sets_synced'] = count( $ad_set_map );
$stats['ads_synced'] = count( $ad_map );
if ( !empty( $started_transaction ) && method_exists( $mdb -> pdo, 'commit' ) && $mdb -> pdo -> inTransaction() )
{
$mdb -> pdo -> commit();
}
$stats['success'] = true;
}
catch ( \Throwable $e )
{
if ( !empty( $started_transaction ) && method_exists( $mdb -> pdo, 'rollBack' ) && $mdb -> pdo -> inTransaction() )
{
$mdb -> pdo -> rollBack();
}
$stats['errors'][] = $e -> getMessage();
$stats['success'] = false;
}
return $stats;
}
static private function upsert_campaign( $client_id, $row )
{
global $mdb;
$campaign_external_id = self::normalize_external_id( $row['campaign_id'] ?? '' );
if ( $campaign_external_id === null )
{
return 0;
}
$account_id = self::normalize_external_id( $row['account_id'] ?? '' ) ?: '0';
$campaign_name = trim( (string) ( $row['campaign_name'] ?? '' ) );
$date_sync = date( 'Y-m-d H:i:s' );
$mdb -> query(
'INSERT INTO facebook_campaigns
(client_id, account_id, campaign_id, campaign_name, status, effective_status, date_sync)
VALUES
(:client_id, :account_id, :campaign_id, :campaign_name, NULL, \'ACTIVE\', :date_sync)
ON DUPLICATE KEY UPDATE
id = LAST_INSERT_ID(id),
account_id = VALUES(account_id),
campaign_name = VALUES(campaign_name),
effective_status = VALUES(effective_status),
date_sync = VALUES(date_sync)',
[
':client_id' => (int) $client_id,
':account_id' => $account_id,
':campaign_id' => $campaign_external_id,
':campaign_name' => $campaign_name,
':date_sync' => $date_sync
]
);
$db_id = (int) $mdb -> id();
if ( $db_id > 0 )
{
return $db_id;
}
$existing = $mdb -> query(
'SELECT id
FROM facebook_campaigns
WHERE client_id = :client_id
AND campaign_id = :campaign_id
LIMIT 1',
[ ':client_id' => (int) $client_id, ':campaign_id' => $campaign_external_id ]
) -> fetchColumn();
return (int) $existing;
}
static private function upsert_campaign_history( $campaign_db_id, $row, $roas_all_time = 0.0 )
{
global $mdb;
$date_add = self::normalize_date( $row['date_start'] ?? null );
if ( !$date_add )
{
return false;
}
$spend = self::to_decimal( $row['spend'] ?? 0 );
$conversion_value = self::extract_conversion_value( $row );
$roas = self::extract_roas( $row, $conversion_value, $spend );
$mdb -> query(
'INSERT INTO facebook_campaigns_history
(facebook_campaign_id, spend, impressions, clicks, ctr, cpc, conversion_value, roas, roas_all_time, date_add)
VALUES
(:campaign_id, :spend, :impressions, :clicks, :ctr, :cpc, :conversion_value, :roas, :roas_all_time, :date_add)
ON DUPLICATE KEY UPDATE
spend = VALUES(spend),
impressions = VALUES(impressions),
clicks = VALUES(clicks),
ctr = VALUES(ctr),
cpc = VALUES(cpc),
conversion_value = VALUES(conversion_value),
roas = VALUES(roas),
roas_all_time = VALUES(roas_all_time)',
[
':campaign_id' => (int) $campaign_db_id,
':spend' => $spend,
':impressions' => self::to_int( $row['impressions'] ?? 0 ),
':clicks' => self::to_int( $row['clicks'] ?? 0 ),
':ctr' => self::to_decimal( $row['ctr'] ?? 0 ),
':cpc' => self::to_decimal( $row['cpc'] ?? 0 ),
':conversion_value' => $conversion_value,
':roas' => $roas,
':roas_all_time' => (float) $roas_all_time,
':date_add' => $date_add
]
);
return true;
}
static private function upsert_ad_set( $client_id, $campaign_db_id, $row )
{
global $mdb;
$ad_set_external_id = self::normalize_external_id( $row['adset_id'] ?? '' );
if ( $ad_set_external_id === null )
{
return 0;
}
$campaign_external_id = self::normalize_external_id( $row['campaign_id'] ?? '' ) ?: '0';
$ad_set_name = trim( (string) ( $row['adset_name'] ?? '' ) );
$date_sync = date( 'Y-m-d H:i:s' );
$mdb -> query(
'INSERT INTO facebook_ad_sets
(client_id, facebook_campaign_id, campaign_id, ad_set_id, ad_set_name, status, effective_status, date_sync)
VALUES
(:client_id, :facebook_campaign_id, :campaign_id, :ad_set_id, :ad_set_name, NULL, \'ACTIVE\', :date_sync)
ON DUPLICATE KEY UPDATE
id = LAST_INSERT_ID(id),
facebook_campaign_id = VALUES(facebook_campaign_id),
campaign_id = VALUES(campaign_id),
ad_set_name = VALUES(ad_set_name),
effective_status = VALUES(effective_status),
date_sync = VALUES(date_sync)',
[
':client_id' => (int) $client_id,
':facebook_campaign_id' => $campaign_db_id > 0 ? (int) $campaign_db_id : null,
':campaign_id' => $campaign_external_id,
':ad_set_id' => $ad_set_external_id,
':ad_set_name' => $ad_set_name,
':date_sync' => $date_sync
]
);
$db_id = (int) $mdb -> id();
if ( $db_id > 0 )
{
return $db_id;
}
$existing = $mdb -> query(
'SELECT id
FROM facebook_ad_sets
WHERE client_id = :client_id
AND ad_set_id = :ad_set_id
LIMIT 1',
[ ':client_id' => (int) $client_id, ':ad_set_id' => $ad_set_external_id ]
) -> fetchColumn();
return (int) $existing;
}
static private function upsert_ad_set_history( $ad_set_db_id, $row )
{
global $mdb;
$date_add = self::normalize_date( $row['date_start'] ?? null );
if ( !$date_add )
{
return false;
}
$spend = self::to_decimal( $row['spend'] ?? 0 );
$conversion_value = self::extract_conversion_value( $row );
$roas = self::extract_roas( $row, $conversion_value, $spend );
$mdb -> query(
'INSERT INTO facebook_ad_sets_history
(facebook_ad_set_id, spend, impressions, clicks, ctr, cpc, conversion_value, roas, date_add)
VALUES
(:ad_set_id, :spend, :impressions, :clicks, :ctr, :cpc, :conversion_value, :roas, :date_add)
ON DUPLICATE KEY UPDATE
spend = VALUES(spend),
impressions = VALUES(impressions),
clicks = VALUES(clicks),
ctr = VALUES(ctr),
cpc = VALUES(cpc),
conversion_value = VALUES(conversion_value),
roas = VALUES(roas)',
[
':ad_set_id' => (int) $ad_set_db_id,
':spend' => $spend,
':impressions' => self::to_int( $row['impressions'] ?? 0 ),
':clicks' => self::to_int( $row['clicks'] ?? 0 ),
':ctr' => self::to_decimal( $row['ctr'] ?? 0 ),
':cpc' => self::to_decimal( $row['cpc'] ?? 0 ),
':conversion_value' => $conversion_value,
':roas' => $roas,
':date_add' => $date_add
]
);
return true;
}
static private function upsert_ad( $client_id, $campaign_db_id, $ad_set_db_id, $row )
{
global $mdb;
$ad_external_id = self::normalize_external_id( $row['ad_id'] ?? '' );
if ( $ad_external_id === null )
{
return 0;
}
$campaign_external_id = self::normalize_external_id( $row['campaign_id'] ?? '' ) ?: '0';
$ad_set_external_id = self::normalize_external_id( $row['adset_id'] ?? '' ) ?: '0';
$ad_name = trim( (string) ( $row['ad_name'] ?? '' ) );
$date_sync = date( 'Y-m-d H:i:s' );
$mdb -> query(
'INSERT INTO facebook_ads
(client_id, facebook_campaign_id, facebook_ad_set_id, campaign_id, ad_set_id, ad_id, ad_name, status, effective_status, date_sync)
VALUES
(:client_id, :facebook_campaign_id, :facebook_ad_set_id, :campaign_id, :ad_set_id, :ad_id, :ad_name, NULL, \'ACTIVE\', :date_sync)
ON DUPLICATE KEY UPDATE
id = LAST_INSERT_ID(id),
facebook_campaign_id = VALUES(facebook_campaign_id),
facebook_ad_set_id = VALUES(facebook_ad_set_id),
campaign_id = VALUES(campaign_id),
ad_set_id = VALUES(ad_set_id),
ad_name = VALUES(ad_name),
effective_status = VALUES(effective_status),
date_sync = VALUES(date_sync)',
[
':client_id' => (int) $client_id,
':facebook_campaign_id' => $campaign_db_id > 0 ? (int) $campaign_db_id : null,
':facebook_ad_set_id' => $ad_set_db_id > 0 ? (int) $ad_set_db_id : null,
':campaign_id' => $campaign_external_id,
':ad_set_id' => $ad_set_external_id,
':ad_id' => $ad_external_id,
':ad_name' => $ad_name,
':date_sync' => $date_sync
]
);
$db_id = (int) $mdb -> id();
if ( $db_id > 0 )
{
return $db_id;
}
$existing = $mdb -> query(
'SELECT id
FROM facebook_ads
WHERE client_id = :client_id
AND ad_id = :ad_id
LIMIT 1',
[ ':client_id' => (int) $client_id, ':ad_id' => $ad_external_id ]
) -> fetchColumn();
return (int) $existing;
}
static private function upsert_ad_history( $ad_db_id, $row )
{
global $mdb;
$date_add = self::normalize_date( $row['date_start'] ?? null );
if ( !$date_add )
{
return false;
}
$spend = self::to_decimal( $row['spend'] ?? 0 );
$conversion_value = self::extract_conversion_value( $row );
$roas = self::extract_roas( $row, $conversion_value, $spend );
$mdb -> query(
'INSERT INTO facebook_ads_history
(facebook_ad_id, spend, impressions, clicks, ctr, cpc, conversion_value, roas, date_add)
VALUES
(:ad_id, :spend, :impressions, :clicks, :ctr, :cpc, :conversion_value, :roas, :date_add)
ON DUPLICATE KEY UPDATE
spend = VALUES(spend),
impressions = VALUES(impressions),
clicks = VALUES(clicks),
ctr = VALUES(ctr),
cpc = VALUES(cpc),
conversion_value = VALUES(conversion_value),
roas = VALUES(roas)',
[
':ad_id' => (int) $ad_db_id,
':spend' => $spend,
':impressions' => self::to_int( $row['impressions'] ?? 0 ),
':clicks' => self::to_int( $row['clicks'] ?? 0 ),
':ctr' => self::to_decimal( $row['ctr'] ?? 0 ),
':cpc' => self::to_decimal( $row['cpc'] ?? 0 ),
':conversion_value' => $conversion_value,
':roas' => $roas,
':date_add' => $date_add
]
);
return true;
}
static private function resolve_campaign_db_id( $client_id, $row, &$campaign_map )
{
$campaign_external_id = self::normalize_external_id( $row['campaign_id'] ?? '' );
if ( $campaign_external_id === null )
{
return 0;
}
if ( isset( $campaign_map[ $campaign_external_id ] ) )
{
return (int) $campaign_map[ $campaign_external_id ];
}
$campaign_db_id = self::upsert_campaign( $client_id, $row );
if ( $campaign_db_id > 0 )
{
$campaign_map[ $campaign_external_id ] = $campaign_db_id;
}
return $campaign_db_id;
}
static private function resolve_ad_set_db_id( $client_id, $campaign_db_id, $row, &$ad_set_map )
{
$ad_set_external_id = self::normalize_external_id( $row['adset_id'] ?? '' );
if ( $ad_set_external_id === null )
{
return 0;
}
if ( isset( $ad_set_map[ $ad_set_external_id ] ) )
{
return (int) $ad_set_map[ $ad_set_external_id ];
}
$ad_set_db_id = self::upsert_ad_set( $client_id, $campaign_db_id, $row );
if ( $ad_set_db_id > 0 )
{
$ad_set_map[ $ad_set_external_id ] = $ad_set_db_id;
}
return $ad_set_db_id;
}
static private function normalize_external_id( $value )
{
$digits = preg_replace( '/\D+/', '', (string) $value );
if ( $digits === '' )
{
return null;
}
return $digits;
}
static private function normalize_date( $value )
{
$value = trim( (string) $value );
if ( $value === '' )
{
return null;
}
$timestamp = strtotime( $value );
if ( !$timestamp )
{
return null;
}
return date( 'Y-m-d', $timestamp );
}
static private function to_decimal( $value )
{
if ( is_string( $value ) )
{
$value = str_replace( ',', '.', $value );
}
return (float) $value;
}
static private function to_int( $value )
{
return (int) round( (float) $value );
}
static private function get_purchase_action_types_priority()
{
return [
'purchase',
'omni_purchase',
'offsite_conversion.fb_pixel_purchase',
'web_in_store_purchase',
'onsite_conversion.purchase',
'app_custom_event.fb_mobile_purchase'
];
}
static private function get_metric_map( $raw )
{
$map = [];
if ( !is_array( $raw ) )
{
return $map;
}
foreach ( $raw as $row )
{
if ( !is_array( $row ) )
{
continue;
}
$action_type = trim( (string) ( $row['action_type'] ?? '' ) );
if ( $action_type === '' )
{
continue;
}
$map[ $action_type ] = self::to_decimal( $row['value'] ?? 0 );
}
return $map;
}
static private function extract_conversion_value( $row )
{
if ( !is_array( $row ) )
{
return 0.0;
}
if ( isset( $row['conversion_value'] ) )
{
return self::to_decimal( $row['conversion_value'] );
}
$action_values_map = self::get_metric_map( $row['action_values'] ?? [] );
if ( empty( $action_values_map ) )
{
return 0.0;
}
foreach ( self::get_purchase_action_types_priority() as $action_type )
{
if ( array_key_exists( $action_type, $action_values_map ) )
{
return self::to_decimal( $action_values_map[ $action_type ] );
}
}
return 0.0;
}
static private function extract_roas( $row, $conversion_value, $spend )
{
if ( !is_array( $row ) )
{
return 0.0;
}
$purchase_roas_map = self::get_metric_map( $row['purchase_roas'] ?? [] );
foreach ( self::get_purchase_action_types_priority() as $action_type )
{
if ( array_key_exists( $action_type, $purchase_roas_map ) )
{
return self::to_decimal( $purchase_roas_map[ $action_type ] ) * 100;
}
}
$conversion_value = self::to_decimal( $conversion_value );
$spend = self::to_decimal( $spend );
if ( $conversion_value > 0 && $spend > 0 )
{
return round( ( $conversion_value / $spend ) * 100, 6 );
}
return 0.0;
}
static private function get_level_config( $level )
{
$level = strtolower( trim( (string) $level ) );
$map = [
'campaign' => [
'base_table' => 'facebook_campaigns',
'history_table' => 'facebook_campaigns_history',
'fk_column' => 'facebook_campaign_id',
'name_column' => 'campaign_name',
'external_id_column' => 'campaign_id'
],
'adset' => [
'base_table' => 'facebook_ad_sets',
'history_table' => 'facebook_ad_sets_history',
'fk_column' => 'facebook_ad_set_id',
'name_column' => 'ad_set_name',
'external_id_column' => 'ad_set_id'
],
'ad' => [
'base_table' => 'facebook_ads',
'history_table' => 'facebook_ads_history',
'fk_column' => 'facebook_ad_id',
'name_column' => 'ad_name',
'external_id_column' => 'ad_id'
]
];
return $map[ $level ] ?? null;
}
static public function get_clients_for_reports()
{
return self::get_clients_for_sync( 0 );
}
static public function get_entities_with_latest_metrics( $client_id, $level, $filters = [] )
{
global $mdb;
$cfg = self::get_level_config( $level );
if ( !$cfg )
{
return [];
}
$client_id = (int) $client_id;
if ( $client_id <= 0 )
{
return [];
}
$filters = is_array( $filters ) ? $filters : [];
$campaign_id = (int) ( $filters['campaign_id'] ?? 0 );
$ad_set_id = (int) ( $filters['ad_set_id'] ?? 0 );
$extra_where = '';
$params = [ ':client_id' => $client_id ];
if ( $level === 'adset' && $campaign_id > 0 )
{
$extra_where .= ' AND b.facebook_campaign_id = :campaign_id';
$params[':campaign_id'] = $campaign_id;
}
if ( $level === 'ad' )
{
if ( $campaign_id > 0 )
{
$extra_where .= ' AND b.facebook_campaign_id = :campaign_id';
$params[':campaign_id'] = $campaign_id;
}
if ( $ad_set_id > 0 )
{
$extra_where .= ' AND b.facebook_ad_set_id = :ad_set_id';
$params[':ad_set_id'] = $ad_set_id;
}
}
$sql = 'SELECT
b.id,
b.' . $cfg['external_id_column'] . ' AS external_id,
b.' . $cfg['name_column'] . ' AS entity_name,
h.date_add,
h.spend,
h.impressions,
h.clicks,
h.ctr,
h.cpc,
h.conversion_value,
h.roas
FROM ' . $cfg['base_table'] . ' b
LEFT JOIN ' . $cfg['history_table'] . ' h
ON h.' . $cfg['fk_column'] . ' = b.id
AND h.date_add = (
SELECT MAX(h2.date_add)
FROM ' . $cfg['history_table'] . ' h2
WHERE h2.' . $cfg['fk_column'] . ' = b.id
)
WHERE b.client_id = :client_id
' . $extra_where . '
ORDER BY b.' . $cfg['name_column'] . ' ASC';
return $mdb -> query( $sql, $params ) -> fetchAll( \PDO::FETCH_ASSOC );
}
static public function get_entity_history( $level, $entity_id, $start, $length, $revert = false )
{
global $mdb;
$cfg = self::get_level_config( $level );
if ( !$cfg )
{
return [];
}
$entity_id = (int) $entity_id;
if ( $entity_id <= 0 )
{
return [];
}
$start = max( 0, (int) $start );
$length = max( 1, min( 2000, (int) $length ) );
$order = $revert ? 'ASC' : 'DESC';
$columns = 'date_add, spend, impressions, clicks, ctr, cpc, conversion_value, roas';
if ( $level === 'campaign' )
{
$columns .= ', roas_all_time';
}
$sql = 'SELECT ' . $columns . '
FROM ' . $cfg['history_table'] . '
WHERE ' . $cfg['fk_column'] . ' = :entity_id
ORDER BY date_add ' . $order . '
LIMIT ' . $start . ', ' . $length;
return $mdb -> query( $sql, [ ':entity_id' => $entity_id ] ) -> fetchAll( \PDO::FETCH_ASSOC );
}
static public function get_entity_history_total( $level, $entity_id )
{
global $mdb;
$cfg = self::get_level_config( $level );
if ( !$cfg )
{
return 0;
}
$entity_id = (int) $entity_id;
if ( $entity_id <= 0 )
{
return 0;
}
$count = $mdb -> query(
'SELECT COUNT(*)
FROM ' . $cfg['history_table'] . '
WHERE ' . $cfg['fk_column'] . ' = :entity_id',
[ ':entity_id' => $entity_id ]
) -> fetchColumn();
return (int) $count;
}
}