Implemented Redis caching for improved department and customer data retrieval. Added Slack integration to send department notifications. Enhanced booking features and status handling, including parsing logic and wash certificate processing.
325 lines
10 KiB
PHP
325 lines
10 KiB
PHP
<?php
|
|
|
|
namespace traits;
|
|
|
|
use classes\db;
|
|
use classes\response;
|
|
use Exception;
|
|
|
|
trait db_object_t
|
|
{
|
|
public int $id; // The id of the object in the database
|
|
private string $table; // The table of the objects in the database (e.g. users)
|
|
|
|
public function __construct()
|
|
{
|
|
$this->structure();
|
|
}
|
|
|
|
/**
|
|
* Structure: Define the table and fields of the objects in the database
|
|
*/
|
|
public function structure(): void
|
|
{
|
|
// Define the table and fields of the objects in the database
|
|
}
|
|
|
|
public function __toString(): string
|
|
{
|
|
// Return the object as a string
|
|
return json_encode($this->getArray());
|
|
}
|
|
|
|
/**
|
|
* Get current object row as an array
|
|
* @return array The current object row as an array
|
|
* @throws Exception If object not found
|
|
*/
|
|
public function getArray(): array
|
|
{
|
|
// Get the object from the database
|
|
global $db;
|
|
// Check if the id is set, if not throw an exception
|
|
if (!isset($this->id)) {
|
|
throw new Exception('Object not found');
|
|
}
|
|
$id = $this->id;
|
|
$sql = "SELECT * FROM $this->table WHERE id = $id";
|
|
$result = $db->query($sql);
|
|
return $db->fetch_assoc($result);
|
|
}
|
|
|
|
/**
|
|
* List objects with pagination (if set)
|
|
* @return array The list of objects in the table
|
|
*/
|
|
public function listObjectsWithPaginationIfSet(): array
|
|
{
|
|
global $response;
|
|
$page = ((int)$response->getRequestParameter('page')) ?? null; // Get the page number
|
|
$limit = ((int)$response->getRequestParameter('limit')) ?? null; // Get the number of objects per page
|
|
$search = $response->getRequestParameter('search') ?? null; // Get the search query
|
|
$filters = $response->getRequestParameter('filters') ?? null; // Get the filters ( Eg. department_id:1,role_id:2 OR department_id:1 )
|
|
$order = $response->getRequestParameter('order') ?? null; // Get the order ( Eg. name:ASC )
|
|
// Make the filters an array
|
|
if ($filters) {
|
|
$filters = explode(',', $filters);
|
|
$temp = [];
|
|
foreach ( $filters as $filter ) {
|
|
$filter = explode(':', $filter);
|
|
$temp[$filter[0]] = $filter[1];
|
|
}
|
|
$filters = $temp;
|
|
}
|
|
// Make the order an array
|
|
if ($order) {
|
|
$order = explode(':', $order);
|
|
$order = [$order[0] => $order[1]];
|
|
// Make the order direction uppercase
|
|
$order = array_map('strtoupper', $order);
|
|
// Check if the order direction is valid
|
|
if (!in_array($order[array_key_first($order)], ['ASC', 'DESC'])) {
|
|
throw new Exception('Invalid order direction, must be ASC or DESC');
|
|
}
|
|
}
|
|
// List the objects with pagination
|
|
if ($page && $limit) {
|
|
return $this->listObjectsWithPagination($page, $limit, $search, $filters, $order);
|
|
}
|
|
return $this->listObjects();
|
|
}
|
|
|
|
/**
|
|
* List objects in the table with pagination
|
|
* @param int $page The page number
|
|
* @param int $limit The number of objects per page
|
|
* @return array The list of objects in the table
|
|
*/
|
|
public function listObjectsWithPagination(int $page, int $limit, ?string $search = null, ?array $filters = null, ?array $order = null): array
|
|
{
|
|
global /** @var response $response */
|
|
/** @var db $db */
|
|
$db, $response;
|
|
$mysqli = $db->conn;
|
|
|
|
$offset = ($page - 1) * $limit;
|
|
|
|
// Fetch table fields
|
|
$fields = [];
|
|
$result = $mysqli->query("SHOW COLUMNS FROM {$this->table}");
|
|
while ($row = $result->fetch_assoc()) {
|
|
$fields[] = $row['Field'];
|
|
}
|
|
$result->free();
|
|
|
|
$whereClauses = [];
|
|
$params = [];
|
|
|
|
// Search clause
|
|
if (!empty($search)) {
|
|
// Use prepared statements to prevent SQL injection
|
|
$searchClauses = [];
|
|
foreach ( $fields as $field ) {
|
|
$searchClauses[] = "`$field` LIKE ?";
|
|
$params[] = "%$search%";
|
|
}
|
|
$whereClauses[] = '(' . implode(' OR ', $searchClauses) . ')';
|
|
}
|
|
|
|
// Filters clause
|
|
if (!empty($filters)) {
|
|
foreach ( $filters as $field => $value ) {
|
|
if (in_array($field, $fields)) {
|
|
$whereClauses[] = "`$field` = ?";
|
|
$params[] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for "deleted_at" column
|
|
if (in_array('deleted_at', $fields)) {
|
|
$whereClauses[] = "`deleted_at` IS NULL";
|
|
}
|
|
|
|
// Combine WHERE clauses
|
|
$whereQuery = !empty($whereClauses) ? 'WHERE ' . implode(' AND ', $whereClauses) : '';
|
|
|
|
// Order clause
|
|
$orderQuery = '';
|
|
if (!empty($order)) {
|
|
$orderParts = [];
|
|
foreach ( $order as $field => $direction ) {
|
|
if (in_array($field, $fields)) {
|
|
$direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
|
|
$orderParts[] = "`$field` $direction";
|
|
}
|
|
}
|
|
$orderQuery = !empty($orderParts) ? 'ORDER BY ' . implode(', ', $orderParts) : '';
|
|
}
|
|
|
|
// Pagination clause
|
|
$limitQuery = 'LIMIT ? OFFSET ?';
|
|
$params[] = $limit;
|
|
$params[] = $offset;
|
|
|
|
// Final query
|
|
$sql = "SELECT * FROM {$this->table} $whereQuery $orderQuery $limitQuery";
|
|
|
|
// Prepare and bind
|
|
$stmt = $mysqli->prepare($sql);
|
|
if ($stmt === false) {
|
|
throw new Exception("Failed to prepare statement: " . $mysqli->error);
|
|
}
|
|
|
|
// Dynamically bind parameters
|
|
$types = str_repeat('s', count($params) - 2) . 'ii'; // 's' for strings, 'i' for limit and offset
|
|
$stmt->bind_param($types, ...$params);
|
|
|
|
$stmt->execute();
|
|
$result = $stmt->get_result();
|
|
$objects = $result->fetch_all(MYSQLI_ASSOC);
|
|
$stmt->close();
|
|
|
|
// Total count query
|
|
$countSql = "SELECT COUNT(*) AS count FROM {$this->table} $whereQuery";
|
|
$countStmt = $mysqli->prepare($countSql);
|
|
if ($countStmt === false) {
|
|
throw new Exception("Failed to prepare count statement: " . $mysqli->error);
|
|
}
|
|
|
|
// Reuse parameters (excluding LIMIT and OFFSET)
|
|
$countParams = array_slice($params, 0, -2);
|
|
$countTypes = substr($types, 0, count($countParams));
|
|
if (!empty($countParams)) {
|
|
$countStmt->bind_param($countTypes, ...$countParams);
|
|
}
|
|
|
|
$countStmt->execute();
|
|
$countResult = $countStmt->get_result();
|
|
$total = $countResult->fetch_assoc()['count'];
|
|
$countStmt->close();
|
|
|
|
// Provide pagination metadata
|
|
$response->paginate($page, $limit, $total, $search, $filters, $order);
|
|
|
|
return $objects;
|
|
}
|
|
|
|
/**
|
|
* List ALL objects in the table (THIS INCLUDES DELETED OBJECTS)
|
|
* @return array The list of objects in the table
|
|
*/
|
|
public function listObjects(): array
|
|
{
|
|
global $db;
|
|
$sql = "SELECT * FROM $this->table";
|
|
$result = $db->query($sql);
|
|
return $db->fetch_all($result);
|
|
}
|
|
|
|
/**
|
|
* Get the total number of objects
|
|
* @param string|null $search The search query
|
|
* @param array|null $filters The filters ( Eg. department_id:1,role_id:2 OR department_id:1 )
|
|
* @return int The total number of objects
|
|
*/
|
|
public function getTotalObjects(string $search = null, array $filters = null): int
|
|
{
|
|
global $db;
|
|
$mysqli = $db->conn;
|
|
|
|
// Fetch table fields
|
|
$fields = [];
|
|
$result = $mysqli->query("SHOW COLUMNS FROM {$this->table}");
|
|
while ($row = $result->fetch_assoc()) {
|
|
$fields[] = $row['Field'];
|
|
}
|
|
$result->free();
|
|
|
|
$whereClauses = [];
|
|
$params = [];
|
|
|
|
// Search clause
|
|
if (!empty($search)) {
|
|
// Use prepared statements to prevent SQL injection
|
|
$searchClauses = [];
|
|
foreach ( $fields as $field ) {
|
|
$searchClauses[] = "`$field` LIKE ?";
|
|
$params[] = "%$search%";
|
|
}
|
|
$whereClauses[] = '(' . implode(' OR ', $searchClauses) . ')';
|
|
}
|
|
|
|
// Filters clause
|
|
if (!empty($filters)) {
|
|
foreach ( $filters as $field => $value ) {
|
|
if (in_array($field, $fields)) {
|
|
$whereClauses[] = "`$field` = ?";
|
|
$params[] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Combine WHERE clauses
|
|
$whereQuery = !empty($whereClauses) ? 'WHERE ' . implode(' AND ', $whereClauses) : '';
|
|
|
|
// Check for "deleted_at" column
|
|
if (in_array('deleted_at', $fields)) {
|
|
$whereClauses[] = "`deleted_at` IS NULL";
|
|
}
|
|
|
|
// Final query
|
|
$sql = "SELECT COUNT(*) AS count FROM {$this->table} $whereQuery";
|
|
|
|
// Prepare and bind
|
|
$stmt = $mysqli->prepare($sql);
|
|
if ($stmt === false) {
|
|
throw new Exception("Failed to prepare statement: " . $mysqli->error);
|
|
}
|
|
|
|
// Dynamically bind parameters
|
|
$types = str_repeat('s', count($params)); // 's' for strings
|
|
$stmt->bind_param($types, ...$params);
|
|
|
|
$stmt->execute();
|
|
$result = $stmt->get_result();
|
|
|
|
return $result->fetch_assoc()['count'];
|
|
}
|
|
|
|
/**
|
|
* Does this object exist in the database?
|
|
* @return bool True if the object exists in the database, false otherwise
|
|
*/
|
|
public function exists(): bool
|
|
{
|
|
// Check if the id is set
|
|
if (!isset($this->id)) {
|
|
return false;
|
|
}
|
|
// Check if the object exists in the database
|
|
global $db;
|
|
$id = $this->id;
|
|
$sql = "SELECT * FROM $this->table WHERE id = $id";
|
|
$result = $db->query($sql);
|
|
return $result->num_rows > 0;
|
|
}
|
|
|
|
/**
|
|
* Get the table of the objects in the database
|
|
* @returns string
|
|
*/
|
|
public function getTable(): string
|
|
{
|
|
return $this->table;
|
|
}
|
|
|
|
/**
|
|
* Set the table of the objects in the database
|
|
* @param string $table The table of the objects in the database
|
|
*/
|
|
public function setTable(string $table): void
|
|
{
|
|
$this->table = $table;
|
|
}
|
|
} |