Product.php

6.66 KB
27/04/2025 13:34
PHP
<?php
class Product extends Model
{
    /**
     * @var string
     */
    protected $table = 'products';

    /**
     * @param $categoryId
     */
    public function getAll($categoryId = null)
    {
        $query = "SELECT p.*, c.name as category_name
                 FROM {$this->table} p
                 LEFT JOIN categories c ON p.category_id = c.id";

        $params = [];
        if ($categoryId) {
            $query .= " WHERE p.category_id = ?";
            $params = [$categoryId];
        }

        $query .= " ORDER BY p.name ASC";

        return $this->db->fetchAll($query, $params);
    }

    /**
     * @param $id
     */
    public function getById($id)
    {
        $query = "SELECT p.*, c.name as category_name
                 FROM {$this->table} p
                 LEFT JOIN categories c ON p.category_id = c.id
                 WHERE p.id = ?";

        return $this->db->fetch($query, [$id]);
    }

    /**
     * @param $data
     */
    public function create($data)
    {
        // Check if SKU already exists
        $exists = $this->db->fetchColumn(
            "SELECT COUNT(*) FROM {$this->table} WHERE sku = ?",
            [$data['sku']]
        );

        if ($exists) {
            throw new Exception('SKU already exists');
        }

        // Prepare data for insert
        $insertData = [
            'sku' => $data['sku'],
            'name' => $data['name'],
            'price' => $data['price'],
            'cost' => $data['cost'],
            'status' => $data['status'] ?? 'active'
        ];

        // Optional fields
        if (isset($data['barcode'])) {
            $insertData['barcode'] = $data['barcode'];
        }

        if (isset($data['description'])) {
            $insertData['description'] = $data['description'];
        }

        if (isset($data['category_id'])) {
            $insertData['category_id'] = $data['category_id'];
        }

        if (isset($data['quantity'])) {
            $insertData['quantity'] = $data['quantity'];
        }

        if (isset($data['low_stock_threshold'])) {
            $insertData['low_stock_threshold'] = $data['low_stock_threshold'];
        }

        $productId = $this->insert($insertData);

        // If initial stock is added, record inventory transaction
        if (isset($data['quantity']) && $data['quantity'] > 0) {
            $inventory = new Inventory();
            $inventory->recordTransaction(
                $productId,
                'adjustment',
                $data['quantity'],
                null,
                'Initial stock',
                $data['user_id'] ?? null
            );
        }

        return $productId;
    }

    /**
     * @param $id
     * @param $data
     */
    public function update($id, $data)
    {
        // Check if product exists
        $product = $this->getById($id);
        if (!$product) {
            throw new Exception('Product not found');
        }

        // Check if SKU is unique
        if (isset($data['sku']) && $data['sku'] !== $product['sku']) {
            $exists = $this->db->fetchColumn(
                "SELECT COUNT(*) FROM {$this->table} WHERE sku = ? AND id != ?",
                [$data['sku'], $id]
            );

            if ($exists) {
                throw new Exception('SKU already exists');
            }
        }

        // Handle quantity updates separately
        $currentQuantity = $product['quantity'];
        $newQuantity = isset($data['quantity']) ? intval($data['quantity']) : $currentQuantity;
        $quantityChanged = $newQuantity !== $currentQuantity;

        // Remove quantity from update data (will be handled separately)
        unset($data['quantity']);

        // Update product data
        if (!empty($data)) {
            parent::update($id, $data);
        }

        // Handle quantity change if needed
        if ($quantityChanged) {
            $this->updateStock(
                $id,
                $newQuantity,
                'Quantity updated via product edit',
                $data['user_id'] ?? null
            );
        }

        return true;
    }

    /**
     * @param $id
     * @param $quantity
     * @param $notes
     * @param $userId
     */
    public function updateStock($id, $quantity, $notes = '', $userId = null)
    {
        // Start transaction
        $this->db->beginTransaction();

        try {
            // Get current quantity
            $currentQty = $this->db->fetchColumn(
                "SELECT quantity FROM {$this->table} WHERE id = ?",
                [$id]
            );

            if ($currentQty === false) {
                throw new Exception('Product not found');
            }

            // Update quantity
            $this->db->update(
                $this->table,
                ['quantity' => $quantity],
                ['id = ?'],
                [$id]
            );

            // Record transaction
            $adjustment = $quantity - $currentQty;

            // Only record if there's an actual change
            if ($adjustment != 0) {
                $inventory = new Inventory();
                $inventory->recordTransaction(
                    $id,
                    'adjustment',
                    abs($adjustment),
                    null,
                    $notes.($adjustment > 0 ? ' (Added)' : ' (Reduced)'),
                    $userId
                );
            }

            $this->db->commit();
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }

    /**
     * @param $id
     */
    public function delete($id)
    {
        // Check if product has transactions
        $txCount = $this->db->fetchColumn(
            "SELECT COUNT(*) FROM inventory_transactions WHERE product_id = ?",
            [$id]
        );

        if ($txCount > 0) {
            // Soft delete - mark as inactive
            $this->update($id, ['status' => 'inactive']);
            return 'deactivated';
        } else {
            // Hard delete
            parent::delete($id);
            return 'deleted';
        }
    }

    /**
     * @return mixed
     */
    public function getLowStock()
    {
        return $this->db->fetchAll(
            "SELECT p.*, c.name as category_name
            FROM {$this->table} p
            LEFT JOIN categories c ON p.category_id = c.id
            WHERE p.quantity <= p.low_stock_threshold AND p.status = 'active'
            ORDER BY p.quantity ASC"
        );
    }

    /**
     * @param $categoryId
     * @return mixed
     */
    public function countByCategoryId($categoryId)
    {
        return $this->db->fetchColumn(
            "SELECT COUNT(*) FROM {$this->table} WHERE category_id = ?",
            [$categoryId]
        );
    }
}