From c16a4d78d07e81ecdd2ff9b1b9090560ab2fe488 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 04 Sep 2026 22:25:09 +0000
Subject: [PATCH] =Got the panel loading up the fields, organized by section! Now to ensure it saves properly. Still need to add: upload fields, selector fields, repeater/taglist/group fields

---
 inc/managers/CustomTable.php |  181 +++++++++++++++++++++++++--------------------
 1 files changed, 100 insertions(+), 81 deletions(-)

diff --git a/inc/managers/CustomTable.php b/inc/managers/CustomTable.php
index c8a9171..d86a631 100644
--- a/inc/managers/CustomTable.php
+++ b/inc/managers/CustomTable.php
@@ -2,6 +2,7 @@
 namespace JVBase\managers;
 
 use Exception;
+use wpdb;
 
 if (!defined('ABSPATH')) {
 	exit;
@@ -19,8 +20,11 @@
  */
 class CustomTable
 {
-	protected \wpdb $wpdb;
+
+	private static ?wpdb $wpdb = null;
+	private static string $prefix;
 	protected string $tableName;
+	protected bool $table_exists = false;
 	protected string $definition;
 	protected string $fullTableName;
 	protected bool $useTransactions;
@@ -45,21 +49,27 @@
 
 	/**
 	 * Fluent factory method
-	 *
-	 * @param string $tableName Table name without prefix/BASE
+	 * @param string $tableName
+	 * @param bool $user
+	 * @param bool $auth
+	 * @param bool $useTransactions
 	 * @return self
-	 *
 	 * @example CustomTable::for('favourites')->insert($data);
 	 */
 	public static function for(string $tableName, bool $user = false, bool $auth = false, bool $useTransactions = false): self
 	{
 		if (!isset(self::$instances[$tableName])) {
 			self::$instances[$tableName] = new self($tableName, $user, $auth, $useTransactions);
+			self::$instances[$tableName]->setFlags();
 		}
 
 
 		return self::$instances[$tableName];
 	}
+		protected function setFlags():void
+		{
+			$this->table_exists = !is_null(self::$wpdb->get_var("SHOW TABLES LIKE '{$this->fullTableName}'"));
+		}
 
 	public static function destroyInstance(string $tableName):void
 	{
@@ -93,20 +103,20 @@
 		protected function ensureUserTable():void
 		{
 			if (!isset(static::$userTable)) {
-				static::$userTable = is_multisite() ? $this->getMultisiteUsersTable() : $this->wpdb->users;
+				static::$userTable = is_multisite() ? $this->getMultisiteUsersTable() : self::$wpdb->users;
 			}
 		}
 			protected function getMultisiteUsersTable():string
 			{
-				$siteUsersTable = $this->wpdb->prefix . 'users';
-				$siteExists = $this->wpdb->get_var(
-					$this->wpdb->prepare("SHOW TABLES LIKE %s", $siteUsersTable)
+				$siteUsersTable = self::$wpdb->prefix . 'users';
+				$siteExists = self::$wpdb->get_var(
+					self::$wpdb->prepare("SHOW TABLES LIKE %s", $siteUsersTable)
 				);
 				if ($siteExists) {
 					return $siteUsersTable;
 				}
 				//fallback to main one
-				return $this->wpdb->users;
+				return self::$wpdb->users;
 			}
 		protected function ensureUserIDType():void
 		{
@@ -119,27 +129,27 @@
 		protected function ensureTermIDType():void
 		{
 			if (!isset(static::$termIDType)) {
-				static::$termIDType = $this->getColumnType($this->wpdb->terms, 'term_id');
+				static::$termIDType = $this->getColumnType(self::$wpdb->terms, 'term_id');
 			}
 		}
 
 		protected function ensureTermTable():void
 		{
 			if (!isset(static::$termTable)) {
-				static::$termTable = $this->wpdb->terms;
+				static::$termTable = self::$wpdb->terms;
 			}
 		}
 
 		protected function ensurePostIDType():void
 		{
 			if (!isset(static::$postIDType)) {
-				static::$postIDType = $this->getColumnType($this->wpdb->posts, 'ID');
+				static::$postIDType = $this->getColumnType(self::$wpdb->posts, 'ID');
 			}
 		}
 		protected function ensurePostTable():void
 		{
 			if (!isset(static::$postTable)) {
-				static::$postTable = $this->wpdb->posts;
+				static::$postTable = self::$wpdb->posts;
 			}
 		}
 
@@ -226,7 +236,7 @@
 		require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
 
 		foreach (self::$instances as $instance) {
-			if (!$instance->wpdb->get_var("SHOW TABLES LIKE '{$instance->fullTableName}'")) {
+			if (!$instance->table_exists) {
 				$instance->createTable();
 			}
 		}
@@ -238,29 +248,29 @@
 			error_log('[CustomTable]No definition set for '.$this->tableName);
 			return;
 		}
-		if ($this->wpdb->get_var("SHOW TABLES LIKE '{$this->fullTableName}'")) {
+		if ($this->table_exists) {
 			return;
 		}
 		require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
 		error_log('Creating Database Table: '. $this->tableName);
 		$charset = self::$charsetCollate;
 		$schema = "CREATE TABLE IF NOT EXISTS {$this->fullTableName} {$this->definition} {$charset}";
-		$this->wpdb->flush();
+		self::$wpdb->flush();
 
 		$hasForeignKey = stripos($schema, 'FOREIGN KEY') !== false;
 		if ($hasForeignKey) {
-			$result = $this->wpdb->query($schema);
-			$success = ($result !== false && !$this->wpdb->last_error);
+			$result = self::$wpdb->query($schema);
+			$success = ($result !== false && !self::$wpdb->last_error);
 		} else {
 			dbDelta($schema.';');
-			$success = !$this->wpdb->last_error;
+			$success = !self::$wpdb->last_error;
 		}
 
 		if (!$success) {
-			$error_msg = "SQL Error creating table {$this->tableName}: " . $this->wpdb->last_error;
+			$error_msg = "SQL Error creating table {$this->tableName}: " . self::$wpdb->last_error;
 			error_log($error_msg);
 			error_log("Failed SQL Query: " . $schema);
-		} elseif ($this->wpdb->get_var("SHOW TABLES LIKE '{$this->fullTableName}'")) {
+		} else {
 			error_log("Successfully created table: {$this->fullTableName}");
 		}
 	}
@@ -273,16 +283,30 @@
 		self::$instances = [];
 	}
 
+	protected function checkWPDB():void
+	{
+		if (is_null(self::$wpdb)) {
+			global $wpdb;
+			self::$wpdb = $wpdb;
+			self::$prefix = $wpdb->prefix;
+
+			$usersStatus = self::$wpdb->get_row("SHOW TABLE STATUS LIKE '{self::$wpdb->users}'");
+			$parentCollation = $usersStatus->Collation ?? 'utf8mb4_general_ci';
+			self::$charsetCollate = "DEFAULT CHARACTER SET utf8mb4 COLLATE {$parentCollation}";
+		}
+
+	}
+
 	/**
 	 * @param string $tableName Table name without prefix/BASE (e.g., 'favourites', 'notifications')
 	 * @param bool $useTransactions Whether to auto-wrap operations in transactions
 	 */
-	public function __construct(string $tableName, bool $user = false, bool $auth = false, bool $useTransactions = false)
+	private function __construct(string $tableName, bool $user = false, bool $auth = false, bool $useTransactions = false)
 	{
-		global $wpdb;
-		$this->wpdb = $wpdb;
+		$this->checkWPDB();
+
 		$this->tableName = $tableName;
-		$this->fullTableName = $wpdb->prefix . apply_filters('jvb_base', BASE) . $tableName;
+		$this->fullTableName = self::$prefix . apply_filters('jvb_base', BASE) . $tableName;
 		$this->useTransactions = $useTransactions;
 
 		$this->cache = Cache::for($tableName);
@@ -293,9 +317,6 @@
 			$this->cache->auth();
 		}
 
-		$usersStatus = $this->wpdb->get_row("SHOW TABLE STATUS LIKE '{$this->wpdb->users}'");
-		$parentCollation = $usersStatus->Collation ?? 'utf8mb4_general_ci';
-		self::$charsetCollate = "DEFAULT CHARACTER SET utf8mb4 COLLATE {$parentCollation}";
 
 	}
 
@@ -479,7 +500,7 @@
 			$data['created_at'] = current_time('mysql');
 		}
 
-		$result = $this->wpdb->insert(
+		$result = self::$wpdb->insert(
 			$this->fullTableName,
 			$data,
 			$format
@@ -492,7 +513,7 @@
 
 		$this->cache->flush();
 
-		return $this->wpdb->insert_id;
+		return self::$wpdb->insert_id;
 	}
 
 	/**
@@ -510,17 +531,15 @@
 
 	/**
 	 * Find or create a record
-	 *
 	 * @param array $searchData Data to search for
 	 * @param array $createData Optional additional data for creation
-	 * @return array ['id' => int, 'created' => bool, 'record' => object]
+	 * @return int|false The ID of the created record, or false
 	 *
 	 * @example
-	 * $result = CustomTable::for('favourites')->findOrCreate(
-	 *     ['user_id' => 1, 'target_id' => 123],
-	 *     ['type' => 'tattoo']
-	 * );
-	 * // Returns: ['id' => 456, 'created' => false, 'record' => object]
+	 *  $result = CustomTable::for('favourites')->findOrCreate(
+	 *      ['user_id' => 1, 'target_id' => 123],
+	 *      ['type' => 'tattoo']
+	 *  );
 	 */
 	public function findOrCreate(array $searchData, array $createData = []):int|false
 	{
@@ -586,7 +605,7 @@
 				  (" . implode(',', $columns_escaped) . ")
 				  VALUES " . implode(',', $placeholders);
 
-		$result = $this->wpdb->query($this->wpdb->prepare($query, $values));
+		$result = self::$wpdb->query(self::$wpdb->prepare($query, $values));
 
 		if ($result === false) {
 			$this->logError('bulkInsert', ['rows' => count($rows)]);
@@ -620,25 +639,24 @@
 				$query .= " WHERE {$where['sql']}";
 				$values = $where['values'];
 
-				return $this->wpdb->get_row($this->wpdb->prepare($query, $values), $output);
+				return self::$wpdb->get_row(self::$wpdb->prepare($query, $values), $output);
 			}
 		);
 	}
 
 	/**
 	 * Get multiple records
-	 *
 	 * @param array $args Query arguments: where, orderby, order, limit, offset
+	 * @param bool $itemsOnly
 	 * @param string $output OBJECT, ARRAY_A, or ARRAY_N
 	 * @return array
-	 *
 	 * @example
-	 * $favs = $table->getMany([
-	 *     'where' => ['user_id' => 1],
-	 *     'orderby' => 'date_added',
-	 *     'order' => 'DESC',
-	 *     'per_page' => 20
-	 * ]);
+	 *  $favs = $table->getMany([
+	 *      'where' => ['user_id' => 1],
+	 *      'orderby' => 'date_added',
+	 *      'order' => 'DESC',
+	 *      'per_page' => 20
+	 *  ]);
 	 */
 	public function getMany(array $args = [], bool $itemsOnly = true, string $output = OBJECT): array
 	{
@@ -680,10 +698,10 @@
 				}
 
 				if (empty($values)) {
-					return $this->wpdb->get_results($query, $output);
+					return self::$wpdb->get_results($query, $output);
 				}
 
-				return $this->wpdb->get_results($this->wpdb->prepare($query, $values), $output);
+				return self::$wpdb->get_results(self::$wpdb->prepare($query, $values), $output);
 			}
 		);
 
