yf_model class

ORM model.

Fileplugins/sys/classes/yf_model.class.php
Use_class('model')

Public properties

A site sets them in its config with $PROJECT_CONF['model']['NAME'] = ...;

PropertyDefaultDescription
$_params null
$_with null

Constants

CREATED_AT'created_at'
UPDATED_AT'updated_at'

Public methods 54

all

public static function all()

Get all matching rows.

    public static function all()
    {
        $obj = new static();
        return $obj->new_query(['where' => func_get_args()])->get_all();
    }

associate

public function associate($model_instance)

Associate here means to auotmatically create foreign key on child model.

    public function associate($model_instance)
    {
        // TODO
    }

belongs_to

public function belongs_to($related, $foreign_key = null, $other_key = null, $relation = null)

Relation one-to-one inversed.

    public function belongs_to($related, $foreign_key = null, $other_key = null, $relation = null)
    {
        if ($relation === null) {
            list(, $caller) = debug_backtrace(false);
            $relation = $caller['function'];
        }
        $instance = $this->_db->model($related);
        return $this->new_relation([
            'type' => __FUNCTION__,
            'related' => $related,
            'relation' => $relation,
            'foreign_key' => $foreign_key ?: strtolower($relation) . '_id',
            'other_key' => $other_key ?: $instance->get_key_name(),
            'query' => $instance->new_query(),
        ]);
    }
public function belongs_to_many($related, $pivot_table = null, $foreign_key = null, $other_key = null, $relation = null)

Relation many-to-many.

    public function belongs_to_many($related, $pivot_table = null, $foreign_key = null, $other_key = null, $relation = null)
    {
        if ($relation === null) {
            list(, $caller) = debug_backtrace(false);
            $relation = $caller['function'];
        }
        $instance = $this->_db->model($related);
        return $this->new_relation([
            'type' => __FUNCTION__,
            'related' => $related,
            'relation' => $relation,
            'pivot_table' => $pivot_table ?: $this->joining_table($related),
            'foreign_key' => $foreign_key ?: $this->get_foreign_key(),
            'other_key' => $other_key ?: $instance->get_foreign_key(),
            'query' => $instance->new_query(),
        ]);
    }

count

public static function count()

Count number of matching records, according to condition.

    public static function count()
    {
        $obj = new static();
        return (int) $obj->new_query(['where' => func_get_args()])->count();
    }

create

public static function create(array $data)

Create new model record inside database.

  • $data array
    public static function create(array $data)
    {
        $obj = new static();
        $insert_id = $obj->new_query()->insert($data);
        if ( ! $insert_id) {
            return null;
        }
        $obj->set_key($insert_id);
        return $obj->find($insert_id);
    }

delete

public function delete()

Delete matching record(s) from database.

    public function delete()
    {
        $where = func_get_args() ?: $this->_key_where(__FUNCTION__);
        if ( ! $where) {
            return false;
        }
        return $this->new_query(['where' => $where])->limit(1)->delete();
    }

destroy

public static function destroy()

Delete matching record(s) from database, quicker method than delete().

    public static function destroy()
    {
        // without ids the builder gets no condition at all, which would delete the whole table
        if ( ! func_get_args()) {
            trigger_error(static::class . '::destroy(): no ids given, nothing done', E_USER_WARNING);
            return false;
        }
        $obj = new static();
        return $obj->new_query(['where' => func_get_args()])->delete();
    }

filter_form

public function filter_form($data = [], $params = [])

Linking with the form builder.

    public function filter_form($data = [], $params = [])
    {
        $filter_name = ($params['filter_name'] ?? null) ?: ($_GET['object'] ?? null) . '__' . ($_GET['action'] ?? null);
        $a = [
            'form_action' => url_admin('/@object/filter_save/' . $filter_name),
            'clear_url' => url_admin('/@object/filter_save/' . $filter_name . '/clear'),
        ];
        $params['selected'] = ($params['selected'] ?? null) ?: ($_SESSION[$filter_name] ?? null);
        return form($a + (array) $data, $params);
    }

find

public static function find()

