<?php
 
 
trait DatabaseTrait
 
{
 
    /**
 
     * Name of the primary field in the database/object.
 
     * @return string name of field
 
     */
 
    abstract static function getPrimaryField(): string;
 
 
    /**
 
     * Name of the table for this class.
 
     * @return string name of table
 
     */
 
    abstract static function getTable(): string;
 
 
    /**
 
     * Name of the Object for this class.
 
     * @param int $id
 
     * @return static name of whatever object in use
 
     */
 
    abstract static function getObject(int $id): static;
 
 
    /**
 
     * Returns the primary key, usually an int.
 
     * @return int
 
     */
 
    function getID(): int
 
    {
 
        return $this->{static::getPrimaryField()};
 
    }
 
 
    /**
 
     * Inserts a new record into the table.
 
     *
 
     * @param array $array An associative array representing the values to be inserted
 
     * @return int The ID of the newly inserted record
 
     * @throws Exception
 
     */
 
    static function insert(array $array): int
 
    {
 
        $db = new Database();
 
        return $db->insert(static::getTable(), $array);
 
    }
 
 
    /**
 
     * Updates the record in the table with the specified values.
 
     *
 
     * @param array $array The array of key-value pairs representing the columns and their new values.
 
     * @return bool Returns true if the update was successful, false otherwise.
 
     * @throws Exception Thrown if the input array is empty.
 
     */
 
    function update(array $array): bool
 
    {
 
        $db = new Database();
 
        return $db->update(static::getTable(), $array, [static::getPrimaryField() => $this->getID()]);
 
    }
 
 
    /**
 
     * Delete the record from the table.
 
     * @return bool Returns true if the record is successfully deleted, otherwise false.
 
     * @throws Exception
 
     */
 
    function delete(): bool
 
    {
 
        $db = new Database();
 
        return $db->delete(static::getTable(), [static::getPrimaryField() => $this->getID()]);
 
    }
 
 
    /**
 
     * Retrieves all records from the table, with optional order by clause.
 
     *
 
     * @param array $orderBy Optional order by clause in the form of ['column' => 'ASC/DESC']
 
     * @return static[] An array of objects representing the records
 
     * @throws Exception
 
     */
 
    static function all(array $orderBy = []): array
 
    {
 
        $db = new Database();
 
        $results = $db->select(static::getTable(), [static::getPrimaryField()], [], $orderBy);
 
        return array_map(fn($row) => static::getObject($row[static::getPrimaryField()]), $results);
 
    }
 
 
    /**
 
     * Get an array of objects based on a set of WHERE conditions
 
     *
 
     * @param array $whereFields An associative array representing the WHERE conditions. The keys are the column names
 
     * and the values are the column values. If a value is null, it will be treated as a NULL condition.
 
     * @param array $orderBy An associative array representing the ORDER BY conditions. The keys are the column names
 
     * and the values are the sort direction (ASC or DESC).
 
     * @return static[] An array of objects.
 
     * @throws Exception
 
     */
 
    static function where(array $whereFields = [], array $orderBy = [], array $whereNot = []): array
 
    {
 
        $db = new Database();
 
        $results = [];
 
 
        $sql = "SELECT `" . static::getPrimaryField() . "` FROM `" . static::getTable() . "`";
 
        $clauses = [];
 
        $types = '';
 
        $params = [];
 
 
        foreach ($whereFields as $col => $val) {
 
            if ($val === null) {
 
                $clauses[] = "$col IS NULL";
 
            } else {
 
                $clauses[] = "$col = ?";
 
                $types .= $db->getParamType($val);
 
                $params[] = $val;
 
            }
 
        }
 
 
        foreach ($whereNot as $col => $val) {
 
            if ($val === null) {
 
                $clauses[] = "$col IS NOT NULL";
 
            } else {
 
                $clauses[] = "$col != ?";
 
                $types .= $db->getParamType($val);
 
                $params[] = $val;
 
            }
 
        }
 
 
        if ($clauses) {
 
            $sql .= " WHERE " . implode(" AND ", $clauses);
 
        }
 
 
        if (!empty($orderBy)) {
 
            $orderParts = [];
 
            foreach ($orderBy as $col => $dir) {
 
                $orderParts[] = "$col $dir";
 
            }
 
            $sql .= " ORDER BY " . implode(', ', $orderParts);
 
        }
 
 
        $stmt = $db->prepareAndExecute($sql, $types, $params);
 
        $result = $stmt->get_result();
 
 
        while ($row = $result->fetch_assoc()) {
 
            $results[] = static::getObject($row[static::getPrimaryField()]);
 
        }
 
        return $results;
 
    }
 
 
    /**
 
     * Retrieves a single, unique object based on the provided filter conditions.
 
     * If the query results in more than one object, an exception is thrown to indicate
 
     * that the object is not unique. If no object matches the conditions, null is returned.
 
     *
 
     * @param array $whereFields Associative array of field-value pairs to filter the query.
 
     * @return static|null The unique object matching the filter conditions, or null if no match is found.
 
     * @throws Exception If more than one result is found for the given filter conditions.
 
     */
 
