src/lib360/db/data/Model.php

Summary

Maintainability
A
35 mins
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\ICondition;

/**
 * Database model class
 */
abstract class Model implements IModel
{

    /**
     * Implementing classes must set this property via setTable method.
     *
     * This table object must
     * - be an instance of ITable
     * - have primary key set
     * - have fields set
     *
     * @var ITable object
     */
    protected static $table;

    /**
     * Internal storage of snapshot of data record
     * @var Record
     */
    protected $record;

    /**
     * Constructor sets child class table and validates it.
     *
     * @throws ModelException when the table object is not fully configured.
     */
    protected function __construct()
    {
        if (!isset(static::$table)) {
            // Set table instance to class
            $this->setTable();
            // Verify storage before moving on
            if (!(static::$table instanceof ITable)) {
                throw new ModelException('Model storage must be an instance of ITable.');
            }
            if (is_null(static::$table->key)) {
                throw new ModelException('Model storage has no key defined.');
            }
            if (count(static::$table->fields) == 0) {
                throw new ModelException('Model storage has no fields defined.');
            }
        }
    }

    /**
     * Creates new empty model instance.
     *
     * @return IModel instance of model implementation
     */
    public static function create()
    {
        $class = get_called_class();
        $model = new $class();
        $record = new Record();
        foreach (static::$table->fields as $field) {
            $record->$field = null;
        }
        $model->setRecord($record);
        return $model;
    }

    /**
     * Creates model from record.
     *
     * @param IRecord $record
     *
     * @return IModel instance of model implementation
     */
    public static function createFromRecord(IRecord $record)
    {
        $class = get_called_class();
        $model = new $class();
        $model->setRecord($record);
        return $model;
    }

    /**
     * Gets model by key.
     *
     * @param mixed $key
     *
     * @return IModel instance of model implementation
     */
    public static function getByKey($key)
    {
        $class = get_called_class();
        $model = new $class();
        $record = static::$table->selectRecord($key);
        $model->setRecord($record);
        return $model;
    }

    /**
     * Gets model list by attributes.
     *
     * @param array $attributes indexed by string keys and mixed values used for criteria
     *
     * @return ModelList list of model objects
     */
    public static function getByAttributes(array $attributes)
    {
        $class = get_called_class();
        // NOTE: makes sure constructor has run and child class table is set
        new $class();
        $recordlist = static::$table->selectRecords($attributes);
        return new ModelList($recordlist, $class);
    }

    /**
     * Gets model list by condition.
     *
     * @param ICondition $condition
     *
     * @return ModelList list of model objects
     */
    public static function getByCondition(ICondition $condition)
    {
        $class = get_called_class();
        // NOTE: makes sure constructor has run and child class table is set
        new $class();
        $recordlist = static::$table->select($condition);
        return new ModelList($recordlist, $class);
    }

    /**
     * Gets attribute value.
     *
     * @param string $name Attribute name
     *
     * @return mixed Attribute value
     */
    public function get($name)
    {
        return $this->record->get($name);
    }

    /**
     * Returns whether the record has a value set for primary key.
     *
     * @return boolean
     */
    public function hasKey()
    {
        $set = false;
        if (isset($this->record[static::$table->key])) {
            $set = !is_null($this->record->get(static::$table->key));
        }
        return $set;
    }

    /**
     * Sets attribute value.
     *
     * @param string $name Attribute name
     * @param mixed $value Attribute value
     */
    public function set($name, $value)
    {
        $this->record->set($name, $value);
    }

    /**
     * Stores updated model attributes in the database.
     *
     * @return boolean true
     *
     * @throws RecordNotFoundException when record to update is not found
     */
    public function store()
    {
        if ($this->hasKey()) {
            static::$table->updateRecord($this->record);
        } else {
            static::$table->insertRecord($this->record);
        }
        return true;
    }

    /**
     * Deletes model from the database.
     *
     * @return boolean true
     *
     * @throws RecordNotFoundException when record to delete is not found
     */
    public function delete()
    {
        static::$table->deleteRecord($this->record);
        return true;
    }

    /**
     * Sets record object.
     *
     * This is useful when creating an instance of a model.
     *
     * @param IRecord $record Record object to set
     */
    protected function setRecord(IRecord $record)
    {
        $this->record = $record;
    }

    /**
     * Exports model to array representation.
     *
     * @return array associative array with field names as indexes
     */
    public function toArray()
    {
        return $this->record->toArray();
    }

}

?>