Search for model data, according to args array, returning first record Usuallly get by primary key, but possible to use complex conditions.

    public static function find()
    {
        $obj = new static();
        $pk = $obj->get_key_name();
        $result = $obj->new_query(['where' => func_get_args()])->get();
        if ( ! $result || ! $result->$pk) {
            return null;
        }
        $obj->set_key($result->$pk);
        $obj->set_data($result);
        return $result;
    }
public static function first_or_create()

Return first matched row or create such one, if not existed.

    public static function first_or_create()
    {
        $obj = new static();
        $args = func_get_args();
        $first = $obj->new_query([
            'where' => $args,
            'order_by' => $obj->get_key_name() . ' asc',
            'limit' => 1,
        ])->get();
        if (is_object($first)) {
            $obj->set_data($first);
            return $first;
        }
        return call_user_func_array([$obj, 'create'], $args);
    }

first_or_new

public static function first_or_new()

Return first matched row or create empty model object.

    public static function first_or_new()
    {
        $obj = new static();
        $args = func_get_args();
        $first = $obj->new_query([
            'where' => $args,
            'order_by' => $obj->get_key_name() . ' asc',
            'limit' => 1,
        ])->get();
        if (is_object($first)) {
            $obj->set_data($first);
            return $first;
        }
        return call_user_func_array([$obj, 'new_instance'], $args);
    }

form

public function form($whereid, $data = [], $params = [])

Linking with the form builder.

    public function form($whereid, $data = [], $params = [])
    {
        $a = (array) $this->new_query((array) $params['query_builder'])->whereid($whereid)->get();
        return form($a + (array) $data, $params);
    }

get_data

public function get_data()

Return current model data.

    public function get_data()
    {
        $data = [];
        foreach (get_object_vars($this) as $var => $value) {
            if (substr($var, 0, 1) === '_') {
                continue;
            }
            $data[$var] = $value;
        }
        return $data;
    }
public function get_foreign_key()

Default model foreign key.

    public function get_foreign_key()
    {
        return strtolower(class_basename($this, '', '_model')) . '_id';
    }

get_key

public function get_key()

Primary key value.

    public function get_key()
    {
        return $this->_primary_id;
    }

get_key_name

public function get_key_name()

Primary key name.

    public function get_key_name()
    {
        if (isset($this->_primary_key)) {
            return $this->_primary_key;
        }
        $table = $this->get_table();
        $utils = $this->_db->utils();
        if ($table && $utils->table_exists($table)) {
            $primary_index = $utils->index_info($table, 'PRIMARY');
        }
        if ( ! isset($primary_index['columns'])) {
            return null;
        }
        $name = current($primary_index['columns']);
        return $this->set_key_name($name);
    }

get_table

public function get_table()
    public function get_table()
    {
        $name = $this->_table;
        if ( ! $name) {
            $name = strtolower(class_basename($this, '', '_model'));
            if ($name === 'model') {
                return false;
            }
            $this->set_table($name);
        }
        return $this->_db->_fix_table_name($name);
    }

has

public function has($relation, $where = [])

Relation querying method $posts = model('post')->has('comments', '>=', 3)->get();.

    public function has($relation, $where = [])
    {
        // TODO
        return $this;
    }

has_many

public function has_many($related, $foreign_key = null, $local_key = null, $relation = null)

Relation one-to-many.

    public function has_many($related, $foreign_key = null, $local_key = null, $relation = null)
    {
        if ($relation === null) {
            list(, $caller) = debug_backtrace(false);
            $relation = $caller['function'];
        }
        $instance = $this->_db->model($related);
        return $this->new_relation([
            'type' => __FUNCTION__,
            'related' => $related,
            'relation' => $relation,
            'foreign_key' => $foreign_key ?: $this->get_foreign_key(),
            'local_key' => $local_key ?: $instance->get_key_name(),
            'query' => $instance->new_query(),
        ]);
    }
public function has_many_through($related, $through_model, $foreign_key = null, $local_key = null, $relation = null)

