- Implemented the main view for Supplemental Feeds, displaying clients with Merchant Account IDs and their associated feed files. - Added styling for the feeds page and its components, including headers, empty states, and dropdown menus for syncing actions. - Created backend logic to generate supplemental feeds for clients, including file handling and data sanitization. - Integrated new routes and views for managing feeds, ensuring proper data retrieval and display. - Updated navigation to include the new Supplemental Feeds section. - Added necessary documentation for CRON job management related to feed generation.
120 lines
3.7 KiB
PHP
120 lines
3.7 KiB
PHP
<?php
|
|
namespace services;
|
|
|
|
class SupplementalFeed
|
|
{
|
|
/**
|
|
* Generuje supplemental feed TSV dla klienta.
|
|
* Zwraca tablice ze statystykami: products_total, products_written, file.
|
|
*/
|
|
static public function generate_for_client( $client_id )
|
|
{
|
|
global $mdb;
|
|
|
|
$client_id = (int) $client_id;
|
|
|
|
$products = $mdb -> query(
|
|
"SELECT p.offer_id, p.title, p.description, p.google_product_category
|
|
FROM products p
|
|
WHERE p.client_id = :client_id
|
|
AND p.offer_id IS NOT NULL
|
|
AND p.offer_id <> ''
|
|
AND ( p.title IS NOT NULL OR p.description IS NOT NULL OR p.google_product_category IS NOT NULL )",
|
|
[ ':client_id' => $client_id ]
|
|
) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
|
|
$feeds_dir = __DIR__ . '/../../feeds';
|
|
if ( !is_dir( $feeds_dir ) )
|
|
{
|
|
mkdir( $feeds_dir, 0755, true );
|
|
}
|
|
|
|
$filename = 'supplemental_' . $client_id . '.tsv';
|
|
$file_path = $feeds_dir . '/' . $filename;
|
|
$fp = fopen( $file_path, 'w' );
|
|
|
|
if ( $fp === false )
|
|
{
|
|
throw new \RuntimeException( 'Nie mozna otworzyc pliku: ' . $file_path );
|
|
}
|
|
|
|
fwrite( $fp, "id\ttitle\tdescription\tgoogle_product_category\n" );
|
|
|
|
$written = 0;
|
|
foreach ( $products as $row )
|
|
{
|
|
$title = self::sanitize_for_tsv( $row['title'] ?? '' );
|
|
$description = self::sanitize_for_tsv( $row['description'] ?? '' );
|
|
$category = trim( (string) ( $row['google_product_category'] ?? '' ) );
|
|
|
|
if ( $title === '' && $description === '' && $category === '' )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fwrite( $fp, implode( "\t", [
|
|
$row['offer_id'],
|
|
$title,
|
|
$description,
|
|
$category
|
|
] ) . "\n" );
|
|
|
|
$written++;
|
|
}
|
|
|
|
fclose( $fp );
|
|
|
|
return [
|
|
'products_total' => count( $products ),
|
|
'products_written' => $written,
|
|
'file' => $filename
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Czyści HTML z tekstu na potrzeby TSV.
|
|
* Zachowuje strukturę (akapity, listy) jako escaped \n.
|
|
* GMC interpretuje literalny \n jako nowa linia w opisie.
|
|
*/
|
|
static private function sanitize_for_tsv( $value )
|
|
{
|
|
$value = (string) $value;
|
|
if ( $value === '' ) return '';
|
|
|
|
// <li> → newline + punktor
|
|
$value = preg_replace( '#<li[^>]*>#i', "\n- ", $value );
|
|
|
|
// Blokowe tagi zamykajace → newline (akapity, divy, listy, naglowki)
|
|
$value = preg_replace( '#</(?:p|div|h[1-6]|tr|ul|ol)>#i', "\n", $value );
|
|
$value = preg_replace( '#<br\s*/?>#i', "\n", $value );
|
|
|
|
// Usun pozostale tagi
|
|
$value = strip_tags( $value );
|
|
|
|
// Dekoduj encje HTML
|
|
$value = html_entity_decode( $value, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
|
|
|
|
// Taby → spacja (tab lamie TSV)
|
|
$value = str_replace( "\t", ' ', $value );
|
|
|
|
// Normalizuj newline
|
|
$value = str_replace( "\r\n", "\n", $value );
|
|
$value = str_replace( "\r", "\n", $value );
|
|
|
|
// Wielokrotne puste linie → max 1
|
|
$value = preg_replace( "/\n{3,}/", "\n\n", $value );
|
|
|
|
// Spacje wielokrotne w ramach linii → jedna
|
|
$value = preg_replace( '/ {2,}/', ' ', $value );
|
|
|
|
// Trim kazdej linii
|
|
$value = implode( "\n", array_map( 'trim', explode( "\n", $value ) ) );
|
|
$value = trim( $value );
|
|
|
|
// Escape newline jako literalny \n dla TSV
|
|
$value = str_replace( "\n", '\\n', $value );
|
|
|
|
return $value;
|
|
}
|
|
}
|