public static function findOne($where = array(), $order = array())
    {
        // try to fetch a model from database
        if ($dataModel = self::select(array('*'), $where, $order, 1)) {

            // hydrate new model instance with fetched data
            $model = new static();
            $model->hydrate($dataModel);

            return $model;
        } else {
            return null;
        }
    }

Find one model from criteria

@param array $where - associative array ex:
simple criteria        array('idMarque' => 1)
custom operator        array('idMarque' => array('operator' => '<=','value' => ''))
raw SQL                array('idMarque' => array('IN (5,6,4)'))
@param array $order - associative array ex:array('libMarque'=>'ASC')

@return static


public static function count($where = array())
    {
        // fetch the count with $where parameters
        $rawSqlFetch = self::select(array("count(*) as nb"), $where);

        return isset($rawSqlFetch[0]) && isset($rawSqlFetch[0]['nb']) ? (int)$rawSqlFetch[0]['nb'] : null;
    }

Count number of model in database from criteria

@param array $where - associative array ex:
simple criteria        array('idMarque' => 1)
custom operator        array('idMarque' => array('operator' => '<=','value' => ''))
raw SQL                array('idMarque' => array('IN (5,6,4)'))

@return int | null


protected static function buildSelectQuery(
        $fields = array('*'),
        $where = array(),
        $order = array(),
        $limitStart = null,
        $limitEnd = null
    )
    {
        // get the formatted model mysql table name with database name
        $modelTableName = static::formatTableNameMySQL();

        // be sure that "*" is prefixed with model table name
        foreach ($fields as &$oneField) {
            if ($oneField == "*") {
                $oneField = $modelTableName . ".*";
                break;
            }
        }

        // create and helper to build a sql query
        $helper = new InternalQueryHelper();

        // prefix columns with model table name
        $where  = $helper->prefixWhereWithTable($where, $modelTableName);
        $orders = $helper->prefixOrderWithTable($order, $modelTableName);

        // starting build select
        $helper->select($fields)
               ->from($modelTableName);

        // check one to one relation with auto get fields
        // and append necessary fields to select
        $nbRelation = 0;
        foreach (static::$_relations as $uneRelation) {
            if ($uneRelation['typeRelation'] == self::ONE_TO_ONE && count($uneRelation['autoGetFields']) > 0) {
                // prefix fields
                foreach ($uneRelation['autoGetFields'] as &$oneField) {
                    $oneField = 'rel' . $nbRelation . "." . $oneField;
                }
                // add fields to select
                $helper->select($uneRelation['autoGetFields']);

                // add query join corresponding to the relation
                $helper->leftJoin(
                       $uneRelation['classRelation']::formatTableNameMySQL() . ' rel' . $nbRelation,
                       'rel' . $nbRelation . '.`' . $uneRelation['targetField'] . '` = ' . $modelTableName . '.`' . $uneRelation['sourceField'] . '`'
                );

                // increment relation count used in prefix
                $nbRelation++;
            }
        }

        // build where clause
        $helper->buildWhereFromArray($where);

        // build order clause
        foreach ($orders as $orderField => $orderVal) {
            $helper->orderBy($orderField, $orderVal);
        }

        // build limit clause
        $helper->limit($limitStart, $limitEnd);

        return $helper;
    }

Build an InternalQueryHelper to select models