    static function whereUnique(array $whereFields): ?static
 
    {
 
        $results = static::where($whereFields);
 
        if (count($results) > 1) {
 
            throw new Exception("Object is not unique");
 
        }
 
        return $results[0] ?? null;
 
    }
 
 
    /**
 
     * @param array $likeFields
 
     * @param array $orderBy
 
     * @return array
 
     * @throws Exception
 
     */
 
    static function whereLIKE(array $likeFields = [], array $orderBy = []): array
 
    {
 
        $db = new Database();
 
        $sql = "SELECT `" . static::getPrimaryField() . "` FROM `" . static::getTable() . "`";
 
        $clauses = [];
 
        $types = '';
 
        $params = [];
 
 
        foreach ($likeFields as $col => $val) {
 
            if ($val === null) {
 
                $clauses[] = "$col IS NULL";
 
            } else {
 
                $clauses[] = "$col LIKE ?";
 
                $types .= 's';
 
                $params[] = "%$val%";
 
            }
 
        }
 
 
        if (!empty($clauses)) {
 
            $sql .= " WHERE " . implode(" OR ", $clauses);
 
        }
 
 
        if (!empty($orderBy)) {
 
            $orderParts = [];
 
            foreach ($orderBy as $col => $dir) {
 
                $orderParts[] = "$col $dir";
 
            }
 
            $sql .= " ORDER BY " . implode(', ', $orderParts);
 
        }
 
 
        $stmt = $db->prepareAndExecute($sql, $types, $params);
 
        $result = $stmt->get_result();
 
 
        $objects = [];
 
        while ($row = $result->fetch_assoc()) {
 
            $objects[] = static::getObject($row[static::getPrimaryField()]);
 
        }
 
        return $objects;
 
    }
 
 
    /**
 
     * Loads the count of all entities with a given where condition (filter specified by
 
     * $conditions array)
 
     *
 
     * @param array<string, mixed> $conditions
 
     * @return int number of matching results in the DB
 
     * @throws Exception
 
     */
 
    static function countWhere(array $conditions = []): int
 
    {
 
        $db = new Database();
 
        return $db->count(static::getTable(), static::getPrimaryField(), $conditions);
 
    }
 
 
    /**
 
     * Loads the count of all entities within the table
 
     * @return int number of matching results in the DB
 
     * @throws Exception
 
     */
 
    static function countAll(): int
 
    {
 
        $db = new Database();
 
        return $db->count(static::getTable(), static::getPrimaryField());
 
    }
 
 
    /**
 
     * This function takes in an array of IDs (or names, if names are unique
 
     * and you have the capability to look up on names), and returns an array of
 
     * objects of this type, all ready to be used.
 
     *
 
     * This will then run through a "duplicate check" (where any duplicate objects are
 
     * culled) and cached locally.
 
     * @param int[] $ids array of IDs to load
 
     * @return static[]
 
     * @throws Exception
 
     */
 
    static function load(array $ids): array
 
    {
 
        if (empty($ids)) return [];
 
 
        $db = new Database();
 
        $placeholders = implode(',', array_fill(0, count($ids), '?'));
 
        $types = str_repeat('i', count($ids));
 
 
        $sql = "SELECT `" . static::getPrimaryField() . "` FROM `" . static::getTable() . "` WHERE `" . static::getPrimaryField() . "` IN ($placeholders)";
 
        $stmt = $db->prepareAndExecute($sql, $types, $ids);
 
        $result = $stmt->get_result();
 
 
        $objects = [];
 
        while ($row = $result->fetch_assoc()) {
 
            $objects[] = static::getObject($row[static::getPrimaryField()]);
 
        }
 
        return $objects;
 
    }
 
}
 
 |