Relation distant through other model.

    public function has_many_through($related, $through_model, $foreign_key = null, $local_key = null, $relation = null)
    {
        if ($relation === null) {
            list(, $caller) = debug_backtrace(false);
            $relation = $caller['function'];
        }
        $instance = $this->_db->model($related);
        return $this->new_relation([
            'type' => __FUNCTION__,
            'related' => $related,
            'relation' => $relation,
            'through_model' => $through_model,
            'foreign_key' => $instance->get_table() . '.' . ($foreign_key ?: $this->get_foreign_key()),
            'local_key' => $local_key ?: $instance->get_key_name(),
            'query' => $instance->new_query(),
        ]);
    }

has_one

public function has_one($related, $foreign_key = null, $local_key = null, $relation = null)

Relation one-to-one.

    public function has_one($related, $foreign_key = null, $local_key = null, $relation = null)
    {
        if ($relation === null) {
            list(, $caller) = debug_backtrace(false);
            $relation = $caller['function'];
        }
        $instance = $this->_db->model($related);
        return $this->new_relation([
            'type' => __FUNCTION__,
            'related' => $related,
            'relation' => $relation,
            'foreign_key' => $foreign_key ?: $this->get_foreign_key(),
            'local_key' => $local_key ?: $instance->get_key_name(),
            'query' => $instance->new_query(),
        ]);
    }

html

public function html($name, $params = [])

Html widget connetion.

    public function html($name, $params = [])
    {
        // TODO
    }

joining_table

public function joining_table($related)

Get the joining table name for a many-to-many relation.

    public function joining_table($related)
    {
        $base = class_basename($this, '', '_model');
        $related = class_basename($related, '', '_model');
        $models = [$related, $base];
        // Now that we have the model names in an array we can just sort them and
        // use the implode function to join them together with an underscores,
        // which is typically used by convention within the database system.
        sort($models);
        $table = strtolower(implode('_', $models));
        return $this->_db->_fix_table_name($table);
    }

morph_many

public function morph_many()

Relation polymorphic one-to-many. Not implemented (polymorphic relations); returns null. Removal or implementation is a 4.0 decision.

    public function morph_many()
    {
        return null;
    }

morph_one

public function morph_one()

Relation polymorphic one-to-one. Not implemented (polymorphic relations); returns null. Removal or implementation is a 4.0 decision.

    public function morph_one()
    {
        return null;
    }

morph_to

public function morph_to()

Relation polymorphic one-to-many. Not implemented (polymorphic relations); returns null. Removal or implementation is a 4.0 decision.

    public function morph_to()
    {
        return null;
    }

morph_to_many

public function morph_to_many()

Relation polymorphic many-to-many. Not implemented (polymorphic relations); returns null. Removal or implementation is a 4.0 decision.

    public function morph_to_many()
    {
        return null;
    }
public function morphed_by_many()

Relation polymorphic many-to-many. Not implemented (polymorphic relations); returns null. Removal or implementation is a 4.0 decision.

    public function morphed_by_many()
    {
        return null;
    }

new_instance

public function new_instance($args = [])

Return new instance of the given model.

    public function new_instance($args = [])
    {
        $model = new static($args);
        return $model;
    }

new_query

public function new_query($params = [])

Return new query builder instance.

    public function new_query($params = [])
    {
        if (($params['where'] ?? null) === null) {
            unset($params['where']);
        }
        $table = ($params['table'] ?? null) ?: $this->get_table();
        if ( ! $table) {
            throw new Exception('MODEL: ' . get_called_class() . ': requires table name to make queries');
        }
        $builder = $this->_db->query_builder();
        $builder->_model = $this;
        $builder->_with = $this->_with;
        $builder->_result_wrapper = [$this, 'new_result'];
        $builder->_remove_as_from_delete = true;
        $builder->from($table . ' AS t0');
        foreach (['select', 'where', 'where_or', 'whereid', 'order_by', 'having', 'group_by'] as $part) {
            if ($params[$part] ?? null) {
                call_user_func_array([$builder, $part], is_array($params[$part]) ? $params[$part] : [$params[$part]]);
            }
        }
        // limit => [10,30] or limit => 5
        if ($params['limit'] ?? null) {
            $count = is_numeric($params['limit']) ? $params['limit'] : $params['limit'][0];
            $offset = is_numeric($params['limit']) ? null : $params['limit'][1];
            $builder->limit($count, $offset);
        }
        foreach (['join', 'left_join', 'inner_join', 'right_join'] as $func) {
            if ($params[$func] ?? null) {
                $join = $params[$func];
                $builder->$func($join['table'], $join['on']);
            }
        }
        return $builder;
    }

