$value) { if (property_exists(get_called_class(), $field)) { $this->$field = $value; } } } public static function create($data) { $FronkDB = FronkDB::singleton(); $db = $FronkDB->link; $table = self::getTable(); self::checkAllFields($data, ['id']); $sqlColumns = []; $sqlValues = []; foreach ($data as $field => $value) { if (!property_exists(get_called_class(), $field)) { throw new Exception("Field $field does not exist in " . get_called_class()); } $sqlValues[] = $value === null ? 'NULL' : "'" . $db->real_escape_string($value) . "'"; $sqlColumns[] = "`$field`"; } $sql = "INSERT INTO `$table` (" . implode(", ", $sqlColumns) . ") VALUES (" . implode(", ", $sqlValues) . ")"; $db->query($sql) or die($db->error); return $db->insert_id; } public static function getTable(): string { return str_replace('Model', '', get_called_class()); } /** * Checks if all required fields of the current class are present in a given data array. * * This method uses reflection to determine which fields are required based on their type declarations. * It then iterates over these required fields and throws an exception if any of them are missing * from the provided data array. * * @param array $data The data array to check. * @param array $skip An optional array of field names to skip during the check. * * @return void * @throws Exception If any required field is missing in the `$data` array. * */ public static function checkAllFields(array $data, array $skip = []) { $requiredVars = array_filter(get_class_vars(get_called_class()), function ($value, $key) { $reflectionProperty = new ReflectionProperty(get_called_class(), $key); return !$reflectionProperty->hasType() || !$reflectionProperty->getType()->allowsNull(); }, ARRAY_FILTER_USE_BOTH); foreach ($requiredVars as $field => $value) { if (in_array($field, $skip)) { continue; } if (!isset($data[$field])) { throw new Exception("Required field $field is missing in data array"); } } } public static function get($id, $die= false): TTCrudBaseModel { $FronkDB = FronkDB::singleton(); $db = $FronkDB->link; $id = $db->real_escape_string($id); $table = self::getTable(); $sql = "SELECT * FROM `$table` WHERE `id` = $id"; if($die) { die($sql); } $result = $db->query($sql); // as TTCRudBaseModel is abstract, we need to get the class name of the child class $class = get_called_class(); return new $class($result->fetch_assoc()); } public static function count($filter = []): int { $FronkDB = FronkDB::singleton(); $db = $FronkDB->link; $table = self::getTable(); $filter = self::getSQLFilter($filter); $sql = "SELECT COUNT(*) as count FROM `$table` $filter"; $result = $db->query($sql); return $result->fetch_assoc()['count']; } public static function getSQLFilter($filter): string { if (empty($filter)) { return ""; } $sql = "WHERE 1=1"; foreach ($filter as $key => $value) { if (!property_exists(get_called_class(), $key)) { throw new Exception("Field $key does not exist in " . get_called_class()); } $sql .= Helper::generateFilterCondition($value, $key, gettype($value) === "integer"); } return $sql; } public static function getAll($filter = [], $limit = null, $offset = 0, $order = ["key" => null]): array { $FronkDB = FronkDB::singleton(); $db = $FronkDB->link; $table = self::getTable(); $filter = self::getSQLFilter($filter); $sql = "SELECT * FROM `$table` $filter"; $sql .= $order['key'] === null ? " ORDER BY `id` ASC" : " ORDER BY `" . $order['key'] . "` " . $order['order']; $sql .= $limit === null ? "" : " LIMIT " . $limit . " OFFSET " . $offset; try { $result = $db->query($sql); } catch (Exception $e) { echo $sql; die($e->getMessage()); } $rows = []; $class = get_called_class(); while ($row = $result->fetch_assoc()) { $rows[] = new $class($row); } return $rows; } public static function update($data) { $FronkDB = FronkDB::singleton(); $db = $FronkDB->link; $table = self::getTable(); // Check if all fields are set self::checkAllFields($data); $values = []; foreach ($data as $field => $value) { if (!property_exists(get_called_class(), $field)) { throw new Exception("Field $field does not exist in " . get_called_class()); } if ($field === "id") { continue; } // TODO: make this cleaner if ($value === "" && (new ReflectionProperty(get_called_class(), $field))->getType()->getName() === "float") { $value = null; } if ($value === "" && (new ReflectionProperty(get_called_class(), $field))->getType()->getName() === "int") { $value = null; } $values[] = $value === null ? "`$field` = NULL" : "`$field` = '" . $db->real_escape_string($value) . "'"; } $sql = "UPDATE `$table` SET " . implode(", ", $values) . " WHERE `id` = " . $db->real_escape_string($data['id']); $db->query($sql); return $db->affected_rows; } public static function delete($id) { $FronkDB = FronkDB::singleton(); $db = $FronkDB->link; $table = self::getTable(); $id = $db->real_escape_string($id); $sql = "DELETE FROM `$table` WHERE `id` = $id"; $db->query($sql); return $db->affected_rows; } }