feat: neue PostgreSQL-Suche (tba-search) aus newmail-vhost übernommen
Deploy to Dev / deploy (push) Successful in 0s
Deploy to Dev / deploy (push) Successful in 0s
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
<?php
|
||||
/**
|
||||
* Krempl PostgreSQL Database Connection Class
|
||||
*
|
||||
* Usage:
|
||||
* require_once __DIR__ . '/core/postgres/KremplDB.php';
|
||||
* $db = new KremplDB();
|
||||
* $results = $db->searchGeraete('AWB 921 PH');
|
||||
*/
|
||||
|
||||
class KremplDB {
|
||||
private $pg_conn = null;
|
||||
private $mysql_conn = null;
|
||||
private $config = null;
|
||||
|
||||
public function __construct($connect_mysql = false) {
|
||||
$config_file = __DIR__ . '/config.ini';
|
||||
|
||||
if (!file_exists($config_file)) {
|
||||
throw new Exception("Config file not found: $config_file");
|
||||
}
|
||||
|
||||
$this->config = parse_ini_file($config_file, true);
|
||||
|
||||
if (!isset($this->config['postgresql'])) {
|
||||
throw new Exception("PostgreSQL config section not found in config.ini");
|
||||
}
|
||||
|
||||
$this->connectPostgres();
|
||||
|
||||
if ($connect_mysql) {
|
||||
$this->connectMySQL();
|
||||
}
|
||||
}
|
||||
|
||||
private function connectPostgres() {
|
||||
$pg = $this->config['postgresql'];
|
||||
|
||||
$conn_string = sprintf(
|
||||
"host=%s port=%s dbname=%s user=%s password=%s",
|
||||
$pg['host'] ?? 'localhost',
|
||||
$pg['port'] ?? '5432',
|
||||
$pg['database'] ?? 'krempl_data',
|
||||
$pg['user'] ?? 'krempl_user',
|
||||
$pg['password'] ?? ''
|
||||
);
|
||||
|
||||
$this->pg_conn = @pg_connect($conn_string);
|
||||
|
||||
if (!$this->pg_conn) {
|
||||
throw new Exception("PostgreSQL connection failed. Check credentials in config.ini");
|
||||
}
|
||||
}
|
||||
|
||||
private function connectMySQL() {
|
||||
if (!isset($this->config['mysql'])) {
|
||||
throw new Exception("MySQL config section not found in config.ini");
|
||||
}
|
||||
|
||||
$my = $this->config['mysql'];
|
||||
|
||||
$this->mysql_conn = @mysqli_connect(
|
||||
$my['host'] ?? 'localhost',
|
||||
$my['user'] ?? '',
|
||||
$my['password'] ?? '',
|
||||
$my['database'] ?? ''
|
||||
);
|
||||
|
||||
if (!$this->mysql_conn) {
|
||||
throw new Exception("MySQL connection failed: " . mysqli_connect_error());
|
||||
}
|
||||
|
||||
mysqli_set_charset($this->mysql_conn, 'utf8mb4');
|
||||
}
|
||||
|
||||
public function getPostgresConnection() {
|
||||
return $this->pg_conn;
|
||||
}
|
||||
|
||||
public function getMySQLConnection() {
|
||||
return $this->mysql_conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for devices using intelligent normalized search
|
||||
*
|
||||
* @param string $search_term The search term (e.g. "AWB 921 PH", "ZCS 2100B")
|
||||
* @param int $limit Maximum number of results (default: 20)
|
||||
* @return array Array of devices with scores and ersatzteile
|
||||
*/
|
||||
public function searchGeraete($search_term, $limit = 20) {
|
||||
if (empty($search_term)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
// Search for devices using the smart search function
|
||||
$query = "SELECT * FROM search_geraete_smart($1, $2)";
|
||||
$result = pg_query_params($this->pg_conn, $query, [$search_term, $limit]);
|
||||
|
||||
if (!$result) {
|
||||
throw new Exception("Search query failed: " . pg_last_error($this->pg_conn));
|
||||
}
|
||||
|
||||
while ($row = pg_fetch_assoc($result)) {
|
||||
// For each device, find matching ersatzteile
|
||||
$row['ersatzteile'] = $this->getErsatzteileForGeraet($row['geraet_id']);
|
||||
$results[] = $row;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all ersatzteile (spare parts) for a specific device
|
||||
*
|
||||
* @param int $geraet_id The device ID
|
||||
* @param int $limit Maximum number of results (default: 200)
|
||||
* @return array Array of ersatzteile with navision_id, krempl_id, etc.
|
||||
*/
|
||||
public function getErsatzteileForGeraet($geraet_id, $limit = 200) {
|
||||
$query = "
|
||||
SELECT
|
||||
e.id AS krempl_id,
|
||||
e.navision_id,
|
||||
e.originalnummer,
|
||||
e.marke
|
||||
FROM ersatzteil_mapping em
|
||||
INNER JOIN ersatzteile e ON em.ersatzteil_id = e.id
|
||||
WHERE $1 = ANY(em.geraet_ids)
|
||||
ORDER BY e.navision_id
|
||||
LIMIT $2
|
||||
";
|
||||
|
||||
$result = pg_query_params($this->pg_conn, $query, [$geraet_id, $limit]);
|
||||
|
||||
if (!$result) {
|
||||
throw new Exception("Ersatzteile query failed: " . pg_last_error($this->pg_conn));
|
||||
}
|
||||
|
||||
$ersatzteile = [];
|
||||
while ($row = pg_fetch_assoc($result)) {
|
||||
$ersatzteile[] = $row;
|
||||
}
|
||||
|
||||
return $ersatzteile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all devices compatible with a specific ersatzteil (by navision_id)
|
||||
*
|
||||
* @param int $navision_id The Navision ID from the shop
|
||||
* @param int $limit Maximum number of results (default: 500)
|
||||
* @return array Array of compatible devices
|
||||
*/
|
||||
public function getGeraeteForNavisionId($navision_id, $limit = 500) {
|
||||
// First find the krempl ersatzteil_id
|
||||
$query1 = "SELECT id FROM ersatzteile WHERE navision_id = $1 LIMIT 1";
|
||||
$result1 = pg_query_params($this->pg_conn, $query1, [$navision_id]);
|
||||
|
||||
if (!$result1) {
|
||||
throw new Exception("Navision lookup failed: " . pg_last_error($this->pg_conn));
|
||||
}
|
||||
|
||||
$row = pg_fetch_assoc($result1);
|
||||
if (!$row) {
|
||||
return []; // No krempl data for this navision_id
|
||||
}
|
||||
|
||||
$ersatzteil_id = $row['id'];
|
||||
|
||||
// Get the array of device IDs
|
||||
$query2 = "SELECT geraet_ids FROM ersatzteil_mapping WHERE ersatzteil_id = $1";
|
||||
$result2 = pg_query_params($this->pg_conn, $query2, [$ersatzteil_id]);
|
||||
|
||||
if (!$result2) {
|
||||
throw new Exception("Mapping lookup failed: " . pg_last_error($this->pg_conn));
|
||||
}
|
||||
|
||||
$row = pg_fetch_assoc($result2);
|
||||
if (!$row || !$row['geraet_ids']) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Parse the PostgreSQL array format
|
||||
$geraet_ids_str = trim($row['geraet_ids'], '{}');
|
||||
$geraet_ids = array_map('intval', explode(',', $geraet_ids_str));
|
||||
|
||||
// Get device details
|
||||
$query3 = "
|
||||
SELECT
|
||||
id,
|
||||
nr,
|
||||
modell_bezeichnung,
|
||||
typ,
|
||||
typ_de,
|
||||
marke,
|
||||
zusatz
|
||||
FROM geraete
|
||||
WHERE id = ANY($1)
|
||||
ORDER BY marke, modell_bezeichnung
|
||||
LIMIT $2
|
||||
";
|
||||
|
||||
$result3 = pg_query_params($this->pg_conn, $query3, ['{' . implode(',', $geraet_ids) . '}', $limit]);
|
||||
|
||||
if (!$result3) {
|
||||
throw new Exception("Geraete lookup failed: " . pg_last_error($this->pg_conn));
|
||||
}
|
||||
|
||||
$geraete = [];
|
||||
while ($row = pg_fetch_assoc($result3)) {
|
||||
$geraete[] = $row;
|
||||
}
|
||||
|
||||
return $geraete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the search index after CSV re-import
|
||||
* Call this after running import.py
|
||||
*
|
||||
* @return int Number of rows indexed
|
||||
*/
|
||||
public function rebuildSearchIndex() {
|
||||
$query = "SELECT rebuild_geraete_search_index()";
|
||||
$result = pg_query($this->pg_conn, $query);
|
||||
|
||||
if (!$result) {
|
||||
throw new Exception("Rebuild index failed: " . pg_last_error($this->pg_conn));
|
||||
}
|
||||
|
||||
$row = pg_fetch_row($result);
|
||||
return (int)$row[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database statistics
|
||||
*
|
||||
* @return array Statistics about the database
|
||||
*/
|
||||
public function getStats() {
|
||||
$stats = [];
|
||||
|
||||
$queries = [
|
||||
'geraete_count' => "SELECT COUNT(*) FROM geraete",
|
||||
'ersatzteile_count' => "SELECT COUNT(*) FROM ersatzteile",
|
||||
'mapping_count' => "SELECT COUNT(*) FROM ersatzteil_mapping",
|
||||
'passendwie_count' => "SELECT COUNT(*) FROM passendwie",
|
||||
'search_index_count' => "SELECT COUNT(*) FROM geraete_search_index",
|
||||
];
|
||||
|
||||
foreach ($queries as $key => $query) {
|
||||
$result = pg_query($this->pg_conn, $query);
|
||||
if ($result) {
|
||||
$row = pg_fetch_row($result);
|
||||
$stats[$key] = (int)$row[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available shop items for a device (Hybrid: PostgreSQL + MySQL)
|
||||
*
|
||||
* This function combines Krempl data (PostgreSQL) with shop inventory (MySQL)
|
||||
* to show only the spare parts that are actually available in the shop.
|
||||
*
|
||||
* @param int $geraet_id The Krempl device ID
|
||||
* @param int $limit Maximum number of items to return (default: 200)
|
||||
* @return array Array of shop items with full details
|
||||
*/
|
||||
public function getAvailableItemsForGeraet($geraet_id, $limit = 200) {
|
||||
// Ensure MySQL is connected
|
||||
if (!$this->mysql_conn) {
|
||||
$this->connectMySQL();
|
||||
}
|
||||
|
||||
// Step 1: Get all Navision IDs from PostgreSQL for this device
|
||||
$navision_ids = [];
|
||||
$query = "
|
||||
SELECT e.navision_id
|
||||
FROM ersatzteil_mapping em
|
||||
INNER JOIN ersatzteile e ON em.ersatzteil_id = e.id
|
||||
WHERE $1 = ANY(em.geraet_ids)
|
||||
AND e.navision_id IS NOT NULL
|
||||
ORDER BY e.navision_id
|
||||
";
|
||||
|
||||
$result = pg_query_params($this->pg_conn, $query, [$geraet_id]);
|
||||
|
||||
if (!$result) {
|
||||
throw new Exception("Failed to get navision_ids: " . pg_last_error($this->pg_conn));
|
||||
}
|
||||
|
||||
while ($row = pg_fetch_assoc($result)) {
|
||||
$navision_ids[] = (int)$row['navision_id'];
|
||||
}
|
||||
|
||||
if (empty($navision_ids)) {
|
||||
return []; // No parts found
|
||||
}
|
||||
|
||||
// Step 2: Query MySQL for items that match these Navision IDs
|
||||
// Logic: number >= 8 digits → navision_id is in `number`
|
||||
// number < 8 digits → navision_id is in `attribute_7`
|
||||
|
||||
$navision_ids_str = implode(',', $navision_ids);
|
||||
|
||||
$mysql_query = "
|
||||
SELECT
|
||||
i.id,
|
||||
i.number,
|
||||
i.name,
|
||||
i.attribute_7 AS navision_id_alt,
|
||||
i.inventory,
|
||||
i.base_price,
|
||||
i.manufacturer_id,
|
||||
i.structure_id,
|
||||
CASE
|
||||
WHEN LENGTH(i.number) >= 8 THEN i.number
|
||||
ELSE i.attribute_7
|
||||
END AS matched_navision_id
|
||||
FROM items i
|
||||
WHERE (
|
||||
(LENGTH(i.number) >= 8 AND i.number IN ($navision_ids_str))
|
||||
OR
|
||||
(LENGTH(i.number) < 8 AND i.attribute_7 IN ($navision_ids_str))
|
||||
)
|
||||
AND i.active = 1
|
||||
ORDER BY i.name
|
||||
LIMIT $limit
|
||||
";
|
||||
|
||||
$mysql_result = mysqli_query($this->mysql_conn, $mysql_query);
|
||||
|
||||
if (!$mysql_result) {
|
||||
throw new Exception("MySQL query failed: " . mysqli_error($this->mysql_conn));
|
||||
}
|
||||
|
||||
$items = [];
|
||||
while ($row = mysqli_fetch_assoc($mysql_result)) {
|
||||
$items[] = $row;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
if ($this->pg_conn) {
|
||||
pg_close($this->pg_conn);
|
||||
}
|
||||
if ($this->mysql_conn) {
|
||||
mysqli_close($this->mysql_conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user