Introduced cron tasks for syncing user economic customer details and discounts. Enhanced caching mechanisms for users and departments, added Slack messaging capabilities, and updated CLI/script routing for better flexibility. New routes and utility methods improve cron execution and logging.
391 lines
12 KiB
PHP
391 lines
12 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;
|
|
}
|
|
|
|
/**
|
|
* Cache object
|
|
* @param string $key The key to cache the object
|
|
* @param mixed $data The data to cache
|
|
*/
|
|
public function cache(string $key, mixed $data, $objectId = null): void
|
|
{
|
|
// If the object id is not set, use the object id
|
|
if (!$objectId) {
|
|
$objectId = $this->id;
|
|
}
|
|
// if the data is an array, object, or resource, convert it to a string
|
|
if (is_array($data) || is_object($data) || is_resource($data)) {
|
|
$data = json_encode($data);
|
|
}
|
|
// Cache the data
|
|
redis->set($this->table . '_' . $objectId . '_' . $key, $data);
|
|
}
|
|
|
|
/**
|
|
* Get cached object
|
|
* @param string $key The key to get the cached object
|
|
* @return mixed The cached object
|
|
*/
|
|
public function getCached(string $key, $objectId = null): mixed
|
|
{
|
|
// If the object id is not set, use the object id
|
|
if (!$objectId) {
|
|
$objectId = $this->id;
|
|
}
|
|
// Get the cached data
|
|
$data = redis->get($this->table . '_' . $objectId . '_' . $key) ?? null;
|
|
// if the data is a JSON string, convert it to an array
|
|
if (is_string($data) && json_decode($data)) {
|
|
$data = json_decode($data);
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Delete cached object
|
|
* @param string $key The key to delete the cached object
|
|
*/
|
|
public function deleteCached(string $key, $objectId = null): void
|
|
{
|
|
// If the object id is not set, use the object id
|
|
if (!$objectId) {
|
|
$objectId = $this->id;
|
|
}
|
|
// Delete the cached data
|
|
redis->delete($this->table . '_' . $objectId . '_' . $key);
|
|
}
|
|
|
|
/**
|
|
* Get all objects fields
|
|
* @param array $fields The fields to get from the objects (Eg. ['name', 'description'])
|
|
* @return array The list of fields from the objects (Eg. [['name' => 'John', 'description' => 'Doe'], ['name' => 'Jane', 'description' => 'Doe']])
|
|
*/
|
|
public function getFields(array $fields): array
|
|
{
|
|
global $db;
|
|
$sql = "SELECT " . implode(', ', $fields) . " FROM $this->table";
|
|
$result = $db->query($sql);
|
|
return $db->fetch_all($result);
|
|
}
|
|
} |