src/lib360/db/data/Table.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

/**
 *  This is Spoof.
 *  Copyright (C) 2011-2017  Spoof project.
 *
 *  Spoof is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Spoof is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Spoof.  If not, see <http://www.gnu.org/licenses/>.
 */

namespace spoof\lib360\db\data;

use spoof\lib360\db\condition\Condition;
use spoof\lib360\db\condition\ConditionGroup;
use spoof\lib360\db\condition\ICondition;
use spoof\lib360\db\connection\Pool;
use spoof\lib360\db\object\Factory;
use spoof\lib360\db\value\Value;

/**
 * A database table class.
 *
 * Provides functionality for simple operations against a database table.
 * This class can either be extended, or instantiated dynamically via TableFactory.
 * @see TableFactory
 */
class Table extends Store implements ITable
{
    /**
     * Optional view default fields array
     *
     * Extending classes may optionally (but recommended) define the list of
     * fields that will be returned by default. This default can be overriden
     * in the select method.
     */
    public $fields = array();

    /**
     * Table primary key.
     */
    public $key;

    /**
     * Gets table records by field criteria.
     *
     * @param array $conditions optional associative array of column names and
     *    their values to use as conditions, values will explicitly be cast as strings
     * @param array $fields optional array of fields to return, can be
     *    associative for (table field) => (select as field) or a simple array of
     *    table field names, will override default $fields property
     *
     * @return IRecordList object containing matched rows @see RecordList
     */
    public function selectRecords(array $conditions = array(), array $fields = null)
    {
        $conditionGroup = $this->getCondition($conditions);
        return $this->select($conditionGroup, array(), $fields);
    }

    /**
     * Gets table record by primary key.
     *
     * @param mixed $id primary key to retrieve the record
     * @param array $fields optional array of fields to return, can be
     *    associative for (table field) => (select as field) or a simple array of
     *    table field names, will override default $fields property
     *
     * @return Record object
     *
     * @throws RecordNotFoundException when no records found for given primary key
     * @throws RecordPrimaryKeyException when more than 1 record is found
     */
    public function selectRecord($id, array $fields = null)
    {
        $conditions = array($this->key => $id);
        $results = $this->selectRecords($conditions, $fields);
        if (count($results) == 0) {
            throw new RecordNotFoundException('Record not found.');
        }
        if (count($results) > 1) {
            throw new RecordPrimaryKeyException('More than 1 row found for primary key');
        }
        return $results[0];
    }

    /**
     * Gets table records that match the supplied database condition.
     *
     * @param ICondition $condition optional condition to apply to the query
     * @param array $values optional associative array of values for aliases
     *    in the condition object
     * @param array $fields optional array of fields to return, can be
     *    associative for (table field) => (select as field) or a simple array of
     *    table field names, will override default $fields property
     *
     * @return \spoof\lib360\db\data\IRecordList a database recordlist object
     * @see \spoof\lib360\db\data\RecordList
     *
     * @todo add order/group by support
     */
    public function select(
        ICondition $condition = null,
        array $values = array(),
        array $fields = null
    ) {
        // get connection object
        $db = Pool::getByName($this->db);
        // get language and query
        $returnFields = is_null($fields) ? $this->fields : $fields;
        $query = Factory::get(Factory::OBJECT_TYPE_LANGUAGE, $this->getLanguage())->getSelect(
            $db->driver, $this,
            $condition, $returnFields
        );
        // get executor and execute
        $result = Factory::get(Factory::OBJECT_TYPE_EXECUTOR, $this->getExecutor())->select(
            $db, $query->query,
            $query->values + $values, $this->name
        );
        // return result
        return $result;
    }

    /**
     * Updates database record(s) based on supplied criteria and values.
     *
     * @param array $fields associative array of fields to update
     *    (table field) => (update value)
     * @param ICondition $condition optional ICondition object to apply to the
     *    update
     * @param array $values optional associative array of values for aliases
     *    in the condition object
     *
     * @return integer number of rows updated
     */
    public function update(
        array $fields,
        ICondition $condition = null,
        array $values = array()
    ) {
        // get connection object
        $db = Pool::getByName($this->db);
        // get language and query
        $query = Factory::get(Factory::OBJECT_TYPE_LANGUAGE, $this->getLanguage())->getUpdate(
            $db->driver, $this,
            $fields, $condition
        );
        // get executor and execute
        $result = Factory::get(Factory::OBJECT_TYPE_EXECUTOR, $this->getExecutor())->update(
            $db, $query->query,
            $query->values + $values
        );
        // return result
        return $result;
    }

