ferrero-opentext/src/DatabaseClient.php
DJP 2bcac0a08f Add full_metadata JSONB column - Store complete DAM metadata without truncation
Database Changes:
- ALTER TABLE master_assets ADD COLUMN full_metadata JSONB
- Stores COMPLETE DAM asset metadata (no 5,000 char truncation)
- PostgreSQL JSONB type for efficient storage and querying

DatabaseClient Changes:
- Added full_metadata to INSERT and ON CONFLICT UPDATE
- Store complete json_encode($assetData) in full_metadata column
- Simplified description to just Box info (no metadata)
- Log metadata size when storing
- NO TRUNCATION - preserves all fields

Workflow Changes (workflow_v3.php):
- load_master_metadata: Read from full_metadata JSONB column
- upload_from_box: Read from full_metadata JSONB column
- Both endpoints now get COMPLETE master metadata
- Added logging for metadata size verification

Impact:
BEFORE: Only 5,000 chars stored (truncated)
AFTER: Full metadata stored (10,000+ chars, all fields preserved)

Next Step:
Re-download master assets to populate full_metadata for existing records.
New downloads will automatically use the new column.

🤖 Generated with Claude Code
2025-10-29 16:53:51 -04:00

289 lines
12 KiB
PHP

<?php
/**
* DatabaseClient - Handles PostgreSQL database operations for asset tracking
* Stores master assets, tracking IDs, and Box file links
*/
class DatabaseClient
{
private $pdo;
public function __construct()
{
try {
$this->pdo = new PDO(
'pgsql:host=localhost;port=5433;dbname=ferrero_tracking',
'ferrero_user',
'ferrero_pass_2025'
);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
error_log("DatabaseClient: Connected to PostgreSQL");
} catch (PDOException $e) {
error_log("DatabaseClient: Connection failed: " . $e->getMessage());
$this->pdo = null;
}
}
/**
* Get the PDO connection
* @return PDO|null
*/
public function getConnection()
{
return $this->pdo;
}
/**
* Store master asset in database
*/
public function storeMasterAsset($trackingId, $assetData, $boxFileId = null, $boxUrl = null, $uploadFolderId = null)
{
if (!$this->pdo) {
return ['success' => false, 'error' => 'Database not connected'];
}
try {
// Extract metadata from DAM asset
$metadata = $this->extractMetadataFields($assetData);
// Debug log what we extracted
error_log("DatabaseClient: Extracted metadata: " . json_encode($metadata));
error_log("DatabaseClient: Upload folder ID: " . ($uploadFolderId ?? 'NULL'));
// Insert using all available columns INCLUDING full_metadata
$stmt = $this->pdo->prepare("
INSERT INTO master_assets (
tracking_id,
opentext_id,
original_filename,
file_extension,
brand_code,
brand_name,
country_code,
language_code,
asset_type,
file_size_bytes,
mime_type,
width_px,
height_px,
upload_directory,
description,
full_metadata,
status
) VALUES (
:tracking_id,
:opentext_id,
:original_filename,
:file_extension,
:brand_code,
:brand_name,
:country_code,
:language_code,
:asset_type,
:file_size_bytes,
:mime_type,
:width_px,
:height_px,
:upload_directory,
:description,
:full_metadata,
:status
)
ON CONFLICT (tracking_id) DO UPDATE SET
upload_directory = EXCLUDED.upload_directory,
brand_code = EXCLUDED.brand_code,
country_code = EXCLUDED.country_code,
language_code = EXCLUDED.language_code,
width_px = EXCLUDED.width_px,
height_px = EXCLUDED.height_px,
description = EXCLUDED.description,
full_metadata = EXCLUDED.full_metadata
");
$pathInfo = pathinfo($assetData['name'] ?? 'unknown');
// Store FULL metadata JSON in dedicated column (NO TRUNCATION)
$metadataJson = json_encode($assetData);
// Keep description simple and readable (Box info only)
$description = "Box File ID: {$boxFileId}\nBox URL: {$boxUrl}\nDAM Asset ID: " . ($assetData['asset_id'] ?? 'Unknown');
error_log("DatabaseClient: Storing full metadata - size: " . strlen($metadataJson) . " bytes");
$stmt->execute([
':tracking_id' => $trackingId,
':opentext_id' => $assetData['asset_id'] ?? 'unknown',
':original_filename' => $pathInfo['filename'],
':file_extension' => '.' . ($pathInfo['extension'] ?? ''),
':brand_code' => $metadata['brand_code'],
':brand_name' => $metadata['brand_name'],
':country_code' => $metadata['country_code'],
':language_code' => $metadata['language_code'],
':asset_type' => $metadata['asset_type'],
':file_size_bytes' => $assetData['file_size'] ?? null,
':mime_type' => $assetData['mime_type'] ?? null,
':width_px' => $metadata['width_px'],
':height_px' => $metadata['height_px'],
':upload_directory' => $uploadFolderId, // Final Assets folder ID
':description' => $description,
':full_metadata' => $metadataJson, // COMPLETE metadata as JSONB
':status' => 'active'
]);
error_log("DatabaseClient: Stored asset " . $trackingId);
return ['success' => true, 'tracking_id' => $trackingId];
} catch (PDOException $e) {
error_log("DatabaseClient: Insert failed: " . $e->getMessage());
return ['success' => false, 'error' => $e->getMessage()];
}
}
/**
* Extract metadata fields from DAM asset
*/
private function extractMetadataFields($assetData)
{
$fields = [
'brand_code' => null,
'brand_name' => null,
'country_code' => null,
'language_code' => null,
'asset_type' => null,
'width_px' => null,
'height_px' => null
];
// Debug: Log what keys exist in assetData
error_log("DatabaseClient: AssetData keys: " . implode(', ', array_keys($assetData)));
// Check if metadata field exists
if (isset($assetData['metadata']['metadata_element_list'])) {
error_log("DatabaseClient: Metadata field EXISTS with " . count($assetData['metadata']['metadata_element_list']) . " categories");
} else {
error_log("DatabaseClient: Metadata field MISSING or wrong structure");
}
// Extract width/height from master_content_info (not asset_content_info)
if (isset($assetData['master_content_info'])) {
$fields['width_px'] = $assetData['master_content_info']['width'] ?? null;
$fields['height_px'] = $assetData['master_content_info']['height'] ?? null;
error_log("DatabaseClient: Width/Height from master_content_info: " . ($fields['width_px'] ?? 'NULL') . 'x' . ($fields['height_px'] ?? 'NULL'));
}
// Fallback to asset_content_info if master_content_info doesn't exist
elseif (isset($assetData['asset_content_info']['master_content'])) {
$content = $assetData['asset_content_info']['master_content'];
$fields['width_px'] = $content['width'] ?? null;
$fields['height_px'] = $content['height'] ?? null;
error_log("DatabaseClient: Width/Height from asset_content_info: " . ($fields['width_px'] ?? 'NULL') . 'x' . ($fields['height_px'] ?? 'NULL'));
} else {
error_log("DatabaseClient: No content_info found for dimensions");
}
// Extract from metadata fields
if (isset($assetData['metadata']['metadata_element_list'])) {
foreach ($assetData['metadata']['metadata_element_list'] as $categoryIndex => $category) {
if (!isset($category['metadata_element_list'])) {
error_log("DatabaseClient: Category " . $categoryIndex . " has no metadata_element_list");
continue;
}
error_log("DatabaseClient: Category " . $categoryIndex . " has " . count($category['metadata_element_list']) . " elements");
foreach ($category['metadata_element_list'] as $element) {
$fieldId = $element['id'] ?? '';
// Extract SUB BRAND (exists in asset metadata)
if ($fieldId === 'FERRERO.FIELD.SUB BRAND') {
$fields['brand_name'] = $this->getFieldValue($element);
if ($fields['brand_name']) {
$fields['brand_code'] = strtoupper(substr($fields['brand_name'], 0, 3));
error_log("DatabaseClient: Extracted SUB BRAND: " . $fields['brand_name']);
}
}
// Extract MAIN LANGUAGES for language code
if ($fieldId === 'MAIN_LANGUAGES' || $fieldId === 'FERRERO.TABULAR.FIELD.MAIN LANGUAGES') {
// This is a tabular field - check for values
if (isset($element['values']) && is_array($element['values']) && count($element['values']) > 0) {
$langValue = $this->getFieldValue($element['values'][0]);
if ($langValue) {
$fields['language_code'] = strtolower(substr($langValue, 0, 2));
error_log("DatabaseClient: Extracted language: " . $fields['language_code']);
}
}
}
// Extract MKTG.ASSET TYPE
if ($fieldId === 'FERRERO.FIELD.MKTG.ASSET TYPE') {
$assetType = $this->getFieldValue($element);
if ($assetType) {
$fields['asset_type'] = strtoupper(substr($assetType, 0, 3)); // First 3 chars
error_log("DatabaseClient: Extracted asset type: " . $fields['asset_type']);
}
}
// Check for tabular fields (for language extraction)
if (isset($element['metadata_element_list']) && $fieldId === 'FERRERO.TABULAR.FIELD.MAIN LANGUAGES') {
foreach ($element['metadata_element_list'] as $tableField) {
// Extract language from tabular field
if (isset($tableField['values']) && is_array($tableField['values'])) {
foreach ($tableField['values'] as $langValue) {
$lang = $this->getFieldValue($langValue);
if ($lang) {
$fields['language_code'] = strtolower(substr($lang, 0, 2));
error_log("DatabaseClient: Extracted language from tabular: " . $fields['language_code']);
break;
}
}
}
}
}
}
}
}
return $fields;
}
/**
* Get field value from metadata element (handles both domain and regular values)
*/
private function getFieldValue($element)
{
$fieldId = $element['id'] ?? 'unknown';
// Handle domain values (with field_value)
if (isset($element['value']['value']['field_value']['value'])) {
$value = $element['value']['value']['field_value']['value'];
error_log("DatabaseClient: getFieldValue({$fieldId}) = {$value} (domain value)");
return $value;
}
// Handle regular string fields
if (isset($element['value']['value']['value'])) {
$value = $element['value']['value']['value'];
error_log("DatabaseClient: getFieldValue({$fieldId}) = {$value} (regular value)");
return $value;
}
// Log when we can't find a value
if (isset($element['value'])) {
error_log("DatabaseClient: getFieldValue({$fieldId}) - value exists but structure unknown");
error_log("DatabaseClient: Value structure: " . json_encode($element['value']));
} else {
error_log("DatabaseClient: getFieldValue({$fieldId}) - no value field at all");
}
return null;
}
/**
* Check if database is available
*/
public function isConnected()
{
return $this->pdo !== null;
}
}