- Create migration for global settings table and add google_ads_customer_id and google_ads_start_date columns to clients table. - Add migration to include product_url column in products_data table. - Insert demo data for campaigns, products, and their history for client 'pomysloweprezenty.pl'. - Implement client management interface with modals for adding and editing clients, including Google Ads Customer ID and data retrieval start date.
750 lines
26 KiB
PHP
750 lines
26 KiB
PHP
<?php
|
|
namespace controls;
|
|
class Cron
|
|
{
|
|
static public function cron_products()
|
|
{
|
|
global $mdb;
|
|
|
|
if ( !$client_id = \S::get( 'client_id' ) )
|
|
{
|
|
echo "Nie podano ID klienta.";
|
|
exit;
|
|
}
|
|
|
|
// check if client exists
|
|
if ( !$mdb -> count( 'clients', [ 'id' => $client_id ] ) )
|
|
{
|
|
echo json_encode( [ 'result' => "Nie znaleziono klienta o podanym ID.", "client" => "Nie istnieje" ] );
|
|
exit;
|
|
}
|
|
|
|
$client_bestseller_min_roas = \factory\Products::get_client_bestseller_min_roas( $client_id );
|
|
|
|
$db_result = $mdb -> query( 'SELECT * FROM products AS p INNER JOIN products_history AS ph ON p.id = ph.product_id WHERE p.client_id = ' . $client_id ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
|
|
$aggregated_data = [];
|
|
|
|
foreach ( $db_result as $row )
|
|
{
|
|
$product_id = $row['product_id'];
|
|
|
|
if ( !isset( $aggregated_data[$client_id] ) )
|
|
{
|
|
$aggregated_data[$client_id] = [];
|
|
}
|
|
|
|
if (!isset($aggregated_data[$client_id][$product_id]))
|
|
{
|
|
$aggregated_data[$client_id][$product_id] = [
|
|
'product_id' => $product_id,
|
|
'name' => $row['name'],
|
|
'impressions' => 0,
|
|
'clicks' => 0,
|
|
'cost' => 0.0,
|
|
'conversions' => 0,
|
|
'conversions_value' => 0.0
|
|
];
|
|
}
|
|
|
|
$aggregated_data[$client_id][$product_id]['impressions'] += $row['impressions'];
|
|
$aggregated_data[$client_id][$product_id]['clicks'] += $row['clicks'];
|
|
$aggregated_data[$client_id][$product_id]['cost'] += $row['cost'];
|
|
$aggregated_data[$client_id][$product_id]['conversions'] += $row['conversions'];
|
|
$aggregated_data[$client_id][$product_id]['conversions_value'] += $row['conversions_value'];
|
|
}
|
|
|
|
$products_ids = $mdb -> select( 'products', 'id', [ 'client_id' => $client_id ] );
|
|
foreach ( $products_ids as $product_id )
|
|
{
|
|
$products_ids_array[] = $product_id;
|
|
}
|
|
|
|
$mdb -> delete( 'products_temp', [ 'product_id' => $products_ids_array ] );
|
|
|
|
foreach ( $aggregated_data as $client_offers )
|
|
{
|
|
foreach ( $client_offers as $offer_data )
|
|
{
|
|
// Obliczamy wartości CPC oraz conversions_price
|
|
$cpc = $offer_data['clicks'] > 0 ? round( $offer_data['cost'] / $offer_data['clicks'], 6 ) : 0;
|
|
$roas = ($offer_data['conversions'] > 0 and $offer_data['cost']) ? round($offer_data['conversions_value'] / $offer_data['cost'], 2) * 100 : 0;
|
|
|
|
$impressions_30 = \factory\Products::get_impressions_30( $offer_data['product_id'] );
|
|
|
|
// update custom_label_4 only current is empty or is bestseller
|
|
$custom_label_4 = \factory\Products::get_product_data( $offer_data['product_id'], 'custom_label_4' );
|
|
if ( $custom_label_4 == null || $custom_label_4 == 'bestseller' and (int)$client_bestseller_min_roas > 0 )
|
|
{
|
|
if ( $roas > $client_bestseller_min_roas and $offer_data['conversions'] > 10 )
|
|
{
|
|
$new_custom_label_4 = 'bestseller';
|
|
}
|
|
else
|
|
{
|
|
$new_custom_label_4 = null;
|
|
}
|
|
|
|
$offers_data_tmp = $mdb -> get( 'products_data', '*', [ 'product_id' => $offer_data['product_id'] ] );
|
|
if ( isset( $offers_data_tmp['id'] ) )
|
|
{
|
|
if ( $new_custom_label_4 != $offers_data_tmp['custom_label_4'] )
|
|
$mdb -> insert( 'products_comments', [
|
|
'product_id' => $offer_data['product_id'],
|
|
'comment' => 'Zmiana pola "custom_label_4" na: ' . $new_custom_label_4,
|
|
'type' => 1,
|
|
'date_add' => date( 'Y-m-d' )
|
|
] );
|
|
|
|
$mdb -> update( 'products_data', [
|
|
'custom_label_4' => $new_custom_label_4
|
|
], [ 'id' => $offers_data_tmp['id'] ] );
|
|
}
|
|
else
|
|
{
|
|
$mdb -> insert( 'products_data', [
|
|
'product_id' => $offer_data['product_id'],
|
|
'custom_label_4' => $new_custom_label_4
|
|
] );
|
|
|
|
if ( $new_custom_label_4 == 'bestseller' )
|
|
{
|
|
$mdb -> insert( 'products_comments', [
|
|
'product_id' => $offer_data['product_id'],
|
|
'comment' => 'Zmiana pola "custom_label_4" na: bestseller',
|
|
'type' => 1,
|
|
'date_add' => date( 'Y-m-d' )
|
|
] );
|
|
}
|
|
}
|
|
}
|
|
|
|
// if ( $impressions_30 <= 30 )
|
|
// $custom_label_3 = 'product_zombie';
|
|
// else
|
|
// $custom_label_3 = null;
|
|
|
|
// $offers_data_tmp = $mdb -> get( 'products_data', '*', [ 'product_id' => $offer_data['product_id'] ] );
|
|
// if ( isset( $offers_data_tmp['id'] ) )
|
|
// {
|
|
// if ( $custom_label_3 != $offers_data_tmp['custom_label_3'] )
|
|
// $mdb -> insert( 'products_comments', [
|
|
// 'product_id' => $offer_data['product_id'],
|
|
// 'comment' => 'Zmiana pola "custom_label_3" na: ' . $custom_label_3,
|
|
// 'type' => 1,
|
|
// 'date_add' => date( 'Y-m-d' )
|
|
// ] );
|
|
|
|
// $mdb -> update( 'products_data', [
|
|
// 'custom_label_3' => $custom_label_3
|
|
// ], [ 'id' => $offers_data_tmp['id'] ] );
|
|
// }
|
|
// else
|
|
// {
|
|
// $mdb -> insert( 'products_data', [
|
|
// 'product_id' => $offer_data['product_id'],
|
|
// 'custom_label_3' => $custom_label_3
|
|
// ] );
|
|
|
|
// if ( $custom_label_3 == 'product_zombie' )
|
|
// {
|
|
// $mdb -> insert( 'products_comments', [
|
|
// 'product_id' => $offer_data['product_id'],
|
|
// 'comment' => 'Zmiana pola "custom_label_3" na: product_zombie',
|
|
// 'type' => 1,
|
|
// 'date_add' => date( 'Y-m-d' )
|
|
// ] );
|
|
// }
|
|
// }
|
|
|
|
// Zapisujemy każdy zsumowany wpis do offers_temp
|
|
$clicks_30 = \factory\Products::get_clicks_30( $offer_data['product_id'] );
|
|
|
|
$mdb -> insert( 'products_temp', [
|
|
'product_id' => $offer_data['product_id'],
|
|
'name' => $offer_data['name'],
|
|
'impressions' => $offer_data['impressions'],
|
|
'impressions_30' => $impressions_30,
|
|
'clicks' => $offer_data['clicks'],
|
|
'clicks_30' => $clicks_30,
|
|
'ctr' => round( $offer_data['clicks'] / $offer_data['impressions'], 4 ) * 100,
|
|
'cost' => $offer_data['cost'],
|
|
'conversions' => $offer_data['conversions'],
|
|
'conversions_value' => $offer_data['conversions_value'],
|
|
'cpc' => $cpc,
|
|
'roas' => $roas,
|
|
]);
|
|
}
|
|
}
|
|
|
|
echo json_encode( [ 'result' => "Agregacja zakończona, dane zapisane do offers_temp." ] );
|
|
exit;
|
|
}
|
|
|
|
static public function cron_products_history_30()
|
|
{
|
|
global $mdb;
|
|
|
|
$start_time = microtime(true);
|
|
|
|
$client_id = \S::get( 'client_id' );
|
|
|
|
if ( !$client_id )
|
|
{
|
|
echo json_encode( [ 'result' => "Nie podano ID klienta." ] );
|
|
exit;
|
|
}
|
|
|
|
if ( !$mdb -> count( 'clients', [ 'id' => $client_id ] ) )
|
|
{
|
|
echo json_encode( [ 'result' => "Nie znaleziono klienta o podanym ID.", "client" => "Nie istnieje" ] );
|
|
exit;
|
|
}
|
|
|
|
$products = $mdb -> select( 'products', 'id', [ 'client_id' => $client_id ] );
|
|
foreach ( $products as $product )
|
|
{
|
|
$dates = $mdb -> query( 'SELECT id, date_add FROM products_history WHERE product_id = ' . $product . ' AND updated = 1 ORDER BY date_add DESC' ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
foreach ( $dates as $date )
|
|
{
|
|
self::cron_product_history_30_save( $product, $date['date_add'] );
|
|
$mdb -> update( 'products_history', [ 'updated' => 0 ], [ 'id' => $date['id'] ] );
|
|
}
|
|
}
|
|
|
|
$end_time = microtime(true);
|
|
$execution_time = $end_time - $start_time;
|
|
echo json_encode( [ 'result' => "Agregacja zakończona, dane zapisane do offers_history_30. Czas wykonania skryptu: " . round($execution_time, 4) . " sekund." ] );
|
|
exit;
|
|
}
|
|
|
|
static public function get_roas_all_time( $product_id, $date_to )
|
|
{
|
|
global $mdb;
|
|
|
|
$roas_all_time = $mdb -> query( 'SELECT SUM(conversions_value) / SUM(cost) * 100 AS roas_all_time FROM products_history WHERE product_id = ' . $product_id . ' AND date_add <= \'' . $date_to . '\'' ) -> fetchColumn();
|
|
return round( $roas_all_time, 2 );
|
|
}
|
|
|
|
static public function cron_product_history_30_save( $product_id, $date_to )
|
|
{
|
|
global $mdb;
|
|
|
|
$data = $mdb -> query( 'SELECT * FROM products_history WHERE product_id = ' . $product_id . ' AND date_add <= \'' . $date_to . '\' ORDER BY date_add DESC LIMIT 30' ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
|
|
// Inicjalizacja tablic do przechowywania danych
|
|
$offers_data = [];
|
|
|
|
// Grupowanie danych według produktów
|
|
foreach ( $data as $entry )
|
|
{
|
|
if ( !isset( $offers_data[$product_id] ) )
|
|
{
|
|
$offers_data[$product_id] = [
|
|
'impressions' => 0,
|
|
'clicks' => 0,
|
|
'cost' => 0.0,
|
|
'conversions' => 0,
|
|
'conversions_value' => 0.0,
|
|
'roas' => 0,
|
|
'days_counted' => []
|
|
];
|
|
}
|
|
|
|
// Sumowanie danych według produktu
|
|
$offers_data[$product_id]['impressions'] += $entry['impressions'];
|
|
$offers_data[$product_id]['clicks'] += $entry['clicks'];
|
|
$offers_data[$product_id]['cost'] += $entry['cost'];
|
|
$offers_data[$product_id]['conversions'] += $entry['conversions'];
|
|
$offers_data[$product_id]['conversions_value'] += $entry['conversions_value'];
|
|
$offers_data[$product_id]['days_counted'][] = $entry['date_add'];
|
|
}
|
|
|
|
foreach ( $offers_data as $offer )
|
|
{
|
|
$day_count = count( $offer['days_counted'] );
|
|
|
|
$impressions = $offer['impressions'];
|
|
$clicks = $offer['clicks'];
|
|
$ctr = ( $clicks > 0 and $impressions ) ? round( $clicks / $impressions, 4 ) * 100 : 0;
|
|
$cost = $offer['cost'];
|
|
$conversions = $offer['conversions'];
|
|
$conversions_value = $offer['conversions_value'];
|
|
$roas = ( $conversions_value > 0 and $cost ) ? round( $conversions_value / $cost, 2 ) * 100 : 0;
|
|
|
|
if ( $mdb -> count( 'products_history', [ 'AND' => [ 'product_id' => $product_id, 'date_add[<=]' => $date_to ] ] ) >= 14 )
|
|
{
|
|
if ( $mdb -> count( 'products_history_30', [ 'AND' => [ 'product_id' => $product_id, 'date_add' => $date_to ] ] ) > 0 )
|
|
{
|
|
$mdb -> update( 'products_history_30', [
|
|
'impressions' => $impressions,
|
|
'clicks' => $clicks,
|
|
'ctr' => $ctr,
|
|
'cost' => $cost,
|
|
'conversions' => $conversions,
|
|
'conversions_value' => $conversions_value,
|
|
'roas' => $roas,
|
|
'roas_all_time' => self::get_roas_all_time( $product_id, $date_to )
|
|
], [ 'AND' => [ 'product_id' => $product_id, 'date_add' => $date_to ] ] );
|
|
}
|
|
else
|
|
{
|
|
$mdb -> insert( 'products_history_30', [
|
|
'product_id' => $product_id,
|
|
'impressions' => $impressions,
|
|
'clicks' => $clicks,
|
|
'ctr' => $ctr,
|
|
'cost' => $cost,
|
|
'conversions' => $conversions,
|
|
'conversions_value' => $conversions_value,
|
|
'roas' => $roas,
|
|
'roas_all_time' => self::get_roas_all_time( $product_id, $date_to ),
|
|
'date_add' => $date_to
|
|
] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static public function cron_xml()
|
|
{
|
|
global $mdb;
|
|
|
|
if ( !$client_id = \S::get( 'client_id' ) )
|
|
{
|
|
echo json_encode( [ 'result' => "Nie podano ID klienta." ] );
|
|
exit;
|
|
}
|
|
|
|
if ( !$mdb -> count( 'clients', [ 'id' => $client_id ] ) )
|
|
{
|
|
echo json_encode( [ 'result' => "Nie znaleziono klienta o podanym ID.", "client" => "Nie istnieje" ] );
|
|
exit;
|
|
}
|
|
|
|
$results = $mdb -> query( 'SELECT * FROM products AS p INNER JOIN products_data AS pd ON p.id = pd.product_id WHERE p.client_id = ' . $client_id ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
|
|
// if empty results
|
|
if ( empty( $results ) )
|
|
{
|
|
echo json_encode( [ 'result' => "Brak produktów do wygenerowania pliku XML." ] );
|
|
exit;
|
|
}
|
|
|
|
$doc = new \DOMDocument('1.0', 'UTF-8');
|
|
$xmlRoot = $doc->createElement('rss');
|
|
$xmlRoot = $doc->appendChild($xmlRoot);
|
|
$xmlRoot->setAttribute('version', '2.0');
|
|
$xmlRoot->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:g', 'http://base.google.com/ns/1.0');
|
|
|
|
$channelNode = $xmlRoot->appendChild($doc->createElement('channel'));
|
|
$channelNode->appendChild($doc->createElement('title', 'Custom Feed'));
|
|
$channelNode->appendChild($doc->createElement('link', 'https://ads.pagedev.pl'));
|
|
|
|
$fieldMappings = [
|
|
'title' => 'g:title',
|
|
'description' => 'g:description',
|
|
'custom_label_4' => 'g:custom_label_4',
|
|
'custom_label_3' => 'g:custom_label_3',
|
|
'google_product_category' => 'g:google_product_category'
|
|
];
|
|
|
|
foreach ($results as $row)
|
|
{
|
|
$hasValidField = false;
|
|
foreach ($fieldMappings as $dbField => $xmlTag) {
|
|
if (!empty($row[$dbField])) {
|
|
$hasValidField = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( $hasValidField )
|
|
{
|
|
$itemNode = $channelNode->appendChild($doc->createElement('item'));
|
|
|
|
$offer_id = $mdb -> get( 'products', 'offer_id', [ 'id' => $row['product_id'] ] );
|
|
$offer_id = str_replace( 'shopify_pl', 'shopify_PL', $offer_id );
|
|
$p_gid = $itemNode->appendChild($doc->createElement('id', $offer_id));
|
|
|
|
foreach ($fieldMappings as $dbField => $xmlTag) {
|
|
if (!empty($row[$dbField])) {
|
|
$itemNode->appendChild($doc->createElement($xmlTag, $row[$dbField]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
file_put_contents('xml/custom-feed-' . $_GET['client_id'] . '.xml', $doc->saveXML());
|
|
|
|
echo json_encode( [ 'result' => "Plik XML został wygenerowany <a href=\"https://adspro.projectpro.pl/xml/custom-feed-" . $_GET['client_id'] . ".xml\">https://adspro.projectpro.pl/xml/custom-feed-" . $_GET['client_id'] . ".xml</a>." ] );
|
|
exit;
|
|
}
|
|
|
|
static public function cron_phrases()
|
|
{
|
|
global $mdb;
|
|
|
|
if ( !$client_id = \S::get( 'client_id' ) )
|
|
{
|
|
echo json_encode( [ 'result' => "Nie podano ID klienta." ] );
|
|
exit;
|
|
}
|
|
|
|
if ( !$mdb -> count( 'clients', [ 'id' => $client_id ] ) )
|
|
{
|
|
echo json_encode( [ 'result' => "Nie znaleziono klienta o podanym ID.", "client" => "Nie istnieje" ] );
|
|
exit;
|
|
}
|
|
|
|
$data = $mdb -> query( 'SELECT * FROM phrases AS p INNER JOIN phrases_history AS ph ON p.id = ph.phrase_id WHERE p.client_id = ' . $client_id ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
|
|
$aggregated_data = [];
|
|
|
|
foreach ( $data as $row )
|
|
{
|
|
$phrase_id = $row['phrase_id'];
|
|
|
|
if ( !isset( $aggregated_data[$client_id] ) )
|
|
{
|
|
$aggregated_data[$client_id] = [];
|
|
}
|
|
|
|
if ( !isset( $aggregated_data[$client_id][$phrase_id] ) )
|
|
{
|
|
$aggregated_data[$client_id][$phrase_id] = [
|
|
'phrase_id' => $phrase_id,
|
|
'phrase' => $row['phrase'],
|
|
'impressions' => 0,
|
|
'clicks' => 0,
|
|
'cost' => 0.0,
|
|
'conversions' => 0,
|
|
'conversions_value' => 0.0
|
|
];
|
|
}
|
|
|
|
$aggregated_data[$client_id][$phrase_id]['impressions'] += $row['impressions'];
|
|
$aggregated_data[$client_id][$phrase_id]['clicks'] += $row['clicks'];
|
|
$aggregated_data[$client_id][$phrase_id]['cost'] += $row['cost'];
|
|
$aggregated_data[$client_id][$phrase_id]['conversions'] += $row['conversions'];
|
|
$aggregated_data[$client_id][$phrase_id]['conversions_value'] += $row['conversions_value'];
|
|
}
|
|
|
|
$phrases_ids = $mdb -> select( 'phrases', 'id', [ 'client_id' => $client_id ] );
|
|
foreach ( $phrases_ids as $phrase_id )
|
|
{
|
|
$phrases_ids_array[] = $phrase_id -> id;
|
|
}
|
|
|
|
$mdb -> delete( 'phrases_temp', [ 'phrase_id' => $phrases_ids_array ] );
|
|
|
|
foreach ( $aggregated_data as $client_phrases )
|
|
{
|
|
foreach ( $client_phrases as $phrase_data )
|
|
{
|
|
$cpc = $phrase_data['clicks'] > 0 ? round( $phrase_data['cost'] / $phrase_data['clicks'], 6 ) : 0;
|
|
$roas = ( $phrase_data['conversions'] > 0 and $phrase_data['cost'] ) ? round( $phrase_data['conversions_value'] / $phrase_data['cost'], 2 ) * 100 : 0;
|
|
|
|
$mdb -> insert( 'phrases_temp', [
|
|
'phrase_id' => $phrase_data['phrase_id'],
|
|
'phrase' => $phrase_data['phrase'],
|
|
'impressions' => $phrase_data['impressions'],
|
|
'clicks' => $phrase_data['clicks'],
|
|
'cost' => $phrase_data['cost'],
|
|
'conversions' => $phrase_data['conversions'],
|
|
'conversions_value' => $phrase_data['conversions_value'],
|
|
'cpc' => $cpc,
|
|
'roas' => $roas,
|
|
] );
|
|
}
|
|
}
|
|
|
|
echo json_encode( [ 'result' => "Agregacja zakończona, dane zapisane do phrases_temp." ] );
|
|
exit;
|
|
}
|
|
|
|
static public function cron_phrases_history_30()
|
|
{
|
|
global $mdb;
|
|
|
|
$start_time = microtime( true ); // Rozpoczęcie mierzenia czasu
|
|
|
|
$client_id = \S::get( 'client_id' ); // Pobranie ID klienta
|
|
|
|
if ( !$client_id ) // Jeśli nie podano ID klienta
|
|
{
|
|
echo json_encode( [ 'result' => "Nie podano ID klienta." ] ); // Wyświetlenie komunikatu
|
|
exit; // Zakończenie działania skryptu
|
|
}
|
|
|
|
if ( !$mdb -> count( 'clients', [ 'id' => $client_id ] ) ) // Sprawdzenie, czy klient istnieje
|
|
{
|
|
echo json_encode( [ 'result' => "Nie znaleziono klienta o podanym ID.", "client" => "Nie istnieje" ] ); // Wyświetlenie komunikatu
|
|
exit; // Zakończenie działania skryptu
|
|
}
|
|
|
|
// Pobranie bieżącej daty i daty sprzed 30 dni
|
|
$phrases = $mdb -> query( 'SELECT * FROM phrases WHERE client_id = ' . $client_id ) -> fetchAll( \PDO::FETCH_ASSOC ); // Pobranie fraz dla danego klienta
|
|
|
|
foreach ( $phrases as $phrase )
|
|
{
|
|
for ( $i = 0; $i < 30; $i++ )
|
|
{
|
|
$date_to = date( 'Y-m-d', strtotime( '-' . ( 1 + $i ) . ' days' ) );
|
|
$date_from = date( 'Y-m-d', strtotime( '-' . ( 31 + $i ) . ' days' ) );
|
|
|
|
$data_updated = false;
|
|
|
|
if ( $mdb -> count( 'phrases_history', [ 'AND' => [ 'phrase_id' => $phrase['id'], 'date_add[>=]' => $date_from, 'date_add[<=]' => $date_to, 'updated' => 1 ] ] ) > 0 )
|
|
{
|
|
$data_updated = true;
|
|
}
|
|
|
|
if ( $data_updated )
|
|
{
|
|
self::cron_phrase_history_30_save( $phrase['id'], $date_from, $date_to );
|
|
}
|
|
}
|
|
|
|
$mdb -> update( 'phrases_history', [ 'updated' => 0 ], [ 'AND' => [ 'phrase_id' => $phrase['id'], 'updated' => 1 ] ] );
|
|
}
|
|
|
|
$end_time = microtime( true ); // Zakończenie mierzenia czasu
|
|
$execution_time = $end_time - $start_time; // Obliczenie czasu wykonania
|
|
|
|
echo json_encode( [ 'result' => "Agregacja zakończona, dane zapisane do phrases_history_30. Czas wykonania skryptu: " . round( $execution_time, 4 ) . " sekund.", 'client' => \factory\Campaigns::get_client_name( $client_id ) ] ); // Wyświetlenie komunikatu
|
|
exit;
|
|
}
|
|
|
|
// ===========================
|
|
// KAMPANIE - Google Ads API
|
|
// ===========================
|
|
|
|
static public function cron_campaigns()
|
|
{
|
|
global $mdb;
|
|
|
|
$api = new \services\GoogleAdsApi();
|
|
|
|
if ( !$api -> is_configured() )
|
|
{
|
|
echo json_encode( [ 'result' => 'Google Ads API nie jest skonfigurowane. Uzupelnij dane w Ustawieniach.' ] );
|
|
exit;
|
|
}
|
|
|
|
// Pobierz klientów z ustawionym Google Ads Customer ID
|
|
$clients = $mdb -> select( 'clients', '*', [
|
|
'google_ads_customer_id[!]' => null
|
|
] );
|
|
|
|
if ( empty( $clients ) )
|
|
{
|
|
echo json_encode( [ 'result' => 'Brak klientow z ustawionym Google Ads Customer ID.' ] );
|
|
exit;
|
|
}
|
|
|
|
$today = date( 'Y-m-d' );
|
|
$processed = 0;
|
|
$errors = [];
|
|
|
|
foreach ( $clients as $client )
|
|
{
|
|
$customer_id = $client['google_ads_customer_id'];
|
|
|
|
// Pobierz dane 30-dniowe
|
|
$campaigns_30 = $api -> get_campaigns_30_days( $customer_id );
|
|
if ( $campaigns_30 === false )
|
|
{
|
|
$last_err = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' );
|
|
$errors[] = 'Blad API dla klienta ' . $client['name'] . ' (ID: ' . $customer_id . '): ' . $last_err;
|
|
continue;
|
|
}
|
|
|
|
// Pobierz dane all-time
|
|
$campaigns_all_time = $api -> get_campaigns_all_time( $customer_id );
|
|
$all_time_map = [];
|
|
if ( is_array( $campaigns_all_time ) )
|
|
{
|
|
foreach ( $campaigns_all_time as $cat )
|
|
{
|
|
$all_time_map[ $cat['campaign_id'] ] = $cat['roas_all_time'];
|
|
}
|
|
}
|
|
|
|
foreach ( $campaigns_30 as $campaign )
|
|
{
|
|
// Upsert kampanii
|
|
if ( !$mdb -> count( 'campaigns', [ 'AND' => [
|
|
'client_id' => $client['id'],
|
|
'campaign_id' => $campaign['campaign_id']
|
|
] ] ) )
|
|
{
|
|
$mdb -> insert( 'campaigns', [
|
|
'client_id' => $client['id'],
|
|
'campaign_id' => $campaign['campaign_id'],
|
|
'campaign_name' => $campaign['campaign_name']
|
|
] );
|
|
$db_campaign_id = $mdb -> id();
|
|
}
|
|
else
|
|
{
|
|
$db_campaign_id = $mdb -> get( 'campaigns', 'id', [ 'AND' => [
|
|
'client_id' => $client['id'],
|
|
'campaign_id' => $campaign['campaign_id']
|
|
] ] );
|
|
|
|
// Aktualizuj nazwe kampanii jesli sie zmienila
|
|
$mdb -> update( 'campaigns', [
|
|
'campaign_name' => $campaign['campaign_name']
|
|
], [ 'id' => $db_campaign_id ] );
|
|
}
|
|
|
|
// Budowanie strategii biddingu
|
|
$bidding_strategy = self::format_bidding_strategy(
|
|
$campaign['bidding_strategy'],
|
|
$campaign['target_roas'] ?? 0
|
|
);
|
|
|
|
// Dane historii
|
|
$history_data = [
|
|
'roas_30_days' => $campaign['roas_30_days'],
|
|
'roas_all_time' => $all_time_map[ $campaign['campaign_id'] ] ?? 0,
|
|
'budget' => $campaign['budget'],
|
|
'money_spent' => $campaign['money_spent'],
|
|
'conversion_value' => $campaign['conversion_value'],
|
|
'bidding_strategy' => $bidding_strategy,
|
|
];
|
|
|
|
// Upsert do campaigns_history
|
|
if ( $mdb -> count( 'campaigns_history', [ 'AND' => [
|
|
'campaign_id' => $db_campaign_id,
|
|
'date_add' => $today
|
|
] ] ) )
|
|
{
|
|
$mdb -> update( 'campaigns_history', $history_data, [ 'AND' => [
|
|
'campaign_id' => $db_campaign_id,
|
|
'date_add' => $today
|
|
] ] );
|
|
}
|
|
else
|
|
{
|
|
$history_data['campaign_id'] = $db_campaign_id;
|
|
$history_data['date_add'] = $today;
|
|
$mdb -> insert( 'campaigns_history', $history_data );
|
|
}
|
|
|
|
$processed++;
|
|
}
|
|
}
|
|
|
|
echo json_encode( [
|
|
'result' => 'Synchronizacja zakonczona. Przetworzono kampanii: ' . $processed . '.',
|
|
'errors' => $errors
|
|
] );
|
|
exit;
|
|
}
|
|
|
|
static private function format_bidding_strategy( $strategy_type, $target_roas = 0 )
|
|
{
|
|
$map = [
|
|
'MAXIMIZE_CONVERSIONS' => 'Maksymalizacja liczby konwersji',
|
|
'MAXIMIZE_CONVERSION_VALUE' => 'Maksymalizacja wartosci konwersji',
|
|
'TARGET_ROAS' => 'Docelowy ROAS',
|
|
'TARGET_CPA' => 'Docelowy CPA',
|
|
'MANUAL_CPC' => 'Reczny CPC',
|
|
'MANUAL_CPM' => 'Reczny CPM',
|
|
'TARGET_IMPRESSION_SHARE' => 'Docelowy udzial w wyswietleniach',
|
|
];
|
|
|
|
$label = $map[ $strategy_type ] ?? $strategy_type ?? 'brak';
|
|
|
|
if ( $target_roas > 0 )
|
|
{
|
|
$label .= ' | docelowy ROAS: ' . round( $target_roas * 100 ) . '%';
|
|
}
|
|
|
|
return $label;
|
|
}
|
|
|
|
// ===========================
|
|
// FRAZY - history 30
|
|
// ===========================
|
|
|
|
static public function cron_phrase_history_30_save( $phrase_id, $date_from, $date_to )
|
|
{
|
|
global $mdb;
|
|
|
|
$data = $mdb -> query( 'SELECT * FROM phrases_history WHERE phrase_id = ' . $phrase_id . ' AND date_add >= \'' . $date_from . '\' AND date_add <= \'' . $date_to . '\' ORDER BY date_add ASC' ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
|
|
// Inicjalizacja tablic do przechowywania danych
|
|
$phrases_data = [];
|
|
|
|
// Grupowanie danych według fraz
|
|
foreach ( $data as $entry )
|
|
{
|
|
$phrase_id = $entry['phrase_id'];
|
|
|
|
if ( !isset( $phrases_data[$phrase_id] ) )
|
|
{
|
|
$phrases_data[$phrase_id] = [
|
|
'impressions' => 0,
|
|
'clicks' => 0,
|
|
'cost' => 0.0,
|
|
'conversions' => 0,
|
|
'conversions_value' => 0.0,
|
|
'roas' => 0,
|
|
'days_counted' => []
|
|
];
|
|
}
|
|
|
|
// Sumowanie danych według fraz
|
|
$phrases_data[$phrase_id]['impressions'] += $entry['impressions'];
|
|
$phrases_data[$phrase_id]['clicks'] += $entry['clicks'];
|
|
$phrases_data[$phrase_id]['cost'] += $entry['cost'];
|
|
$phrases_data[$phrase_id]['conversions'] += $entry['conversions'];
|
|
$phrases_data[$phrase_id]['conversions_value'] += $entry['conversions_value'];
|
|
$phrases_data[$phrase_id]['days_counted'][] = $entry['date_add'];
|
|
}
|
|
|
|
foreach ( $phrases_data as $phrase )
|
|
{
|
|
$day_count = count( $phrase['days_counted'] );
|
|
|
|
$impressions = $phrase['impressions'] / $day_count;
|
|
$clicks = $phrase['clicks'] / $day_count;
|
|
$cost = $phrase['cost'] / $day_count;
|
|
$conversions = $phrase['conversions'] / $day_count;
|
|
$conversions_value = $phrase['conversions_value'] / $day_count;
|
|
$roas = ( $conversions_value > 0 and $cost ) ? round( $conversions_value / $cost, 2 ) * 100 : 0;
|
|
|
|
if ( $day_count > 14 )
|
|
{
|
|
if ( $mdb -> count( 'phrases_history_30', [ 'AND' => [ 'phrase_id' => $phrase_id, 'date_add' => $date_to ] ] ) > 0 )
|
|
{
|
|
$mdb -> update( 'phrases_history_30', [
|
|
'impressions' => $impressions,
|
|
'clicks' => $clicks,
|
|
'cost' => $cost,
|
|
'conversions' => $conversions,
|
|
'conversions_value' => $conversions_value,
|
|
'roas' => $roas
|
|
], [ 'AND' => [ 'phrase_id' => $phrase_id, 'date_add' => $date_to ] ] );
|
|
}
|
|
else
|
|
{
|
|
$mdb -> insert( 'phrases_history_30', [
|
|
'phrase_id' => $phrase_id,
|
|
'impressions' => $impressions,
|
|
'clicks' => $clicks,
|
|
'cost' => $cost,
|
|
'conversions' => $conversions,
|
|
'conversions_value' => $conversions_value,
|
|
'roas' => $roas,
|
|
'date_add' => $date_to
|
|
] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|