new_relation

public function new_relation($relation)

Return new instance of model relation.

    public function new_relation($relation)
    {
        return new yf_model_relation($relation, $this);
    }

new_result

public function new_result($result = [])

Return new instance of model result.

    public function new_result($result = [])
    {
        return new yf_model_result($result, $this);
    }

query

public static function query()

Query builder custom constructor.

    public static function query()
    {
        $obj = new static();
        return $obj->new_query(func_get_args());
    }

save

public function save()

Save model back into database.

    public function save()
    {
        $data = $this->get_data();
        $pk = $this->get_key_name();
        if ( ! ($data[$pk] ?? null)) {
            $insert_id = $this->new_query()->insert($data);
            if ( ! $insert_id) {
                return null;
            }
            $data[$pk] = $insert_id;
            $this->set_data($data);
            $this->set_key($insert_id);
            return $insert_id;
        }
        $this->set_key($data[$pk]);
        if (isset($data[self::UPDATED_AT])) {
            $data[self::UPDATED_AT] = date('Y-m-d H:i:s');
        }
        return $this->new_query(['whereid' => $this->get_key()])->update($data);
    }

select

public static function select()

Query builder custom constructor.

    public static function select()
    {
        $obj = new static();
        return $obj->new_query([__FUNCTION__ => func_get_args()]);
    }

set

public function set($name, $value = null)

Set model attribute, example: (new bear)->set('name', 'Joe').

    public function set($name, $value = null)
    {
        if (is_array($name)) {
            $func = __FUNCTION__;
            foreach ($name as $k => $v) {
                $this->$func($k, $v);
            }
        } else {
            $mutator = 'set_attr_' . $name;
            if (method_exists($this, $mutator)) {
                $this->$mutator($value);
            } else {
                $this->$name = $value;
            }
        }
        return $this;
    }

set_data

public function set_data($data = [])

Set current model data.

    public function set_data($data = [])
    {
        if ($data instanceof yf_model_result) {
            $data = $data->get_data();
        }
        foreach ((array) $data as $k => $v) {
            if (substr($k, 0, 1) === '_') {
                continue;
            }
            $this->$k = $v;
        }
        $pk = $this->get_key_name();
        if ($pk !== null && isset($data[$pk])) {
            $this->set_key($data[$pk]);
        }
    }

set_db_object

public function set_db_object($db = null)
    public function set_db_object($db = null)
    {
        $this->_db = $db ?: db();
        return $this;
    }

set_key

public function set_key($id)

Primary key value set.

    public function set_key($id)
    {
        $this->_primary_id = $id;
        return $this->_primary_id;
    }

set_key_name

public function set_key_name($name)

Primary key name set.

    public function set_key_name($name)
    {
        $this->_primary_key = $name;
        return $this->_primary_key;
    }

set_table

public function set_table($name)
    public function set_table($name)
    {
        $this->_table = $name;
        return $this->_table;
    }

table

public function table($params = [])

Linking with the table builder.

    public function table($params = [])
    {
        $sql = $this->new_query((array) $params['query_builder'])->sql();
        $filter_name = ($params['filter_name'] ?? null) ?: (($this->_params['filter_name'] ?? null) ?: ($_GET['object'] ?? null) . '__' . ($_GET['action'] ?? null));
        $params['filter'] = ($params['filter'] ?? null) ?: (($this->_params['filter'] ?? null) ?: ($_SESSION[$filter_name] ?? null));
        return table($sql, $params);
    }

touch

public function touch()

Update only model's timestamps.

    public function touch()
    {
        $where = func_get_args() ?: $this->_key_where(__FUNCTION__);
        if ( ! $where) {
            return false;
        }
        return $this->new_query(['where' => $where])->update([self::UPDATED_AT => date('Y-m-d H:i:s')]);
    }

validate

public function validate($rules = [], $params = [])

Model validation will be here.

    public function validate($rules = [], $params = [])
    {
        // TODO
    }

where

public static function where()