@param array $fields     - selected fields
@param array $where      - associative array ex:
simple criteria       array('idMarque' => 1)
custom operator       array('idMarque' => array('operator' => '<=','value' => '5'))
raw SQL without operator       array('idMarque' => array('IN (5,6,4)')
@param array $order      - associative array ex:array('libMarque'=>'ASC')
@param int   $limitStart - int
@param int   $limitEnd   - int

@return InternalQueryHelper


public static function select(
        $fields = array('*'),
        $where = array(),
        $order = array(),
        $limitStart = null,
        $limitEnd = null,
        $pdoFetchMode = null
    )
    {
        // validate model PHP structure if necessary
        static::_validateModel();

        // build and execute query
        $mysqlQuery = static::buildSelectQuery($fields, $where, $order, $limitStart, $limitEnd);
        $query      = static::$_dataSource->prepare($mysqlQuery->buildQuery());
        $query->execute($mysqlQuery->getWhereParamsValues());

        // check for mysql error
        $errorcode = $query->errorInfo();
        if ($errorcode[0] != "00000") {
            throw new Exception($errorcode[2]);
        }

        // if no fetch mode specified fallback to FETCH_ASSOC
        if ($pdoFetchMode === null) {
            $pdoFetchMode = \PDO::FETCH_ASSOC;
        }

        // if limit say its a findOne only fetch
        if ($limitStart == 1 && ($limitEnd === null || $limitEnd === 1)) {
            return $query->fetch($pdoFetchMode);
        }

        // fetch and return data from database
        return $query->fetchAll($pdoFetchMode);
    }

Build a select mysql query for this model from criteria in parameters
return a raw mysql fetch assoc
Using Raw SQL _setVal, assume that you properly filter user input

@param array $fields       - selected fields
@param array $where        - associative array ex:
simple criteria       array('idMarque' => 1)
custom operator       array('idMarque' => array('operator' => '<=','value' => '5'))
raw SQL without operator       array('idMarque' => array('IN (5,6,4)')
@param array $order        - associative array ex:array('libMarque'=>'ASC')
@param int   $limitStart   - int
@param int   $limitEnd     - int
@param int   $pdoFetchMode - PDO Fetch Mode (default : \PDO::FETCH_ASSOC)

@return array
@throws Exception

Build a select mysql query for this model from criteria in parameters return a raw mysql fetch assoc Using Raw SQL _setVal assume that you properly filter user input


public function hydrate($data, $strictLoad = true)
    {
        // using reflection to check if property exist
        $reflection = new \ReflectionObject($this);
        foreach ($data as $k => $v) {
            // if strictLoad is disabled, all properties are allowed to be hydrated
            if (!$strictLoad) {
                $this->{$k} = $v;
                continue;
            }
            // check if property really exists in class
            if ($reflection->hasProperty($k)) {
                $this->{$k} = $v;
                continue;
            }
            // test relation auto get fields
            foreach (static::$_relations as $uneRelation) {
                // check if this field is in auto get from relation
                if ($uneRelation['typeRelation'] == self::ONE_TO_ONE && in_array($k, $uneRelation['autoGetFields'])) {
                    $this->{$k} = $v;
                    break;
                }
            }
        }

        // model is not new anymore
        $this->_isNew = false;
    }

Hydrate model from a fetch assoc
including OneToOne relation auto get field

@param $data
@param $strictLoad - if true, all fields from $data will be loaded


public function delete()
    {
        // validate model PHP structure if necessary
        static::_validateModel();

        // build delete query helper for this model
        $query = new InternalQueryHelper();
        $query->delete(self::formatTableNameMySQL())
              ->where(static::$_primaryKey, "=", "?");

        // delete model from database
        $query = static::$_dataSource->prepare($query->buildQuery());
        $query->execute(array($this->{static::$_primaryKey}));

        // check for mysql error
        $errorcode = $query->errorInfo();
        if ($errorcode[0] != "00000") {
            throw new Exception($errorcode[2]);
        }

        // model is not stored anymore in database
        $this->_isNew = true;

        return true;
    }

Delete this model from database

@return array
@throws Exception


private function update()
    {
        // validate model PHP structure if necessary
        static::_validateModel();

        // build update query on model table
        $helper = new InternalQueryHelper();
        $helper->update(self::formatTableNameMySQL());

        // setting model fields value
        $params = array();
        foreach (static::$_tableFields as $unChamp) {
            // array is for raw SQL value
            if (is_array($this->$unChamp) && isset($this->{$unChamp}[0])) {
                $helper->set($unChamp, $this->{$unChamp}[0]);
            } else {
                // Mysql prepared value
                $helper->set($unChamp, '?');
                $params[] = $this->{$unChamp};
            }
        }

        // restrict with model primary key
        $helper->where(self::formatTableNameMySQL() . ".`" . static::$_primaryKey . "`", "=", "?");
        $params[] = $this->{static::$_primaryKey};

        // update model in database
        $query = static::$_dataSource->prepare($helper->buildQuery());
        $query->execute($params);

        // check for mysql error
        $errorcode = $query->errorInfo();
        if ($errorcode[0] != "00000") {
            throw new Exception($errorcode[2]);
        }

        return true;
    }

Update model field in database

@return bool
@throws Exception


private function insert()
    {
        // validate model PHP structure if necessary
        static::_validateModel();

        // create insert query for this model
        $queryHelp = new InternalQueryHelper();
        $queryHelp->insertInto(self::formatTableNameMySQL());

        // if primary key has forced value and is not present in tableField array
        if (!empty($this->{static::$_primaryKey}) && !in_array(static::$_primaryKey, static::$_tableFields)) {
            array_unshift(static::$_tableFields, static::$_primaryKey);
        } else {
            // use autoincrement for primary key
            $queryHelp->values(static::$_primaryKey, 'NULL');
        }

        // save model fields
        $params = array();
        foreach (static::$_tableFields as $unChamp) {
            // array is for raw SQL value
            if (is_array($this->$unChamp) && isset($this->{$unChamp}[0])) {
                $val = $this->{$unChamp}[0];
            } else {
                // mysql prepared value
                $val      = '?';
                $params[] = $this->$unChamp;
            }

            $queryHelp->values($unChamp, $val);
        }

        // execute insert query
        $query = static::$_dataSource->prepare($queryHelp->buildQuery());
        $query->execute($params);

        // check for mysql error
        $errorcode = $query->errorInfo();
        if ($errorcode[0] != "00000") {
            throw new Exception($errorcode[2]);
        }

        // model is saved, not new anymore
        $this->_isNew = false;

        // if empty PK grab the last insert ID for auto_increment fields
        if (empty($this->{static::$_primaryKey})) {
            $this->{static::$_primaryKey} = static::$_dataSource->lastInsertId();
        }

        return true;
    }


public function keep_memory( $keep_memory = true ) {

		$prev = $this->keep_memory;

		$this->keep_memory = $keep_memory;

		return $prev;
	}

public function keep_memory ( $ keep_memory = true ) { $ prev = $ this -> keep_memory ; $ this -> keep_memory = $ keep_memory ; return $ prev ; }

Configure the collection to keep memory of added or removed elements.

@since 2.0

@param bool $keep_memory

@return bool

Configure the collection to keep memory of added or removed elements .


public function dont_remember( Closure $callback ) {

		$prev = $this->keep_memory( false );

		$callback( $this );

		$this->keep_memory( $prev );
	}

public function dont_remember ( Closure $ callback ) { $ prev = $ this -> keep_memory ( false ) ; $ callback ( $ this ) ; $ this -> keep_memory ( $ prev ) ; }

Perform operations on the collection without remembering them.

@since 2.0

@param Closure $callback This ModelCollection is passed as the first parameter.

Perform operations on the collection without remembering them .


public function get_model( $pk ) {

		$i = $this->get_model_index( $pk );

		if ( $i === - 1 ) {
			return null;
		}

		return $this->elements[ $i ];
	}

public function get_model ( $ pk ) { $ i = $ this -> get_model_index ( $ pk ) ; if ( $ i === - 1 ) { return null ; } return $ this -> elements [ $ i ] ; }

Retrieve a model by its primary key.

@since 2.0

@param string|int $pk

@return Model|mixed|null


public function remove_model( $pk ) {

		$i = $this->get_model_index( $pk );

		if ( $i === - 1 ) {
			return false;
		}

		$this->remove( $i );

		return true;
	}

public function remove_model ( $ pk ) { $ i = $ this -> get_model_index ( $ pk ) ; if ( $ i === - 1 ) { return false ; } $ this -> remove ( $ i ) ; return true ; }

Remove a model from the collection.

@since 2.0

@param string|int $pk

@return bool


protected function get_model_index( $pk ) {

		if ( isset( $this->pk_map[ $pk ] ) ) {
			return $this->pk_map[ $pk ];
		}

		foreach ( $this->elements as $i => $element ) {

			if ( $this->saver->get_pk( $element ) === $pk ) {

				$this->pk_map[ $pk ] = $i;

				return $i;
			}
		}

		return - 1;
	}

protected function get_model_index ( $ pk ) { if ( isset ( $ this -> pk_map [ $ pk ] ) ) { return $ this -> pk_map [ $ pk ] ; } foreach ( $ this -> elements as $ i => $ element ) { if ( $ this -> saver -> get_pk ( $ element ) === $ pk ) { $ this -> pk_map [ $ pk ] = $ i ; return $ i ; } } return - 1 ; }

Get the index of a model.

@since 2.0

@param string|int $pk

@return int


public function save( array $options = array() ) {

		foreach ( $this->elements as $element ) {
			$this->saver->save( $element, $options );
		}
	}

public function save ( array $ options = array ( ) ) { foreach ( $ this -> elements as $ element ) { $ this -> saver -> save ( $ element , $ options ) ; } }

Save all items in the collection.

@since 2.0

@param array $options


public function prefixWhereWithTable($data, $tableName)
    {
        if (count($data) == 0) {
            return $data;
        }

        foreach ($data as $key => $v) {
            $newKey        = $tableName . '.' . trim($key, '`');
            $data[$newKey] = $v;
            unset($data[$key]);
        }

        return $data;
    }

public function prefixWhereWithTable ( $ data , $ tableName ) { if ( count ( $ data ) == 0 ) { return $ data ; } foreach ( $ data as $ key => $ v ) { $ newKey = $ tableName . '.' . trim ( $ key , '`' ) ; $ data [ $ newKey ] = $ v ; unset ( $ data [ $ key ] ) ; } return $ data ; }

Prefix fields from where associated array
with table name

@param array  $data      - associative array with where data
@param string $tableName - table name to use as prefix

@return mixed


public function prefixOrderWithTable($data, $tableName)
    {
        if (count($data) == 0) {
            return $data;
        }

        foreach ($data as $key => $v) {
            // if $v is empty, we have a custom order like RAND() and do not have to prefix
            if (!empty($v)) {
                $newKey        = $tableName . '.' . trim($key, '`');
                $data[$newKey] = $v;
                unset($data[$key]);
            }
        }

        return $data;
    }

Prefix fields from orderBy associated array
with table name

@param array  $data      - associative array with order data
@param string $tableName - table name to use as prefix

@return mixed


protected function get_definition_without_column_name( array $exclude_options = array() ) {

		$definition = $this->get_mysql_type();

		if ( $this->type_options ) {
			$definition .= '(' . implode( ',', $this->type_options ) . ')';
		}

		if ( $this->options ) {

			$options = array_udiff( $this->options, $exclude_options, function ( $a, $b ) {
				return strcmp( strtolower( $a ), strtolower( $b ) );
			} );

			$definition .= ' ' . implode( ' ', $options );
		}

		return $definition;
	}

protected
function
get_definition_without_column_name
(
array
$
exclude_options
=
array
(
)
)
{
$
definition
=
$
this
->
get_mysql_type
(
)
;
if
(
$
this
->
type_options
)
{
$
definition
.=
'('
.
implode
(
','
,
$
this
->
type_options
)
.
')'
;
}
if
(
$
this
->
options
)
{
$
options
=
array_udiff
(
$
this
->
options
,
$
exclude_options
,
function
(
$
a
,
$
b
)
{
return
strcmp
(
strtolower
(
$
a
)
,
strtolower
(
$
b
)
)
;
}
)
;
$
definition
.=
' '
.
implode
(
' '
,
$
options
)
;
}
return
$
definition
;
}

Get the column definition without the column name attached.

@since 2.0

@param array $exclude_options

@return string

Get the column definition without the column name attached .


public static function register( Table $table, $complex_query_class = '', $model_class = '' ) {

		if ( $complex_query_class && ! is_subclass_of( $complex_query_class, 'IronBound\DB\Query\Complex_Query' ) ) {
			throw new \InvalidArgumentException( '$complex_query_class must subclass Complex_Query' );
		}

		if ( $model_class && ! is_subclass_of( $model_class, 'IronBound\DB\Model' ) ) {
			throw new \InvalidArgumentException( '$model_class must subclass Model.' );
		}

		static::$tables[ $table->get_slug() ] = array(
			'table' => $table,
			'query' => $complex_query_class,
			'model' => $model_class
		);

		/** @var \wpdb $wpdb */
		global $wpdb;

		$name = $table->get_table_name( $wpdb );
		$name = preg_replace( "/^{$wpdb->prefix}/", "", $name );

		$wpdb->{$name}  = $table->get_table_name( $wpdb );
		$wpdb->tables[] = $name;

		static::fire_plugin_event( $table, 'registered' );
	}

Register a db table.

@since 1.0

@param Table  $table Table object.
@param string $complex_query_class
@param string $model_class

@throws \InvalidArgumentException If $complex_query_class is not a subclass of Complex_Query.


public static function make_simple_query_object( $slug, \wpdb $wpdb = null ) {

		$table = static::get( $slug );
		$wpdb  = $wpdb ?: $GLOBALS['wpdb'];

		if ( $table ) {
			return new Simple_Query( $wpdb, $table );
		} else {
			return null;
		}
	}

public static function make_simple_query_object ( $ slug , \ wpdb $ wpdb = null ) { $ table = static :: get ( $ slug ) ; $ wpdb = $ wpdb ? : $ GLOBALS [ 'wpdb' ] ; if ( $ table ) { return new Simple_Query ( $ wpdb , $ table ) ; } else { return null ; } }

Make a query object for the selected db table.

@since 1.0

@param string $slug Table name.
@param \wpdb  $wpdb

@return Simple_Query|null

Make a query object for the selected db table .


public static function make_complex_query_object( $slug, array $args = array() ) {

		$table = static::get( $slug );

		if ( empty( $table ) || empty( static::$tables[ $slug ]['query'] ) ) {
			return null;
		}

		$class = static::$tables[ $slug ]['query'];

		$query = new $class( $args );

		return $query;
	}

public static function make_complex_query_object ( $ slug , array $ args = array ( ) ) { $ table = static :: get ( $ slug ) ; if ( empty ( $ table ) || empty ( static :: $ tables [ $ slug ] [ 'query' ] ) ) { return null ; } $ class = static :: $ tables [ $ slug ] [ 'query' ] ; $ query = new $ class ( $ args ) ; return $ query ; }

Make a complex query object.

@since 1.0

@param string $slug
@param array  $args

@return Complex_Query|null


public static function get_model( $slug ) {

		$slug = $slug instanceof Table ? $slug->get_slug() : $slug;

		if ( isset( static::$tables[ $slug ] ) ) {
			return static::$tables[ $slug ]['model'];
		} else {
			return null;
		}
	}

public static function get_model ( $ slug ) { $ slug = $ slug instanceof Table ? $ slug -> get_slug ( ) : $ slug ; if ( isset ( static :: $ tables [ $ slug ] ) ) { return static :: $ tables [ $ slug ] [ 'model' ] ; } else { return null ; } }

Get the model for a table.

@since 2.0

@param string|Table $slug

@return string|null


public static function maybe_install_table( Table $table, \wpdb $wpdb = null ) {

		$wpdb = $wpdb ?: $GLOBALS['wpdb'];

		$installed = (int) get_option( $table->get_table_name( $wpdb ) . '_version', 0 );

		if ( $installed >= $table->get_version() ) {
			return false;
		}

		if ( $installed === 0 ) {
			if ( ! static::is_table_installed( $table, $wpdb ) ) {
				$prev = $wpdb->show_errors( false );
				$wpdb->query( $table->get_creation_sql( $wpdb ) );
				$wpdb->show_errors( $prev );

				if ( $wpdb->last_error ) {
					throw new Exception( $wpdb->last_error );
				}

				static::fire_plugin_event( $table, 'installed' );
			}
		} else {

			$update = $installed + 1;

			while ( $update <= $table->get_version() ) {

				if ( method_exists( $table, "v{$update}_schema_update" ) ) {
					$method = "v{$update}_schema_update";

					$table->{$method}( $wpdb, $installed );

					static::fire_plugin_event( $table, 'updated_schema', array( $update, $installed ) );
				}

				$update += 1;
			}

			static::fire_plugin_event( $table, 'updated' );
		}

		update_option( $table->get_table_name( $wpdb ) . '_version', $table->get_version() );

		return true;
	}

public static function maybe_install_table ( Table $ table , \ wpdb $ wpdb = null ) { $ wpdb = $ wpdb ? : $ GLOBALS [ 'wpdb' ] ; $ installed = ( int ) get_option ( $ table -> get_table_name ( $ wpdb ) . '_version' , 0 ) ; if ( $ installed >= $ table -> get_version ( ) ) { return false ; } if ( $ installed === 0 ) { if ( ! static :: is_table_installed ( $ table , $ wpdb ) ) { $ prev = $ wpdb -> show_errors ( false ) ; $ wpdb -> query ( $ table -> get_creation_sql ( $ wpdb ) ) ; $ wpdb -> show_errors ( $ prev ) ; if ( $ wpdb -> last_error ) { throw new Exception ( $ wpdb -> last_error ) ; } static :: fire_plugin_event ( $ table , 'installed' ) ; } } else { $ update = $ installed + 1 ; while ( $ update <= $ table -> get_version ( ) ) { if ( method_exists ( $ table , "v{$update}_schema_update" ) ) { $ method = "v{$update}_schema_update" ; $ table -> { $ method } ( $ wpdb , $ installed ) ; static :: fire_plugin_event ( $ table , 'updated_schema' , array ( $ update , $ installed ) ) ; } $ update += 1 ; } static :: fire_plugin_event ( $ table , 'updated' ) ; } update_option ( $ table -> get_table_name ( $ wpdb ) . '_version' , $ table -> get_version ( ) ) ; return true ; }

Maybe install a table.

Will skip a table if it is up-to-date.

@since 1.0

@param Table $table
@param \wpdb $wpdb

@return bool True if installed or updated, false if skipped.
@throws \IronBound\DB\Exception


public static function maybe_uninstall_table( Table $table, \wpdb $wpdb = null ) {

		$wpdb = $wpdb ?: $GLOBALS['wpdb'];

		if ( static::is_table_installed( $table, $wpdb ) ) {

			$tn = $table->get_table_name( $wpdb );

			$wpdb->query( "DROP TABLE IF EXISTS `{$tn}`" );
			static::fire_plugin_event( $table, 'deleted' );

			delete_option( $tn . '_version' );

			return true;
		}

		return false;
	}

public
static
function
maybe_uninstall_table
(
Table
$
table
,
\
wpdb
$
wpdb
=
)
{
$
wpdb
=
$
wpdb
:
$
GLOBALS
[
'wpdb'
]
;
if
(
static
::
is_table_installed
(
$
table
,
$
wpdb
)
)
{
$
tn
=
$
table
->
get_table_name
(
$
wpdb
)
;
$
wpdb
->
query
(
"DROP TABLE IF EXISTS `{$tn}`"
)
;
static
::
fire_plugin_event
(
$
table
,
'deleted'
)
;
delete_option
(
$
tn
.
'_version'
)
;
return
true
;
}
return
false
;
}

Maybe delete a table.

@since 2.0

@param Table $table
@param \wpdb $wpdb

@return bool Return true if table is installed and deleted. False otherwise.


public static function is_table_installed( Table $table, \wpdb $wpdb = null ) {

		$wpdb = $wpdb ?: $GLOBALS['wpdb'];

		$name = $table->get_table_name( $wpdb );

		$results = $wpdb->get_results( "SHOW TABLES LIKE '$name'" );

		return is_array( $results ) && count( $results ) > 0;
	}

public
static
function
is_table_installed
(
Table
$
table
,
\
wpdb
$
wpdb
=
)
{
$
wpdb
=
$
wpdb
:
$
GLOBALS
[
'wpdb'
]
;
$
name
=
$
table
->
get_table_name
(
$
wpdb
)
;
$
results
=
$
wpdb
->
get_results
(
"SHOW TABLES LIKE '$name'"
)
;
return
is_array
(
$
results
)
&&
count
(
$
results
)
>
0
;
}

Check if a table is installed.

@since 1.0

@param Table $table
@param \wpdb $wpdb

@return bool


protected static function fire_plugin_event( Table $table, $event, $args = array() ) {

		foreach ( static::$plugins as $plugin ) {
			if ( $plugin->accepts( $table ) ) {
				if ( method_exists( $plugin, $event ) ) {
					$args = array( $table ) + $args;
					call_user_func_array( array( $plugin, $event ), $args );
				}
			}
		}
	}

protected static function fire_plugin_event ( Table $ table , $ event , $ args = array ( ) ) { foreach ( static :: $ plugins as $ plugin ) { if ( $ plugin -> accepts ( $ table ) ) { if ( method_exists ( $ plugin , $ event ) ) { $ args = array ( $ table ) + $ args ; call_user_func_array ( array ( $ plugin , $ event ) , $ args ) ; } } } }

Fire a plugin event on a given table.

@since 2.0

@param Table  $table
@param string $event Name of the method on the plugin object. Either 'registered', 'installed', or 'updated'.
@param array  $args  Additional arguments to pass to the plugin handler.


protected function do_save( \WP_Post $post ) {

		if ( ! $post->ID ) {
			$id = wp_insert_post( wp_slash( $post->to_array() ), true );
		} else {
			$id = wp_update_post( $post, true );
		}

		if ( is_wp_error( $id ) ) {
			throw new \InvalidArgumentException( 'Error encountered while saving WP_Post: ' . $id->get_error_message() );
		}

		return get_post( $id );
	}

Do the saving for a post.

@since 2.0

@param \WP_Post $post

@return \WP_Post


protected function get_column() {

		$column  = $this->foreign_column ?: $this->foreign_table->get_primary_key();
		$columns = $this->foreign_table->get_columns();

		return $columns[ $column ];
	}

protected function get_column ( ) { $ column = $ this -> foreign_column ? : $ this -> foreign_table -> get_primary_key ( ) ; $ columns = $ this -> foreign_table -> get_columns ( ) ; return $ columns [ $ column ] ; }

Get the referenced column.

@since 2.0

@return BaseColumn


protected function parse_defaults( array $columns ) {
		foreach ( $columns as $column_name => $column ) {
			if ( array_key_exists( $column_name, $this->defaults ) ) {
				continue;
			}

			switch ( $column->get_mysql_type() ) {
				case 'TINYINT':
				case 'SMALLINT':
				case 'MEDIUMINT':
				case 'INT':
				case 'BIGINT':
					$this->defaults[ $column_name ] = 0;
					break;
				case 'FLOAT':
				case 'DOUBLE':
				case 'DECIMAL':
					$this->defaults[ $column_name ] = 0.0;
					break;
				case 'DATE':
				case 'DATETIME':
					$this->defaults[ $column_name ] = null;
					break;
				default:
					$this->defaults[ $column_name ] = '';
					break;
			}
		}
	}

protected function parse_defaults ( array $ columns ) { foreach ( $ columns as $ column_name => $ column ) { if ( array_key_exists ( $ column_name , $ this -> defaults ) ) { continue ; } switch ( $ column -> get_mysql_type ( ) ) { case 'TINYINT' : case 'SMALLINT' : case 'MEDIUMINT' : case 'INT' : case 'BIGINT' : $ this -> defaults [ $ column_name ] = 0 ; break ; case 'FLOAT' : case 'DOUBLE' : case 'DECIMAL' : $ this -> defaults [ $ column_name ] = 0.0 ; break ; case 'DATE' : case 'DATETIME' : $ this -> defaults [ $ column_name ] = null ; break ; default : $ this -> defaults [ $ column_name ] = '' ; break ; } } }

Parse defaults based on the column types.

@param array $columns

Parse defaults based on the column types .


public static function from_model( $model ) {

		$query        = new static( $model::table(), $GLOBALS['wpdb'] );
		$query->model = $model;

		return $query;
	}

public static function from_model ( $ model ) { $ query = new static ( $ model :: table ( ) , $ GLOBALS [ 'wpdb' ] ) ; $ query -> model = $ model ; return $ query ; }

Create a new FluentQuery object from a model.

@since 2.0

@param string $model

@return static

Create a new FluentQuery object from a model .


public function select( $columns ) {

		if ( $columns === Select::ALL ) {

			$this->select->all( $this->alias );

			return $this;
		}

		if ( ! is_array( $columns ) ) {
			$columns = func_get_args();
		}

		foreach ( $columns as $column ) {
			$this->select->also( $this->prepare_column( $column ) );
		}

		$this->select->also( $this->prepare_column( $this->table->get_primary_key() ) );

		$this->focused_select = true;

		return $this;
	}

public function select ( $ columns ) { if ( $ columns === Select :: ALL ) { $ this -> select -> all ( $ this -> alias ) ; return $ this ; } if ( ! is_array ( $ columns ) ) { $ columns = func_get_args ( ) ; } foreach ( $ columns as $ column ) { $ this -> select -> also ( $ this -> prepare_column ( $ column ) ) ; } $ this -> select -> also ( $ this -> prepare_column ( $ this -> table -> get_primary_key ( ) ) ) ; $ this -> focused_select = true ; return $ this ; }

Select certain columns.

@since 2.0

@param string $columns,...

@return $this


public function select_single( $column ) {
		$this->select->also( $this->prepare_column( $column ) );
		$this->select->also( $this->prepare_column( $this->table->get_primary_key() ) );

		$this->select_single = true;

		return $this;
	}

public function select_single ( $ column ) { $ this -> select -> also ( $ this -> prepare_column ( $ column ) ) ; $ this -> select -> also ( $ this -> prepare_column ( $ this -> table -> get_primary_key ( ) ) ) ; $ this -> select_single = true ; return $ this ; }

Select a single column.

Will return a map of primary key -> column value.

@since 1.36.0

@param string $column

@return $this


public function select_all( $local_only = true ) {

		$this->select->all( $local_only ? $this->alias : null );

		return $this;
	}

public function select_all ( $ local_only = true ) { $ this -> select -> all ( $ local_only ? $ this -> alias : null ) ; return $ this ; }

Select all results.

@since 2.0

@param bool $local_only Only retrieve results local to this table.

@return $this


public function expression( $function, $column, $as ) {
		$this->select->expression( $function, $this->prepare_column( $column ), $as );

		$this->expressions[] = $as;

		return $this;
	}

public function expression ( $ function , $ column , $ as ) { $ this -> select -> expression ( $ function , $ this -> prepare_column ( $ column ) , $ as ) ; $ this -> expressions [ ] = $ as ; return $ this ; }

Select an expression, such as 'COUNT'.

@since 2.0

@param string $function
@param string $column
@param string $as

@return $this


public function where( $column, $equality = '', $value = '', Closure $callback = null, $boolean = null ) {

		if ( $equality ) {
			$this->assert_comparator( $equality );
		}

		if ( is_array( $column ) ) {

			$where = Where::for_clause();

			foreach ( $column as $col => $val ) {
				$where->qAnd( $this->generate_where_tag( $col, true, $val ) );
			}

		} else {
			$where = $this->generate_where_tag( $column, $equality, $value );
		}

		if ( $callback ) {
			$_where      = $this->where;
			$this->where = $where;
			$callback( $this );
			$this->where = $_where;
		}

		if ( $this->where->is_empty() && ! $where->is_for_clause() ) {
			$this->where = $where;
		} else {
			$boolean = $boolean ?: 'and';
			$boolean = 'q' . ucfirst( $boolean );
			$this->where->{$boolean}( $where );
		}

		return $this;
	}

public function where ( $ column , $ equality = '' , $ value = '' , Closure $ callback = null , $ boolean = null ) { if ( $ equality ) { $ this -> assert_comparator ( $ equality ) ; } if ( is_array ( $ column ) ) { $ where = Where :: for_clause ( ) ; foreach ( $ column as $ col => $ val ) { $ where -> qAnd ( $ this -> generate_where_tag ( $ col , true , $ val ) ) ; } } else { $ where = $ this -> generate_where_tag ( $ column , $ equality , $ value ) ; } if ( $ callback ) { $ _where = $ this -> where ; $ this -> where = $ where ; $ callback ( $ this ) ; $ this -> where = $ _where ; } if ( $ this -> where -> is_empty ( ) && ! $ where -> is_for_clause ( ) ) { $ this -> where = $ where ; } else { $ boolean = $ boolean ? : 'and' ; $ boolean = 'q' . ucfirst ( $ boolean ) ; $ this -> where -> { $ boolean } ( $ where ) ; } return $ this ; }

Filter results by a condition.

@since 2.0

@param string|array|Where $column
@param string|bool        $equality
@param mixed              $value
@param Closure|null       $callback Called with $this as the first parameter. Setup for nesting on the new where
tag.
@param string             $boolean

@return $this

@throws \IronBound\DB\Exception\InvalidColumnException
@throws \InvalidArgumentException


protected function generate_where_tag( $column, $equality = '', $value = '' ) {

		if ( $column instanceof Where ) {
			return $column;
		}

		if ( is_array( $value ) ) {

			if ( count( $value ) === 0 ) {
				throw new \InvalidArgumentException( 'Must provide at least one value for IN query.' );
			}

			$self  = $this;
			$value = array_map( function ( $value ) use ( $column, $self ) {
				return $self->escape_value( $column, $value );
			}, $value );
		} else {
			$value = $this->escape_value( $column, $value );
		}

		$column = $this->prepare_column( $column );

		return new Where( $column, $equality, $value );
	}

Generate a Where tag.

@since 2.0.0

@param string       $column
@param string|bool  $equality
@param string|array $value

@return Where

@throws \IronBound\DB\Exception\InvalidColumnException
@throws \InvalidArgumentException


public function add_nested_where( Closure $callback, $boolean = 'and' ) {

		$_where      = $this->where;
		$this->where = Where::for_clause();

		$callback( $this );

		$clause      = $this->where;
		$this->where = $_where;

		if ( $clause->is_empty() ) {
			return;
		}

		$boolean = $boolean ?: 'and';
		$boolean = 'q' . ucfirst( $boolean );
		$this->where->{$boolean}( $clause );
	}

public function add_nested_where ( Closure $ callback , $ boolean = 'and' ) { $ _where = $ this -> where ; $ this -> where = Where :: for_clause ( ) ; $ callback ( $ this ) ; $ clause = $ this -> where ; $ this -> where = $ _where ; if ( $ clause -> is_empty ( ) ) { return ; } $ boolean = $ boolean ? : 'and' ; $ boolean = 'q' . ucfirst ( $ boolean ) ; $ this -> where -> { $ boolean } ( $ clause ) ; }

Add a nested where clause.

@since 2.0.0

@param Closure $callback
@param string  $boolean


public function where_joined( Table $table, Closure $callback, $boolean = 'and' ) {

		$found = false;

		foreach ( $this->joined_tables as $alias => $maybe_table ) {
			if ( $maybe_table->get_slug() === $table->get_slug() ) {
				$found = $alias;
				break;
			}
		}

		if ( ! $found ) {
			throw new \InvalidArgumentException( "Table {$table->get_slug()} has not already been joined to this table." );
		}

		$other_query                = new FluentQuery( $table, $this->wpdb );
		$other_query->alias         = $found;
		$other_query->alias_count   = $this->alias_count + 1;
		$other_query->where         = Where::for_clause();
		$other_query->joined_tables = $this->joined_tables;

		$callback( $other_query );

		$boolean = 'q' . ucfirst( $boolean );
		$this->where->{$boolean}( $other_query->where );
	}

Add a where condition checking an already joined table to the main where clause.

@param Table   $table
@param Closure $callback
@param string  $boolean

Add a where condition checking an already joined table to the main where clause .


public function or_where( $column, $equality = '', $value = '', Closure $callback = null ) {
		return $this->where( $column, $equality, $value, $callback, 'or' );
	}

public function or_where ( $ column , $ equality = '' , $ value = '' , Closure $ callback = null ) { return $ this -> where ( $ column , $ equality , $ value , $ callback , 'or' ) ; }

Add a OR where clause.

@since 2.0

@param string       $column
@param string|bool  $equality
@param mixed        $value
@param Closure|null $callback Called with $this as the first parameter. Setup for nesting on the new where tag.

@return $this


public function where_date( $query, $column, Closure $callback = null, $boolean = 'and' ) {

		$query = new \WP_Date_Query( $query, $this->prepare_column( $column ) );

		return $this->where( new Where_Date( $query ), '', '', $callback, $boolean );
	}

public function where_date ( $ query , $ column , Closure $ callback = null , $ boolean = 'and' ) { $ query = new \ WP_Date_Query ( $ query , $ this -> prepare_column ( $ column ) ) ; return $ this -> where ( new Where_Date ( $ query ) , '' , '' , $ callback , $ boolean ) ; }

Perform a date based where query.

@since 2.0

@param array        $query
@param string       $column
@param Closure|null $callback
@param string       $boolean

@return $this
@throws InvalidColumnException


public function where_meta( $query, MetaTable $table = null, $meta_type = '' ) {

		if ( ! $table && $this->model && method_exists( $this->model, 'get_meta_table' ) ) {
			$table = call_user_func( array( $this->model, 'get_meta_table' ) );
		}

		if ( ! $meta_type && $this->model && method_exists( $this->model, 'get_meta_type' ) ) {
			$meta_type = call_user_func( array( $this->model, 'get_meta_type' ) );
		}

		if ( ! $table ) {
			throw new \InvalidArgumentException( "MetaTable can't be determined from the given arguments." );
		}

		if ( ! $meta_type ) {
			throw new \InvalidArgumentException( "\$meta_type can't be determined from the given arguments." );
		}

		if ( ! $query instanceof \WP_Meta_Query ) {
			$query = new \WP_Meta_Query( $query );
		}

		$fn = function ( $key, $original ) use ( $table, $meta_type ) {

			if ( $original === $meta_type . '_id' ) {
				$key = $table->get_primary_id_column();
			}

			return $key;
		};

		add_filter( 'sanitize_key', $fn, 10, 2 );

		$sql = $query->get_sql(
			$meta_type,
			$this->alias,
			$this->table->get_primary_key()
		);

		remove_filter( 'sanitize_key', $fn, 10 );

		$this->meta_join = $sql['join'];

		$where = $sql['where'];
		$where = preg_replace( '/^\sAND\s/', '', $where );

		if ( $this->where ) {
			$this->where->qAnd( new Where_Raw( $where ) );
		} else {
			$this->where = new Where_Raw( $where );
		}

		return $this;
	}

Perform a meta query.

@since 2.0

@param array|\WP_Meta_Query $query
@param MetaTable|null       $table     Table metadata is stored in. If not specified, will be retrieved from the
model.
@param string               $meta_type Type of metadata. Will be determined from the model if not given.

@return $this

@throws \InvalidArgumentException If a MetaTable or $meta_type can't be determined.


public function order_by( $column, $direction = null ) {

		$column = $this->prepare_column( $column );

		if ( is_null( $this->order ) ) {
			$this->order = new Order( $column, $direction );
		} else {
			$this->order->then( $column, $direction );
		}

		return $this;
	}

public function order_by ( $ column , $ direction = null ) { $ column = $ this -> prepare_column ( $ column ) ; if ( is_null ( $ this -> order ) ) { $ this -> order = new Order ( $ column , $ direction ) ; } else { $ this -> order -> then ( $ column , $ direction ) ; } return $ this ; }

Order the results by a given column.

Can be called multiple times to add additional order by clauses.

@since 2.0

@param string $column
@param string $direction

@return $this

@throws \IronBound\DB\Exception\InvalidColumnException


public function order_by_expression( $expression_alias, $direction = null ) {
		if ( ! in_array( $expression_alias, $this->expressions, true ) ) {
			throw new InvalidColumnException( 'Cannot order by expression alias because the alias has not been used.' );
		}

		if ( is_null( $this->order ) ) {
			$this->order = new Order( $expression_alias, $direction );
		} else {
			$this->order->then( $expression_alias, $direction );
		}

		return $this;
	}

Order results by a previously calculated expression.

@param string $expression_alias
@param string $direction

@return $this
@throws InvalidColumnException

Order results by a previously calculated expression .


public function group_by( $column ) {

		$column = $this->prepare_column( $column );

		if ( is_null( $this->group ) ) {
			$this->group = new Group( $column );
		} else {
			$this->group->then( $column );
		}

		return $this;
	}

public function group_by ( $ column ) { $ column = $ this -> prepare_column ( $ column ) ; if ( is_null ( $ this -> group ) ) { $ this -> group = new Group ( $ column ) ; } else { $ this -> group -> then ( $ column ) ; } return $ this ; }

Group the results by a given column.

Can be called multiple times to add additional group by columns.

@since 2.0

@param string $column

@return $this

@throws \IronBound\DB\Exception\InvalidColumnException


public function group_by_expression( $function, $column ) {

		$column = $this->prepare_column( $column );

		$group = "{$function}($column)";

		if ( is_null( $this->group ) ) {
			$this->group = new Group( $group );
		} else {
			$this->group->then( $group );
		}

		return $this;
	}

public function group_by_expression ( $ function , $ column ) { $ column = $ this -> prepare_column ( $ column ) ; $ group = "{$function}($column)" ; if ( is_null ( $ this -> group ) ) { $ this -> group = new Group ( $ group ) ; } else { $ this -> group -> then ( $ group ) ; } return $ this ; }

Group the results by a given column after applying an expression to it.

Can be called multiple times to add additional group by expressions.

@since 2.2.0

@param string $function
@param string $column

@return $this

@throws \IronBound\DB\Exception\InvalidColumnException

Group the results by a given column after applying an expression to it .


public function join( Table $table, $this_column, $other_column, $comparator = '=', $callback = null, $type = 'INNER' ) {

		$this->assert_comparator( $comparator );

		$other_alias = 't' . ( ++ $this->alias_count );

		$other_query              = new FluentQuery( $table, $this->wpdb );
		$other_query->alias       = $other_alias;
		$other_query->alias_count = $this->alias_count + 1;

		$other_query->joined_tables                 = $this->joined_tables;
		$other_query->joined_tables[ $this->alias ] = $this->table;

		$from = new From( $table->get_table_name( $this->wpdb ), $other_alias );

		$where = new Where_Raw(
			"{$this->prepare_column( $this_column )} $comparator {$other_query->prepare_column( $other_column )}"
		);

		if ( $callback ) {
			$callback( $other_query );

			if ( ! $other_query->where->is_empty() ) {
				$where->qAnd( $other_query->where );
			}
		}

		$this->joins[] = new Join( $from, $where, $type );

		$this->joined_tables[ $other_alias ] = $table;

		return $this;
	}

Simple join statement.

@since 2.0

@param Table       $table
@param string      $this_column
@param string      $other_column
@param bool|string $comparator
@param callable    $callback Called with a FluentQuery object. Can be used to build additional where queries
for the Join clause.
@param string      $type     Join type. Defaults to 'INNER'.

@return $this
@throws InvalidColumnException


public function join_correlated_subquery( Table $table, $this_column, $comparator, $other_column, Closure $callback, $type = 'INNER' ) {

		$this->assert_comparator( $comparator );

		$other_alias = 't' . ( ++ $this->alias_count );

		$other_query              = new FluentQuery( $table, $this->wpdb );
		$other_query->from        = new From( $table->get_table_name( $this->wpdb ), $other_alias );
		$other_query->alias       = $other_alias;
		$other_query->alias_count = $this->alias_count + 1;

		$other_query->joined_tables                 = $this->joined_tables;
		$other_query->joined_tables[ $this->alias ] = $this->table;

		$other_query->select->also( $other_query->prepare_column( $other_column ) );
		$other_query->take( 1 );
		$callback( $other_query );

		$from = new From( $table->get_table_name( $this->wpdb ), $other_alias );

		$where = new Where_Raw(
			"{$this->prepare_column( $this_column )} $comparator ({$other_query->build_sql()})"
		);

		$this->joins[] = new Join( $from, $where, $type );

		$this->joined_tables[ $other_alias ] = $table;

		return $this;
	}

Perform a join, but instead of comparing to a simple value,
use a correlated sub query to find the match.

Will automatically build the SELECT and LIMIT statements.

@param Table   $table        Table to join to.
@param string  $this_column  Column on the current table to check equality for.
@param string  $comparator   How to compare the two values, should likely be '='.
@param string  $other_column The column on the joined table to check.
@param Closure $callback     Callback receiving a FluentQuery to build the sub query from.
@param string  $type

@return $this
@throws InvalidColumnException

Perform a join but instead of comparing to a simple value use a correlated sub query to find the match .


public function paginate( $page, $per_page ) {
		$this->count           = $per_page;
		$this->offset          = $per_page * ( $page - 1 );
		$this->calc_found_rows = true;

		return $this;
	}

public function paginate ( $ page , $ per_page ) { $ this -> count = $ per_page ; $ this -> offset = $ per_page * ( $ page - 1 ) ; $ this -> calc_found_rows = true ; return $ this ; }

Paginate results.

@since 2.0

@param int $page
@param int $per_page

@return $this


public function each( $number, $callback ) {

		$this->offset = 0;
		$this->count  = $number;

		$query = clone $this;

		do {

			$_query  = clone $query;
			$results = $query->results();

			foreach ( $results as $result ) {
				if ( $callback( $result ) === false ) {
					return false;
				}
			}

			$_query->offset += $number;
			$query          = $_query;

		} while ( $results->count() === $number );

		return true;
	}

public function each ( $ number , $ callback ) { $ this -> offset = 0 ; $ this -> count = $ number ; $ query = clone $ this ; do { $ _query = clone $ query ; $ results = $ query -> results ( ) ; foreach ( $ results as $ result ) { if ( $ callback ( $ result ) === false ) { return false ; } } $ _query -> offset += $ number ; $ query = $ _query ; } while ( $ results -> count ( ) === $ number ) ; return true ; }

Execute a callback over every row, chunking by a certain number.

@since 2.0

@param int      $number
@param callable $callback

@return bool

Execute a callback over every row chunking by a certain number .


public function with( $relations, $callback = null ) {

		$default = null;

		if ( is_string( $relations ) ) {
			$relations = func_get_args();

			if ( func_num_args() === 2 && $relations[1] instanceof Closure ) {
				$relations = array(
					$relations[0] => $relations[1],
				);
			}
		}

		$parsed = array();

		foreach ( $relations as $relation => $callback ) {

			if ( ! $callback instanceof Closure ) {
				$relation = $callback;
				$callback = $default;
			}

			if ( strpos( $relation, '.' ) !== false ) {
				$parsed = $this->parse_nested_with( $relation, $parsed );
			} else {
				$parsed[ $relation ] = $callback;
			}
		}

		$this->relations = $parsed;

		return $this;
	}

public function with ( $ relations , $ callback = null ) { $ default = null ; if ( is_string ( $ relations ) ) { $ relations = func_get_args ( ) ; if ( func_num_args ( ) === 2 && $ relations [ 1 ] instanceof Closure ) { $ relations = array ( $ relations [ 0 ] => $ relations [ 1 ] , ) ; } } $ parsed = array ( ) ; foreach ( $ relations as $ relation => $ callback ) { if ( ! $ callback instanceof Closure ) { $ relation = $ callback ; $ callback = $ default ; } if ( strpos ( $ relation , '.' ) !== false ) { $ parsed = $ this -> parse_nested_with ( $ relation , $ parsed ) ; } else { $ parsed [ $ relation ] = $ callback ; } } $ this -> relations = $ parsed ; return $ this ; }

Perform this query while loading a set of relations.

@param string|array $relations,... An array of relations to Closure callbacks
@param Closure      $callback      The callback for the relation. Called with a FluentQuery object to allow for
customizing which models are eager loaded.

@return $this

Perform this query while loading a set of relations .


protected function parse_nested_with( $name, $results ) {

		$parts = explode( '.', $name );
		$first = $parts[0];

		$results[ $first ] = false;

		$this->assign_array_by_path( $results, $name, true );

		return $results;
	}

protected function parse_nested_with ( $ name , $ results ) { $ parts = explode ( '.' , $ name ) ; $ first = $ parts [ 0 ] ; $ results [ $ first ] = false ; $ this -> assign_array_by_path ( $ results , $ name , true ) ; return $ results ; }

Parse the nested relations.

@since 2.0

@param string $name
@param array  $results

@return array


protected function assign_array_by_path( &$arr, $path, $value ) {

		$keys = explode( '.', $path );

		foreach ( $keys as $key ) {
			$arr = &$arr[ $key ];
		}

		$arr = $value;
	}

protected function assign_array_by_path ( & $ arr , $ path , $ value ) { $ keys = explode ( '.' , $ path ) ; foreach ( $ keys as $ key ) { $ arr = & $ arr [ $ key ] ; } $ arr = $ value ; }

Fill an array by array dot notation.

@since 2.0

@param array  $arr
@param string $path
@param mixed  $value


protected function make_limit_tag() {

		if ( ! $this->count ) {
			return $this;
		}

		$this->limit = new Limit( $this->count, $this->offset );

		return $this;
	}

protected function make_limit_tag ( ) { if ( ! $ this -> count ) { return $ this ; } $ this -> limit = new Limit ( $ this -> count , $ this -> offset ) ; return $ this ; }

Make the limit tag for this query.

@since 2.0

@return $this


protected function build_sql() {

		$this->make_limit_tag();

		$builder = new Builder();

		if ( ! $this->select->is_all() && ! $this->select->get_columns() ) {
			$this->select->all( $this->alias );
		}

		$this->select->calc_found_rows( $this->calc_found_rows );

		$builder->append( $this->select );
		$builder->append( $this->from );

		foreach ( $this->joins as $join ) {
			$builder->append( $join );
		}

		if ( $this->meta_join ) {
			$builder->append( new Generic( '', $this->meta_join ) );
		}

		if ( $this->where ) {
			$builder->append( $this->where );
		}

		if ( $this->group ) {
			$builder->append( $this->group );
		}

		if ( $this->having ) {
			$builder->append( $this->having );
		}

		if ( $this->order ) {
			$builder->append( $this->order );
		}

		if ( $this->limit ) {
			$builder->append( $this->limit );
		}

		return $builder->build();
	}

protected function build_sql ( ) { $ this -> make_limit_tag ( ) ; $ builder = new Builder ( ) ; if ( ! $ this -> select -> is_all ( ) && ! $ this -> select -> get_columns ( ) ) { $ this -> select -> all ( $ this -> alias ) ; } $ this -> select -> calc_found_rows ( $ this -> calc_found_rows ) ; $ builder -> append ( $ this -> select ) ; $ builder -> append ( $ this -> from ) ; foreach ( $ this -> joins as $ join ) { $ builder -> append ( $ join ) ; } if ( $ this -> meta_join ) { $ builder -> append ( new Generic ( '' , $ this -> meta_join ) ) ; } if ( $ this -> where ) { $ builder -> append ( $ this -> where ) ; } if ( $ this -> group ) { $ builder -> append ( $ this -> group ) ; } if ( $ this -> having ) { $ builder -> append ( $ this -> having ) ; } if ( $ this -> order ) { $ builder -> append ( $ this -> order ) ; } if ( $ this -> limit ) { $ builder -> append ( $ this -> limit ) ; } return $ builder -> build ( ) ; }

Build the SQL statement.

@since 2.0

@return string


protected function update_meta_cache() {

		$ids       = $this->results->getKeys();
		$table     = $this->meta_table ?: call_user_func( array( $this->model, 'get_meta_table' ) );
		$meta_type = $this->meta_type ?: call_user_func( array( $this->model, 'get_meta_type' ) );

		$fn = function ( $key, $original ) use ( $table, $meta_type ) {

			if ( $original === $meta_type . '_id' ) {
				$key = $table->get_primary_id_column();
			}

			return $key;
		};

		add_filter( 'sanitize_key', $fn, 10, 2 );

		update_meta_cache( $meta_type, $ids );

		remove_filter( 'sanitize_key', $fn );
	}

protected function update_meta_cache ( ) { $ ids = $ this -> results -> getKeys ( ) ; $ table = $ this -> meta_table ? : call_user_func ( array ( $ this -> model , 'get_meta_table' ) ) ; $ meta_type = $ this -> meta_type ? : call_user_func ( array ( $ this -> model , 'get_meta_type' ) ) ; $ fn = function ( $ key , $ original ) use ( $ table , $ meta_type ) { if ( $ original === $ meta_type . '_id' ) { $ key = $ table -> get_primary_id_column ( ) ; } return $ key ; } ; add_filter ( 'sanitize_key' , $ fn , 10 , 2 ) ; update_meta_cache ( $ meta_type , $ ids ) ; remove_filter ( 'sanitize_key' , $ fn ) ; }


public function find( $primary_key ) {

		if ( is_array( $primary_key ) ) {
			return $this->find_many( $primary_key );
		}

		return $this->where( $this->table->get_primary_key(), true, $primary_key )->first();
	}

public function find ( $ primary_key ) { if ( is_array ( $ primary_key ) ) { return $ this -> find_many ( $ primary_key ) ; } return $ this -> where ( $ this -> table -> get_primary_key ( ) , true , $ primary_key ) -> first ( ) ; }

Find a model by its primary key.

@since 2.0

@param string|int|array $primary_key

@return Model|Collection


public function find_many( array $primary_keys ) {
		return $this->where( $this->table->get_primary_key(), true, $primary_keys )->results();
	}

public function find_many ( array $ primary_keys ) { return $ this -> where ( $ this -> table -> get_primary_key ( ) , true , $ primary_keys ) -> results ( ) ; }

Find many models by their primary keys.

@since 2.0

@param array $primary_keys

@return Collection

Find many models by their primary keys .


public function find_or_fail( $primary_key ) {

		$result = $this->find( $primary_key );

		if ( is_array( $primary_key ) ) {
			if ( count( $result ) == count( array_unique( $primary_key ) ) ) {
				return $result;
			}
		} elseif ( $result ) {
			return $result;
		}

		throw new ModelNotFoundException( "No model found for '$primary_key'." );
	}

public
function
find_or_fail
(
$
primary_key
)
{
$
result
=
$
this
->
find
(
$
primary_key
)
;
if
(
is_array
(
$
primary_key
)
)
{
if
(
count
(
$
result
)
==
count
(
array_unique
(
$
primary_key
)
)
)
{
return
$
result
;
}
}
elseif
(
$
result
)
{
return
$
result
;
}
throw
new
ModelNotFoundException
(
"No model found for '$primary_key'."
)
;
}

Find a model by its primary key or throw an exception.

@since 2.0

@param string|int|array $primary_key

@return Model|Collection

@throws ModelNotFoundException

Find a model by its primary key or throw an exception .


public function find_or_new( $primary_key ) {

		$model = $this->find( $primary_key );

		if ( is_null( $model ) ) {
			$model = new $this->model;
		}

		return $model;
	}

public function find_or_new ( $ primary_key ) { $ model = $ this -> find ( $ primary_key ) ; if ( is_null ( $ model ) ) { $ model = new $ this -> model ; } return $ model ; }

Find a model by its primary key or create a new model.

@since 2.0

@param string|int $primary_key

@return Model

Find a model by its primary key or create a new model .


public function first_or_new( array $attributes ) {

		$model = $this->where( $attributes )->first();

		if ( $model ) {
			return $model;
		}

		return new $this->model( (object) $attributes );
	}

public function first_or_new ( array $ attributes ) { $ model = $ this -> where ( $ attributes ) -> first ( ) ; if ( $ model ) { return $ model ; } return new $ this -> model ( ( object ) $ attributes ) ; }

Find the first model matching a given a set of attributes, or construct a new one.

The newly constructed model follows the fillable rules for the model.

@since 2.0

@param array $attributes

@return Model

Find the first model matching a given a set of attributes or construct a new one .


public function first_or_create( array $attributes ) {

		$model = $this->where( $attributes )->first();

		if ( $model ) {
			return $model;
		}

		$model = new $this->model( (object) $attributes );
		$model->save();

		return $model;
	}

public function first_or_create ( array $ attributes ) { $ model = $ this -> where ( $ attributes ) -> first ( ) ; if ( $ model ) { return $ model ; } $ model = new $ this -> model ( ( object ) $ attributes ) ; $ model -> save ( ) ; return $ model ; }

Find the first model matching a given set of attributes, or construct and save a new one.

The newly constructed model follows the fillable rules for the model.

@since 2.0

@param array $attributes

@return Model

Find the first model matching a given set of attributes or construct and save a new one .


public function update_or_create( array $attributes, array $values ) {

		$model = $this->first_or_new( $attributes );
		$model->fill( $values )->save();

		return $model;
	}

public function update_or_create ( array $ attributes , array $ values ) { $ model = $ this -> first_or_new ( $ attributes ) ; $ model -> fill ( $ values ) -> save ( ) ; return $ model ; }

Find a model matching a given set of attributes. If it does not exist,
a new model is constructed with the given set of attributes.

The model is then updated with the given set of values.

@since 2.0

@param array $attributes
@param array $values

@return Model

Find a model matching a given set of attributes . If it does not exist a new model is constructed with the given set of attributes .


protected function handle_eager_loading( $models ) {

		/** @var Model $model */
		$model = new $this->model;

		foreach ( $this->relations as $relation => $customize_callback ) {
			if ( is_array( $customize_callback ) ) {
				$loaded = $model->get_relation( $relation )->eager_load( $models );
				$this->do_nested_eager_load( $loaded, $relation, $customize_callback );
			} else {
				$model->get_relation( $relation )->eager_load( $models, $customize_callback );
			}
		}
	}

protected
function
handle_eager_loading
(
$
models
)
{
/** @var Model $model */
$
model
=
new
$
this
->
model
;
foreach
(
$
this
->
relations
as
$
relation
=>
$
customize_callback
)
{
if
(
is_array
(
$
customize_callback
)
)
{
$
loaded
=
$
model
->
get_relation
(
$
relation
)
->
eager_load
(
$
models
)
;
$
this
->
do_nested_eager_load
(
$
loaded
,
$
relation
,
$
customize_callback
)
;
}
else
{
$
model
->
get_relation
(
$
relation
)
->
eager_load
(
$
models
,
$
customize_callback
)
;
}
}
}

Handle eager loading of relations.

@since 2.0

@param Model[] $models


protected function do_nested_eager_load( Collection $loaded, $relation, $nested ) {

		$model = $loaded->first();

		foreach ( $nested as $value => $ignore ) {
			if ( is_string( $value ) ) {
				if ( $model instanceof Model ) {
					$model->get_relation( $value )->eager_load( $loaded->toArray() );
				}

				return;
			} else {
				if ( ! $model instanceof Model ) {
					return;
				}

				$loaded = $model->get_relation( $relation )->eager_load( $loaded->toArray() );

				$this->do_nested_eager_load( $loaded, $nested, $value );
			}
		}
	}

protected function do_nested_eager_load ( Collection $ loaded , $ relation , $ nested ) { $ model = $ loaded -> first ( ) ; foreach ( $ nested as $ value => $ ignore ) { if ( is_string ( $ value ) ) { if ( $ model instanceof Model ) { $ model -> get_relation ( $ value ) -> eager_load ( $ loaded -> toArray ( ) ) ; } return ; } else { if ( ! $ model instanceof Model ) { return ; } $ loaded = $ model -> get_relation ( $ relation ) -> eager_load ( $ loaded -> toArray ( ) ) ; $ this -> do_nested_eager_load ( $ loaded , $ nested , $ value ) ; } } }

Handle a nested eager loaded.

@since 2.0

@param Collection $loaded
@param string     $relation
@param array      $nested


protected function assert_comparator( $operator ) {
		if ( is_bool( $operator ) ) {
			return;
		}

		if ( in_array( $operator, array(
			'=',
			'!=',
			'>',
			'<',
			'>=',
			'<=',
			'<=>',
			'<>',
			'LIKE',
			'BETWEEN',
			'COALESCE',
			'GREATEST',
			'IN',
			'INTERVAL',
			'IS',
			'IS NOT',
			'IS NOT NULL',
			'IS NULL',
			'ISNULL',
			'LEAST',
			'NOT BETWEEN',
			'NOT IN',
			'NOT LIKE',
		), true ) ) {
			return;
		}

		throw new \InvalidArgumentException( sprintf( 'Invalid SQL operator % s . ', $operator ) );
	}

protected
function
assert_comparator
(
$
operator
)
{
if
(
is_bool
(
$
operator
)
)
{
return
;
}
if
(
in_array
(
$
operator
,
array
(
'='
,
'!='
,
'>'
,
'<'
,
'>='
,
'<='
,
'<=>'
,
'<>'
,
'LIKE'
,
'BETWEEN'
,
'COALESCE'
,
'GREATEST'
,
'IN'
,
'INTERVAL'
,
'IS'
,
'IS NOT'
,
'IS NOT NULL'
,
'IS NULL'
,
'ISNULL'
,
'LEAST'
,
'NOT BETWEEN'
,
'NOT IN'
,
'NOT LIKE'
,
)
,
true
)
)
{
return
;
}
throw
new
\
InvalidArgumentException
(
sprintf
(
'Invalid SQL operator % s . '
,
$
operator
)
)
;
}

Assert the operator is valid.

@since 2.0

@param string|bool $operator

@throws \InvalidArgumentException


public function prepare_column( $column ) {

		$columns = $this->table->get_columns();

		if ( ! isset( $columns[ $column ] ) ) {
			throw new InvalidColumnException( "Invalid database column '$column'." );
		}

		return "{$this->alias}.`{$column}`";
	}

public
function
prepare_column
(
$
column
)
{
$
columns
=
$
this
->
table
->
get_columns
(
)
;
if
(
!
isset
(
$
columns
[
$
column
]
)
)
{
throw
new
InvalidColumnException
(
"Invalid database column '$column'."
)
;
}
return
"{$this->alias}.`{$column}`"
;
}

Prepare a column.

@since 2.0

@param $column

@return string
@throws InvalidColumnException


public function escape_value( $column, $value ) {

		$columns = $this->table->get_columns();

		if ( ! isset( $columns[ $column ] ) ) {
			throw new InvalidColumnException( "Invalid database column '$column'." );
		}

		if ( is_null( $value ) ) {
			return null;
		}

		if ( empty( $value ) ) {
			return '';
		}

		return esc_sql( $columns[ $column ]->prepare_for_storage( $value ) );
	}

public
function
escape_value
(
$
column
,
$
value
)
{
$
columns
=
$
this
->
table
->
get_columns
(
)
;
if
(
!
isset
(
$
columns
[
$
column
]
)
)
{
throw
new
InvalidColumnException
(
"Invalid database column '$column'."
)
;
}
if
(
is_null
(
$
value
)
)
{
return
;
}
if
(
empty
(
$
value
)
)
{
return
''
;
}
return
esc_sql
(
$
columns
[
$
column
]
->
prepare_for_storage
(
$
value
)
)
;
}

Escape a value.

@since 2.0

@param string $column
@param mixed  $value

@return mixed

@throws InvalidColumnException


final public static function configure(array $configuration)
    {
        // override with default configuration if not present
        $configuration += static::$_defaultConfiguration;

        // test if datasource is a PDO instance
        if ($configuration['datasource'] === null || !$configuration['datasource'] instanceof \PDO) {
            throw new Exception("PDO Datasource is required!");
        }

        // set global datasource for all model
        static::$_dataSource = $configuration['datasource'];
        Model::setDataSource(static::$_dataSource);

        // store PicORM configuration
        static::$_configuration = $configuration;
    }

Set PicORM global configuration

@param array $configuration

@throws Exception


protected final function get_col_value( $col ) {
		if ( ! isset( $this->data[ $col ] ) ) {
			throw new \InvalidArgumentException( "Column does not exist." );
		}

		return $this->data[ $col ];
	}

protected
final
function
get_col_value
(
$
col
)
{
if
(
!
isset
(
$
this
->
data
[
$
col
]
)
)
{
throw
new
\
InvalidArgumentException
(
"Column does not exist."
)
;
}
return
$
this
->
data
[
$
col
]
;
}

Get the value of a column.

@since 1.0

@param string $col

@return mixed


protected function create() {

		foreach ( $this->data as $col => $val ) {

			if ( method_exists( $this, "validate_$col" ) ) {
				$this->data[ $col ] = $this->{"validate_$col"}( $val );
			}
		}

		return Manager::make_simple_query_object( $this->get_table()->get_slug() )
		              ->insert( $this->data );
	}

protected function create ( ) { foreach ( $ this -> data as $ col => $ val ) { if ( method_exists ( $ this , "validate_$col" ) ) { $ this -> data [ $ col ] = $ this -> { "validate_$col" } ( $ val ) ; } } return Manager :: make_simple_query_object ( $ this -> get_table ( ) -> get_slug ( ) ) -> insert ( $ this -> data ) ; }

Create the object.

This will call validate functions on columns if they exist. These should
be used to validate data that is dependent on other data.

@since 1.0

@return int|string PK of new record.
@throws Exception


public function then( $col, $direction = null ) {
		if ( $this->is_rand ) {
			throw new \LogicException( "This ORDER BY statement is already RAND()." );
		}

		$this->orders[ $col ] = $direction;

		return $this;
	}

Add an additional order by clause.

@since 1.0

@param string      $col
@param string|null $direction

@return Order


protected function add_order( $col, $direction ) {
		if ( $direction !== null && ! in_array( $direction, array( self::ASC, self::DESC ) ) ) {
			throw new \InvalidArgumentException( "Invalid ORDER BY direction." );
		}

		$this->orders[ $col ] = $direction;
	}

protected
function
add_order
(
$
col
,
$
direction
)
{
if
(
$
direction
!==
&&
!
in_array
(
$
direction
,
array
(
self
::
ASC
,
self
::
DESC
)
)
)
{
throw
new
\
InvalidArgumentException
(
"Invalid ORDER BY direction."
)
;
}
$
this
->
orders
[
$
col
]
=
$
direction
;
}

Sanitize inputs and save state.

@since 1.0

@param string      $col
@param string|null $direction


public function all( $as = null ) {
		$this->all_columns = true;

		if ( $as ) {
			$this->all_as = $as;
		}

		return $this;
	}

public function all ( $ as = null ) { $ this -> all_columns = true ; if ( $ as ) { $ this -> all_as = $ as ; } return $ this ; }

Convert this select statement to retrieve all columns.

@since 2.0

@param string $as

@return $this

Convert this select statement to retrieve all columns .


protected function build_eager_load_map( $models ) {

		$map = array();

		$foreign = $this->foreign_key;

		foreach ( $models as $model ) {
			$pk = $model->get_raw_attribute( $foreign );

			if ( $pk instanceof Model ) {
				$pk = $pk->get_pk();
			}

			$map[ $pk ][ $model->get_pk() ] = $model;
		}

		return $map;
	}

protected function build_eager_load_map ( $ models ) { $ map = array ( ) ; $ foreign = $ this -> foreign_key ; foreach ( $ models as $ model ) { $ pk = $ model -> get_raw_attribute ( $ foreign ) ; if ( $ pk instanceof Model ) { $ pk = $ pk -> get_pk ( ) ; } $ map [ $ pk ] [ $ model -> get_pk ( ) ] = $ model ; } return $ map ; }

Build an eager load map.

@since 2.0

@param Collection $models

@return array


protected function do_save( $comment ) {

		if ( ! $comment->comment_ID ) {
			$id = wp_insert_comment( wp_slash( $comment->to_array() ) );
		} else {
			if ( wp_update_comment( wp_slash( $comment->to_array() ) ) ) {
				$id = $comment->comment_ID;
			}
		}

		if ( empty( $id ) ) {
			throw new \InvalidArgumentException( 'Error encountered while saving WP_Comment.' );
		}

		return get_comment( $id );
	}

Do the saving for a comment.

@since 2.0

@param \WP_Comment $comment

@return \WP_Comment


protected function get_default_arg( $arg ) {

		$args = $this->get_default_args();

		if ( isset( $args[ $arg ] ) ) {
			return $args[ $arg ];
		} else {
			throw new \InvalidArgumentException();
		}
	}

protected function get_default_arg ( $ arg ) { $ args = $ this -> get_default_args ( ) ; if ( isset ( $ args [ $ arg ] ) ) { return $ args [ $ arg ] ; } else { throw new \ InvalidArgumentException ( ) ; } }

Get a default arg.

@since 1.0

@param string $arg

@return mixed


protected function query() {
		$results = $this->wpdb->get_results( $this->sql );

		// we query for found rows first to prevent instantiation of record objects from interfering with the count
		if ( $this->args['sql_calc_found_rows'] ) {

			$count_results     = $this->wpdb->get_results( "SELECT FOUND_ROWS() AS COUNT" );

			if ( empty( $count_results ) || empty( $count_results[0] ) ) {
				$this->total_items = 0;
			} else {
				$this->total_items = $count_results[0]->COUNT;
			}

		} elseif ( $this->args['return_value'] == 'count' ) {

			if ( empty( $results ) || empty( $results[0] ) ) {
				$this->total_items = 0;
			} else {
				$this->total_items = $results[0]->COUNT;
			}
		}

		$this->results = $this->parse_results( $results );
	}

Query the database and store the results.

@since 1.0

Query the database and store the results .


protected function parse_results( $results ) {

		if ( is_array( $this->args['return_value'] ) ) {
			return $results;
		} elseif ( $this->args['return_value'] == 'count' ) {

			if ( empty( $results ) || empty( $results[0] ) ) {
				return 0;
			}

			return $results[0]->COUNT;
		} elseif ( $this->args['return_value'] != 'object' ) {
			$values = array();
			$field  = $this->args['return_value'];

			foreach ( $results as $result ) {
				$values[] = $result->$field;
			}

			return $values;
		} else {
			$records = array();

			foreach ( $results as $result ) {

				$object = $this->make_object( $result );
				Cache::update( $object );

				$records[ $result->{$this->table->get_primary_key()} ] = $object;
			}

			return $records;
		}
	}

protected function parse_results ( $ results ) { if ( is_array ( $ this -> args [ 'return_value' ] ) ) { return $ results ; } elseif ( $ this -> args [ 'return_value' ] == 'count' ) { if ( empty ( $ results ) || empty ( $ results [ 0 ] ) ) { return 0 ; } return $ results [ 0 ] -> COUNT ; } elseif ( $ this -> args [ 'return_value' ] != 'object' ) { $ values = array ( ) ; $ field = $ this -> args [ 'return_value' ] ; foreach ( $ results as $ result ) { $ values [ ] = $ result -> $ field ; } return $ values ; } else { $ records = array ( ) ; foreach ( $ results as $ result ) { $ object = $ this -> make_object ( $ result ) ; Cache :: update ( $ object ) ; $ records [ $ result -> { $ this -> table -> get_primary_key ( ) } ] = $ object ; } return $ records ; } }

Parse the results returned from the DB.

@since 1.0

@param array $results

@return array

Parse the results returned from the DB .


protected function parse_select( $alias = 'q' ) {

		if ( is_array( $this->args['return_value'] ) ) {
			$select = new Select( null );

			foreach ( $this->args['return_value'] as $column ) {
				$select->also( "$alias.$column" );
			}
		} elseif ( $this->args['return_value'] == 'count' ) {
			$select = new Select( 'COUNT(1)', 'COUNT' );
		} elseif ( $this->args['return_value'] != 'object' ) {
			$select = new Select( "$alias." . $this->args['return_value'] );
		} else {
			$select = new Select( "$alias.*" );
		}

		if ( $this->args['sql_calc_found_rows'] ) {
			$select->calc_found_rows();
		}

		$select->filter_distinct( $this->args['distinct'] );

		return $select;
	}

protected function parse_select ( $ alias = 'q' ) { if ( is_array ( $ this -> args [ 'return_value' ] ) ) { $ select = new Select ( null ) ; foreach ( $ this -> args [ 'return_value' ] as $ column ) { $ select -> also ( "$alias.$column" ) ; } } elseif ( $ this -> args [ 'return_value' ] == 'count' ) { $ select = new Select ( 'COUNT(1)' , 'COUNT' ) ; } elseif ( $ this -> args [ 'return_value' ] != 'object' ) { $ select = new Select ( "$alias." . $ this -> args [ 'return_value' ] ) ; } else { $ select = new Select ( "$alias.*" ) ; } if ( $ this -> args [ 'sql_calc_found_rows' ] ) { $ select -> calc_found_rows ( ) ; } $ select -> filter_distinct ( $ this -> args [ 'distinct' ] ) ; return $ select ; }

Build the select query.

@since 1.0

@param string $alias

@return Select


protected function parse_order( $alias = 'q' ) {

		if ( ! is_array( $this->args['order'] ) && $this->args['order'] === 'rand' ) {
			return new Order( Order::RAND );
		} elseif ( ! is_array( $this->args['order'] ) ) {
			throw new \InvalidArgumentException( "Order must either be 'rand' or an array of columns to directions." );
		}

		$white_list = $this->table->get_columns();

		foreach ( $this->args['order'] as $column => $direction ) {

			$direction = strtoupper( $direction );

			if ( ! in_array( $direction, array( Order::ASC, Order::DESC ) ) ) {
				throw new \InvalidArgumentException( "Invalid order direction $direction for column $column." );
			}

			$column = $this->translate_order_by_to_column_name( $column );

			if ( ! isset( $white_list[ $column ] ) ) {
				throw new \InvalidArgumentException( "Invalid order column $column." );
			}

			$column = "{$alias}.$column";

			if ( ! isset( $order ) ) {
				$order = new Order( $column, $direction );
			} else {
				$order->then( $column, $direction );
			}
		}

		if ( isset( $order ) ) {
			return $order;
		} else {
			return new Order( "{$alias}.{$this->table->get_primary_key()}", Order::ASC );
		}
	}

Parse the orderby query. There is always a default or.

@since 1.0

@param string $alias

@return Order

Parse the orderby query . There is always a default or .


protected function parse_pagination() {

		if ( $this->args['items_per_page'] == - 1 ) {
			return null;
		}

		if ( $this->args['page'] < 1 ) {
			throw new \InvalidArgumentException( "page parameter must be at least 1." );
		}

		$per_page = absint( $this->args['items_per_page'] );
		$page     = absint( $this->args['page'] );

		$count  = $per_page;
		$offset = $per_page * ( $page - 1 );

		return new Limit( $count, $offset );
	}

protected
function
parse_pagination
(
)
{
if
(
$
this
->
args
[
'items_per_page'
]
==
1
)
{
return
;
}
if
(
$
this
->
args
[
'page'
]
<
1
)
{
throw
new
\
InvalidArgumentException
(
"page parameter must be at least 1."
)
;
}
$
per_page
=
absint
(
$
this
->
args
[
'items_per_page'
]
)
;
$
page
=
absint
(
$
this
->
args
[
'page'
]
)
;
$
count
=
$
per_page
;
$
offset
=
$
per_page
*
(
$
page
1
)
;
return
new
Limit
(
$
count
,
$
offset
)
;
}

Parse the pagination query.

@since 1.0

@return Limit|null


protected function numerically_equivalent( $a, $b ) {
		return is_numeric( $a ) && is_numeric( $b ) && strcmp( (string) $a, (string) $b ) === 0;
	}

protected function numerically_equivalent ( $ a , $ b ) { return is_numeric ( $ a ) && is_numeric ( $ b ) && strcmp ( ( string ) $ a , ( string ) $ b ) === 0 ; }

Check if two things are numerically equivalent.

@since 2.0

@param mixed $a
@param mixed $b

@return bool

Check if two things are numerically equivalent .


protected function has_changes( $old, $new ) {

		foreach ( $new as $key => $value ) {
			if ( $value !== $old[ $key ] && ! $this->numerically_equivalent( $value, $old[ $key ] ) ) {
				return true;
			}
		}

		return false;
	}

protected function has_changes ( $ old , $ new ) { foreach ( $ new as $ key => $ value ) { if ( $ value !== $ old [ $ key ] && ! $ this -> numerically_equivalent ( $ value , $ old [ $ key ] ) ) { return true ; } } return false ; }

Check if there are changes.

@since 2.0

@param array $old
@param array $new

@return bool


public function fetch()
    {
        $modelName = $this->_className;

        // execute fetch query
        $query = $this->_dataSource->prepare($this->_queryHelper->buildQuery());
        $query->execute($this->_queryHelper->getWhereParamsValues());

        // check for mysql error
        $errorcode = $query->errorInfo();
        if ($errorcode[0] != "00000") {
            throw new Exception($errorcode[2]);
        }

        // fetch query and hydrate models
        $fetch = $query->fetchAll(\PDO::FETCH_ASSOC);
        foreach ($fetch as &$unRes) {
            /** @var $object \PicORM\Model */
            $object = new $modelName();
            $object->hydrate($unRes, false);
            $unRes = $object;
        }

        // configure collection after fetch
        $this->isFetched = true;
        $this->models    = $fetch;

        // if pagination used grab the total found model
        if ($this->_usePagination) {
            $this->_paginationFoundModels = $this->queryFoundModels();
        }

        return $this;
    }

Execute query and fetch models from database

@return $this
@throws Exception


public function delete()
    {
        $modelClass = $this->_className;

        // cloning fetch query to get where,order by and limit values
        $deleteQuery = clone($this->_queryHelper);

        // transform query to delete
        $deleteQuery->cleanQueryBeforeSwitching()
                    ->delete($modelClass::formatTableNameMySQL());

        // execute query
        $query = $this->_dataSource->prepare($deleteQuery->buildQuery());
        $query->execute($deleteQuery->getWhereParamsValues());

        // check for mysql error
        $errorcode = $query->errorInfo();
        if ($errorcode[0] != "00000") {
            throw new Exception($errorcode[2]);
        }

        return true;
    }

Delete model in collection

@throws Exception
@return bool - true if deleted


public function update(array $setValues)
    {
        $modelClass = $this->_className;

        // cloning fetch query to get where,order by and limit values
        $updateQuery = clone($this->_queryHelper);

        // transform query to update
        $updateQuery->cleanQueryBeforeSwitching()
                    ->update($modelClass::formatTableNameMySQL());

        // build set values
        $params = array();
        foreach ($setValues as $fieldName => $value) {
            $updateQuery->set($fieldName, '?');
            $params[] = $value;
        }

        // merge set values with where values
        $params = array_merge($params, $updateQuery->getWhereParamsValues());

        // execute query
        $query = $this->_dataSource->prepare($updateQuery->buildQuery());
        $query->execute($params);

        // check for mysql error
        $errorcode = $query->errorInfo();
        if ($errorcode[0] != "00000") {
            throw new Exception($errorcode[2]);
        }
    }

Update models in collection with specified values

@param array $setValues - Associative array with model field name as key, and model field value as value

@throws Exception

@return void


public function getTotalPages()
    {
        if ($this->_usePagination === false) {
            return 0;
        }
        if (!$this->isFetched) {
            $this->fetch();
        }

        return (int)ceil($this->_paginationFoundModels / $this->_paginationNbModelByPage);
    }

public function getTotalPages ( ) { if ( $ this -> _usePagination === false ) { return 0 ; } if ( ! $ this -> isFetched ) { $ this -> fetch ( ) ; } return ( int ) ceil ( $ this -> _paginationFoundModels / $ this -> _paginationNbModelByPage ) ; }

Return total page available

@return int


public function paginate($neededNumPage)
    {
        if ($this->_usePagination === false) {
            return $this;
        }

        // build the limit start
        $limitStart = max(0, $neededNumPage - 1) * $this->_paginationNbModelByPage;

        // limit fetch query for page $neededNumPage
        $this->_queryHelper->limit($limitStart, $this->_paginationNbModelByPage);

        return $this;
    }

Paginate collection to match a num page

@param int $neededNumPage - Needed page number

@return Collection


protected function queryFoundModels()
    {
        $countQueryHelper = clone($this->_queryHelper);
        $countQueryHelper->resetSelect("count(*)");
        $countQueryHelper->resetOrderBy();
        $countQueryHelper->resetLimit();
        $query = $this->_dataSource->prepare($countQueryHelper->buildQuery());
        $query->execute($countQueryHelper->getWhereParamsValues());

        return (int)$query->fetch(\PDO::FETCH_COLUMN);
    }

protected function queryFoundModels ( ) { $ countQueryHelper = clone ( $ this -> _queryHelper ) ; $ countQueryHelper -> resetSelect ( "count(*)" ) ; $ countQueryHelper -> resetOrderBy ( ) ; $ countQueryHelper -> resetLimit ( ) ; $ query = $ this -> _dataSource -> prepare ( $ countQueryHelper -> buildQuery ( ) ) ; $ query -> execute ( $ countQueryHelper -> getWhereParamsValues ( ) ) ; return ( int ) $ query -> fetch ( \ PDO :: FETCH_COLUMN ) ; }

Execute query to count number of model in database without limit

@return mixed


public function countModelsWithoutLimit()
    {
        if (!$this->isFetched) {
            $this->fetch();
        }

        // no pagination, we have to manually count number of model
        if (!$this->_usePagination) {
            $this->_paginationFoundModels = $this->queryFoundModels();
        }

        return $this->_paginationFoundModels;
    }

Remove limit from fetch query and count the number
of models in database

@return mixed


public function offsetExists($offset)
    {
        if (!$this->isFetched) {
            $this->fetch();
        }

        return isset($this->models[$offset]);
    }

public function offsetExists ( $ offset ) { if ( ! $ this -> isFetched ) { $ this -> fetch ( ) ; } return isset ( $ this -> models [ $ offset ] ) ; }

Whether a offset exists

@param mixed $offset - Offset name to test

@return bool


public function offsetSet($offset, $value)
    {
        if (!$this->isFetched) {
            $this->fetch();
        }

        if (is_null($offset)) {
            $this->models[] = $value;
        } else {
            $this->models[$offset] = $value;
        }
    }

public function offsetSet ( $ offset , $ value ) { if ( ! $ this -> isFetched ) { $ this -> fetch ( ) ; } if ( is_null ( $ offset ) ) { $ this -> models [ ] = $ value ; } else { $ this -> models [ $ offset ] = $ value ; } }

Offset to set

@param mixed $offset - Offset name to set
@param mixed $value  - Value to set

@return void


public function offsetGet($offset)
    {
        if (!$this->isFetched) {
            $this->fetch();
        }

        return isset($this->models[$offset]) ? $this->models[$offset] : null;
    }

public function offsetGet ( $ offset ) { if ( ! $ this -> isFetched ) { $ this -> fetch ( ) ; } return isset ( $ this -> models [ $ offset ] ) ? $ this -> models [ $ offset ] : null ; }

Offset to retrieve

@param mixed $offset - Offset name to get

@return mixed|null


public function build() {

		if ( empty( $this->parts ) ) {
			return '';
		}

		$query = '';

		foreach ( $this->parts as $part) {

			if ($part instanceof Builder) {
				$query .= "($part)";
			} elseif ( $sql = (string) $part ) {
				$query .= "$part ";
			}
		}

		return $query;
	}

public
function
build
(
)
{
if
(
empty
(
$
this
->
parts
)
)
{
return
''
;
}
$
query
=
''
;
foreach
(
$
this
->
parts
as
$
part
)
{
if
(
$
part
instanceof
Builder
)
{
$
query
.=
"($part)"
;
}
elseif
(
$
sql
=
(
string
)
$
part
)
{
$
query
.=
"$part "
;
}
}
return
$
query
;
}

Build a sql statement.

@since 1.0

@return string


protected static function boot_if_not_booted() {

		if ( isset( static::$_booted[ get_called_class() ] ) ) {
			return;
		}

		static::$_booted[ get_called_class() ] = true;

		$instance = new static();

		$instance->fire_model_event( 'booting' );
		static::boot();
		$instance->fire_model_event( 'booted' );
	}

protected static function boot_if_not_booted ( ) { if ( isset ( static :: $ _booted [ get_called_class ( ) ] ) ) { return ; } static :: $ _booted [ get_called_class ( ) ] = true ; $ instance = new static ( ) ; $ instance -> fire_model_event ( 'booting' ) ; static :: boot ( ) ; $ instance -> fire_model_event ( 'booted' ) ; }

Boot this model if it has not been previously booted.

Fires two model events, 'booting' and 'booted'.

@since 2.0

Boot this model if it has not been previously booted .


protected static function boot_traits() {

		$class = get_called_class();

		// this method will return an empty array on PHP < 5.4
		$uses = class_uses_recursive( get_called_class() );

		foreach ( $uses as $trait ) {
			if ( method_exists( $class, $method = 'boot_' . class_basename( $trait ) ) ) {
				forward_static_call( array( $class, $method ) );
			}
		}
	}

Boot any traits this model uses.

This looks for and executes static methods named 'boot_TraitName'.

@since 2.0


public function fill( array $data = array() ) {

		foreach ( $data as $column => $value ) {

			if ( $this->is_fillable( $column ) ) {
				$this->set_attribute( $column, $value );
			}
		}

		return $this;
	}

public function fill ( array $ data = array ( ) ) { foreach ( $ data as $ column => $ value ) { if ( $ this -> is_fillable ( $ column ) ) { $ this -> set_attribute ( $ column , $ value ) ; } } return $ this ; }

Fill this model's attributes.

Filling a model in this way is used to prevent sensitive properties from being mass-assigned.
For example when creating a new model like so: `MyModel::create( $_POST['model'] )`.

If the model is unguarded, all attributes can be assigned via this method.
Otherwise, the attribute can be filled according to {$_fillable} or {$_guarded}.

@since   2.0

@used-by IronBound\DB\Model::__construct()
@used-by IronBound\DB\Model::create()
@used-by IronBound\DB\Model::init()
@used-by IronBound\DB\Query\FluentQuery::update_or_create()

@param array $data

@return $this


public function with_guarded( $attribute, $callback ) {

		if ( is_array( $attribute ) ) {
			$attributes = $attribute;
		} else {
			$attributes = func_get_args();
			$callback   = array_pop( $attributes );
		}

		$unguarded = static::$_unguarded;

		if ( $unguarded ) {
			static::$_unguarded = false;
		}

		$_fillable = $this->_fillable;
		$_guarded  = $this->_guarded;

		if ( $this->_fillable ) {
			$this->_fillable = array_diff( $this->_fillable, $attributes );

			// If the diff removes all fillable attributes we need to use _guarded.
			if ( ! $this->_fillable ) {
				$this->_guarded = $attributes;
			}
		} else {
			$this->_guarded = array_unique( array_merge( $this->_guarded, $attributes ) );
		}

		$callback( $this );

		$this->_fillable = $_fillable;
		$this->_guarded  = $_guarded;

		static::$_unguarded = $unguarded;
	}

Make a function call with a given attribute guarded.

@since 2.1.0

@param string|array $attribute,...
@param callable     $callback

Make a function call with a given attribute guarded .


public function with_unguarded( $attribute, $callback ) {

		if ( static::$_unguarded ) {
			$callback( $this );

			return;
		}

		if ( is_array( $attribute ) ) {
			$attributes = $attribute;
		} else {
			$attributes = func_get_args();
			$callback   = array_pop( $attributes );
		}

		$_fillable = $this->_fillable;
		$_guarded  = $this->_guarded;

		if ( $this->_fillable ) {
			$this->_fillable = array_merge( $this->_fillable, $attributes );
		} else {
			$this->_guarded = array_diff( $this->_guarded, $attributes );
		}

		$callback( $this );

		$this->_fillable = $_fillable;
		$this->_guarded  = $_guarded;

	}

public function with_unguarded ( $ attribute , $ callback ) { if ( static :: $ _unguarded ) { $ callback ( $ this ) ; return ; } if ( is_array ( $ attribute ) ) { $ attributes = $ attribute ; } else { $ attributes = func_get_args ( ) ; $ callback = array_pop ( $ attributes ) ; } $ _fillable = $ this -> _fillable ; $ _guarded = $ this -> _guarded ; if ( $ this -> _fillable ) { $ this -> _fillable = array_merge ( $ this -> _fillable , $ attributes ) ; } else { $ this -> _guarded = array_diff ( $ this -> _guarded , $ attributes ) ; } $ callback ( $ this ) ; $ this -> _fillable = $ _fillable ; $ this -> _guarded = $ _guarded ; }

Make a function call with a given attribute unguarded.

@since 2.1.0

@param string   $attribute,...
@param callable $callback

Make a function call with a given attribute unguarded .


protected function is_fillable( $column ) {

		if ( static::$_unguarded ) {
			return true;
		}

		if ( empty( $this->_fillable ) ) {
			return ! in_array( $column, $this->_guarded );
		} else {
			return in_array( $column, $this->_fillable );
		}
	}

protected function is_fillable ( $ column ) { if ( static :: $ _unguarded ) { return true ; } if ( empty ( $ this -> _fillable ) ) { return ! in_array ( $ column , $ this -> _guarded ) ; } else { return in_array ( $ column , $ this -> _fillable ) ; } }

Determine if a given attribute is fillable.

@since 2.0

@param string $column

@return bool

Determine if a given attribute is fillable .


public function set_attribute( $attribute, $value ) {

		if ( $this->has_relation( $attribute ) ) {
			if ( is_object( $value ) ) {
				$this->set_relation_value( $attribute, $value );

				return $this;
			}

			unset( $this->_relations[ $attribute ] );
		} elseif ( ! array_key_exists( $attribute, static::table()->get_columns() ) ) {
			throw new \OutOfBoundsException(
				sprintf( "Requested attribute '%s' does not exist for '%s'.", $attribute, get_class( $this ) )
			);
		} elseif ( method_exists( $this, $this->get_mutator_method_for_attribute( $attribute ) ) ) {
			$value = call_user_func( array( $this, $this->get_mutator_method_for_attribute( $attribute ) ), $value );
		}

		$columns = static::table()->get_columns();
		$this->set_raw_attribute( $attribute, $columns[ $attribute ]->prepare_for_storage( $value ) );

		return $this;
	}

Set an attribute's value.

This will call any defined mutators.

@since 2.0

@param string $attribute Attribute name to update.
@param mixed  $value     New value. In most cases this should be working form of the data.
Not the storage version. For example a `WP_Post` object instead of post ID or a
`DateTime` object instead of a 'Y-m-d H:i:s' string.

@return $this

@throws \OutOfBoundsException If the requested attribute does not exist.


public function set_raw_attribute( $attribute, $value ) {

		$attributes = $this->get_raw_attributes();

		$attributes[ $attribute ] = $value;
		$this->set_raw_attributes( $attributes );

		return $this;
	}

public function set_raw_attribute ( $ attribute , $ value ) { $ attributes = $ this -> get_raw_attributes ( ) ; $ attributes [ $ attribute ] = $ value ; $ this -> set_raw_attributes ( $ attributes ) ; return $ this ; }

Set an attribute's value in the attributes storage.

@since   2.0

@used-by IronBound\DB\Model::set_attribute()

@param string $attribute Attribute name to update.
@param mixed  $value     New value.

@return $this

Set an attribute s value in the attributes storage .


public function get_attribute( $attribute ) {

		if ( $this->has_relation( $attribute ) ) {
			return $this->get_relation_value( $attribute );
		}

		if ( array_key_exists( $attribute, static::table()->get_columns() ) ) {
			return $this->get_attribute_value( $attribute );
		}

		throw new \OutOfBoundsException(
			sprintf( "Requested attribute '%s' does not exist for '%s'.", $attribute, get_class( $this ) )
		);
	}

Get an attribute's or relation's value.

If it is an attribute, any accessors will be called.

If it is a relation, its results will be loaded and returned. The relation's results are cached.

@since 2.0

@param string $attribute Attribute name.

@return mixed

@throws \OutOfBoundsException If the given attribute does not exist.

Get an attribute s or relation s value .


public function get_raw_attribute( $attribute ) {

		$attributes = $this->get_raw_attributes();

		return isset( $attributes[ $attribute ] ) ? $attributes[ $attribute ] : null;
	}

public function get_raw_attribute ( $ attribute ) { $ attributes = $ this -> get_raw_attributes ( ) ; return isset ( $ attributes [ $ attribute ] ) ? $ attributes [ $ attribute ] : null ; }

Get an attribute's value from storage.

@since   2.0

@used-by IronBound\DB\Model::get_attribute()

@param string $attribute

@return mixed


protected function get_attribute_value( $attribute ) {

		$value = $this->get_attribute_from_array( $attribute );

		if ( method_exists( $this, $this->get_accessor_method_for_attribute( $attribute ) ) ) {
			$value = call_user_func( array( $this, $this->get_accessor_method_for_attribute( $attribute ) ), $value );
		}

		return $value;
	}

protected function get_attribute_value ( $ attribute ) { $ value = $ this -> get_attribute_from_array ( $ attribute ) ; if ( method_exists ( $ this , $ this -> get_accessor_method_for_attribute ( $ attribute ) ) ) { $ value = call_user_func ( array ( $ this , $ this -> get_accessor_method_for_attribute ( $ attribute ) ) , $ value ) ; } return $ value ; }

Retrieves an attribute's value and calls the accessor method if it exists.

@since 2.0

@param string $attribute

@return mixed

Retrieves an attribute s value and calls the accessor method if it exists .


protected function get_attribute_from_array( $attribute ) {

		$raw     = $this->get_raw_attribute( $attribute );
		$columns = static::table()->get_columns();

		return $columns[ $attribute ]->convert_raw_to_value( $raw );
	}

protected function get_attribute_from_array ( $ attribute ) { $ raw = $ this -> get_raw_attribute ( $ attribute ) ; $ columns = static :: table ( ) -> get_columns ( ) ; return $ columns [ $ attribute ] -> convert_raw_to_value ( $ raw ) ; }

Get an attribute's value from the internal attributes array.

Will preferentially retrieve the value from the attribute value cache,
and update the attribute value cache if the raw value is scalar.

@since 2.0

@param string $attribute

@return mixed|null

Get an attribute s value from the internal attributes array .


public function get_all_relations() {

		if ( isset( static::$_relation_attribute_cache[ get_class( $this ) ] ) ) {
			return static::$_relation_attribute_cache[ get_class( $this ) ];
		}

		$relations = array();
		$methods   = get_class_methods( $this );

		foreach ( $methods as $method ) {
			preg_match( '/^_(\S+)_relation$/', $method, $matches );

			if ( empty( $matches[1] ) ) {
				continue;
			}

			$relations[] = $matches[1];
			$matches     = null;
		}

		static::$_relation_attribute_cache[ get_class( $this ) ] = $relations;

		return $relations;
	}

public function get_all_relations ( ) { if ( isset ( static :: $ _relation_attribute_cache [ get_class ( $ this ) ] ) ) { return static :: $ _relation_attribute_cache [ get_class ( $ this ) ] ; } $ relations = array ( ) ; $ methods = get_class_methods ( $ this ) ; foreach ( $ methods as $ method ) { preg_match ( '/^_(\S+)_relation$/' , $ method , $ matches ) ; if ( empty ( $ matches [ 1 ] ) ) { continue ; } $ relations [ ] = $ matches [ 1 ] ; $ matches = null ; } static :: $ _relation_attribute_cache [ get_class ( $ this ) ] = $ relations ; return $ relations ; }

Get all relations this model has.

Will be cached on subsequent runs.

@since 2.0

@return array


public function get_relation( $attribute ) {

		if ( ! $this->has_relation( $attribute ) ) {
			throw new \OutOfBoundsException(
				sprintf( "Requested relation '%s' does not exist for '%s'.", $attribute, get_class( $this ) )
			);
		}

		$method   = "_{$attribute}_relation";
		$relation = $this->{$method}();

		if ( ! $relation instanceof Relation ) {
			throw new \UnexpectedValueException( 'Relation methods must return an IronBound\DB\Relations\Relation object.' );
		}

		return $relation;
	}

Get a relation's controller object.

@since 2.0

@param string $attribute Relation name.

@return Relation

@throws \OutOfBoundsException If no relation exists by the given name.
@throws \UnexpectedValueException If relation method returns an invalid value.


public function set_relation_value( $attribute, $value ) {

		if ( ! $this->has_relation( $attribute ) ) {
			throw new \OutOfBoundsException(
				sprintf( "Requested relation '%s' does not exist for '%s'.", $attribute, get_class( $this ) )
			);
		}

		$this->_relations[ $attribute ] = $value;

		return $this;
	}

Set the value for a relation.

@since 2.0

@param string $attribute Relation name.
@param mixed  $value     Relation value.

@return $this

@throws \OutOfBoundsException If no relation exists by the given name.


protected function get_relation_value( $attribute ) {

		if ( array_key_exists( $attribute, $this->_relations ) ) {
			return $this->_relations[ $attribute ];
		}

		$relation = $this->get_relation( $attribute );
		$this->set_relation_value( $attribute, $relation->get_results() );

		return $this->get_relation_value( $attribute );
	}

protected function get_relation_value ( $ attribute ) { if ( array_key_exists ( $ attribute , $ this -> _relations ) ) { return $ this -> _relations [ $ attribute ] ; } $ relation = $ this -> get_relation ( $ attribute ) ; $ this -> set_relation_value ( $ attribute , $ relation -> get_results ( ) ) ; return $ this -> get_relation_value ( $ attribute ) ; }

Get a relation's value.

Will load from the cache if possible. Otherwise, will get the relation controller and retrieve and
cache the results.

@since 2.0

@param string $attribute Relation name.

@return Collection|Model|mixed


public function refresh( $destroy_changes = false ) {

		if ( ! $this->exists() ) {
			return;
		}

		$data = (array) static::get_data_from_pk( $this->get_pk() );

		$this->_original = $data;

		if ( $destroy_changes ) {
			$this->set_raw_attributes( $data );
		}
	}

public function refresh ( $ destroy_changes = false ) { if ( ! $ this -> exists ( ) ) { return ; } $ data = ( array ) static :: get_data_from_pk ( $ this -> get_pk ( ) ) ; $ this -> _original = $ data ; if ( $ destroy_changes ) { $ this -> set_raw_attributes ( $ data ) ; } }

Refresh the attributes on this model.

This will fetch the latest data from either the cache or DB.

@param bool $destroy_changes If true, will destroy local changes.

@since 2.0.0


public function get_dirty() {
		$dirty = array();

		foreach ( $this->get_raw_attributes() as $key => $value ) {
			if ( ! array_key_exists( $key, $this->_original ) ) {
				$dirty[ $key ] = $value;
			} elseif ( $value !== $this->_original[ $key ] && ! $this->original_is_numerically_equivalent( $key ) ) {
				$dirty[ $key ] = $value;
			}
		}

		return $dirty;
	}

public function get_dirty ( ) { $ dirty = array ( ) ; foreach ( $ this -> get_raw_attributes ( ) as $ key => $ value ) { if ( ! array_key_exists ( $ key , $ this -> _original ) ) { $ dirty [ $ key ] = $ value ; } elseif ( $ value !== $ this -> _original [ $ key ] && ! $ this -> original_is_numerically_equivalent ( $ key ) ) { $ dirty [ $ key ] = $ value ; } } return $ dirty ; }

Get the attributes that have been changed since the last sync.

@since 2.0

@return array Array of attribute names to changed values.

Get the attributes that have been changed since the last sync .


public static function get( $pk ) {

		if ( ! $pk ) {
			return null;
		}

		if ( ! is_scalar( $pk ) ) {
			throw new \InvalidArgumentException( 'Primary key must be scalar.' );
		}

		$data = static::get_data_from_pk( $pk );

		if ( $data ) {

			$object = new static( new \stdClass() );
			$object->set_raw_attributes( (array) $data, true );
			$object->_exists = true;

			if ( static::$_cache && ! static::is_data_cached( $pk ) ) {
				Cache::update( $object );
			}

			foreach ( static::$_eager_load as $eager_load ) {
				if ( ! $object->is_relation_loaded( $eager_load ) ) {
					$object->get_relation( $eager_load )->eager_load( array( $object ) );
				}
			}

			return $object;
		} else {
			return null;
		}
	}

public
static
function
get
(
$
pk
)
{
if
(
!
$
pk
)
{
return
;
}
if
(
!
is_scalar
(
$
pk
)
)
{
throw
new
\
InvalidArgumentException
(
'Primary key must be scalar.'
)
;
}
$
data
=
static
::
get_data_from_pk
(
$
pk
)
;
if
(
$
data
)
{
$
object
=
new
static
(
new
\
stdClass
(
)
)
;
$
object
->
set_raw_attributes
(
(
array
)
$
data
,
true
)
;
$
object
->
_exists
=
true
;
if
(
static
::
$
_cache
&&
!
static
::
is_data_cached
(
$
pk
)
)
{
Cache
::
update
(
$
object
)
;
}
foreach
(
static
::
$
_eager_load
as
$
eager_load
)
{
if
(
!
$
object
->
is_relation_loaded
(
$
eager_load
)
)
{
$
object
->
get_relation
(
$
eager_load
)
->
eager_load
(
array
(
$
object
)
)
;
}
}
return
$
object
;
}
else
{
return
;
}
}

Retrieve an instance of this model by its primary key.

@since 1.0

@param int|string $pk Primary key of this record.

@returns static|null

@throws \InvalidArgumentException If `$pk` is not scalar.

Retrieve an instance of this model by its primary key .