    /**
     * Stores record modifications in the table by record's primary key.
     *
     * @param IRecord $record
     *
     * @return int Number of rows updated
     *
     * @throws RecordNotFoundException when record to update is not found
     */
    public function updateRecord(IRecord $record)
    {
        $updated = 0;
        if ($record->isModified()) {
            $fields = array();
            foreach($record->getModified() as $key => $value) {
                $fields[$key] = new Value($value);
            }
            $condition = $this->getCondition(
                array($this->key => $record->getOriginal($this->key))
            );
            $updated = $this->update($fields, $condition);
            if ($updated == 0) {
                throw new RecordNotFoundException('Record not found for update');
            }
            $record->clearModified();
        }
        return $updated;
    }

    /**
     * Inserts a database record.
     *
     * @param array $fields associative array of fields for insert
     *    (table field) => (insert value)
     *
     * @return mixed inserted row ID
     */
    public function insert(array $fields)
    {
        // get connection object
        $db = Pool::getByName($this->db);
        // get language and query
        $query = Factory::get(Factory::OBJECT_TYPE_LANGUAGE, $this->getLanguage())->getInsert(
            $db->driver, $this,
            $fields
        );
        // get executor and execute
        $result = Factory::get(Factory::OBJECT_TYPE_EXECUTOR, $this->getExecutor())->insert(
            $db, $query->query,
            $query->values
        );
        // return result
        return $result;
    }

    /**
     * Inserts a database record.
     *
     * @param IRecord $record
     *
     * @return mixed inserted row ID
     */
    public function insertRecord(IRecord $record)
    {
        $fields = array();
        foreach ($record as $key => $value) {
            $fields[$key] = new Value($value);
        }
        $insertedId = $this->insert($fields);
        $record->__set($this->key, $insertedId);
        $record->clearModified();
        return $insertedId;
    }

    /**
     * Deletes table records that match the supplied database condition.
     *
     * @param ICondition $condition optional condition to apply to the query
     * @param array $values optional associative array of values for aliases
     *    in the condition object
     *
     * @return integer number of rows deleted
     */
    public function delete(ICondition $condition = null, array $values = array())
    {
        // get connection object
        $db = Pool::getByName($this->db);
        // get language and query
        $query = Factory::get(Factory::OBJECT_TYPE_LANGUAGE, $this->getLanguage())->getDelete(
            $db->driver, $this,
            $condition
        );
        // get executor and execute
        $result = Factory::get(Factory::OBJECT_TYPE_EXECUTOR, $this->getExecutor())->delete(
            $db, $query->query,
            $query->values + $values
        );
        // return result
        return $result;
    }

    /**
     * Deletes a record from the table.
     *
     * @param IRecord $record
     *
     * @return integer number of rows deleted
     *
     * @throws RecordNotFoundException when record to delete is not found
     */
    public function deleteRecord(IRecord $record)
    {
        $condition = $this->getCondition(
            array($this->key => $record->getOriginal($this->key))
        );
        $deleted = $this->delete($condition);
        if ($deleted == 0) {
            throw new RecordNotFoundException('Record not found for delete');
        }
        $record->clear();
        return $deleted;
    }

    /**
     * Gets condition object for given array of field names and values.
     *
     * This is a convenience function that returns a Condition or
     * ConditionGroup object containing conditions of database table fields
     * (represented as condition keys) equal literal values represented by
     * as condition array values. If more than one element is present, the
     * condition group will be returned and conditions will be joined with the
     * AND operator.
     *
     * @param array $conditions Array containing fields as keys and their values
     *
     * @return Condition|ConditionGroup|null
     */
    protected function getCondition(array $conditions) {
        $conditionGroup = null;
        if (count($conditions) == 1) {
            foreach ($conditions as $column => $value) {
                $conditionGroup = new Condition(
                    new Value($column, Value::TYPE_COLUMN),
                    Condition::OPERATOR_EQUALS,
                    new Value((string)$value)
                );
            }
        } elseif (count($conditions) > 1) {
            foreach ($conditions as $column => $value) {
                $condition = new Condition(
                    new Value($column, Value::TYPE_COLUMN),
                    Condition::OPERATOR_EQUALS,
                    new Value((string)$value)
                );
                if (is_null($conditionGroup)) {
                    $conditionGroup = new ConditionGroup($condition);
                } else {
                    $conditionGroup->addCondition(ConditionGroup::OPERATOR_AND, $condition);
                }
            }
        }
        return $conditionGroup;
    }

}

?>