Refactor order and order item management: add cache invalidation, improve object change tracking, and enhance asArray conversion with caching and expiration support.

This commit is contained in:
Jepp9350
2025-06-26 08:55:58 +02:00
parent 0e90c80ee1
commit 5b88ef9dc3
5 changed files with 172 additions and 49 deletions
@@ -90,10 +90,19 @@ class collected_order_invoices_o extends db
}
/**
* Get the invoice collection as an array
* @throws ApiErrorException If the payment method is Stripe and the request fails
* @throws Exception If the request was not successful
*/
public function asArray(): array
{
// Require the invoice collection to be selected
self::requireSelected();
// Check if the object is cached
$cached = self::getCached('asArray', $this->id);
if ($cached !== null) {
return (array)$cached;
}
$tmp = [
'id' => (int)$this->id,
'customer_number' => (int)$this->customer_number->value(),
@@ -130,6 +139,9 @@ class collected_order_invoices_o extends db
]);
$tmp['stripe'] = $stripe_details;
}
// Cache the result
self::cache('asArray', $tmp, $this->id);
self::setCachedExpiration('asArray', self::$asArrayCacheExpiration, $this->id);
return $tmp;
}
@@ -141,6 +153,8 @@ class collected_order_invoices_o extends db
*/
public function getOrders(bool $count = false): array|int
{
// Require the invoice collection to be selected
self::requireSelected();
$orders = new orders_o();
$order_ids = $orders->getFieldsWhere(
[
@@ -542,7 +556,8 @@ class collected_order_invoices_o extends db
public function objectChanged(): void
{
//TODO: Add cache invalidation
// Invalidate the cache, so the next time the object is requested, it will be fetched from the database
self::deleteCached('asArray', $this->id);
}
/**
@@ -609,6 +624,8 @@ class collected_order_invoices_o extends db
foreach ( $orders as $order ) {
self::addInvoiceToDraft($order['id'], true, $draft_id, $currency);
}
// Object changed
self::objectChanged();
return $this;
}
@@ -837,6 +854,8 @@ class collected_order_invoices_o extends db
// Set the order to the new invoice collection
$order_object->invoice_collection_id->set($tmp->id);
}
// Invalidate the cache for the invoice collection
$this->objectChanged();
}
/**
@@ -912,6 +931,7 @@ class collected_order_invoices_o extends db
$transaction->invoice_collection_id->set($this->id);
$transaction->created_at->set(self::getFirstDayOfMonth($this->created_at->value()));
$transaction->objectChanged();
$this->objectChanged();
// Add the vehicle subscriptions to the transaction
$order_items_o = new order_items_o();
foreach ( $vehicle_array as $vehicle ) {
@@ -974,6 +994,7 @@ class collected_order_invoices_o extends db
$order_item_object->requireSelected();
$order_item_object->include_in_invoice->set(0);
$order_item_object->price->set((int)self::getWashSubscriptionPrice((new products_o())->select((int)$order_item['product_id'])->price->value()) / 2);
$order_item_object->objectChanged();
}
// Check if the order item product id is equal any of the vehicle subscription addons product id
if (isset($vehicle_regs[$order['reg_1']]['addons'][$order_item['product_id']])) {
@@ -999,10 +1020,13 @@ class collected_order_invoices_o extends db
$order_item_object->requireSelected();
$order_item_object->include_in_invoice->set(0);
$order_item_object->price->set($tmp_subscription_price);
$order_item_object->objectChanged();
}
}
$order_object->objectChanged();
}
}
$this->objectChanged();
//print_r($vehicle_array);
}
@@ -1034,6 +1058,8 @@ class collected_order_invoices_o extends db
$order->requireSelected();
$order->delete();
}
// Clear the cache for the invoice collection
$this->objectChanged();
}
private static function getFirstDayOfMonth(string $timestamp): string
@@ -1137,7 +1163,10 @@ class collected_order_invoices_o extends db
$order_item_object->requireSelected();
$order_item_object->include_in_invoice->set(0);
$order_item_object->price->set(0);
$order_item_object->objectChanged();
}
}
// Invalidate the cache for the invoice collection
$this->objectChanged();
}
}
+40 -9
View File
@@ -4,6 +4,7 @@ namespace objects;
use classes\db;
use classes\object_property;
use Exception;
use traits\db_object_t;
class order_items_o extends db
@@ -62,11 +63,6 @@ class order_items_o extends db
$this->setTable('order_items');
}
public function objectChanged(): void
{
// No need to invalidate the cache, since the order_items object is not cached
}
public function getOrderItemById(int $id): order_items_o
{
global $db;
@@ -113,11 +109,37 @@ class order_items_o extends db
if ($related_item_id) {
$this->related_item_id->set($related_item_id);
}
} catch (\Exception $e) {
// Inform the order object that a new item has been added
$this->getOrder()->objectChanged();
} catch (Exception $e) {
$response->error($e->getMessage());
}
}
/**
* @throws Exception
*/
public function objectChanged(): void
{
// This method is called when the object is changed, to inform the order object
// that an item has been added, edited or removed.
// This is used to update the order total and other related properties.
$order = $this->getOrder();
$order->objectChanged();
}
/**
* Get the order object associated with this order item
* @return orders_o The order object associated with this order item
* @throws Exception If the order item is not selected
* @throws Exception If no order is selected
*/
public function getOrder(): orders_o
{
self::requireSelected();
return (new orders_o())->select($this->id);
}
public function edit(int $id, int $order_id, int $product_id, string $reference, string $notes, int $cashier_id, int $price, int $quantity): void
{
global $db, $response;
@@ -132,7 +154,9 @@ class order_items_o extends db
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
// Inform the order object that an item has been edited
$this->objectChanged();
} catch (Exception $e) {
$response->error($e->getMessage());
}
}
@@ -173,13 +197,17 @@ class order_items_o extends db
if ($notes) {
$this->notes->set($notes);
}
$this->objectChanged();
} catch (\Exception $e) {
} catch (Exception $e) {
$response->error($e->getMessage());
}
}
/**
* @throws Exception
*/
public function removeOrderItem(int $id): void
{
// TODO: Implement delete() method instead
@@ -187,6 +215,8 @@ class order_items_o extends db
$this->id = $id;
$sql = "DELETE FROM $this->table WHERE id = $this->id or related_item_id = $this->id";
$db->query($sql);
// Inform the order object that an item has been removed
$this->getOrder()->objectChanged();
}
public function getItemAsArray(): array
@@ -236,7 +266,8 @@ class order_items_o extends db
$db->query($sql);
// Set the values of the object properties
$this->getObjectProperties();
} catch (\Exception $e) {
$this->objectChanged();
} catch (Exception $e) {
$response->error($e->getMessage());
}
}
+60 -33
View File
@@ -34,6 +34,7 @@ class orders_o extends db
public object_property $wash_id; // The XL Vask Wash ID, if any
public object_property $lane; // The lane used for the order, if any
public function structure(): void
{
$this->setTable('orders');
@@ -116,9 +117,49 @@ class orders_o extends db
{
// Set the deleted_at property to the current timestamp
$this->deleted_at->set(date('Y-m-d H:i:s'));
$this->objectChanged();
// Save the object
}
/**
* @throws Exception If the order is not selected
* This function is called when the order object is changed.
*/
public function objectChanged(): void
{
self::requireSelected();
// Reset the cached object
self::deleteCached('asArray', $this->id);
// Inform the order collection that the order has changed
$this->getOrderCollection()->objectChanged();
}
/**
* Get the order collection for the order
* @return collected_order_invoices_o The order collection
* @throws Exception If the order is not selected
*/
public function getOrderCollection(): collected_order_invoices_o
{
self::requireSelected();
$order_collection = new collected_order_invoices_o();
$order_collection->select($this->invoice_collection_id->value());
if (!$order_collection->exists()) {
throw new Exception('Order collection not found');
}
return $order_collection;
}
public function exists(): bool
{
// Check if the id is greater than 0, and that the deleted_at property is null
if ($this->id > 0) {
$this->getObjectProperties();
return $this->deleted_at->value() === null;
}
return false;
}
public function restore(): void
{
// Set the deleted_at property to null
@@ -192,16 +233,6 @@ class orders_o extends db
$this->{$data['field']}->set($data['value']);
}
public function exists(): bool
{
// Check if the id is greater than 0, and that the deleted_at property is null
if ($this->id > 0) {
$this->getObjectProperties();
return $this->deleted_at->value() === null;
}
return false;
}
/**
* Mark the order as completed
* @throws Exception If the order is not selected
@@ -223,11 +254,6 @@ class orders_o extends db
$this->objectChanged();
}
public function objectChanged(): void
{
// Since the orders object is not cached, there is no need to invalidate the cache
}
/**
* Get the order by invoice id
* @param int $invoiceId
@@ -427,22 +453,6 @@ class orders_o extends db
return $count;
}
/**
* Get the order collection for the order
* @return collected_order_invoices_o The order collection
* @throws Exception If the order is not selected
*/
public function getOrderCollection(): collected_order_invoices_o
{
self::requireSelected();
$order_collection = new collected_order_invoices_o();
$order_collection->select($this->invoice_collection_id->value());
if (!$order_collection->exists()) {
throw new Exception('Order collection not found');
}
return $order_collection;
}
/**
* Get the wash subscription transactions for a customer
* @throws Exception If something goes wrong
@@ -473,9 +483,22 @@ class orders_o extends db
);
}
public function asArray(): array
/**
* @return array The order as an array
* @throws Exception
* Convert the order object to an array
*/
public function asArray(bool $skipCache = false): array
{
return [
self::requireSelected();
if (!$skipCache) {
// Check if the object is cached
$cached = self::getCached('asArray', $this->id);
if ($cached) {
return (array)$cached;
}
}
$tmp = [
'id' => $this->id,
'customer_id' => (int)$this->customer_id->value(),
'cashier_id' => (int)$this->cashier_id->value(),
@@ -495,6 +518,10 @@ class orders_o extends db
'lane' => $this->lane->value(),
'closed_at' => (int)$this->invoice_collection_id->value() ? (new collected_order_invoices_o())->select((int)$this->invoice_collection_id->value())->closed_at->value() : null,
];
// Cache the object
self::cache('asArray', $tmp, $this->id);
self::setCachedExpiration('asArray', self::$asArrayCacheExpiration, $this->id);
return $tmp;
}
public function getNetAmount(): float
+3 -2
View File
@@ -41,7 +41,7 @@ class ordersRoute
// Log the incident
(new logs_o())->add('orders', 'global', 1, $user->id, 'LIST_ORDERS', 'Successfully listed orders');
// Create economic_module_orders object
$economic_module_orders = new economic_module_orders();
//$economic_module_orders = new economic_module_orders();
$orders = new orders_o();
if (!$restrict_only_own) {
$department_ids = $user->getGroup()->getDepartments();
@@ -216,7 +216,8 @@ class ordersRoute
if (isset($data['created_at'])) {
$order->created_at->set($data['created_at']);
}
// If the department ID is set, validate it
// Void any cached key for the order
$order->objectChanged();
// Log the incident
(new logs_o())->add('orders', $order->department_id->value(), 1, $user->id, 'EDIT_ORDER', 'Successfully updated an order (ID: ' . $data['id'] . ')');
// Return a success message
+39 -4
View File
@@ -44,10 +44,11 @@ use objects\users_o;
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)
private array $searchableFields = []; // The fields to search in the database (e.g. ['name', 'email']). If empty, all fields will be searched
private array $whereClauses = []; // The where clauses to add to the pagination query
public static int $asArrayCacheExpiration = 600; // The id of the object in the database
public int $id; // The table of the objects in the database (e.g. users)
private string $table; // The fields to search in the database (e.g. ['name', 'email']). If empty, all fields will be searched
private array $searchableFields = []; // The where clauses to add to the pagination query
private array $whereClauses = []; // The cache expiration time for the asArray function, in seconds. This is used to cache the result of the asArray function to improve performance. Default is 10 minutes (600 seconds).
public function __construct()
{
@@ -744,6 +745,40 @@ trait db_object_t
redis->set($this->table . '_' . $objectId . '_' . $key, $data);
}
/**
* Set cached object expiration time
* @param string $key The key to set the cached object expiration time
* @param int $seconds The number of seconds to set the cached object expiration time
* @param null $objectId
* @return void
*/
public function setCachedExpiration(string $key, int $seconds, $objectId = null): void
{
// If the object id is not set, use the object id
if (!$objectId) {
$objectId = $this->id;
}
// Set the expiration time for the cached data
redis->expire($this->table . '_' . $objectId . '_' . $key, $seconds);
}
/**
* Get a cached object key (redis key)
* @param string $key The key to get the cached object
* @return string The cached object key
* @throws Exception If the object is not selected, it throws an exception
*/
public function getCachedKey(string $key, $objectId = null): string
{
self::requireSelected();
// If the object id is not set, use the object id
if (!$objectId) {
$objectId = $this->id;
}
// Return the cached data key
return $this->table . '_' . $objectId . '_' . $key;
}
/**
* Get cached object
* @param string $key The key to get the cached object