- Created XmlFiles control class for handling XML file views and regeneration. - Implemented method to retrieve clients with XML feeds in the factory class. - Added database migration to include google_merchant_account_id in clients table. - Created migrations for products_keyword_planner_terms and products_merchant_sync_log tables. - Added campaign_keywords table migration for managing campaign keyword data. - Developed main view template for displaying XML files and their statuses. - Introduced a debug script for analyzing product URLs and their statuses.
854 lines
26 KiB
PHP
854 lines
26 KiB
PHP
<?
|
|
namespace factory;
|
|
class Products
|
|
{
|
|
static public function delete_product( $product_id ) {
|
|
global $mdb;
|
|
$mdb -> delete( 'products', [ 'id' => $product_id ] );
|
|
return true;
|
|
}
|
|
|
|
static public function delete_products( $product_ids ) {
|
|
global $mdb;
|
|
if ( empty( $product_ids ) || !is_array( $product_ids ) ) {
|
|
return false;
|
|
}
|
|
foreach ( $product_ids as $product_id ) {
|
|
$mdb -> delete( 'products', [ 'id' => $product_id ] );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static public function get_product_comments( $product_id )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> query( 'SELECT id, comment, date_add FROM products_comments WHERE product_id = \'' . $product_id . '\' ORDER BY date_add DESC' ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function delete_product_comment( $comment_id )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> delete( 'products_comments', [ 'id' => $comment_id ] );
|
|
}
|
|
|
|
static public function get_product_comment_by_date( $product_id, $date )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> get( 'products_comments', [ 'id', 'comment' ], [ 'AND' => [ 'product_id' => $product_id, 'date_add' => $date ] ] );
|
|
}
|
|
|
|
static public function get_min_roas( $product_id )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> get( 'products', 'min_roas', [ 'id' => $product_id ] );
|
|
}
|
|
|
|
static public function get_client_bestseller_min_roas( $client_id )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> get( 'clients', 'bestseller_min_roas', [ 'id' => $client_id ] );
|
|
}
|
|
|
|
static public function save_client_bestseller_min_roas( $client_id, $min_roas )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> update( 'clients', [ 'bestseller_min_roas' => $min_roas ], [ 'id' => $client_id ] );
|
|
}
|
|
|
|
static public function save_min_roas( $product_id, $min_roas )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> update( 'products', [ 'min_roas' => $min_roas ], [ 'id' => $product_id ] );
|
|
}
|
|
|
|
static private function build_scope_filters( &$sql, &$params, $campaign_id, $ad_group_id )
|
|
{
|
|
$campaign_id = (int) $campaign_id;
|
|
$ad_group_id = (int) $ad_group_id;
|
|
|
|
if ( $campaign_id > 0 )
|
|
{
|
|
$sql .= ' AND pt.campaign_id = :campaign_id';
|
|
$params[':campaign_id'] = $campaign_id;
|
|
}
|
|
|
|
if ( $ad_group_id > 0 )
|
|
{
|
|
$sql .= ' AND pt.ad_group_id = :ad_group_id';
|
|
$params[':ad_group_id'] = $ad_group_id;
|
|
}
|
|
}
|
|
|
|
static public function get_products( $client_id, $search, $limit, $start, $order_name, $order_dir, $campaign_id = 0, $ad_group_id = 0 )
|
|
{
|
|
global $mdb;
|
|
|
|
$limit = max( 1, (int) $limit );
|
|
$start = max( 0, (int) $start );
|
|
$order_dir = strtoupper( (string) $order_dir ) === 'ASC' ? 'ASC' : 'DESC';
|
|
|
|
$order_map = [
|
|
'offer_id' => 'offer_id',
|
|
'campaign_name' => 'campaign_name',
|
|
'ad_group_name' => 'ad_group_name',
|
|
'name' => 'name',
|
|
'impressions' => 'impressions',
|
|
'impressions_30' => 'impressions_30',
|
|
'clicks' => 'clicks',
|
|
'clicks_30' => 'clicks_30',
|
|
'ctr' => 'ctr',
|
|
'cost' => 'cost',
|
|
'cpc' => 'cpc',
|
|
'conversions' => 'conversions',
|
|
'conversions_value' => 'conversions_value',
|
|
'roas' => 'roas',
|
|
'min_roas' => 'min_roas'
|
|
];
|
|
|
|
$order_sql = $order_map[ $order_name ] ?? 'clicks';
|
|
|
|
$params = [ ':client_id' => (int) $client_id ];
|
|
$sql = 'SELECT
|
|
p.id AS product_id,
|
|
p.offer_id,
|
|
p.min_roas,
|
|
pt.campaign_id,
|
|
CASE
|
|
WHEN COUNT( DISTINCT pt.campaign_id ) > 1 THEN \'--- wiele kampanii ---\'
|
|
ELSE COALESCE( MAX( c.campaign_name ), \'--- brak kampanii ---\' )
|
|
END AS campaign_name,
|
|
CASE
|
|
WHEN COUNT( DISTINCT pt.ad_group_id ) > 1 THEN \'--- wiele grup reklam ---\'
|
|
WHEN MAX( pt.ad_group_id ) = 0 THEN \'PMax (bez grup reklam)\'
|
|
ELSE COALESCE( MAX( ag.ad_group_name ), \'--- brak grupy reklam ---\' )
|
|
END AS ad_group_name,
|
|
CASE
|
|
WHEN COUNT( DISTINCT pt.ad_group_id ) = 1 THEN MAX( pt.ad_group_id )
|
|
ELSE 0
|
|
END AS ad_group_id,
|
|
MAX( pt.name ) AS name,
|
|
SUM( pt.impressions ) AS impressions,
|
|
SUM( pt.impressions_30 ) AS impressions_30,
|
|
SUM( pt.clicks ) AS clicks,
|
|
SUM( pt.clicks_30 ) AS clicks_30,
|
|
CASE
|
|
WHEN SUM( pt.impressions ) > 0 THEN ROUND( SUM( pt.clicks ) / SUM( pt.impressions ) * 100, 2 )
|
|
ELSE 0
|
|
END AS ctr,
|
|
SUM( pt.cost ) AS cost,
|
|
CASE
|
|
WHEN SUM( pt.clicks ) > 0 THEN ROUND( SUM( pt.cost ) / SUM( pt.clicks ), 6 )
|
|
ELSE 0
|
|
END AS cpc,
|
|
SUM( pt.conversions ) AS conversions,
|
|
SUM( pt.conversions_value ) AS conversions_value,
|
|
CASE
|
|
WHEN SUM( pt.cost ) > 0 THEN ROUND( SUM( pt.conversions_value ) / SUM( pt.cost ) * 100, 2 )
|
|
ELSE 0
|
|
END AS roas
|
|
FROM products_temp AS pt
|
|
INNER JOIN products AS p ON p.id = pt.product_id
|
|
LEFT JOIN campaigns AS c ON c.id = pt.campaign_id
|
|
LEFT JOIN campaign_ad_groups AS ag ON ag.id = pt.ad_group_id
|
|
WHERE p.client_id = :client_id';
|
|
|
|
self::build_scope_filters( $sql, $params, $campaign_id, $ad_group_id );
|
|
|
|
if ( $search )
|
|
{
|
|
$sql .= ' AND (
|
|
pt.name LIKE :search
|
|
OR p.offer_id LIKE :search
|
|
OR c.campaign_name LIKE :search
|
|
OR ag.ad_group_name LIKE :search
|
|
)';
|
|
$params[':search'] = '%' . $search . '%';
|
|
}
|
|
|
|
$sql .= ' GROUP BY p.id, p.offer_id, p.min_roas, pt.campaign_id';
|
|
$sql .= ' ORDER BY ' . $order_sql . ' ' . $order_dir . ', product_id DESC LIMIT ' . $start . ', ' . $limit;
|
|
|
|
return $mdb -> query( $sql, $params ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
public static function get_roas_bounds( int $client_id, ?string $search = null, int $campaign_id = 0, int $ad_group_id = 0 ): array
|
|
{
|
|
global $mdb;
|
|
|
|
$params = [ ':client_id' => $client_id ];
|
|
|
|
$sql = 'SELECT MIN( p.min_roas ) AS min_roas, MAX( pt.roas ) AS max_roas
|
|
FROM products_temp AS pt
|
|
INNER JOIN products AS p ON p.id = pt.product_id
|
|
LEFT JOIN campaigns AS c ON c.id = pt.campaign_id
|
|
LEFT JOIN campaign_ad_groups AS ag ON ag.id = pt.ad_group_id
|
|
WHERE p.client_id = :client_id
|
|
AND pt.conversions > 10';
|
|
|
|
self::build_scope_filters( $sql, $params, $campaign_id, $ad_group_id );
|
|
|
|
if ( $search )
|
|
{
|
|
$sql .= ' AND (
|
|
pt.name LIKE :search
|
|
OR p.offer_id LIKE :search
|
|
OR c.campaign_name LIKE :search
|
|
OR ag.ad_group_name LIKE :search
|
|
)';
|
|
$params[':search'] = '%' . $search . '%';
|
|
}
|
|
|
|
$row = $mdb -> query( $sql, $params ) -> fetch( \PDO::FETCH_ASSOC );
|
|
|
|
return [
|
|
'min' => isset( $row['min_roas'] ) ? (float) $row['min_roas'] : 0.0,
|
|
'max' => isset( $row['max_roas'] ) ? (float) $row['max_roas'] : 0.0,
|
|
];
|
|
}
|
|
|
|
static public function get_records_total_products( $client_id, $search, $campaign_id = 0, $ad_group_id = 0 )
|
|
{
|
|
global $mdb;
|
|
|
|
$params = [ ':client_id' => (int) $client_id ];
|
|
$sql = 'SELECT COUNT(0)
|
|
FROM (
|
|
SELECT p.id, pt.campaign_id
|
|
FROM products_temp AS pt
|
|
INNER JOIN products AS p ON p.id = pt.product_id
|
|
LEFT JOIN campaigns AS c ON c.id = pt.campaign_id
|
|
LEFT JOIN campaign_ad_groups AS ag ON ag.id = pt.ad_group_id
|
|
WHERE p.client_id = :client_id';
|
|
|
|
self::build_scope_filters( $sql, $params, $campaign_id, $ad_group_id );
|
|
|
|
if ( $search )
|
|
{
|
|
$sql .= ' AND (
|
|
pt.name LIKE :search
|
|
OR p.offer_id LIKE :search
|
|
OR c.campaign_name LIKE :search
|
|
OR ag.ad_group_name LIKE :search
|
|
)';
|
|
$params[':search'] = '%' . $search . '%';
|
|
}
|
|
|
|
$sql .= ' GROUP BY p.id, pt.campaign_id
|
|
) AS grouped_rows';
|
|
|
|
return $mdb -> query( $sql, $params ) -> fetchColumn();
|
|
}
|
|
|
|
static public function get_product_full_context( $product_id )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> query(
|
|
'SELECT
|
|
p.id,
|
|
p.offer_id,
|
|
p.name,
|
|
p.min_roas,
|
|
COALESCE( SUM( pt.impressions ), 0 ) AS impressions,
|
|
COALESCE( SUM( pt.impressions_30 ), 0 ) AS impressions_30,
|
|
COALESCE( SUM( pt.clicks ), 0 ) AS clicks,
|
|
COALESCE( SUM( pt.clicks_30 ), 0 ) AS clicks_30,
|
|
CASE
|
|
WHEN COALESCE( SUM( pt.impressions ), 0 ) > 0
|
|
THEN ROUND( COALESCE( SUM( pt.clicks ), 0 ) / COALESCE( SUM( pt.impressions ), 0 ) * 100, 2 )
|
|
ELSE 0
|
|
END AS ctr,
|
|
COALESCE( SUM( pt.cost ), 0 ) AS cost,
|
|
CASE
|
|
WHEN COALESCE( SUM( pt.clicks ), 0 ) > 0
|
|
THEN ROUND( COALESCE( SUM( pt.cost ), 0 ) / COALESCE( SUM( pt.clicks ), 0 ), 6 )
|
|
ELSE 0
|
|
END AS cpc,
|
|
COALESCE( SUM( pt.conversions ), 0 ) AS conversions,
|
|
COALESCE( SUM( pt.conversions_value ), 0 ) AS conversions_value,
|
|
CASE
|
|
WHEN COALESCE( SUM( pt.cost ), 0 ) > 0
|
|
THEN ROUND( COALESCE( SUM( pt.conversions_value ), 0 ) / COALESCE( SUM( pt.cost ), 0 ) * 100, 2 )
|
|
ELSE 0
|
|
END AS roas
|
|
FROM products AS p
|
|
LEFT JOIN products_temp AS pt ON pt.product_id = p.id
|
|
WHERE p.id = :pid
|
|
GROUP BY p.id, p.offer_id, p.name, p.min_roas',
|
|
[ ':pid' => $product_id ]
|
|
) -> fetch( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function get_product_data( $product_id, $field )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> get( 'products_data', $field, [ 'product_id' => $product_id ] );
|
|
}
|
|
|
|
static public function get_product_merchant_context( $product_id )
|
|
{
|
|
global $mdb;
|
|
|
|
return $mdb -> query(
|
|
'SELECT
|
|
p.id AS product_id,
|
|
p.client_id,
|
|
p.offer_id,
|
|
cl.google_merchant_account_id
|
|
FROM products AS p
|
|
INNER JOIN clients AS cl ON cl.id = p.client_id
|
|
WHERE p.id = :product_id
|
|
LIMIT 1',
|
|
[ ':product_id' => (int) $product_id ]
|
|
) -> fetch( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function add_product_merchant_sync_log( $row )
|
|
{
|
|
global $mdb;
|
|
|
|
$data = [
|
|
'product_id' => (int) ( $row['product_id'] ?? 0 ),
|
|
'field_name' => (string) ( $row['field_name'] ?? '' ),
|
|
'old_value' => isset( $row['old_value'] ) ? (string) $row['old_value'] : null,
|
|
'new_value' => isset( $row['new_value'] ) ? (string) $row['new_value'] : null,
|
|
'sync_status' => (string) ( $row['sync_status'] ?? 'pending' ),
|
|
'sync_source' => (string) ( $row['sync_source'] ?? 'products_ui' ),
|
|
'merchant_account_id' => isset( $row['merchant_account_id'] ) ? (string) $row['merchant_account_id'] : null,
|
|
'merchant_product_id' => isset( $row['merchant_product_id'] ) ? (string) $row['merchant_product_id'] : null,
|
|
'offer_id' => isset( $row['offer_id'] ) ? (string) $row['offer_id'] : null,
|
|
'api_response' => isset( $row['api_response'] ) ? (string) $row['api_response'] : null,
|
|
'error_message' => isset( $row['error_message'] ) ? (string) $row['error_message'] : null,
|
|
'date_add' => date( 'Y-m-d H:i:s' )
|
|
];
|
|
|
|
if ( $data['product_id'] <= 0 || trim( $data['field_name'] ) === '' )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return $mdb -> insert( 'products_merchant_sync_log', $data );
|
|
}
|
|
|
|
static public function get_product_merchant_sync_logs( $product_id, $limit = 50 )
|
|
{
|
|
global $mdb;
|
|
|
|
$product_id = (int) $product_id;
|
|
$limit = max( 1, (int) $limit );
|
|
|
|
if ( $product_id <= 0 )
|
|
{
|
|
return [];
|
|
}
|
|
|
|
return $mdb -> query(
|
|
'SELECT
|
|
id,
|
|
field_name,
|
|
old_value,
|
|
new_value,
|
|
sync_status,
|
|
sync_source,
|
|
merchant_account_id,
|
|
merchant_product_id,
|
|
offer_id,
|
|
error_message,
|
|
date_add
|
|
FROM products_merchant_sync_log
|
|
WHERE product_id = :product_id
|
|
ORDER BY id DESC
|
|
LIMIT ' . $limit,
|
|
[ ':product_id' => $product_id ]
|
|
) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function get_product_ads_keyword_context( $product_id )
|
|
{
|
|
global $mdb;
|
|
|
|
return $mdb -> query(
|
|
'SELECT
|
|
p.id AS product_id,
|
|
p.client_id,
|
|
cl.google_ads_customer_id
|
|
FROM products AS p
|
|
INNER JOIN clients AS cl ON cl.id = p.client_id
|
|
WHERE p.id = :product_id
|
|
LIMIT 1',
|
|
[ ':product_id' => (int) $product_id ]
|
|
) -> fetch( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function get_cached_keyword_planner_terms( $product_id, $source_url, $limit = 15 )
|
|
{
|
|
global $mdb;
|
|
|
|
$product_id = (int) $product_id;
|
|
$source_url = trim( (string) $source_url );
|
|
$limit = max( 1, (int) $limit );
|
|
|
|
if ( $product_id <= 0 || $source_url === '' )
|
|
{
|
|
return [];
|
|
}
|
|
|
|
return $mdb -> query(
|
|
'SELECT keyword_text, avg_monthly_searches, competition, competition_index
|
|
FROM products_keyword_planner_terms
|
|
WHERE product_id = :product_id
|
|
AND source_url = :source_url
|
|
ORDER BY avg_monthly_searches DESC, keyword_text ASC
|
|
LIMIT ' . $limit,
|
|
[
|
|
':product_id' => $product_id,
|
|
':source_url' => $source_url
|
|
]
|
|
) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function replace_keyword_planner_terms( $product_id, $source_url, $customer_id, $terms )
|
|
{
|
|
global $mdb;
|
|
|
|
$product_id = (int) $product_id;
|
|
$source_url = trim( (string) $source_url );
|
|
$customer_id = trim( (string) $customer_id );
|
|
$terms = is_array( $terms ) ? $terms : [];
|
|
|
|
if ( $product_id <= 0 || $source_url === '' )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$mdb -> delete( 'products_keyword_planner_terms', [
|
|
'AND' => [
|
|
'product_id' => $product_id,
|
|
'source_url' => $source_url
|
|
]
|
|
] );
|
|
|
|
if ( empty( $terms ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
foreach ( $terms as $term )
|
|
{
|
|
$keyword_text = trim( (string) ( $term['keyword'] ?? '' ) );
|
|
if ( $keyword_text === '' )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
$mdb -> insert( 'products_keyword_planner_terms', [
|
|
'product_id' => $product_id,
|
|
'source_url' => $source_url,
|
|
'keyword_text' => mb_substr( $keyword_text, 0, 255 ),
|
|
'avg_monthly_searches' => (int) ( $term['avg_monthly_searches'] ?? 0 ),
|
|
'competition' => $term['competition'] ?? null,
|
|
'competition_index' => isset( $term['competition_index'] ) ? (int) $term['competition_index'] : null,
|
|
'source_customer_id' => $customer_id !== '' ? $customer_id : null,
|
|
'date_add' => date( 'Y-m-d H:i:s' )
|
|
] );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static public function set_product_data( $product_id, $field, $value )
|
|
{
|
|
global $mdb;
|
|
|
|
if ( !$mdb -> count( 'products_data', [ 'product_id' => $product_id ] ) )
|
|
$result = $mdb -> insert( 'products_data', [ 'product_id' => $product_id, $field => $value ] );
|
|
else
|
|
$result = $mdb -> update( 'products_data', [ $field => $value ], [ 'product_id' => $product_id ] );
|
|
return $result;
|
|
}
|
|
|
|
static public function get_product_history( $client_id, $product_id, $start, $limit, $campaign_id = 0, $ad_group_id = 0 )
|
|
{
|
|
global $mdb;
|
|
|
|
$limit = max( 1, (int) $limit );
|
|
$start = max( 0, (int) $start );
|
|
|
|
return $mdb -> query(
|
|
'SELECT
|
|
MAX( ph.id ) AS id,
|
|
SUM( ph.impressions ) AS impressions,
|
|
SUM( ph.clicks ) AS clicks,
|
|
CASE WHEN SUM( ph.impressions ) > 0 THEN ROUND( SUM( ph.clicks ) / SUM( ph.impressions ) * 100, 2 ) ELSE 0 END AS ctr,
|
|
SUM( ph.cost ) AS cost,
|
|
SUM( ph.conversions ) AS conversions,
|
|
SUM( ph.conversions_value ) AS conversions_value,
|
|
ph.date_add
|
|
FROM products_history AS ph
|
|
INNER JOIN products AS p ON p.id = ph.product_id
|
|
WHERE ph.product_id = :product_id
|
|
AND p.client_id = :client_id
|
|
AND ph.campaign_id = :campaign_id
|
|
AND ph.ad_group_id = :ad_group_id
|
|
GROUP BY ph.date_add
|
|
ORDER BY ph.date_add DESC
|
|
LIMIT ' . $start . ', ' . $limit,
|
|
[
|
|
':product_id' => (int) $product_id,
|
|
':client_id' => (int) $client_id,
|
|
':campaign_id' => (int) $campaign_id,
|
|
':ad_group_id' => (int) $ad_group_id
|
|
]
|
|
) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function get_records_total_product_history( $client_id, $product_id, $campaign_id = 0, $ad_group_id = 0 )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> query(
|
|
'SELECT COUNT( DISTINCT ph.date_add )
|
|
FROM products_history AS ph
|
|
INNER JOIN products AS p ON p.id = ph.product_id
|
|
WHERE ph.product_id = :product_id
|
|
AND p.client_id = :client_id
|
|
AND ph.campaign_id = :campaign_id
|
|
AND ph.ad_group_id = :ad_group_id',
|
|
[
|
|
':product_id' => (int) $product_id,
|
|
':client_id' => (int) $client_id,
|
|
':campaign_id' => (int) $campaign_id,
|
|
':ad_group_id' => (int) $ad_group_id
|
|
]
|
|
) -> fetchColumn();
|
|
}
|
|
|
|
static public function get_product_history_30( $client_id, $product_id, $start, $limit, $campaign_id = 0, $ad_group_id = 0 )
|
|
{
|
|
global $mdb;
|
|
return $mdb -> query(
|
|
'SELECT ph3.*
|
|
FROM products_history_30 AS ph3
|
|
INNER JOIN products AS p ON p.id = ph3.product_id
|
|
WHERE ph3.product_id = :product_id
|
|
AND p.client_id = :client_id
|
|
AND ph3.campaign_id = :campaign_id
|
|
AND ph3.ad_group_id = :ad_group_id
|
|
ORDER BY ph3.date_add ASC
|
|
LIMIT ' . (int) $start . ', ' . (int) $limit,
|
|
[
|
|
':product_id' => (int) $product_id,
|
|
':client_id' => (int) $client_id,
|
|
':campaign_id' => (int) $campaign_id,
|
|
':ad_group_id' => (int) $ad_group_id
|
|
]
|
|
) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function get_impressions_30( $product_id, $campaign_id = null, $ad_group_id = null )
|
|
{
|
|
global $mdb;
|
|
|
|
$sql = 'SELECT COALESCE( SUM( impressions ), 0 ) AS total
|
|
FROM products_history
|
|
WHERE product_id = :product_id
|
|
AND date_add >= :date_from';
|
|
|
|
$params = [
|
|
':product_id' => (int) $product_id,
|
|
':date_from' => date( 'Y-m-d', strtotime( '-30 days', time() ) )
|
|
];
|
|
|
|
if ( $campaign_id !== null )
|
|
{
|
|
$sql .= ' AND campaign_id = :campaign_id';
|
|
$params[':campaign_id'] = (int) $campaign_id;
|
|
}
|
|
|
|
if ( $ad_group_id !== null )
|
|
{
|
|
$sql .= ' AND ad_group_id = :ad_group_id';
|
|
$params[':ad_group_id'] = (int) $ad_group_id;
|
|
}
|
|
|
|
return $mdb -> query( $sql, $params ) -> fetchColumn();
|
|
}
|
|
|
|
static public function get_clicks_30( $product_id, $campaign_id = null, $ad_group_id = null )
|
|
{
|
|
global $mdb;
|
|
|
|
$sql = 'SELECT COALESCE( SUM( clicks ), 0 ) AS total
|
|
FROM products_history
|
|
WHERE product_id = :product_id
|
|
AND date_add >= :date_from';
|
|
|
|
$params = [
|
|
':product_id' => (int) $product_id,
|
|
':date_from' => date( 'Y-m-d', strtotime( '-30 days', time() ) )
|
|
];
|
|
|
|
if ( $campaign_id !== null )
|
|
{
|
|
$sql .= ' AND campaign_id = :campaign_id';
|
|
$params[':campaign_id'] = (int) $campaign_id;
|
|
}
|
|
|
|
if ( $ad_group_id !== null )
|
|
{
|
|
$sql .= ' AND ad_group_id = :ad_group_id';
|
|
$params[':ad_group_id'] = (int) $ad_group_id;
|
|
}
|
|
|
|
return $mdb -> query( $sql, $params ) -> fetchColumn();
|
|
}
|
|
|
|
static public function add_product_comment( $product_id, $comment, $date = null )
|
|
{
|
|
global $mdb;
|
|
|
|
if ( !$date )
|
|
$date = date( 'Y-m-d' );
|
|
else
|
|
$date = date( 'Y-m-d', strtotime( $date ) );
|
|
|
|
if ( $mdb -> count( 'products_comments', [ 'AND' => [ 'product_id' => $product_id, 'date_add' => $date ] ] ) )
|
|
return $mdb -> update( 'products_comments', [ 'comment' => $comment ], [ 'AND' => [ 'product_id' => $product_id, 'date_add' => $date ] ] );
|
|
else
|
|
return $mdb -> insert( 'products_comments', [ 'product_id' => $product_id, 'comment' => $comment, 'date_add' => $date ] );
|
|
}
|
|
|
|
static public function get_product_scope_context( $product_id )
|
|
{
|
|
global $mdb;
|
|
|
|
return $mdb -> query(
|
|
'SELECT
|
|
p.id,
|
|
p.client_id,
|
|
p.offer_id,
|
|
p.name,
|
|
cl.google_ads_customer_id,
|
|
cl.google_merchant_account_id
|
|
FROM products AS p
|
|
INNER JOIN clients AS cl ON cl.id = p.client_id
|
|
WHERE p.id = :product_id
|
|
LIMIT 1',
|
|
[ ':product_id' => (int) $product_id ]
|
|
) -> fetch( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function get_campaign_scope_context( $campaign_id )
|
|
{
|
|
global $mdb;
|
|
|
|
return $mdb -> query(
|
|
'SELECT id, client_id, campaign_id, campaign_name
|
|
FROM campaigns
|
|
WHERE id = :campaign_id
|
|
LIMIT 1',
|
|
[ ':campaign_id' => (int) $campaign_id ]
|
|
) -> fetch( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static public function get_ad_group_scope_context( $ad_group_id )
|
|
{
|
|
global $mdb;
|
|
|
|
return $mdb -> query(
|
|
'SELECT id, campaign_id, ad_group_id, ad_group_name
|
|
FROM campaign_ad_groups
|
|
WHERE id = :ad_group_id
|
|
LIMIT 1',
|
|
[ ':ad_group_id' => (int) $ad_group_id ]
|
|
) -> fetch( \PDO::FETCH_ASSOC );
|
|
}
|
|
|
|
static private function get_next_local_campaign_external_id( $client_id )
|
|
{
|
|
global $mdb;
|
|
|
|
$min_external_id = (int) $mdb -> query(
|
|
'SELECT MIN( campaign_id )
|
|
FROM campaigns
|
|
WHERE client_id = :client_id',
|
|
[ ':client_id' => (int) $client_id ]
|
|
) -> fetchColumn();
|
|
|
|
if ( $min_external_id < 0 )
|
|
{
|
|
return $min_external_id - 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static private function get_next_local_ad_group_external_id( $campaign_id )
|
|
{
|
|
global $mdb;
|
|
|
|
$min_external_id = (int) $mdb -> query(
|
|
'SELECT MIN( ad_group_id )
|
|
FROM campaign_ad_groups
|
|
WHERE campaign_id = :campaign_id',
|
|
[ ':campaign_id' => (int) $campaign_id ]
|
|
) -> fetchColumn();
|
|
|
|
if ( $min_external_id < 0 )
|
|
{
|
|
return $min_external_id - 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static public function create_local_campaign( $client_id, $campaign_name )
|
|
{
|
|
global $mdb;
|
|
|
|
$client_id = (int) $client_id;
|
|
$campaign_name = trim( (string) $campaign_name );
|
|
|
|
if ( $client_id <= 0 || $campaign_name === '' )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
$existing_campaign_id = (int) $mdb -> get( 'campaigns', 'id', [
|
|
'AND' => [
|
|
'client_id' => $client_id,
|
|
'campaign_name' => $campaign_name
|
|
]
|
|
] );
|
|
|
|
if ( $existing_campaign_id > 0 )
|
|
{
|
|
return $existing_campaign_id;
|
|
}
|
|
|
|
$mdb -> insert( 'campaigns', [
|
|
'client_id' => $client_id,
|
|
'campaign_id' => self::get_next_local_campaign_external_id( $client_id ),
|
|
'campaign_name' => $campaign_name
|
|
] );
|
|
|
|
return (int) $mdb -> id();
|
|
}
|
|
|
|
static public function create_local_ad_group( $campaign_id, $ad_group_name )
|
|
{
|
|
global $mdb;
|
|
|
|
$campaign_id = (int) $campaign_id;
|
|
$ad_group_name = trim( (string) $ad_group_name );
|
|
|
|
if ( $campaign_id <= 0 || $ad_group_name === '' )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
$existing_ad_group_id = (int) $mdb -> get( 'campaign_ad_groups', 'id', [
|
|
'AND' => [
|
|
'campaign_id' => $campaign_id,
|
|
'ad_group_name' => $ad_group_name
|
|
]
|
|
] );
|
|
|
|
if ( $existing_ad_group_id > 0 )
|
|
{
|
|
return $existing_ad_group_id;
|
|
}
|
|
|
|
$mdb -> insert( 'campaign_ad_groups', [
|
|
'campaign_id' => $campaign_id,
|
|
'ad_group_id' => self::get_next_local_ad_group_external_id( $campaign_id ),
|
|
'ad_group_name' => $ad_group_name,
|
|
'impressions_30' => 0,
|
|
'clicks_30' => 0,
|
|
'cost_30' => 0,
|
|
'conversions_30' => 0,
|
|
'conversion_value_30' => 0,
|
|
'roas_30' => 0,
|
|
'impressions_all_time' => 0,
|
|
'clicks_all_time' => 0,
|
|
'cost_all_time' => 0,
|
|
'conversions_all_time' => 0,
|
|
'conversion_value_all_time' => 0,
|
|
'roas_all_time' => 0,
|
|
'date_sync' => date( 'Y-m-d' )
|
|
] );
|
|
|
|
return (int) $mdb -> id();
|
|
}
|
|
|
|
static public function assign_product_scope( $product_id, $campaign_id, $ad_group_id )
|
|
{
|
|
global $mdb;
|
|
|
|
$product_id = (int) $product_id;
|
|
$campaign_id = (int) $campaign_id;
|
|
$ad_group_id = (int) $ad_group_id;
|
|
|
|
if ( $product_id <= 0 || $campaign_id <= 0 || $ad_group_id <= 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$product = self::get_product_scope_context( $product_id );
|
|
if ( !$product )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$campaign_client_id = (int) $mdb -> get( 'campaigns', 'client_id', [ 'id' => $campaign_id ] );
|
|
if ( $campaign_client_id <= 0 || $campaign_client_id !== (int) $product['client_id'] )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$ad_group_campaign_id = (int) $mdb -> get( 'campaign_ad_groups', 'campaign_id', [ 'id' => $ad_group_id ] );
|
|
if ( $ad_group_campaign_id <= 0 || $ad_group_campaign_id !== $campaign_id )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$scope_exists = (int) $mdb -> count( 'products_temp', [
|
|
'AND' => [
|
|
'product_id' => $product_id,
|
|
'campaign_id' => $campaign_id,
|
|
'ad_group_id' => $ad_group_id
|
|
]
|
|
] ) > 0;
|
|
|
|
if ( $scope_exists )
|
|
{
|
|
$mdb -> update( 'products_temp', [
|
|
'name' => $product['name']
|
|
], [
|
|
'AND' => [
|
|
'product_id' => $product_id,
|
|
'campaign_id' => $campaign_id,
|
|
'ad_group_id' => $ad_group_id
|
|
]
|
|
] );
|
|
|
|
return true;
|
|
}
|
|
|
|
return $mdb -> insert( 'products_temp', [
|
|
'product_id' => $product_id,
|
|
'campaign_id' => $campaign_id,
|
|
'ad_group_id' => $ad_group_id,
|
|
'name' => $product['name'],
|
|
'impressions' => 0,
|
|
'impressions_30' => 0,
|
|
'clicks' => 0,
|
|
'clicks_30' => 0,
|
|
'ctr' => 0,
|
|
'cost' => 0,
|
|
'conversions' => 0,
|
|
'conversions_value' => 0,
|
|
'cpc' => 0,
|
|
'roas' => 0
|
|
] );
|
|
}
|
|
}
|