Files
adsPRO/autoload/factory/class.Products.php
Jacek Pyziak 4635cefcbb feat: update font to Roboto across templates and add campaign/ad group filters in product views
- Changed font from Open Sans to Roboto in layout files.
- Added campaign and ad group filters in products main view.
- Enhanced product history to include campaign and ad group IDs.
- Updated migrations to support new campaign and ad group dimensions in product statistics.
- Introduced new migration files for managing campaign types and dropping obsolete columns.
2026-02-18 01:21:22 +01:00

410 lines
13 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' => 'p.offer_id',
'campaign_name' => 'c.campaign_name',
'ad_group_name' => 'ag.ad_group_name',
'name' => 'pt.name',
'impressions' => 'pt.impressions',
'impressions_30' => 'pt.impressions_30',
'clicks' => 'pt.clicks',
'clicks_30' => 'pt.clicks_30',
'ctr' => 'pt.ctr',
'cost' => 'pt.cost',
'cpc' => 'pt.cpc',
'conversions' => 'pt.conversions',
'conversions_value' => 'pt.conversions_value',
'roas' => 'pt.roas',
'min_roas' => 'p.min_roas'
];
$order_sql = $order_map[ $order_name ] ?? 'pt.clicks';
$params = [ ':client_id' => (int) $client_id ];
$sql = 'SELECT pt.*, p.offer_id, p.min_roas,
COALESCE( c.campaign_name, \'--- brak kampanii ---\' ) AS campaign_name,
CASE
WHEN pt.ad_group_id = 0 THEN \'PMax (bez grup reklam)\'
ELSE COALESCE( ag.ad_group_name, \'--- brak grupy reklam ---\' )
END AS ad_group_name
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 .= ' ORDER BY ' . $order_sql . ' ' . $order_dir . ', pt.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 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 . '%';
}
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 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 ] );
}
}