Implements: Serializable
Object Relational Mapping (ORM) is a method of abstracting database access to standard PHP calls. All table rows are represented as model objects, with object properties representing row data. ORM in Kohana generally follows the Active Record pattern.
Class declared in MODPATH/orm/classes/kohana/orm.php on line 16.
array
$_belongs_tolink to this"Belongs to" relationships
array
$_cast_datalink to thisData to be loaded into the model from a database call cast
array
$_changedlink to thisarray
$_column_cachelink to thisStores column information for ORM models
array(0)
string
$_created_columnlink to thisAuto-update columns for creation
Database
$_dblink to thisDatabase Object
array
$_db_appliedlink to thisDatabase methods applied
Database_Query_Builder_Where
$_db_builderlink to thisDatabase query builder
String
$_db_grouplink to thisDatabase config group
array
$_db_pendinglink to thisDatabase methods pending
bool
$_db_resetlink to thisReset builder
string
$_errors_filenamelink to thisThe message filename used for validation errors. Defaults to ORM::$_object_name
string
$_foreign_key_suffixlink to thisForeign key suffix
array
$_has_manylink to this"Has many" relationships
array
$_has_onelink to this"Has one" relationships
array
$_load_withlink to thisRelationships that should always be joined
bool
$_loadedlink to thisarray
$_objectlink to thisCurrent object
string
$_object_namelink to thisModel name
string
$_object_plurallink to thisPlural model name
array
$_original_valueslink to thisstring
$_primary_keylink to thisTable primary key
mixed
$_primary_key_valuelink to thisPrimary key value
array
$_relatedlink to thisbool
$_reload_on_wakeuplink to thisModel configuration, reload on wakeup?
bool
$_savedlink to thisarray
$_serialize_columnslink to thisAuto-serialize and unserialize columns on get/set
array
$_sortinglink to thisarray
$_table_columnslink to thisTable columns
string
$_table_namelink to thisTable name
bool
$_table_names_plurallink to thisModel configuration, table names plural?
string
$_updated_columnlink to thisAuto-update columns for updates
bool
$_validlink to thisValidation
$_validationlink to thisValidation object created before saving/updating
array
$_with_appliedlink to thisWith calls already applied
Constructs a new model and loads a record if given
mixed
$id
= NULL - Parameter for find or object to loadvoid
public
function
__construct(
$id
= NULL)
{
$this
->_initialize();
if
(
$id
!== NULL)
{
if
(
is_array
(
$id
))
{
foreach
(
$id
as
$column
=>
$value
)
{
// Passing an array of column => values
$this
->where(
$column
,
'='
,
$value
);
}
$this
->find();
}
else
{
// Passing the primary key
$this
->where(
$this
->_object_name.
'.'
.
$this
->_primary_key,
'='
,
$id
)->find();
}
}
elseif
( !
empty
(
$this
->_cast_data))
{
// Load preloaded data from a database call cast
$this
->_load_values(
$this
->_cast_data);
$this
->_cast_data =
array
();
}
}
Handles retrieval of all model values, relationships, and metadata.
string
$column
required - Column namemixed
public
function
__get(
$column
)
{
if
(
array_key_exists
(
$column
,
$this
->_object))
{
return
(in_array(
$column
,
$this
->_serialize_columns))
?
$this
->_unserialize_value(
$this
->_object[
$column
])
:
$this
->_object[
$column
];
}
elseif
(isset(
$this
->_related[
$column
]))
{
// Return related model that has already been fetched
return
$this
->_related[
$column
];
}
elseif
(isset(
$this
->_belongs_to[
$column
]))
{
$model
=
$this
->_related(
$column
);
// Use this model's column and foreign model's primary key
$col
=
$model
->_object_name.
'.'
.
$model
->_primary_key;
$val
=
$this
->_object[
$this
->_belongs_to[
$column
][
'foreign_key'
]];
// Make sure we don't run WHERE "AUTO_INCREMENT column" = NULL queries. This would
// return the last inserted record instead of an empty result.
// See: http://mysql.localhost.net.ar/doc/refman/5.1/en/server-session-variables.html#sysvar_sql_auto_is_null
if
(
$val
!== NULL)
{
$model
->where(
$col
,
'='
,
$val
)->find();
}
return
$this
->_related[
$column
] =
$model
;
}
elseif
(isset(
$this
->_has_one[
$column
]))
{
$model
=
$this
->_related(
$column
);
// Use this model's primary key value and foreign model's column
$col
=
$model
->_object_name.
'.'
.
$this
->_has_one[
$column
][
'foreign_key'
];
$val
=
$this
->pk();
$model
->where(
$col
,
'='
,
$val
)->find();
return
$this
->_related[
$column
] =
$model
;
}
elseif
(isset(
$this
->_has_many[
$column
]))
{
$model
= ORM::factory(
$this
->_has_many[
$column
][
'model'
]);
if
(isset(
$this
->_has_many[
$column
][
'through'
]))
{
// Grab has_many "through" relationship table
$through
=
$this
->_has_many[
$column
][
'through'
];
// Join on through model's target foreign key (far_key) and target model's primary key
$join_col1
=
$through
.
'.'
.
$this
->_has_many[
$column
][
'far_key'
];
$join_col2
=
$model
->_object_name.
'.'
.
$model
->_primary_key;
$model
->join(
$through
)->on(
$join_col1
,
'='
,
$join_col2
);
// Through table's source foreign key (foreign_key) should be this model's primary key
$col
=
$through
.
'.'
.
$this
->_has_many[
$column
][
'foreign_key'
];
$val
=
$this
->pk();
}
else
{
// Simple has_many relationship, search where target model's foreign key is this model's primary key
$col
=
$model
->_object_name.
'.'
.
$this
->_has_many[
$column
][
'foreign_key'
];
$val
=
$this
->pk();
}
return
$model
->where(
$col
,
'='
,
$val
);
}
else
{
throw
new
Kohana_Exception(
'The :property property does not exist in the :class class'
,
array
(
':property'
=>
$column
,
':class'
=> get_class(
$this
)));
}
}
Checks if object data is set.
string
$column
required - Column nameboolean
public
function
__isset(
$column
)
{
return
(isset(
$this
->_object[
$column
]) OR
isset(
$this
->_related[
$column
]) OR
isset(
$this
->_has_one[
$column
]) OR
isset(
$this
->_belongs_to[
$column
]) OR
isset(
$this
->_has_many[
$column
]));
}
Base set method - this should not be overridden.
string
$column
required - Column namemixed
$value
required - Column valuevoid
public
function
__set(
$column
,
$value
)
{
if
( ! isset(
$this
->_object_name))
{
// Object not yet constructed, so we're loading data from a database call cast
$this
->_cast_data[
$column
] =
$value
;
}
else
{
// Set the model's column to given value
$this
->set(
$column
,
$value
);
}
}
Displays the primary key of a model when it is converted to a string.
string
public
function
__toString()
{
return
(string)
$this
->pk();
}
Unsets object data.
string
$column
required - Column namevoid
public
function
__unset(
$column
)
{
unset(
$this
->_object[
$column
],
$this
->_changed[
$column
],
$this
->_related[
$column
]);
}
Adds a new relationship to between this model and another.
// Add the login role using a model instance
$model
->add(
'roles'
, ORM::factory(
'role'
,
array
(
'name'
=>
'login'
)));
// Add the login role if you know the roles.id is 5
$model
->add(
'roles'
, 5);
// Add multiple roles (for example, from checkboxes on a form)
$model
->add(
'roles'
,
array
(1, 2, 3, 4));
string
$alias
required - Alias of the has_many "through" relationshipmixed
$far_keys
required - Related model, primary key, or an array of primary keysORM
public
function
add(
$alias
,
$far_keys
)
{
$far_keys
= (
$far_keys
instanceof
ORM) ?
$far_keys
->pk() :
$far_keys
;
$columns
=
array
(
$this
->_has_many[
$alias
][
'foreign_key'
],
$this
->_has_many[
$alias
][
'far_key'
]);
$foreign_key
=
$this
->pk();
$query
= DB::insert(
$this
->_has_many[
$alias
][
'through'
],
$columns
);
foreach
( (
array
)
$far_keys
as
$key
)
{
$query
->values(
array
(
$foreign_key
,
$key
));
}
$query
->execute(
$this
->_db);
return
$this
;
}
Creates a new "AND HAVING" condition for the query.
mixed
$column
required - Column name or array($column, $alias) or objectstring
$op
required - Logic operatormixed
$value
= NULL - Column value$this
public
function
and_having(
$column
,
$op
,
$value
= NULL)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'and_having'
,
'args'
=>
array
(
$column
,
$op
,
$value
),
);
return
$this
;
}
Closes an open "AND HAVING (...)" grouping.
$this
public
function
and_having_close()
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'and_having_close'
,
'args'
=>
array
(),
);
return
$this
;
}
Opens a new "AND HAVING (...)" grouping.
$this
public
function
and_having_open()
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'and_having_open'
,
'args'
=>
array
(),
);
return
$this
;
}
Creates a new "AND WHERE" condition for the query.
mixed
$column
required - Column name or array($column, $alias) or objectstring
$op
required - Logic operatormixed
$value
required - Column value$this
public
function
and_where(
$column
,
$op
,
$value
)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'and_where'
,
'args'
=>
array
(
$column
,
$op
,
$value
),
);
return
$this
;
}
Closes an open "AND WHERE (...)" grouping.
$this
public
function
and_where_close()
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'and_where_close'
,
'args'
=>
array
(),
);
return
$this
;
}
Opens a new "AND WHERE (...)" grouping.
$this
public
function
and_where_open()
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'and_where_open'
,
'args'
=>
array
(),
);
return
$this
;
}
Returns the values of this object as an array, including any related one-one models that have already been loaded using with()
array
public
function
as_array()
{
$object
=
array
();
foreach
(
$this
->_object
as
$column
=>
$value
)
{
// Call __get for any user processing
$object
[
$column
] =
$this
->__get(
$column
);
}
foreach
(
$this
->_related
as
$column
=>
$model
)
{
// Include any related objects that are already loaded
$object
[
$column
] =
$model
->as_array();
}
return
$object
;
}
public
function
belongs_to()
{
return
$this
->_belongs_to;
}
Enables the query to be cached for a specified amount of time.
integer
$lifetime
= NULL - Number of seconds to cache$this
public
function
cached(
$lifetime
= NULL)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'cached'
,
'args'
=>
array
(
$lifetime
),
);
return
$this
;
}
Check whether the model data has been modified. If $field is specified, checks whether that field was modified.
string
$field
= NULL - Field to check for changesbool
- Whether or not the field has changed
public
function
changed(
$field
= NULL)
{
return
(
$field
=== NULL)
?
$this
->_changed
: Arr::get(
$this
->_changed,
$field
);
}
Validates the current model's data
Validation
$extra_validation
= NULL - Validation objectORM
public
function
check(Validation
$extra_validation
= NULL)
{
// Determine if any external validation failed
$extra_errors
= (
$extra_validation
AND !
$extra_validation
->check());
// Always build a new validation object
$this
->_validation();
$array
=
$this
->_validation;
if
((
$this
->_valid =
$array
->check()) === FALSE OR
$extra_errors
)
{
$exception
=
new
ORM_Validation_Exception(
$this
->errors_filename(),
$array
);
if
(
$extra_errors
)
{
// Merge any possible errors from the external object
$exception
->add_object(
'_external'
,
$extra_validation
);
}
throw
$exception
;
}
return
$this
;
}
Unloads the current object and clears the status.
ORM
public
function
clear()
{
// Create an array with all the columns set to NULL
$values
=
array_combine
(
array_keys
(
$this
->_table_columns),
array_fill
(0,
count
(
$this
->_table_columns), NULL));
// Replace the object and reset the object status
$this
->_object =
$this
->_changed =
$this
->_related =
$this
->_original_values =
array
();
// Replace the current object with an empty one
$this
->_load_values(
$values
);
// Reset primary key
$this
->_primary_key_value = NULL;
// Reset the loaded state
$this
->_loaded = FALSE;
$this
->reset();
return
$this
;
}
Count the number of records in the table.
integer
public
function
count_all()
{
$selects
=
array
();
foreach
(
$this
->_db_pending
as
$key
=>
$method
)
{
if
(
$method
[
'name'
] ==
'select'
)
{
// Ignore any selected columns for now
$selects
[] =
$method
;
unset(
$this
->_db_pending[
$key
]);
}
}
if
( !
empty
(
$this
->_load_with))
{
foreach
(
$this
->_load_with
as
$alias
)
{
// Bind relationship
$this
->with(
$alias
);
}
}
$this
->_build(Database::SELECT);
$records
=
$this
->_db_builder->from(
array
(
$this
->_table_name,
$this
->_object_name))
->select(
array
(
'COUNT("*")'
,
'records_found'
))
->execute(
$this
->_db)
->get(
'records_found'
);
// Add back in selected columns
$this
->_db_pending +=
$selects
;
$this
->reset();
// Return the total number of records in a table
return
$records
;
}
Insert a new object to the database
Validation
$validation
= NULL - Validation objectORM
public
function
create(Validation
$validation
= NULL)
{
if
(
$this
->_loaded)
throw
new
Kohana_Exception(
'Cannot create :model model because it is already loaded.'
,
array
(
':model'
=>
$this
->_object_name));
// Require model validation before saving
if
( !
$this
->_valid OR
$validation
)
{
$this
->check(
$validation
);
}
$data
=
array
();
foreach
(
$this
->_changed
as
$column
)
{
// Generate list of column => values
$data
[
$column
] =
$this
->_object[
$column
];
}
if
(
is_array
(
$this
->_created_column))
{
// Fill the created column
$column
=
$this
->_created_column[
'column'
];
$format
=
$this
->_created_column[
'format'
];
$data
[
$column
] =
$this
->_object[
$column
] = (
$format
=== TRUE) ? time() :
date
(
$format
);
}
$result
= DB::insert(
$this
->_table_name)
->columns(
array_keys
(
$data
))
->values(
array_values
(
$data
))
->execute(
$this
->_db);
if
( !
array_key_exists
(
$this
->_primary_key,
$data
))
{
// Load the insert id as the primary key if it was left out
$this
->_object[
$this
->_primary_key] =
$this
->_primary_key_value =
$result
[0];
}
else
{
$this
->_primary_key_value =
$this
->_object[
$this
->_primary_key];
}
// Object is now loaded and saved
$this
->_loaded =
$this
->_saved = TRUE;
// All changes have been saved
$this
->_changed =
array
();
$this
->_original_values =
$this
->_object;
return
$this
;
}
public
function
created_column()
{
return
$this
->_created_column;
}
Deletes a single record while ignoring relationships.
ORM
public
function
delete
()
{
if
( !
$this
->_loaded)
throw
new
Kohana_Exception(
'Cannot delete :model model because it is not loaded.'
,
array
(
':model'
=>
$this
->_object_name));
// Use primary key value
$id
=
$this
->pk();
// Delete the object
DB::
delete
(
$this
->_table_name)
->where(
$this
->_primary_key,
'='
,
$id
)
->execute(
$this
->_db);
return
$this
->clear();
}
Enables or disables selecting only unique columns using "SELECT DISTINCT"
boolean
$value
required - Enable or disable distinct columns$this
public
function
distinct(
$value
)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'distinct'
,
'args'
=>
array
(
$value
),
);
return
$this
;
}
public
function
errors_filename()
{
return
$this
->_errors_filename;
}
Creates and returns a new model.
string
$model
required - Model namemixed
$id
= NULL - Parameter for find()ORM
public
static
function
factory(
$model
,
$id
= NULL)
{
// Set class name
$model
=
'Model_'
.ucfirst(
$model
);
return
new
$model
(
$id
);
}
Filter definitions for validation
array
public
function
filters()
{
return
array
();
}
Finds and loads a single database row into the object.
ORM
public
function
find()
{
if
(
$this
->_loaded)
throw
new
Kohana_Exception(
'Method find() cannot be called on loaded objects'
);
if
( !
empty
(
$this
->_load_with))
{
foreach
(
$this
->_load_with
as
$alias
)
{
// Bind auto relationships
$this
->with(
$alias
);
}
}
$this
->_build(Database::SELECT);
return
$this
->_load_result(FALSE);
}
Finds multiple database rows and returns an iterator of the rows found.
Database_Result
public
function
find_all()
{
if
(
$this
->_loaded)
throw
new
Kohana_Exception(
'Method find_all() cannot be called on loaded objects'
);
if
( !
empty
(
$this
->_load_with))
{
foreach
(
$this
->_load_with
as
$alias
)
{
// Bind auto relationships
$this
->with(
$alias
);
}
}
$this
->_build(Database::SELECT);
return
$this
->_load_result(TRUE);
}
Choose the tables to select "FROM ..."
mixed
$tables
required - Table name or array($table, $alias) or object$this
public
function
from(
$tables
)
{
$tables
= func_get_args();
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'from'
,
'args'
=>
$tables
,
);
return
$this
;
}
Creates a "GROUP BY ..." filter.
mixed
$columns
required - Column name or array($column, $alias) or object$this
public
function
group_by(
$columns
)
{
$columns
= func_get_args();
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'group_by'
,
'args'
=>
$columns
,
);
return
$this
;
}
Tests if this object has a relationship to a different model, or an array of different models.
// Check if $model has the login role
$model
->has(
'roles'
, ORM::factory(
'role'
,
array
(
'name'
=>
'login'
)));
// Check for the login role if you know the roles.id is 5
$model
->has(
'roles'
, 5);
// Check for all of the following roles
$model
->has(
'roles'
,
array
(1, 2, 3, 4));
// Check if $model has any roles
$model
->has(
'roles'
)
string
$alias
required - Alias of the has_many "through" relationshipmixed
$far_keys
= NULL - Related model, primary key, or an array of primary keysDatabase_Result
public
function
has(
$alias
,
$far_keys
= NULL)
{
if
(
$far_keys
=== NULL)
{
return
(bool) DB::select(
array
(
'COUNT("*")'
,
'records_found'
))
->from(
$this
->_has_many[
$alias
][
'through'
])
->where(
$this
->_has_many[
$alias
][
'foreign_key'
],
'='
,
$this
->pk())
->execute(
$this
->_db)->get(
'records_found'
);
}
$far_keys
= (
$far_keys
instanceof
ORM) ?
$far_keys
->pk() :
$far_keys
;
// We need an array to simplify the logic
$far_keys
= (
array
)
$far_keys
;
// Nothing to check if the model isn't loaded or we don't have any far_keys
if
( !
$far_keys
OR !
$this
->_loaded)
return
FALSE;
$count
= (int) DB::select(
array
(
'COUNT("*")'
,
'records_found'
))
->from(
$this
->_has_many[
$alias
][
'through'
])
->where(
$this
->_has_many[
$alias
][
'foreign_key'
],
'='
,
$this
->pk())
->where(
$this
->_has_many[
$alias
][
'far_key'
],
'IN'
,
$far_keys
)
->execute(
$this
->_db)->get(
'records_found'
);
// Rows found need to match the rows searched
return
$count
===
count
(
$far_keys
);
}
public
function
has_many()
{
return
$this
->_has_many;
}
public
function
has_one()
{
return
$this
->_has_one;
}
Alias of and_having()
mixed
$column
required - Column name or array($column, $alias) or objectstring
$op
required - Logic operatormixed
$value
= NULL - Column value$this
public
function
having(
$column
,
$op
,
$value
= NULL)
{
return
$this
->and_having(
$column
,
$op
,
$value
);
}
Closes an open "AND HAVING (...)" grouping.
$this
public
function
having_close()
{
return
$this
->and_having_close();
}
Alias of and_having_open()
$this
public
function
having_open()
{
return
$this
->and_having_open();
}
Adds addition tables to "JOIN ...".
mixed
$table
required - Column name or array($column, $alias) or objectstring
$type
= NULL - Join type (LEFT, RIGHT, INNER, etc)$this
public
function
join(
$table
,
$type
= NULL)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'join'
,
'args'
=>
array
(
$table
,
$type
),
);
return
$this
;
}
Label definitions for validation
array
public
function
labels()
{
return
array
();
}
Returns last executed query
string
public
function
last_query()
{
return
$this
->_db->last_query;
}
Return up to "LIMIT ..." results
integer
$number
required - Maximum results to return$this
public
function
limit(
$number
)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'limit'
,
'args'
=>
array
(
$number
),
);
return
$this
;
}
Proxy method to Database list_columns.
array
public
function
list_columns()
{
// Proxy to database
return
$this
->_db->list_columns(
$this
->_table_name);
}
public
function
load_with()
{
return
$this
->_load_with;
}
public
function
loaded()
{
return
$this
->_loaded;
}
public
function
object()
{
return
$this
->_object;
}
public
function
object_name()
{
return
$this
->_object_name;
}
public
function
object_plural()
{
return
$this
->_object_plural;
}
Start returning results after "OFFSET ..."
integer
$number
required - Starting result number$this
public
function
offset(
$number
)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'offset'
,
'args'
=>
array
(
$number
),
);
return
$this
;
}
Adds "ON ..." conditions for the last created JOIN statement.
mixed
$c1
required - $ci column name or array($column, $alias) or objectstring
$op
required - Logic operatormixed
$c2
required - Column name or array($column, $alias) or object$this
public
function
on(
$c1
,
$op
,
$c2
)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'on'
,
'args'
=>
array
(
$c1
,
$op
,
$c2
),
);
return
$this
;
}
Creates a new "OR HAVING" condition for the query.
mixed
$column
required - Column name or array($column, $alias) or objectstring
$op
required - Logic operatormixed
$value
= NULL - Column value$this
public
function
or_having(
$column
,
$op
,
$value
= NULL)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'or_having'
,
'args'
=>
array
(
$column
,
$op
,
$value
),
);
return
$this
;
}
Closes an open "OR HAVING (...)" grouping.
$this
public
function
or_having_close()
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'or_having_close'
,
'args'
=>
array
(),
);
return
$this
;
}
Opens a new "OR HAVING (...)" grouping.
$this
public
function
or_having_open()
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'or_having_open'
,
'args'
=>
array
(),
);
return
$this
;
}
Creates a new "OR WHERE" condition for the query.
mixed
$column
required - Column name or array($column, $alias) or objectstring
$op
required - Logic operatormixed
$value
required - Column value$this
public
function
or_where(
$column
,
$op
,
$value
)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'or_where'
,
'args'
=>
array
(
$column
,
$op
,
$value
),
);
return
$this
;
}
Closes an open "OR WHERE (...)" grouping.
$this
public
function
or_where_close()
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'or_where_close'
,
'args'
=>
array
(),
);
return
$this
;
}
Opens a new "OR WHERE (...)" grouping.
$this
public
function
or_where_open()
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'or_where_open'
,
'args'
=>
array
(),
);
return
$this
;
}
Applies sorting with "ORDER BY ..."
mixed
$column
required - Column name or array($column, $alias) or objectstring
$direction
= NULL - Direction of sorting$this
public
function
order_by(
$column
,
$direction
= NULL)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'order_by'
,
'args'
=>
array
(
$column
,
$direction
),
);
return
$this
;
}
public
function
original_values()
{
return
$this
->_original_values;
}
Set the value of a parameter in the query.
string
$param
required - Parameter key to replacemixed
$value
required - Value to use$this
public
function
param(
$param
,
$value
)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'param'
,
'args'
=>
array
(
$param
,
$value
),
);
return
$this
;
}
Returns the value of the primary key
mixed
- Primary key
public
function
pk()
{
return
$this
->_primary_key_value;
}
public
function
primary_key()
{
return
$this
->_primary_key;
}
Reloads the current object from the database.
ORM
public
function
reload()
{
$primary_key
=
$this
->pk();
// Replace the object and reset the object status
$this
->_object =
$this
->_changed =
$this
->_related =
$this
->_original_values =
array
();
// Only reload the object if we have one to reload
if
(
$this
->_loaded)
return
$this
->clear()
->where(
$this
->_object_name.
'.'
.
$this
->_primary_key,
'='
,
$primary_key
)
->find();
else
return
$this
->clear();
}
Reload column definitions.
boolean
$force
= bool FALSE - Force reloadingORM
public
function
reload_columns(
$force
= FALSE)
{
if
(
$force
=== TRUE OR
empty
(
$this
->_table_columns))
{
if
(isset(ORM::
$_column_cache
[
$this
->_object_name]))
{
// Use cached column information
$this
->_table_columns = ORM::
$_column_cache
[
$this
->_object_name];
}
else
{
// Grab column information from database
$this
->_table_columns =
$this
->list_columns();
// Load column cache
ORM::
$_column_cache
[
$this
->_object_name] =
$this
->_table_columns;
}
}
return
$this
;
}
Removes a relationship between this model and another.
// Remove a role using a model instance
$model
->remove(
'roles'
, ORM::factory(
'role'
,
array
(
'name'
=>
'login'
)));
// Remove the role knowing the primary key
$model
->remove(
'roles'
, 5);
// Remove multiple roles (for example, from checkboxes on a form)
$model
->remove(
'roles'
,
array
(1, 2, 3, 4));
// Remove all related roles
$model
->remove(
'roles'
);
string
$alias
required - Alias of the has_many "through" relationshipmixed
$far_keys
= NULL - Related model, primary key, or an array of primary keysORM
public
function
remove(
$alias
,
$far_keys
= NULL)
{
$far_keys
= (
$far_keys
instanceof
ORM) ?
$far_keys
->pk() :
$far_keys
;
$query
= DB::
delete
(
$this
->_has_many[
$alias
][
'through'
])
->where(
$this
->_has_many[
$alias
][
'foreign_key'
],
'='
,
$this
->pk());
if
(
$far_keys
!== NULL)
{
// Remove all the relationships in the array
$query
->where(
$this
->_has_many[
$alias
][
'far_key'
],
'IN'
, (
array
)
$far_keys
);
}
$query
->execute(
$this
->_db);
return
$this
;
}
Clears query builder. Passing FALSE is useful to keep the existing query conditions for another query.
bool
$next
= bool TRUE - Pass FALSE to avoid resetting on the next callORM
public
function
reset(
$next
= TRUE)
{
if
(
$next
AND
$this
->_db_reset)
{
$this
->_db_pending =
array
();
$this
->_db_applied =
array
();
$this
->_db_builder = NULL;
$this
->_with_applied =
array
();
}
// Reset on the next call?
$this
->_db_reset =
$next
;
return
$this
;
}
Rule definitions for validation
array
public
function
rules()
{
return
array
();
}
Updates or Creates the record depending on loaded()
Validation
$validation
= NULL - Validation objectORM
public
function
save(Validation
$validation
= NULL)
{
return
$this
->loaded() ?
$this
->update(
$validation
) :
$this
->create(
$validation
);
}
public
function
saved()
{
return
$this
->_saved;
}
Choose the columns to select from.
mixed
$columns
= NULL - Column name or array($column, $alias) or object$this
public
function
select(
$columns
= NULL)
{
$columns
= func_get_args();
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'select'
,
'args'
=>
$columns
,
);
return
$this
;
}
Allows serialization of only the object data and state, to prevent "stale" objects being unserialized, which also requires less memory.
string
public
function
serialize()
{
// Store only information about the object
foreach
(
array
(
'_primary_key_value'
,
'_object'
,
'_changed'
,
'_loaded'
,
'_saved'
,
'_sorting'
,
'_original_values'
)
as
$var
)
{
$data
[
$var
] =
$this
->{
$var
};
}
return
serialize(
$data
);
}
Handles setting of column
string
$column
required - Column namemixed
$value
required - Column valuevoid
public
function
set(
$column
,
$value
)
{
if
(in_array(
$column
,
$this
->_serialize_columns))
{
$value
=
$this
->_serialize_value(
$value
);
}
if
(
array_key_exists
(
$column
,
$this
->_object))
{
// Filter the data
$value
=
$this
->run_filter(
$column
,
$value
);
// See if the data really changed
if
(
$value
!==
$this
->_object[
$column
])
{
$this
->_object[
$column
] =
$value
;
// Data has changed
$this
->_changed[
$column
] =
$column
;
// Object is no longer saved or valid
$this
->_saved =
$this
->_valid = FALSE;
}
}
elseif
(isset(
$this
->_belongs_to[
$column
]))
{
// Update related object itself
$this
->_related[
$column
] =
$value
;
// Update the foreign key of this model
$this
->_object[
$this
->_belongs_to[
$column
][
'foreign_key'
]] = (
$value
instanceof
ORM)
?
$value
->pk()
: NULL;
$this
->_changed[
$column
] =
$this
->_belongs_to[
$column
][
'foreign_key'
];
}
else
{
throw
new
Kohana_Exception(
'The :property: property does not exist in the :class: class'
,
array
(
':property:'
=>
$column
,
':class:'
=> get_class(
$this
)));
}
return
$this
;
}
public
function
table_columns()
{
return
$this
->_table_columns;
}
public
function
table_name()
{
return
$this
->_table_name;
}
Checks whether a column value is unique. Excludes itself if loaded.
string
$field
required - The field to check for uniquenessmixed
$value
required - The value to check for uniquenessbool
- Whteher the value is unique
public
function
unique(
$field
,
$value
)
{
$model
= ORM::factory(
$this
->object_name())
->where(
$field
,
'='
,
$value
)
->find();
if
(
$this
->loaded())
{
return
( ! (
$model
->loaded() AND
$model
->pk() !=
$this
->pk()));
}
return
( !
$model
->loaded());
}
Prepares the database connection and reloads the object.
string
$data
required - String for unserializationvoid
public
function
unserialize(
$data
)
{
// Initialize model
$this
->_initialize();
foreach
(unserialize(
$data
)
as
$name
=>
$var
)
{
$this
->{
$name
} =
$var
;
}
if
(
$this
->_reload_on_wakeup === TRUE)
{
// Reload the object
$this
->reload();
}
}
Updates a single record or multiple records
Validation
$validation
= NULL - Validation objectORM
public
function
update(Validation
$validation
= NULL)
{
if
( !
$this
->_loaded)
throw
new
Kohana_Exception(
'Cannot update :model model because it is not loaded.'
,
array
(
':model'
=>
$this
->_object_name));
// Run validation if the model isn't valid or we have additional validation rules.
if
( !
$this
->_valid OR
$validation
)
{
$this
->check(
$validation
);
}
if
(
empty
(
$this
->_changed))
{
// Nothing to update
return
$this
;
}
$data
=
array
();
foreach
(
$this
->_changed
as
$column
)
{
// Compile changed data
$data
[
$column
] =
$this
->_object[
$column
];
}
if
(
is_array
(
$this
->_updated_column))
{
// Fill the updated column
$column
=
$this
->_updated_column[
'column'
];
$format
=
$this
->_updated_column[
'format'
];
$data
[
$column
] =
$this
->_object[
$column
] = (
$format
=== TRUE) ? time() :
date
(
$format
);
}
// Use primary key value
$id
=
$this
->pk();
// Update a single record
DB::update(
$this
->_table_name)
->set(
$data
)
->where(
$this
->_primary_key,
'='
,
$id
)
->execute(
$this
->_db);
if
(isset(
$data
[
$this
->_primary_key]))
{
// Primary key was changed, reflect it
$this
->_primary_key_value =
$data
[
$this
->_primary_key];
}
// Object has been saved
$this
->_saved = TRUE;
// All changes have been saved
$this
->_changed =
array
();
$this
->_original_values =
$this
->_object;
return
$this
;
}
public
function
updated_column()
{
return
$this
->_updated_column;
}
public
function
validation()
{
if
( ! isset(
$this
->_validation))
{
// Initialize the validation object
$this
->_validation();
}
return
$this
->_validation;
}
Set values from an array with support for one-one relationships. This method should be used for loading in post data, etc.
array
$values
required - Array of column => valarray
$expected
= NULL - Array of keys to take from $valuesORM
public
function
values(
array
$values
,
array
$expected
= NULL)
{
// Default to expecting everything except the primary key
if
(
$expected
=== NULL)
{
$expected
=
array_keys
(
$this
->_table_columns);
// Don't set the primary key by default
unset(
$values
[
$this
->_primary_key]);
}
foreach
(
$expected
as
$key
=>
$column
)
{
if
(
is_string
(
$key
))
{
// isset() fails when the value is NULL (we want it to pass)
if
( !
array_key_exists
(
$key
,
$values
))
continue
;
// Try to set values to a related model
$this
->{
$key
}->values(
$values
[
$key
],
$column
);
}
else
{
// isset() fails when the value is NULL (we want it to pass)
if
( !
array_key_exists
(
$column
,
$values
))
continue
;
// Update the column, respects __set()
$this
->
$column
=
$values
[
$column
];
}
}
return
$this
;
}
Alias of and_where()
mixed
$column
required - Column name or array($column, $alias) or objectstring
$op
required - Logic operatormixed
$value
required - Column value$this
public
function
where(
$column
,
$op
,
$value
)
{
// Add pending database call which is executed after query type is determined
$this
->_db_pending[] =
array
(
'name'
=>
'where'
,
'args'
=>
array
(
$column
,
$op
,
$value
),
);
return
$this
;
}
Closes an open "AND WHERE (...)" grouping.
$this
public
function
where_close()
{
return
$this
->and_where_close();
}
Alias of and_where_open()
$this
public
function
where_open()
{
return
$this
->and_where_open();
}
Binds another one-to-one object to this model. One-to-one objects can be nested using 'object1:object2' syntax
string
$target_path
required - Target model to bind tovoid
public
function
with(
$target_path
)
{
if
(isset(
$this
->_with_applied[
$target_path
]))
{
// Don't join anything already joined
return
$this
;
}
// Split object parts
$aliases
=
explode
(
':'
,
$target_path
);
$target
=
$this
;
foreach
(
$aliases
as
$alias
)
{
// Go down the line of objects to find the given target
$parent
=
$target
;
$target
=
$parent
->_related(
$alias
);
if
( !
$target
)
{
// Can't find related object
return
$this
;
}
}
// Target alias is at the end
$target_alias
=
$alias
;
// Pop-off top alias to get the parent path (user:photo:tag becomes user:photo - the parent table prefix)
array_pop
(
$aliases
);
$parent_path
= implode(
':'
,
$aliases
);
if
(
empty
(
$parent_path
))
{
// Use this table name itself for the parent path
$parent_path
=
$this
->_object_name;
}
else
{
if
( ! isset(
$this
->_with_applied[
$parent_path
]))
{
// If the parent path hasn't been joined yet, do it first (otherwise LEFT JOINs fail)
$this
->with(
$parent_path
);
}
}
// Add to with_applied to prevent duplicate joins
$this
->_with_applied[
$target_path
] = TRUE;
// Use the keys of the empty object to determine the columns
foreach
(
array_keys
(
$target
->_object)
as
$column
)
{
$name
=
$target_path
.
'.'
.
$column
;
$alias
=
$target_path
.
':'
.
$column
;
// Add the prefix so that load_result can determine the relationship
$this
->select(
array
(
$name
,
$alias
));
}
if
(isset(
$parent
->_belongs_to[
$target_alias
]))
{
// Parent belongs_to target, use target's primary key and parent's foreign key
$join_col1
=
$target_path
.
'.'
.
$target
->_primary_key;
$join_col2
=
$parent_path
.
'.'
.
$parent
->_belongs_to[
$target_alias
][
'foreign_key'
];
}
else
{
// Parent has_one target, use parent's primary key as target's foreign key
$join_col1
=
$parent_path
.
'.'
.
$parent
->_primary_key;
$join_col2
=
$target_path
.
'.'
.
$parent
->_has_one[
$target_alias
][
'foreign_key'
];
}
// Join the related object into the result
$this
->join(
array
(
$target
->_table_name,
$target_path
),
'LEFT'
)->on(
$join_col1
,
'='
,
$join_col2
);
return
$this
;
}
Initializes the Database Builder to given query type
integer
$type
required - Type of Database queryORM
protected
function
_build(
$type
)
{
// Construct new builder object based on query type
switch
(
$type
)
{
case
Database::SELECT:
$this
->_db_builder = DB::select();
break
;
case
Database::UPDATE:
$this
->_db_builder = DB::update(
array
(
$this
->_table_name,
$this
->_object_name));
break
;
case
Database::
DELETE
:
// Cannot use an alias for DELETE queries
$this
->_db_builder = DB::
delete
(
$this
->_table_name);
}
// Process pending database method calls
foreach
(
$this
->_db_pending
as
$method
)
{
$name
=
$method
[
'name'
];
$args
=
$method
[
'args'
];
$this
->_db_applied[
$name
] =
$name
;
call_user_func_array(
array
(
$this
->_db_builder,
$name
),
$args
);
}
return
$this
;
}
Returns an array of columns to include in the select query. This method can be overridden to change the default select behavior.
array
- Columns to select
protected
function
_build_select()
{
$columns
=
array
();
foreach
(
$this
->_table_columns
as
$column
=>
$_
)
{
$columns
[] =
array
(
$this
->_object_name.
'.'
.
$column
,
$column
);
}
return
$columns
;
}
Prepares the model database connection, determines the table name, and loads column information.
void
protected
function
_initialize()
{
// Set the object name and plural name
$this
->_object_name =
strtolower
(
substr
(get_class(
$this
), 6));
$this
->_object_plural = Inflector::plural(
$this
->_object_name);
if
( !
$this
->_errors_filename)
{
$this
->_errors_filename =
$this
->_object_name;
}
if
( !
is_object
(
$this
->_db))
{
// Get database instance
$this
->_db = Database::instance(
$this
->_db_group);
}
if
(
empty
(
$this
->_table_name))
{
// Table name is the same as the object name
$this
->_table_name =
$this
->_object_name;
if
(
$this
->_table_names_plural === TRUE)
{
// Make the table name plural
$this
->_table_name = Inflector::plural(
$this
->_table_name);
}
}
foreach
(
$this
->_belongs_to
as
$alias
=>
$details
)
{
$defaults
[
'model'
] =
$alias
;
$defaults
[
'foreign_key'
] =
$alias
.
$this
->_foreign_key_suffix;
$this
->_belongs_to[
$alias
] =
array_merge
(
$defaults
,
$details
);
}
foreach
(
$this
->_has_one
as
$alias
=>
$details
)
{
$defaults
[
'model'
] =
$alias
;
$defaults
[
'foreign_key'
] =
$this
->_object_name.
$this
->_foreign_key_suffix;
$this
->_has_one[
$alias
] =
array_merge
(
$defaults
,
$details
);
}
foreach
(
$this
->_has_many
as
$alias
=>
$details
)
{
$defaults
[
'model'
] = Inflector::singular(
$alias
);
$defaults
[
'foreign_key'
] =
$this
->_object_name.
$this
->_foreign_key_suffix;
$defaults
[
'through'
] = NULL;
$defaults
[
'far_key'
] = Inflector::singular(
$alias
).
$this
->_foreign_key_suffix;
$this
->_has_many[
$alias
] =
array_merge
(
$defaults
,
$details
);
}
// Load column information
$this
->reload_columns();
// Clear initial model state
$this
->clear();
}
Loads a database result, either as a new record for this model, or as an iterator for multiple rows.
bool
$multiple
= bool FALSE - Return an iterator or load a single rowORM|Database_Result
protected
function
_load_result(
$multiple
= FALSE)
{
$this
->_db_builder->from(
array
(
$this
->_table_name,
$this
->_object_name));
if
(
$multiple
=== FALSE)
{
// Only fetch 1 record
$this
->_db_builder->limit(1);
}
// Select all columns by default
$this
->_db_builder->select_array(
$this
->_build_select());
if
( ! isset(
$this
->_db_applied[
'order_by'
]) AND !
empty
(
$this
->_sorting))
{
foreach
(
$this
->_sorting
as
$column
=>
$direction
)
{
if
(
strpos
(
$column
,
'.'
) === FALSE)
{
// Sorting column for use in JOINs
$column
=
$this
->_object_name.
'.'
.
$column
;
}
$this
->_db_builder->order_by(
$column
,
$direction
);
}
}
if
(
$multiple
=== TRUE)
{
// Return database iterator casting to this object type
$result
=
$this
->_db_builder->as_object(get_class(
$this
))->execute(
$this
->_db);
$this
->reset();
return
$result
;
}
else
{
// Load the result as an associative array
$result
=
$this
->_db_builder->as_assoc()->execute(
$this
->_db);
$this
->reset();
if
(
$result
->
count
() === 1)
{
// Load object values
$this
->_load_values(
$result
->current());
}
else
{
// Clear the object, nothing was found
$this
->clear();
}
return
$this
;
}
}
Loads an array of values into into the current object.
array
$values
required - Values to loadORM
protected
function
_load_values(
array
$values
)
{
if
(
array_key_exists
(
$this
->_primary_key,
$values
))
{
if
(
$values
[
$this
->_primary_key] !== NULL)
{
// Flag as loaded and valid
$this
->_loaded =
$this
->_valid = TRUE;
// Store primary key
$this
->_primary_key_value =
$values
[
$this
->_primary_key];
}
else
{
// Not loaded or valid
$this
->_loaded =
$this
->_valid = FALSE;
}
}
// Related objects
$related
=
array
();
foreach
(
$values
as
$column
=>
$value
)
{
if
(
strpos
(
$column
,
':'
) === FALSE)
{
// Load the value to this model
$this
->_object[
$column
] =
$value
;
}
else
{
// Column belongs to a related model
list (
$prefix
,
$column
) =
explode
(
':'
,
$column
, 2);
$related
[
$prefix
][
$column
] =
$value
;
}
}
if
( !
empty
(
$related
))
{
foreach
(
$related
as
$object
=>
$values
)
{
// Load the related objects with the values in the result
$this
->_related(
$object
)->_load_values(
$values
);
}
}
if
(
$this
->_loaded)
{
// Store the object in its original state
$this
->_original_values =
$this
->_object;
}
return
$this
;
}
Returns an ORM model for the given one-one related alias
string
$alias
required - Alias nameORM
protected
function
_related(
$alias
)
{
if
(isset(
$this
->_related[
$alias
]))
{
return
$this
->_related[
$alias
];
}
elseif
(isset(
$this
->_has_one[
$alias
]))
{
return
$this
->_related[
$alias
] = ORM::factory(
$this
->_has_one[
$alias
][
'model'
]);
}
elseif
(isset(
$this
->_belongs_to[
$alias
]))
{
return
$this
->_related[
$alias
] = ORM::factory(
$this
->_belongs_to[
$alias
][
'model'
]);
}
else
{
return
FALSE;
}
}
protected
function
_serialize_value(
$value
)
{
return
json_encode(
$value
);
}
protected
function
_unserialize_value(
$value
)
{
return
json_decode(
$value
);
}
Initializes validation rules, and labels
void
protected
function
_validation()
{
// Build the validation object with its rules
$this
->_validation = Validation::factory(
$this
->_object)
->bind(
':model'
,
$this
)
->bind(
':original_values'
,
$this
->_original_values)
->bind(
':changed'
,
$this
->_changed);
foreach
(
$this
->rules()
as
$field
=>
$rules
)
{
$this
->_validation->rules(
$field
,
$rules
);
}
// Use column names by default for labels
$columns
=
array_keys
(
$this
->_table_columns);
// Merge user-defined labels
$labels
=
array_merge
(
array_combine
(
$columns
,
$columns
),
$this
->labels());
foreach
(
$labels
as
$field
=>
$label
)
{
$this
->_validation->label(
$field
,
$label
);
}
}
Filters a value for a specific column
string
$field
required - The column namestring
$value
required - The value to filterstring
protected
function
run_filter(
$field
,
$value
)
{
$filters
=
$this
->filters();
// Get the filters for this column
$wildcards
=
empty
(
$filters
[TRUE]) ?
array
() :
$filters
[TRUE];
// Merge in the wildcards
$filters
=
empty
(
$filters
[
$field
]) ?
$wildcards
:
array_merge
(
$wildcards
,
$filters
[
$field
]);
// Bind the field name and model so they can be used in the filter method
$_bound
=
array
(
':field'
=>
$field
,
':model'
=>
$this
,
);
foreach
(
$filters
as
$array
)
{
// Value needs to be bound inside the loop so we are always using the
// version that was modified by the filters that already ran
$_bound
[
':value'
] =
$value
;
// Filters are defined as array($filter, $params)
$filter
=
$array
[0];
$params
= Arr::get(
$array
, 1,
array
(
':value'
));
foreach
(
$params
as
$key
=>
$param
)
{
if
(
is_string
(
$param
) AND
array_key_exists
(
$param
,
$_bound
))
{
// Replace with bound value
$params
[
$key
] =
$_bound
[
$param
];
}
}
if
(
is_array
(
$filter
) OR !
is_string
(
$filter
))
{
// This is either a callback as an array or a lambda
$value
= call_user_func_array(
$filter
,
$params
);
}
elseif
(
strpos
(
$filter
,
'::'
) === FALSE)
{
// Use a function call
$function
=
new
ReflectionFunction(
$filter
);
// Call $function($this[$field], $param, ...) with Reflection
$value
=
$function
->invokeArgs(
$params
);
}
else
{
// Split the class and method of the rule
list(
$class
,
$method
) =
explode
(
'::'
,
$filter
, 2);
// Use a static method call
$method
=
new
ReflectionMethod(
$class
,
$method
);
// Call $Class::$method($this[$field], $param, ...) with Reflection
$value
=
$method
->invokeArgs(NULL,
$params
);
}
}
return
$value
;
}