@@ -720,11 +738,13 @@
 		return sanitize_sql_orderby($orderby);
 	}
 
-
 	/**
 	 * Get a specific column value from all matches
 	 * @param string $column
 	 * @param array $where
+	 * @param string|null $orderby
+	 * @param string|null $order
+	 * @param int|null $limit
 	 * @return array
 	 */
 	public function pluck(string $column, array $where = [], ?string $orderby = null, ?string $order = null, ?int $limit = null): array
@@ -776,10 +796,10 @@
 				}
 
 				if (empty($values)) {
-					return (int) $this->wpdb->get_var($query);
+					return (int) self::$wpdb->get_var($query);
 				}
 
-				return (int) $this->wpdb->get_var($this->wpdb->prepare($query, $values));
+				return (int) self::$wpdb->get_var(self::$wpdb->prepare($query, $values));
 			}
 		);
 
@@ -822,7 +842,7 @@
 			$data['updated_at'] = current_time('mysql');
 		}
 
-		$result = $this->wpdb->update(
+		$result = self::$wpdb->update(
 			$this->fullTableName,
 			$data,
 			$where,
@@ -854,7 +874,7 @@
 	 */
 	public function delete(array $where, ?array $where_format = null): int|false
 	{
-		$result = $this->wpdb->delete(
+		$result = self::$wpdb->delete(
 			$this->fullTableName,
 			$where,
 			$where_format
@@ -877,7 +897,7 @@
 	 *
 	 * @param string $query SQL query (use {table} as placeholder)
 	 * @param array $values Values for prepare()
-	 * @return mixed Query result
+	 * @return int|bool|\mysqli_result|null Query result
 	 *
 	 * @example
 	 * $results = $table->query(
@@ -885,15 +905,15 @@
 	 *     [1, 'pending', 'active']
 	 * );
 	 */
-	public function query(string $query, array $values = []): mixed
+	public function query(string $query, array $values = []): int|bool|null|\mysqli_result
 	{
 		$query = str_replace('{table}', $this->fullTableName, $query);
 
 		if (empty($values)) {
-			return $this->wpdb->query($query);
+			return self::$wpdb->query($query);
 		}
 
-		return $this->wpdb->query($this->wpdb->prepare($query, $values));
+		return self::$wpdb->query(self::$wpdb->prepare($query, $values));
 	}
 
 	/**
@@ -909,10 +929,10 @@
 		$query = str_replace('{table}', $this->fullTableName, $query);
 
 		if (empty($values)) {
-			return $this->wpdb->get_results($query, $output);
+			return self::$wpdb->get_results($query, $output);
 		}
 
-		return $this->wpdb->get_results($this->wpdb->prepare($query, $values), $output);
+		return self::$wpdb->get_results(self::$wpdb->prepare($query, $values), $output);
 	}
 
 	/**
@@ -920,17 +940,17 @@
 	 *
 	 * @param string $query SQL query (use {table} as placeholder)
 	 * @param array $values Values for prepare()
-	 * @return mixed
+	 * @return string|null
 	 */
-	public function queryVar(string $query, array $values = []): mixed
+	public function queryVar(string $query, array $values = []): ?string
 	{
 		$query = str_replace('{table}', $this->fullTableName, $query);
 
 		if (empty($values)) {
-			return $this->wpdb->get_var($query);
+			return self::$wpdb->get_var($query);
 		}
 
-		return $this->wpdb->get_var($this->wpdb->prepare($query, $values));
+		return self::$wpdb->get_var(self::$wpdb->prepare($query, $values));
 	}
 
 	// =========================================================================
@@ -942,7 +962,7 @@
 	 */
 	public function startTransaction(): void
 	{
-		$this->wpdb->query('START TRANSACTION');
+		self::$wpdb->query('START TRANSACTION');
 	}
 
 	/**
@@ -950,7 +970,7 @@
 	 */
 	public function commit(): void
 	{
-		$this->wpdb->query('COMMIT');
+		self::$wpdb->query('COMMIT');
 	}
 
 	/**
@@ -958,7 +978,7 @@
 	 */
 	public function rollback(): void
 	{
-		$this->wpdb->query('ROLLBACK');
+		self::$wpdb->query('ROLLBACK');
 	}
 
 	/**
@@ -1007,7 +1027,7 @@
 	 */
 	public function getInsertId(): int
 	{
-		return $this->wpdb->insert_id;
+		return self::$wpdb->insert_id;
 	}
 
 	/**
@@ -1015,7 +1035,7 @@
 	 */
 	public function getLastError(): string
 	{
-		return $this->wpdb->last_error;
+		return self::$wpdb->last_error;
 	}
 
 	/**
@@ -1023,7 +1043,7 @@
 	 */
 	public function getAffectedRows(): int
 	{
-		return $this->wpdb->rows_affected;
+		return self::$wpdb->rows_affected;
 	}
 
 	public function getUserIDType():string
@@ -1061,16 +1081,16 @@
 	// PRIVATE HELPERS
 	// =========================================================================
 	private function getColumnType(string $table, string $column):string|false {
-		$tableExists = $this->wpdb->get_var(
-			$this->wpdb->prepare("SHOW TABLES LIKE %s", $table)
+		$tableExists = self::$wpdb->get_var(
+			self::$wpdb->prepare("SHOW TABLES LIKE %s", $table)
 		);
 		if (!$tableExists) {
 			error_log("[CustomTable] Table {$table} does not exist for getColumnType");
 			return 'bigint(20) unsigned'; // safe fallback
 		}
 
-		$result = $this->wpdb->get_row(
-			$this->wpdb->prepare(
+		$result = self::$wpdb->get_row(
+			self::$wpdb->prepare(
 				"SELECT COLUMN_TYPE
             FROM INFORMATION_SCHEMA.COLUMNS
             WHERE TABLE_SCHEMA = DATABASE()
@@ -1089,7 +1109,7 @@
 
 	private function tableExists():bool
 	{
-		return !is_null($this->wpdb->get_var($this->wpdb->prepare("SHOW TABLES LIKE %s", $this->fullTableName)));
+		return !is_null(self::$wpdb->get_var(self::$wpdb->prepare("SHOW TABLES LIKE %s", $this->fullTableName)));
 	}
 	/**
 	 * Build WHERE clause from associative array
@@ -1165,7 +1185,7 @@
 		static $cache = [];
 
 		if (!isset($cache[$this->tableName])) {
-			$columns = $this->wpdb->get_col("DESCRIBE {$this->fullTableName}");
+			$columns = self::$wpdb->get_col("DESCRIBE {$this->fullTableName}");
 			$cache[$this->tableName] = array_flip($columns);
 		}
 
@@ -1178,14 +1198,13 @@
 	private function logError(string $operation, array $context = []): void
 	{
 		if (function_exists('JVB')) {
-			JVB()->error()->log(
+			Errors::getInstance()->log(
 				$this->tableName,
-				"CustomTable {$operation} failed: " . $this->wpdb->last_error,
-				$context,
-				'error'
+				"CustomTable {$operation} failed: " . self::$wpdb->last_error,
+				$context
 			);
 		} else {
-			error_log("[CustomTable:{$this->tableName}] {$operation} failed: " . $this->wpdb->last_error);
+			error_log("[CustomTable:{$this->tableName}] {$operation} failed: " . self::$wpdb->last_error);
 		}
 	}
 

--
Gitblit v1.10.0