Query builder custom constructor.

    public static function where()
    {
        $obj = new static();
        return $obj->new_query([__FUNCTION__ => func_get_args()]);
    }

with

public function with($model)

Eager loading with relations. Examples:.

model('post')->with('comments')->whereid($id)->first();

foreach (model('book')->with('author')->get_all() as $book) { // select * from authors where id in (1, 2, 3, 4, 5, ...)
echo $book->author->name;
}

    public function with($model)
    {
        // TODO
        return $this;
    }

Methods starting with _ hooks and helpers; not routable as page actions

__call

public function __call($name, $args)

Catch missing method call.

    public function __call($name, $args)
    {
        $where_prefix = 'where_';
        $scope_prefix = 'scope_';
        $get_prefix = 'get_attr_';
        $set_prefix = 'set_attr_';
        if (strpos($name, $where_prefix) !== false) {
            $name = substr($name, strlen($where_prefix));
            array_unshift($args, 't0.' . $name);
            return call_user_func_array([$this, 'where'], $args);
        } elseif (strpos($name, $scope_prefix) !== false) {
            if (method_exists($this, $name)) {
                return call_user_func_array([$this, $name], $args);
            }
        } elseif (strpos($name, $get_prefix) !== false) {
            $accessor = $get_prefix . $name;
            if (method_exists($this, $accessor)) {
                return $this->$accessor($args);
            }
        } elseif (strpos($name, $set_prefix) !== false) {
            $mutator = $set_prefix . $name;
            if (method_exists($this, $mutator)) {
                return $this->$mutator($args);
            }
        }
        return main()->extend_call($this, $name, $args);
    }

__callStatic

public static function __callStatic($method, $args)

Catch static calls.

    public static function __callStatic($method, $args)
    {
        $where_prefix = 'where_';
        $scope_prefix = 'scope_';
        $obj = new static();
        $name = $args[0] ?? '';
        if (strpos($name, $where_prefix) !== false) {
            $name = substr($name, strlen($where_prefix));
            array_unshift($args, 't0.' . $name);
            return call_user_func_array([$obj, 'where'], $args);
        } elseif (strpos($name, $scope_prefix) !== false) {
            if (method_exists($obj, $name)) {
                return call_user_func_array([$obj, $name], $args);
            }
        }
        return call_user_func_array([$obj, $method], $args);
    }

__clone

public function __clone()

We cleanup object properties when cloning.

    public function __clone()
    {
        $persist_properties = [
            '_table',
            '_fillable',
        ];
        foreach ((array) get_object_vars($this) as $k => $v) {
            if ( ! in_array($k, $persist_properties)) {
                $this->$k = null;
            }
        }
    }

__construct

public function __construct($args = [], $params = [])
    public function __construct($args = [], $params = [])
    {
        $this->set_db_object($params['db'] ?? null);
        $this->set_data($args);
    }

__get

public function __get($name)
    public function __get($name)
    {
        if (method_exists($this, $name)) {
            return $this->$name()?->get_data();
        }
        if (isset($this->$name)) {
            return $this->$name;
        }
        return null;
    }

__toString

public function __toString(): string
    public function __toString()
    {
        return json_encode($this->get_data());
    }

_key_where

public function _key_where($caller = '')

Where-clause for the loaded record. Without arguments delete() / touch() act on this record only: an empty where-clause is skipped by the builder, which turned them into "DELETE ... LIMIT 1" / an update of every row.

  • $caller string
    public function _key_where($caller = '')
    {
        $pk = $this->get_key_name();
        $id = $pk ? ($this->get_data()[$pk] ?? $this->get_key()) : null;
        $table = $this->get_table();
        $primary = $pk && $table ? $this->_db->utils()->index_info($table, 'PRIMARY') : [];
        // the builder also drops "pk = 0" (empty value), and a composite key would match several rows
        if ( ! $pk || empty($id) || ! is_scalar($id) || count((array) ($primary['columns'] ?? [])) > 1) {
            trigger_error(static::class . '::' . $caller . '(): no single primary key value, nothing done', E_USER_WARNING);
            return [];
        }
        return [$pk, '=', is_string($id) ? $this->_db->es_for_builder($id) : $id];
    }