diff --git a/assets/css/combined.css b/assets/css/combined.css index 400d9b62b..245176af8 100644 --- a/assets/css/combined.css +++ b/assets/css/combined.css @@ -673,7 +673,7 @@ footer a{ } #main table td{ - width: 700px; + width: 800px; } #main table table td{ @@ -769,7 +769,7 @@ table table{ } #main table pre, #main table code{ - max-width: 700px; + width: 100%; display: block; overflow: auto; } diff --git a/assets/js/combined.js b/assets/js/combined.js index 0cce548f7..6f64076be 100644 --- a/assets/js/combined.js +++ b/assets/js/combined.js @@ -339,9 +339,11 @@ panes.main = { "Configuration": "general/configuration.html", "Constants": "general/constants.html", "Environments": "general/environments.html", + "Functions": "general/functions.html", "Model-View-Controller": "general/mvc.html", "Models": "general/models.html", "Views": "general/views.html", + "Polyfills": "general/polyfills.html", "Presenters": "general/presenters.html", "Controllers": { "Base" : "general/controllers/base.html", @@ -543,6 +545,8 @@ panes.orm = { "Orm Package": { "Introduction": "packages/orm/intro.html", "Creating Models": "packages/orm/creating_models.html", + "Model methods": "packages/orm/methods.html", + "View support": "packages/orm/views.html", "CRUD": "packages/orm/crud.html", "Relating Models": { "Introduction": "packages/orm/relations/intro.html", @@ -561,7 +565,8 @@ panes.orm = { "Introduction": "packages/orm/observers/intro.html", "Included observers": "packages/orm/observers/included.html", "Writing your own": "packages/orm/observers/creating.html" - } + }, + "Tasks": "packages/orm/tasks.html" } }; diff --git a/classes/agent/config.html b/classes/agent/config.html index 920e15a6f..3843967ba 100644 --- a/classes/agent/config.html +++ b/classes/agent/config.html @@ -258,7 +258,7 @@
Cache configuration
diff --git a/classes/agent/usage.html b/classes/agent/usage.html index 2a306dcde..80e77cabc 100644 --- a/classes/agent/usage.html +++ b/classes/agent/usage.html @@ -520,7 +520,7 @@

is_robot()

diff --git a/classes/arr.html b/classes/arr.html index 62d64ee96..5926c777b 100644 --- a/classes/arr.html +++ b/classes/arr.html @@ -101,7 +101,7 @@

is_multi($arr, $all_keys = false)

-

is_assoc($arr)

+

is_assoc($arr, $consecutive)

The is_assoc method checks if the array passed is an associative array or not.

@@ -123,6 +123,11 @@

is_assoc($arr)

+ + + + +
required The array to check.
$consecutive
true
If true, it expects an indexed array to have sequential, consecutive numeric keys, starting with zero. If false, a key has to be not an integer for the array to be classified as associative.
@@ -148,11 +153,17 @@

is_assoc($arr)

$arr = array(0 => 'foo', 1 => 'bar', '2' => 'baz', 3 => 'yay'); echo Arr::is_assoc($arr); // Result: false! + +// non-consecutive numeric keys, not assoc! +$arr = array(0 => 'foo', 3 => 'bar', '9' => 'baz', 12 => 'yay'); +echo Arr::is_assoc($arr, false); +// Result: false! +

Note: the $consecutive flag has been added to be able to test for all indexed arrays, to avoid breaking backward compatibilty with pre-1.9 code which expects all indexed arrays to be zero-based and have consecutive numeric keys.

@@ -965,7 +976,7 @@

filter_keys($array, $keys, $remove =

-

filter_recursive($array, $callback = null)

+

filter_recursive($array, $callback = null, $mode = 0)

The filter_recursive method provides a recursive version of PHP's array_filter() function. Like its counterpart, you can optionally pass a callback function to determine what should be filtered.

@@ -992,6 +1003,11 @@

filter_recursive($array, $callba

+ + + + +
null
A valid callback that returns a boolean.
$mode
ARRAY_FILTER_USE_KEY
either ARRAY_FILTER_USE_KEY or ARRAY_FILTER_USE_BOTH
@@ -1035,7 +1051,7 @@

filter_recursive($array, $callba ) // Let's use a callback -print_r(Arr::filter_recursive($arr, function($item){ return $item !== ""; })); +print_r(Arr::filter_recursive($arr, function($k){ return $k !== 1; })); // Result: Array @@ -1048,15 +1064,28 @@

filter_recursive($array, $callba ( [data] => a value ) - [1] => Array - ( - ) [2] => Array ( [data] => 0 ) ) ) + +print_r(Arr::filter_recursive($arr, function($v, $k){ return ! empty($v); }, \ARRAY_FILTER_USE_BOTH)); + +// Result: +Array +( + [user_name] => John + [user_surname] => Lastname + [info] => Array + ( + [0] => Array + ( + [data] => a value + ) + ) +) @@ -1913,7 +1942,7 @@

sort($array, $key, $order = 'asc', $sort_fla

-

multisort($array, $conditions, $ignore_case = false)

+

multisort($array, $conditions, $ignore_case = false, $reindex = true)

The sort method sorts a multi-dimensional array by multiple values.

@@ -1941,10 +1970,15 @@

multisort($array, $conditions, $ignore_

- + + + + + +
Array of sorting conditions.
$ornire_case$ignore_case
false
Whether to sort case insensitive.
$reindex
true
When an indexed array is multisorted, do we want the result to be re-indexed or not.
@@ -2032,6 +2066,10 @@

multisort($array, $conditions, $ignore_ +

+ By default, Arr::multisort() works similar to PHP's default array_multisort, which preserves keys of associative arrays, but re-indexes indexed arrays. + You can disable this behavior, and preserve keys if indexed arrays too, by passing false for $reindex. +

@@ -2930,7 +2968,7 @@

in_arrayi($needle, $hay

- © FuelPHP Development Team 2010-2019 - FuelPHP is released under the MIT license. + © FuelPHP Development Team 2010-2026 - FuelPHP is released under the MIT license.

diff --git a/classes/asset/advanced.html b/classes/asset/advanced.html index e27e3af4b..4f83441e5 100644 --- a/classes/asset/advanced.html +++ b/classes/asset/advanced.html @@ -263,7 +263,7 @@

add_type($type, $path = null, $renderer

- © FuelPHP Development Team 2010-2019 - FuelPHP is released under the MIT license. + © FuelPHP Development Team 2010-2026 - FuelPHP is released under the MIT license.

diff --git a/classes/asset/config.html b/classes/asset/config.html index fb6f35452..cb25bde07 100644 --- a/classes/asset/config.html +++ b/classes/asset/config.html @@ -221,7 +221,7 @@

Static assets

diff --git a/classes/asset/usage.html b/classes/asset/usage.html index 1d21a3d80..22c8444cf 100644 --- a/classes/asset/usage.html +++ b/classes/asset/usage.html @@ -598,7 +598,7 @@

render($group = null, $raw = false)

diff --git a/classes/autoloader.html b/classes/autoloader.html index b8f5522a6..dffe567ba 100644 --- a/classes/autoloader.html +++ b/classes/autoloader.html @@ -465,7 +465,7 @@

import($path, $folder = 'classes')

diff --git a/classes/cache/advanced.html b/classes/cache/advanced.html index 3f8856a8c..2c0c8c38f 100644 --- a/classes/cache/advanced.html +++ b/classes/cache/advanced.html @@ -55,7 +55,7 @@

Advanced usage

diff --git a/classes/cache/config.html b/classes/cache/config.html index 309ee5f6f..128f21be9 100644 --- a/classes/cache/config.html +++ b/classes/cache/config.html @@ -174,7 +174,7 @@
Redis driver configuration
diff --git a/classes/cache/usage.html b/classes/cache/usage.html index 8c8bcbf44..a65cc7fd2 100644 --- a/classes/cache/usage.html +++ b/classes/cache/usage.html @@ -343,7 +343,7 @@

call($identifier, $callback, $args = array()

- © FuelPHP Development Team 2010-2019 - FuelPHP is released under the MIT license. + © FuelPHP Development Team 2010-2026 - FuelPHP is released under the MIT license.

diff --git a/classes/cli.html b/classes/cli.html index a0a87d877..aff4f0e53 100644 --- a/classes/cli.html +++ b/classes/cli.html @@ -142,6 +142,69 @@

color($text, $foreground, $background = nul +

Supported color codes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ColorSampleColorSample
blackdark_gray
redlight_red
greenlight_green
brown, yellowlight_yellow
bluelight_blue
purplelight_purple
cyanlight_cyan
light_graywhite
+

+ The colors in the first column are also available as background colors. Colors may depend on the terminal and terminal theme used.
+ Note that if you prefer UK English spelling, you can substitute "grey" for "gray". +

@@ -502,7 +565,7 @@

stderr($resource = null)

diff --git a/classes/config.html b/classes/config.html index 45d7394a6..5df9a1cc2 100644 --- a/classes/config.html +++ b/classes/config.html @@ -43,21 +43,21 @@

Config File Types

You can use different file layouts to store your configuration. The layout type is determined by the file extension: @@ -107,6 +108,11 @@

Slightly Advanced Routing

'(\d{2})/about' => 'site/about/$1', // Routes /12/about to /site/about/12 ); +

+ Note that you need to disable "routing.strip_extensions" in your application config when + you want to use ":decimal", as by default everthing after a dot will be seen as + extension and stripped. This is not an issue when you use a decimal comma. +

Advanced Routing

@@ -206,6 +212,37 @@

Inline routes

} }; +

Route recursion

+ +

+ By default, the requested URI is processed by the routing engine only once. Using the configuration key 'routing.recursive', + you can enable recursive route lookups. +

+ +

+ This means that when a given URI resolves to a defined route, but this route doesn't resolve to a valid controller action, + the resulting route is reprocessed. This allows you to define aliases or redirects: +

+ +
<?php
+return array(
+	// default routes
+	'_root_'       => 'homepage/index',
+
+	// alias in another language
+	'un/:slug'       => 'one/$1',
+	'deux/:slug'     => 'two/$1',
+	'trois/:slug'    => 'three/$1',
+
+	// redirect old uri's
+	'old/uri/:slug'  => 'controller/new/$1',
+
+	// site routes
+	'one/:slug'      => 'controller/one/$1',
+	'two/:slug'      => 'controller/two/$1',
+	'three/:slug'    => 'controller/three/$1',
+);
+

Modules & Routing

Read about how modules handle routing.

@@ -214,7 +251,7 @@

Modules & Routing

diff --git a/general/security.html b/general/security.html index 66826e393..53529c4de 100644 --- a/general/security.html +++ b/general/security.html @@ -203,7 +203,7 @@

SQL injection

diff --git a/general/tasks.html b/general/tasks.html index 47b377fdc..49598cced 100644 --- a/general/tasks.html +++ b/general/tasks.html @@ -76,7 +76,7 @@

Splitting tasks into more methods

diff --git a/general/unit_testing.html b/general/unit_testing.html index f236efe01..c4617ee54 100644 --- a/general/unit_testing.html +++ b/general/unit_testing.html @@ -185,7 +185,7 @@

diff --git a/general/viewmodels.html b/general/viewmodels.html index b33e90564..58b1a0b27 100644 --- a/general/viewmodels.html +++ b/general/viewmodels.html @@ -45,7 +45,7 @@

Viewmodels

diff --git a/general/views.html b/general/views.html index 832e298f3..4a11f71be 100644 --- a/general/views.html +++ b/general/views.html @@ -322,7 +322,7 @@

Wrap your view in a layout

diff --git a/index.html b/index.html index 81e80e468..ce1395999 100644 --- a/index.html +++ b/index.html @@ -36,8 +36,13 @@

Welcome to FuelPHP

- FuelPHP is a simple, flexible, community driven PHP 5 web framework. It was born out of the frustrations people have with the current available frameworks and developed - with support from a community of developers. FuelPHP is extremely portable, works on almost any server and prides itself on clean syntax. + FuelPHP is a simple, flexible, community driven PHP framework for developing web based applications. + It is compatible with every PHP version from 5.3.3 onwards, and prides itself on providing maximum + backward compatibility. +

+ It was born out of the frustrations people have with the current available frameworks and developed + with support from a community of developers. FuelPHP is extremely portable, works on almost any server + and prides itself on clean syntax.

Quick Installation

@@ -93,7 +98,7 @@

Development Branch Documentation

diff --git a/installation/download.html b/installation/download.html index f6d7cbd4e..4a178744a 100644 --- a/installation/download.html +++ b/installation/download.html @@ -35,16 +35,17 @@

Download Fuel

-

Fuel is now at v1.8.1. You can download Fuel using the links below:

+

Fuel is now at v1.8.2. You can download Fuel using the links below:

@@ -101,7 +102,7 @@

Example

diff --git a/packages/oil/server.html b/packages/oil/server.html index e4ef394bf..1a3ef9ea3 100644 --- a/packages/oil/server.html +++ b/packages/oil/server.html @@ -55,7 +55,7 @@

Server

diff --git a/packages/oil/test.html b/packages/oil/test.html index 75666e55d..7bcc20477 100644 --- a/packages/oil/test.html +++ b/packages/oil/test.html @@ -110,7 +110,7 @@

PHPUnit Test Runner Configuration

diff --git a/packages/orm/creating_models.html b/packages/orm/creating_models.html index f9d793160..2a8c03f85 100644 --- a/packages/orm/creating_models.html +++ b/packages/orm/creating_models.html @@ -217,6 +217,38 @@

protected static $_to_array_exclude

custom properties and relations, allowing you to exclude properties from all of them.

+

protected static $_property_map

+ +

+ Sometimes you run into issues where column names in a database are not compatible or practical for use + in PHP. For example, some database platforms allow characters like "<" or ">", or even spaces, in + column names. +

+ +

+ This is not an issue for the database itself, for the queries the ORM generates, or for creating ORM model + objects, but it is for accessing the data. After all, you can't have spaces in an object property name, and + names like "$model->small<large" are illegal in PHP. To deal with this, you can create a + property map, in which you map column names to property names: +

+
protected static $_property_map = array(
+	'with a space' => 'with_no_space',
+	'small<large' => 'small_large',
+);
+
+protected static $_properties = array(
+	'with a space',
+	'small<large',
+);
+

+ This mapping allows you to access the column values using "$model->with_no_space" and "$model->small_large". +

+ +

+ In terms of precedence, mapped property names are checked after real properties, relations and EAV properties, but before + any custom properties the model object my have. +

+

protected static $_observers

Adding observers is explained in Observers

@@ -226,7 +258,7 @@

protected static $_observers

diff --git a/packages/orm/crud.html b/packages/orm/crud.html index 2a4416b9a..d98a6c73f 100644 --- a/packages/orm/crud.html +++ b/packages/orm/crud.html @@ -439,7 +439,7 @@

Custom SQL< diff --git a/packages/orm/eav.html b/packages/orm/eav.html index 8ef8c84fc..222c5ba7c 100644 --- a/packages/orm/eav.html +++ b/packages/orm/eav.html @@ -92,11 +92,11 @@

Implementation

// set up the statistics relation the usual way protected static $_has_many = array( 'statistics' => array( - 'key_from' => 'id', // key in this model - 'model_to' => 'Model_Statistic', // related model - 'key_to' => 'patient_id', // key in the related model - 'cascade_save' => true, // update the related table on save - 'cascade_delete' => true, // delete the related data when deleting the parent + 'key_from' => 'id', // key in this model + 'model_to' => 'Model_Statistic', // related model + 'key_to' => 'patient_id', // key in the related model + 'cascade_save' => true, // update the related table on save + 'constraint' => \Orm\Relation::CONSTRAINT_CASCADE, // delete the related data when deleting the parent ) ); @@ -126,7 +126,7 @@

Implementation

'model_to' => 'Model_Patient', 'key_to' => 'id', 'cascade_save' => true, - 'cascade_delete' => true, + 'constraint' => \Orm\Relation::CONSTRAINT_CASCADE, ) ); } @@ -194,7 +194,7 @@

Example

diff --git a/packages/orm/intro.html b/packages/orm/intro.html index 9b37ff1d0..f1ead7f46 100644 --- a/packages/orm/intro.html +++ b/packages/orm/intro.html @@ -48,6 +48,24 @@

Introduction

doing all the hard work for you.

+
+

A word of warning

+ +

+ Contrary to popular belief, an ORM model is not the same a an application model. ORM models map directly to a single record (the Active Active Record Pattern), + where as application model classes contain your business logic, and may (or may not) use the ORM to access the data in the configured storage backend. +

+

+ Although it is possible, and perfectly fine in most cases, to use the ORM directly in your controllers, it is strongly advised not to do so, but seperate your controller logic from your business logic, + which makes it a lot easier to make changes to that logic without having to search through all your controllers and other classes to make sure the changes have no side effect. Prime examples are for + example changes in database relations, adding or removing columns, and or changing the meaning or contents of a column. +

+

+ The most common way to seperate the two types of model is to put them into different namespaces in your application, so you have no name collisions and you can not accidentially mix up the two. +

+ +
+

Installation

@@ -59,6 +77,65 @@

Installation

),
+
+ +

Configuration

+ +

There are a few features or behaviours in the ORM that are configurable:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParamTypeDefaultDescription
cachingbool
true
+ By default, the ORM caches all hydrated objects, so that when you request the same object a second time, you get the cached + (and potentially modified!) object, and not a new object for the same record (which can lead to data inconsistency).
+ You can override whatever default you set on a per-query basis if needed using the caching() method. +
relation_lazy_loadbool
false
+ When set to true, an isset() or empty() check on an object relation will always return true, + whether or not the related records are fetched. When the actual related records are accessed, they will be "lazy loaded" with + an additional query. +
sql_max_timestamp_mysqlstring
'2038-01-18 22:14:08'
+
sql_max_timestamp_unixinteger
2147483647
+ Used to set the upper limit for the timestamp in the Temporal model<.strong>, if no limit is given. +
+ +

+ Lazy loading of relations was default behavior in the ORM prior to version 1.9. If you have an application that relies on this, do not forget + to enable this feature in the config when you upgrade your application to this or a later version! +

+ +
+

Object Caching

@@ -184,7 +261,7 @@

My query result doesn't reflect the record values in the database

diff --git a/packages/orm/methods.html b/packages/orm/methods.html new file mode 100644 index 000000000..a9c679dac --- /dev/null +++ b/packages/orm/methods.html @@ -0,0 +1,1612 @@ + + + + + + + + + + + Model methodss - Orm Package - FuelPHP Documentation + + +
+ + +
+ +

ORM Model Class

+ +

+ The ORM Model Class is not only the container for a database record, it also provides methods to manipulate the object and its data. +

+ +
+

cached_object($objpk, $class = null)

+

The cached_object method allows you to retrieve specific model object from the object cache, by primary key and model class name.

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$objpkrequiredThe primary key value of the model object you want to retrieve. If the model has a compound primary key, you can pass the key values in an array, in the order the keys are defined in the model.
$class
null
The model you want to lookup. If not given, the current model name is used.
+
ReturnsOrm\Model | false
Example +
// tries to retrieve OtherModel object with primary key value "12" from the cache
+$from_cache = MyModel::cached_object(12, 'OtherModel');
+
+
+ +
+

condition($type = null)

+

The condition method allows you to retrieve the models defined conditions.

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$type
null
The type of condition you want to retrieve. Currently supported are 'order_by' and 'where'. When no type is given, all conditions are returned.
+
Returnsarray()
Example +
// tries to retrieve MyModel where conditions
+$where = MyModel::conditions('where');
+
+
+ +
+

connection($writeable = false)

+

The connection method allows you to retrieve the models defined database connection.

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$writeable
false
The type of connection you want to retrieve. A model can have seperate connection definitions for read and write operations. By default, the read connection is returned.
+
Returnsstring | null
Example +
// retrieves the MyModel write connection
+$where = MyModel::connection(true);
+
+
+ +
+

find($id = null, $options = null)

+

The find method allows you to retrieve a record as a model object or an array of model objects.

+ + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$id
null
The primary key of the record you want to retrieve, or one of the special keywords 'all', 'first' or 'last'.
$options
null
Array of options that can be passed, including relations and query conditions.
+
ReturnsOrm/Model | array | null
+

See the CRUD documentation for detailed examples on how to use this method.

+
+ +
+

flush_cache($class = null)

+

The flush_cache method allows you to (selectively) delete model objects from the object cache.

+ + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$class
null
+ If not given, the entire cache is emptied. Optionally you can specify a model (either as a string containing a class name, + or a model object, and only flush the cache for that model. +
+
Returnsvoid
+
+ +
+

primary_key()

+

The primary_key method returns an array with the column names that define the tables primary key.

+ + + + + + + + + + + + + + + +
StaticYes
Returnsarray
Example +
// returns array('id') for default PK's
+$pk = MyModel::primary_key();
+
+
+ +
+

implode_pk($data)

+

The implode_pk method allows you convert the value of a (compound) primary key into a string.

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$datarequired + Either an array with key-value pairs, or an object with properties. All column names that are part of + the models primary key must be present, no validation is done.
null
is returned + if the data doesn't contain all key values. +
+
Returnsstring | null
Example +
// with PK defined as both columns "A" and "B", this returns "[1][2]"
+$pk = MyModel::implode_pk(array('A' => 1, 'B' => 2));
+
+
+ +
+

forge($data = array(), $new = true, $view = null, $cache = true)

+

The forge method allows you to forge a new model instance.

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$data
array()
+ Multi-dimensional array of data to populate the new model object. This array may included data for + related model objects. +
$new
true
+ If set to
false
,
$data
+ must contain at least all primary key values. The object will be set to "loaded" state, as if it was retrieved from the + database. This is mainly used after reading data from the database and hydrating it into model objects, or to recreate + objects from data stored elsewhere, for example in an external cache. +
$view
null
+ Used internally when models are created from on of the table views defined in the model. It changes the model object into + a read-only state. +
$cache
true
+ If set to true and
$new
set to false, and the data given contains all PK values, any existing + model object is cache is returned, instead of a newly instantiated object, optionally updated with any remaining data passed. +
+
Returnsstring | null
Example +
// create a new MyModel model object
+$mymodel = MyModel::forge();
+
+
+ +
+

get_filtered_properties()

+

+ The get_filtered_properties method returns the list of properties returned by a call to

to_array()
. + It will take into account the list of excluded property names defined in the model. +

+ + + + + + + + + + + + + + + +
StaticYes
Returnsarray
Example +
// with PK defined as both columns "A" and "B", this returns "[1][2]"
+$pk = MyModel::implode_pk(array('A' => 1, 'B' => 2));
+
+

+ Excluding properties can come in handy for properties you don't want to expose, for example if you want to return model data to the caller in a JSON response. + See the static model property $_to_array_exclude. +

+
+ +
+

max($key = null)

+

+ The max method returns the maximum value of the given column name. +

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$key
null
+ Name of a numeric table column. If not given, the primary key column is used. +
+
Returnsint | bool
Example +
// get the last id issued
+$last = MyModel::max('id');
+
+

+ This method does not support compound primary keys (as you can only query MAX() on a single column), and will return false if there is no result. +

+
+ +
+

min($key = null)

+

+ The min method returns the minimum value of the given column name. +

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$key
null
+ Name of a numeric table column. If not given, the primary key column is used. +
+
Returnsint | bool
Example +
// get the first id issued
+$last = MyModel::min('id');
+
+

+ This method does not support compound primary keys (as you can only query MAX() on a single column), and will return false if there is no result. +

+
+ +
+

observers($specific = null, $default = null)

+

+ The observers method returns a list of all observers defined in the model. +

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$specific
null
+ Name of a spefific observer to get the definition of. Supports dot-notation to directly get any array element in the definition. + If none given, it will return all observers defined in the current model class. +
$default
null
+ The value to return is no data was found. +
+
Returnsmixed
Example +
// get the observers defined in the model
+// might return array('Orm\Observer_Typing' => array())
+MyModel::observers();
+
+

+ The array value associated with the observer in the result will contain the additional configuration the observer requires. +

+
+ +
+

properties()

+

+ The properties method returns an array of all properties (i.e. column names) defined in the model class. +

+ + + + + + + + + + + +
StaticYes
Returnsarray
+

+ Note: If the model does not define its properties, the ORM will attempt to generate it dynamically, by running + a "list all columns" query on the defined table. As this is performance impacting, you should not use this in a production + environment, but you could dump the generated array, and use that to populate the model properties array. +

+

+ Not all database drivers support a "list all columns" method, and will throw an exception instead. +

+
+ +
+

property($key, $default = null)

+

+ The property method returns the definition of a single model class property. +

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$keyrequired + Name of a property you want to query. +
$default
null
+ The value to return is no data was found. +
+
Returnsmixed
Example +
// get the definition of the primary key
+MyModel::property('id');
+
+

+ If no properties are defined in the model class, a call to properties() will be made in an attempt to generate them. +

+
+ +
+

query()

+

+ The query method returns an ORM query object for the current model class. +

+ + + + + + + + + + + + + + + +
StaticYes
ReturnsORM\Query
Example +
// get query object, and get the first record
+$query = MyModel::query()->get_one();
+
+

See the CRUD documentation for detailed examples on how to use this method.

+
+ +
+ +

+ The related_class method returns the name of the ORM model class a relation maps to. +

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$relationrequired + Name of a defined model class relation. +
+
Returnsstring | null
Example +
// might return "\Some\Namespace\Model_Child"
+MyModel::related_class('children');
+
+
+ +
+ +

+ The relations method returns the ORM definition of the given relation. +

+ + + + + + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$specific
false
+ Name of a defined model class relation, or
false
to return an array of all defined relations. +
+
ReturnsRelation | array | false
Example +
/**
+ * might return:
+ * object(Orm\HasMany)
+ *   protected 'name' => string 'children' (length=12)
+ *   protected 'model_from' => string 'Model\Parent' (length=19)
+ *   protected 'model_to' => string 'Model\Child' (length=23)
+ *   protected 'key_from' =>
+ *     array (size=1)
+ *       0 => string 'id' (length=2)
+ *   protected 'key_to' =>
+ *     array (size=1)
+ *       0 => string 'parent_id' (length=6)
+ *   protected 'conditions' =>
+ *     array (size=1)
+ *       'where' =>
+ *         array (size=1)
+ *           0 =>
+ *             array (size=3)
+ *               ...
+ *   protected 'singular' => boolean false
+ *   protected 'cascade_save' => boolean true
+ *   protected 'constraint' => string 'delete_default'
+ */
+MyModel::relations('children');
+
+
+ +
+

set_connection($connection)

+

+ The connection method allows you to set or override the defined database connection to be used by the model class. +

+ + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$connectionrequired + Name of database definition, defined in the db.php config file. +
+
Example +
// switch database connection to an Oracle db
+MyModel::connection('oracle');
+
+

+ This sets both the read and write connection, unless a seperate write connection was defined. +

+
+ +
+

set_write_connection($connection)

+

+ The set_write_connection method allows you to set or override the defined database connection for writes, to be used by the model class. +

+ + + + + + + + + + + + + + + +
StaticYes
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$connectionrequired + Name of database definition, defined in the db.php config file. +
+
Example +
// switch database connection to the master db
+MyModel::connection('master');
+
+

+ This sets the database write connection, which will override the generic connection for write queries. +

+
+ +
+

table()

+

+ The table method returns the name of the table associated with the model class. If none is defined in the model, one will be generated. +

+ + + + + + + + + + + + + + + +
StaticYes
Returnsstring
Example +
// might return "prefix_mymodel"
+$table = MyModel::table();
+
+

+ The generated table name will be derived from the model name, after the namespaces and any "model_" prefix is stripped, lowercase, and made plural (for english names only). + So "\Some\Model_Cat" will map to a table called "cats". +

+
+ +
+

delete($cascade = null, $use_transaction = false)

+

The delete method deletes the record associated with the current model object from the database.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$cascade
null
+ This argument allows you to override the constraint definition on a relation.
null
is the default behavior, + in which case the constraint as defined on the relation will be used. +
true
is the equivalent of \Orm\Relation::CONSTRAINT_CASCADE, while
false
+ is the equivalent of \Orm\Relation::CONSTRAINT_SETDEFAULT. +
$use_transaction
false
+ Whether or not you want to wrap the delete in a database transaction. This allows automatic rollback, especially important when you use cascading deletes and + one of them fails. Without a transaction, you'll end up with partially deleted relations. +
+
ReturnsModel | false
Example +
// delete the current record
+$record->delete();
+
+

+ When the delete() operation fails logically, the method returns false. Otherwise it will return a new model + instance, retaining all model data but with the primary keys reset, so a model in new state. +

+

+ Note also that database exceptions that drivers might throw are not caught in this method ! +

+
+ +
+

disable_event($event)

+

The disable_event method allows you to disable a particular observer event defined in the model on a per-model instance basis.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$eventrequired + Name of the event to disable. Supported are 'after_load', 'after_create', 'after_clone', 'before_save', 'after_save', 'before_insert', 'after_insert', + 'before_update', 'after_update', , 'before_delete' and 'after_delete'. +
+
Returnsvoid
Example +
// disable the after_save observers
+$record->disable_event('after_save');
+
+
+ +
+

enable_event($event)

+

The disable_event method allows you to enable a particular observer event that was disabled previously. See disable_event().

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$eventrequired + Name of the event to disable. Supported are 'after_load', 'after_create', 'after_clone', 'before_save', 'after_save', 'before_insert', 'after_insert', + 'before_update', 'after_update', , 'before_delete' and 'after_delete'. +
+
Returnsvoid
Example +
// remove a defined block of all after_save observers
+$record->enable_event('after_save');
+
+
+ +
+

from_array(array $values, $from_cache = false)

+

The from_array method allows you to populate a model object, and optionally related models objects, from a multi-dimensional array.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$valuesrequired + Multi-dimensional array of key-value pairs, representing column names and column values. +
$from_cache
false
+ When set to true, the model object cache will be checked before creating any related model objects from the data passed. +
+
ReturnsModel, the current object, for chaining.
Example +
// create a model object and populate it
+$mymodel = MyModel::forge();
+$mymodel->from_array($data);
+
+//NB: this is identical to:
+$mymodel = MyModel::forge($data);
+
+

+ Be careful with enabling $from_cache. As objects are passed around by reference in PHP, modifying a cached object with the data + passed will also modify any variable containing a reference to that object. This can lead to unexpected results and possible data corruption ! +

+
+ +
+

get($property, array $conditions = array())

+

The get method allows you to get a property from the model object. Properties include columns, related objects, EAV values or custom/runtime data stored in the object.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$propertyrequired + The name of the property whos value you want to retrieve. +
$conditions
array()
+ Only used for relations that aren't fetched yet. This allows you to add additional conditions (order_by or where clauses) + to the query that will fetch the related model objects. This allows you to fetch a subset, or to alter the default ordering. +
+
Returnsmixed
Example +
// get all the related child records (from a defined related called 'children')
+$mymodel->get('children');
+
+
+ +
+

get_diff()

+

The get_diff method returns an array with all properties that have been modified on the object since it was loaded.

+ + + + + + + + + + + + + + + +
StaticNo
Returnsarray
Example +
// returns array('id' => 2)
+$mymodel = MyModel::find(1);
+$mymodel->id = 2;
+print_r($mymodel->get_diff());
+
+
+ +
+

get_pk_assoc()

+

The get_pk_assoc method returns an array with a key-value pair for all primary key columns of the current model object.

+ + + + + + + + + + + + + + + +
StaticNo
Returnsarray
Example +
// returns array('id' => 1)
+$mymodel = MyModel::find(1);
+print_r($mymodel->get_pk_assoc());
+
+
+ +
+

is_changed($property = null, $observe = false, $recurse = false)

+

The is_changed method allows you to test if a model object has been altered since it was loaded.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$property
null
+ The name of specific property whos value you want to check for changes. If not given, or
null
, + all properties will be checked. +
$observe
false
+ Whether or not 'before_save' observers should run. This can be desirable since you could have observers defined + on the model that would autonomously alter data when saving the model, without code or user interaction being involved. +
$recurse
false
+ When
true
, it will call
is_changed()
+ on any related object of relations given in $property. +
+
Returnsbool
Example +
// you can use get_diff() to check what has changed
+if ($mymodel->is_changed()
+{
+	echo "something has changed in the object !";
+}
+// not only check if child objects were added or removed, but
+// also if loaded child objects themselfs have been modified
+if ($mymodel->is_changed('child', false, true)
+{
+	echo "something related to 'child' model objects is changed";
+}
+
+

+ Using the $recurse option is particularly handy if you've loaded a hierarchical tree of model objects, with relations that + have "cascade_save" enabled, or use an "EAV" container, so it returns a true status of the fact something in the chain needs saving. +

+
+ +
+

is_fetched($relation)

+

The is_fetched method allows you check if related objects have been loaded.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$relationrequired + The name of the relation you want to check. +
+
Returnsbool
Example +
if ( ! $mymodel->is_fetched('children'))
+{
+	echo "no child records have been loaded yet !";
+}
+
+

+ Note that you can't use isset($mymodel->children), as that would access the property, which in turn would trigger a query + to autoload the related records (which in turn would make is_fetched() return true) ! +

+
+ +
+

is_new()

+

The is_new method can be used to check if the object has a representation in the database, i.e. if a corresponding record exists.

+ + + + + + + + + + + + + + + +
StaticNo
Returnsbool
Example +
if ( ! $mymodel->is_new())
+{
+	echo "no record exists of this object yet";
+}
+
+

+ Note that that if your model uses database assigned primary keys (like AUTO_INCREMENT or SERIAL), the objects primary key properties will not + have a value at this point. But the absence of such values doesn't say anything about the "new" state, as the model could have primary key + values that are manually assigned or generated by observers, in which case it could already have a primary key set, even though it is still + classified as "new". +

+
+ +
+

is_parent($all = false)

+

The is_new method can be used to check if the object has a representation in the database, i.e. if a corresponding record exists.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$all
false
+ If set to
false
, it will return
true
+ as soon as a relation is detected of which the current model object is a parent. Otherwise, it will check all relations, and will + return an array of relations from which the current model object is a parent (which could be empty). +
+
Returnsbool | array
Example +
if ($rels = $mymodel->is_parent(true))
+{
+	echo "don't delete it, it still has child records in " . impode(",", $rels) . " !";
+}
+
+

+ You can use this as a manual foreign key check, either because you don't want to wait for a database driver exception in case of a foreign key + violation, or because the database you work with doesn't have any foreign key constraints defined, and you want to make sure you don't want to + corrupt the database by leaving child records without a parent. +

+
+ +
+

reset()

+

The reset method deletes all changes made to the model object, and resets it to the state it was after loading.

+ + + + + + + +
StaticNo
+

+ If you call reset() on an object in "new" state, it will be completely wiped, like you had just called ClassName::forge(). +

+
+ +
+

save($cascade = null, $use_transaction = false)

+

The save method save the record associated with the current model object to the database, by using an INSERT if the object is in "new" state, or an UPDATE if not.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$cascade
null
+ This argument allows you to control cascading save behaviour.
null
is the default behavior, + configured in the relations of the model.
true
or
false
+ explicitly enables or disables cascading saves, or you can pass an array with relation names, if you want selective cascading saves. +
$use_transaction
false
+ Whether or not you want to wrap the save in a database transaction. This allows automatic rollback, especially important when you use cascading saves and + one of them fails for some reason. Without a transaction, you'll end up with partially saved relations. +
+
ReturnsModel | false
Example +
// saves the current record
+$record->save();
+
+

+ When the save() operation fails logically, the method returns false. Otherwise it will return true. +

+

+ Note also that database exceptions that drivers might throw are not caught in this method ! +

+
+ +
+

to_array($custom = false, $recurse = false, $eav = false)

+

The to_array method allows you to convert the model object, any relational data loaded, and optionally any custom/runtime data or EAV data, to be return as a multi-dimensional array.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + + + + + + + + + + + +
ParamDefaultDescription
$custom
false
+ Set this to true if you want to include custom or runtime data stored in the model object. +
$recurse
false
+ Used internally for object recursion detected. No NOT EVER set this to anything other than
false
or weird things will happen! +
$eav
false
+ Set this to true if you want to include EAV properties stored in the model object. +
+
Returnsarray
Example +
// dump the contents of the current object into an array
+$array = $record->to_array();
+
+

+ Did I mention not to set the recurse variable to anything other than false? +

+
+ +
+

to_object($custom = false)

+

The to_object method allows you to convert the model object into a StdClass object with only the data properties.

+ + + + + + + + + + + + + + + + + + + +
StaticNo
Parameters + + + + + + + + + + + +
ParamDefaultDescription
$custom
false
+ Set this to true if you want to include custom or runtime data stored in the model object. +
+
ReturnsStdClass
Example +
// these are identical
+$obj1 = $record->to_object();
+$obj2 = (object) $record->to_array();
+
+
+
+ +
+

+ © FuelPHP Development Team 2010-2026 - FuelPHP is released under the MIT license. +

+
+
+ + diff --git a/packages/orm/model/nestedset.html b/packages/orm/model/nestedset.html index 356def7b1..715843384 100644 --- a/packages/orm/model/nestedset.html +++ b/packages/orm/model/nestedset.html @@ -1509,6 +1509,42 @@

has_next_sibling()

// Does Ben have a next sibling? echo's 'false' echo $ben->has_next_sibling() ? 'true' : 'false'; + + + + + + + +
+

has_siblings()

+

+ The has_siblings method tells you if the object you're calling it on has any siblings, i.e. if it is the only child or not. +

+ + + + + + + + + + + + + + + + + @@ -1972,7 +2008,7 @@ - + @@ -1992,6 +2028,15 @@
StaticNo
Parameters + None. +
ReturnsBool, true if the object has siblings, false if not
Example +
// Does Henry have siblings? echo's 'true'
+echo $henry->has_siblings() ? 'true' : 'false';
+
+// Does Tim have siblings? echo's 'false'
+echo $tim->has_siblings() ? 'true' : 'false';
 
$related
required
The name of the relation to include.The name of the relation to include, or an array with relation names and/or relation/condition combinations.
$conditions
@@ -183,7 +185,7 @@

Cascade delete will delete as normal. Any relations that are not Soft or Temporal will be deleted as normal.

+

When deleting the record, any "constraint" defined on relations work as normal, including the delete behaviour of related "soft" or "temporal" models.

@@ -241,13 +243,13 @@

This cannot be undone, it will delete all revisions of the entity from the database. If you do this the data will be destroyed!

+

This will delete all revisions of the entity from the database!

diff --git a/packages/orm/observers/creating.html b/packages/orm/observers/creating.html index f82a90422..55013cfbf 100644 --- a/packages/orm/observers/creating.html +++ b/packages/orm/observers/creating.html @@ -109,7 +109,7 @@

Event names

- © FuelPHP Development Team 2010-2019 - FuelPHP is released under the MIT license. + © FuelPHP Development Team 2010-2026 - FuelPHP is released under the MIT license.

diff --git a/packages/orm/observers/included.html b/packages/orm/observers/included.html index 9b48cb051..2016b7c19 100644 --- a/packages/orm/observers/included.html +++ b/packages/orm/observers/included.html @@ -47,29 +47,77 @@

Observers: Included observers

Included observers are listed below:

-

Observer_Self

+

Observer_Audit

-

While not exactly good practice, in some cases it's probably cleanest to just have the event method with - your model. That's where the Observer_Self comes in, it checks if you model has a method named after - the event and prefixed with _event_. For example for the after_save event you need - to add a public method _event_after_save() to your model so it can have the observer call on - the model itself.

+

This observer logs changes made to model objects, when you insert, update or delete them.

+ +

Making this observer work requires some configuration in the ORM configuration file:

+ +
// audit observer config
+'audit' => array(
+	'enabled' => false,
+	'table' => null,
+	'session_key' => null,
+);
+

+ You need to explicitly enable the observer in the configuration (adding the observer to the model alone is not enough), + you need to define the name of the audit table to use, and you need to define which session value defines the session + uniquely. If you don't define a session key, the internal session key (the value returned by Session::key()) is used. +

+ +

+ The audit observer uses two tables to record changes: the audit log table, named in the configuration, containing a single + record per session, uniquely identifying the session, the user, and timestamps for the first and last change in the session. + And a table recording the model name, the operation type and the model property diff, in json format. It uses the table name + defined in the configuration, with a "_diff" suffix. +

+ +

These are the definitions for the audit tables (here called "audit" and "audit_diff"):

+ +
\DBUtil::create_table('audit', array(
+	'id' => array('type' => 'int', 'constraint' => 11, 'auto_increment' => true, 'null' => false, 'unsigned' => true),
+	'key' => array('type' => 'varchar', 'constraint' => 50, 'null' => false),
+	'user_id' => array('type' => 'int', 'constraint' => 11, 'null' => false),
+	'ip' => array('type' => 'varchar', 'constraint' => 45, 'null' => false),
+	'first' => array('type' => 'int', 'constraint' => 10, 'unsigned' => true, 'null' => false, 'default' => 0),
+	'last' => array('type' => 'int', 'constraint' => 10, 'unsigned' => true, 'null' => false, 'default' => 0),
+), array('id'));
+
+// unique index on key to speed up lookups
+\DBUtil::create_index('audit', 'key', 'key', 'UNIQUE');
+
+\DBUtil::create_table('audit_diff', array(
+	'id' => array('type' => 'int', 'constraint' => 11, 'auto_increment' => true, 'unsigned' => true),
+	'audit_id' => array('type' => 'int', 'constraint' => 11, 'unsigned' => true, 'null' => false),
+	'logged' => array('type' => 'int', 'constraint' => 10, 'unsigned' => true, 'null' => false, 'default' => 0),
+	'model' => array('type' => 'varchar', 'constraint' => 100, 'null' => false),
+	'type' => array('type' => 'enum', 'constraint' => array('INSERT', 'UPDATE', 'DELETE'), 'null' => false),
+	'diff' => array('type' => 'longtext', 'null' => false, 'default' => ''),
+), array('id'));
+
+\DBUtil::create_index('audit_diff', 'audit_id', 'audit_id');
+ +

Once setup, all that remains is to add the observer to your models:

// Just add the Observer
-protected static $_observers = array('Orm\\Observer_Self');
+protected static $_observers = array('Orm\\Observer_Audit');
+);
-// But if you only observe some events you can optimize by only adding those -protected static $_observers = array('Orm\\Observer_Self' => array( - 'events' => array('after_save', 'before_insert') -));
+

+ Note that this records every change to your tables if they are updated via ORM models, which means that especially + the diff table can grow large very rapidly, especially in large and much used applications. So make sure you have + provisions in place to keep the size of the tables under control, either by purging older records, or migrating + older records to archival storage. +

Observer_CreatedAt

@@ -94,6 +142,122 @@

Observer_CreatedAt

If you want to be able to set the created_at timestamp in your code, set the 'overwrite' property to false. This will cause the observer to keep the value you have set manually.

+

Observer_Typing

+ +

+ This is for 2 things: type enforcement for input and type casting for output from the DB. That means + that when you're saving the Typing observer will try to cast the input value to the expected type and + throw an exception when it can't. And when you're retrieving DB data, normally it would all be strings + (even integers and floats) but with the typing observer those will be cast to their scalar type. +

+ +

In addition to the above the Typing observer also adds support for serialized & json fields. Both should + be string type fields ("text" preferably) but will have their value encoded for saving (using + serialize() or json_encode()) and decoded when retrieving from the DB (using + unserialize() or json_decode()).

+ +

The Observer_Typing isn't meant as an alternative to validation, don't try to use it as + such. Neither are the exceptions thrown by this observer meant to be read by the visitor of your site, + they're meant to help you debug your code.

+ +
// Just add the Observer
+protected static $_observers = array('Orm\\Observer_Typing');
+
+// But adding it just for these specific events is enough
+protected static $_observers = array('Orm\\Observer_Typing' => array(
+	'events' => array('before_save', 'after_save', 'after_load')
+));
+ +

+ For this observer to work you must have your the $_properties static variable set in your + model, or not set at all using detection with DB::list_columns() (MySQL only!). When + configuring it yourself the following settings are available: +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParamValid inputDescription
data_typevarchar, int, integer, tinyint, smallint, mediumint, bigint, float, double, decimal[:d], + text, tinytext, mediumtext, longtext, enum, set, bool, boolean, serialize, encrypt, json, + time_unix, time_mysql, timestamp, date, datetime + The SQL data type, Required to have the typing observer used on a field. +
db_decimalsintNumber of decimals the value in the database has, if different from the specification in "data_type".
nullbool + Whether null is allowed as a value, default true +
character_maximum_lengthint + The maximum number of characters allowed for a string data type (varchar, text, serialized or encrypted result) +
encryption_keystring + A custom encryption key to encrypt and decrypt this value. If given, it will replace the "crypto_key" set in the Crypt config file. +
minint + The minimal value for an integer +
maxint + The maximum value for an integer +
optionsarray + Array of valid string values for set or enum data type
+ Note: currently the options themselves cannot contain comma's. +
+

+ In case of data_type "decimal", you can suffix the type with the number of decimals required. If defined, + the value returned is a string, formatted with the defined number of decimal digits, and taking the + current defined locale into account. +

+ +

+ The date and time related data types will be converted to and from a Date object. +

+ +

+ By default the Typing observer is locale aware, meaning that it can deal with incoming string values + that use a decimal comma for fields defined as decimal or float, if the current locale defines the + decimal separator as a comma. It will also format outgoing decimal values according to the current + locale settings. You can disable this by defining the "orm.use_locale" config key, either in your + main config.php file or in a separate orm.php config file, and set it to false. +

+

Observer_UpdatedAt

This observer acts only upon before_save or before_update and expects your model to have a @@ -285,118 +449,22 @@

Observer_Validation

// The fieldset will be build as HTML when cast to string echo $fieldset; -

Observer_Typing

- -

- This is for 2 things: type enforcement for input and type casting for output from the DB. That means - that when you're saving the Typing observer will try to cast the input value to the expected type and - throw an exception when it can't. And when you're retrieving DB data, normally it would all be strings - (even integers and floats) but with the typing observer those will be cast to their scalar type. -

- -

In addition to the above the Typing observer also adds support for serialized & json fields. Both should - be string type fields ("text" preferably) but will have their value encoded for saving (using - serialize() or json_encode()) and decoded when retrieving from the DB (using - unserialize() or json_decode()).

+

Observer_Self

-

The Observer_Typing isn't meant as an alternative to validation, don't try to use it as - such. Neither are the exceptions thrown by this observer meant to be read by the visitor of your site, - they're meant to help you debug your code.

+

While not exactly good practice, in some cases it's probably cleanest to just have the event method with + your model. That's where the Observer_Self comes in, it checks if you model has a method named after + the event and prefixed with _event_. For example for the after_save event you need + to add a public method _event_after_save() to your model so it can have the observer call on + the model itself.

// Just add the Observer
-protected static $_observers = array('Orm\\Observer_Typing');
+protected static $_observers = array('Orm\\Observer_Self');
 
-// But adding it just for these specific events is enough
-protected static $_observers = array('Orm\\Observer_Typing' => array(
-	'events' => array('before_save', 'after_save', 'after_load')
+// But if you only observe some events you can optimize by only adding those
+protected static $_observers = array('Orm\\Observer_Self' => array(
+	'events' => array('after_save', 'before_insert')
 ));
-

- For this observer to work you must have your the $_properties static variable set in your - model, or not set at all using detection with DB::list_columns() (MySQL only!). When - configuring it yourself the following settings are available: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParamValid inputDescription
data_typevarchar, int, integer, tinyint, smallint, mediumint, bigint, float, double, decimal[:d], - text, tinytext, mediumtext, longtext, enum, set, bool, boolean, serialize, encrypt, json, - time_unix, time_mysql - The SQL data type, Required to have the typing observer used on a field. -
db_decimalsintNumber of decimals the value in the database has, if different from the specification in "data_type".
nullbool - Whether null is allowed as a value, default true -
character_maximum_lengthint - The maximum number of characters allowed for a string data type (varchar, text, serialized or encrypted result) -
encryption_keystring - A custom encryption key to encrypt and decrypt this value. If given, it will replace the "crypto_key" set in the Crypt config file. -
minint - The minimal value for an integer -
maxint - The maximum value for an integer -
optionsarray - Array of valid string values for set or enum data type
- Note: currently the options themselves cannot contain comma's. -
-

- In case of data_type "decimal", you can suffix the type with the number of decimals required. If defined, - the value returned is a string, formatted with the defined number of decimal digits, and taking the - current defined locale into account. -

- -

- By default the Typing observer is locale aware, meaning that it can deal with incoming string values - that use a decimal comma for fields defined as decimal or float, if the current locale defines the - decimal separator as a comma. It will also format outgoing decimal values according to the current - locale settings. You can disable this by defining the "orm.use_locale" config key, either in your - main config.php file or in a separate orm.php config file, and set it to false. -

-

Observer_Slug

@@ -431,7 +499,7 @@

Observer_Slug

- © FuelPHP Development Team 2010-2019 - FuelPHP is released under the MIT license. + © FuelPHP Development Team 2010-2026 - FuelPHP is released under the MIT license.

diff --git a/packages/orm/observers/intro.html b/packages/orm/observers/intro.html index e36275b2d..3251dd9e8 100644 --- a/packages/orm/observers/intro.html +++ b/packages/orm/observers/intro.html @@ -77,7 +77,7 @@

Adding observers to your model

- © FuelPHP Development Team 2010-2019 - FuelPHP is released under the MIT license. + © FuelPHP Development Team 2010-2026 - FuelPHP is released under the MIT license.

diff --git a/packages/orm/relations/belongs_to.html b/packages/orm/relations/belongs_to.html index 33e3ac436..e28db39fb 100644 --- a/packages/orm/relations/belongs_to.html +++ b/packages/orm/relations/belongs_to.html @@ -82,7 +82,7 @@

Full config example with defaults as values

'model_to' => 'Model_Post', 'key_to' => 'id', 'cascade_save' => true, - 'cascade_delete' => false, + 'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT, ) );
@@ -91,7 +91,7 @@

Full config example with defaults as values

diff --git a/packages/orm/relations/has_many.html b/packages/orm/relations/has_many.html index c6d4ec415..52bc6de94 100644 --- a/packages/orm/relations/has_many.html +++ b/packages/orm/relations/has_many.html @@ -84,7 +84,7 @@

Full config example with defaults as values

'model_to' => 'Model_Comment', 'key_to' => 'post_id', 'cascade_save' => true, - 'cascade_delete' => false, + 'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT, ) ); @@ -93,7 +93,7 @@

Full config example with defaults as values

diff --git a/packages/orm/relations/has_one.html b/packages/orm/relations/has_one.html index e364e49c4..6e72af10c 100644 --- a/packages/orm/relations/has_one.html +++ b/packages/orm/relations/has_one.html @@ -84,7 +84,7 @@

Full config example with defaults as values

'model_to' => 'Model_Profile', 'key_to' => 'user_id', 'cascade_save' => true, - 'cascade_delete' => false, + 'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT, ) ); @@ -93,7 +93,7 @@

Full config example with defaults as values

diff --git a/packages/orm/relations/intro.html b/packages/orm/relations/intro.html index a92b00f89..d5582a51f 100644 --- a/packages/orm/relations/intro.html +++ b/packages/orm/relations/intro.html @@ -71,7 +71,7 @@

Fully configured

'key_from' => 'article_id', 'key_to' => 'parent_article_id', 'cascade_save' => true, - 'cascade_delete' => false, + 'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT, // there are some more options for specific relation types )); @@ -131,8 +131,8 @@

Configuration Options

argument when calling save() or delete() on the originating model. - cascade_delete - bool false + constraint + string \Orm\Relation::CONSTRAINT_SETDEFAULT conditions @@ -145,6 +145,24 @@

Configuration Options

+ +

+ 'constraint' here refers to the ON DELETE constraint of the relation. The options are:
+ \Orm\Relation::CONSTRAINT_SETDEFAULT, when deleting a parent, set the foreign key in the child to its default value, as defined in the model properties. + If no default is defined, the foreign key is set to null.
+ \Orm\Relation::CONSTRAINT_CASCADE, when deleting a parent, also delete all related children.
+ \Orm\Relation::CONSTRAINT_RESTRICT, throw an DeleteConstraintViolation exception when the parent has related children. + The message of the exception will contain the name of the relation on which the violation occurred, which can be used to provide feedback to the user. +

+

+ Note that 'constraint' can be defined on all relationship types. Be careful however of using \Orm\Relation::CONSTRAINT_CASCADE on a "belongs_to" relation, + as that would delete the parent record, which only makes sense if the other side of the relationship is defined as "has_one". +

+

+ The boolean option 'cascade_delete' is deprecated as of Fuel v1.9. It did the same as \Orm\Relation::CONSTRAINT_CASCADE when true, or + \Orm\Relation::CONSTRAINT_SETDEFAULT when false, but lacked the option to block deletes when related records are present. +

+
@@ -300,25 +318,23 @@

Relation types

The Orm supports the following relationship types natively:

+
diff --git a/packages/orm/relations/many_many.html b/packages/orm/relations/many_many.html index cd356fbf7..e140209af 100644 --- a/packages/orm/relations/many_many.html +++ b/packages/orm/relations/many_many.html @@ -158,16 +158,114 @@

Full config example with defaults as values

'model_to' => 'Model_User', 'key_to' => 'id', 'cascade_save' => true, - 'cascade_delete' => false, + 'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT, ) ); + +

A hybrid many-many relation

+ +

+ There are situations where you have a many-many relation between two tables, but you have values that are + not a property of either table, but of the relation. +

+ +

+ An example of this could be a relation between "Recipes" and "Ingredients", where a recipe has multiple ingredients, + and ingredients can be used in multiple recipes. But something like how much of an ingredient is needed in the recipe + is a property of the relation between the two. In this case, you can define a hybrid relation between the two: +

+ +
// recipe model relationship definitions
+class Model_Recipe extends /Orm/Model
+{
+	protected static $_many_many = array(
+		'ingredients' => array(
+			'key_from' => 'id',
+			'key_through_from' => 'recipe_id', // column 1 from the table in between, should match a recipe.id
+			'table_through' => 'recipes_ingredients', // both models plural without prefix in alphabetical order
+			'key_through_to' => 'ingredient_id', // column 2 from the through-table, should match an ingredient.id
+			'model_to' => 'Model_Ingredient',
+			'key_to' => 'id',
+			'cascade_save' => true,
+			'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT, // don't delete the ingredients when deleting the recipe
+		)
+	);
+
+	protected static $_has_many = array(
+		'comments' => array(
+			'key_from' => 'id',
+			'model_to' => 'Model_RecipeIngredient',
+			'key_to' => 'recipe_id',
+			'cascade_save' => true,
+			'constraint' => \Orm\Relation::CONSTRAINT_CASCADE, // delete the through table records on delete of the recipe
+		)
+	);
+}
+
+// ingredient model relationship definitions
+class Model_Ingredient extends /Orm/Model
+{
+	protected static $_many_many = array(
+		'recipes' => array(
+			'key_from' => 'id',
+			'key_through_from' => 'ingredient_id', // column 1 from the table in between, should match an ingredient.id
+			'table_through' => 'recipes_ingredients', // both models plural without prefix in alphabetical order
+			'key_through_to' => 'recipe_id', // column 2 from the through-table, should match a recipe.id
+			'model_to' => 'Model_Ingredient',
+			'key_to' => 'id',
+			'cascade_save' => true,
+			'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT,  // don't delete the recipes when deleting the ingredient
+		)
+	);
+
+	protected static $_has_many = array(
+		'comments' => array(
+			'key_from' => 'id',
+			'model_to' => 'Model_RecipeIngredient',
+			'key_to' => 'ingredient_id',
+			'cascade_save' => true,
+			'constraint' => \Orm\Relation::CONSTRAINT_CASCADE, // delete the through table records on delete of the ingredient
+		)
+	);
+
+// through-table model relationship definitions
+class Model_RecipeIngredient extends /Orm/Model
+{
+	protected static $_table_name = 'recipes_ingredients';
+
+	protected static $_belongs_to = array(
+		'recipe' => array(
+			'key_from' => 'recipe_id',
+			'model_to' => 'Model_Recipe',
+			'key_to' => 'id',
+			'cascade_save' => true,
+			'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT,
+		),
+		'ingredient' => array(
+			'key_from' => 'ingredient_id',
+			'model_to' => 'Model_Ingredient',
+			'key_to' => 'id',
+			'cascade_save' => true,
+			'constraint' => \Orm\Relation::CONSTRAINT_SETDEFAULT,
+		)
+	);
+}
+ +

+ If you're just using it as a "through-table", that table doesn't have to have its own primary key, you could define a compound + primary key for it, using both the "recipe_id" and "ingredient_id" properties. Although this works fine for the hybrid relationship + too, you'll have to use array('recipe_id', 'ingredient_id') for the "key_from" of the "has_many", and "key_to" of the "belongs_to" + relation definitions. Depending on your requirements it might be better to give the through table its own primary "id" key (as used in this example) + and use unique indexes for the foreign keys. +

+ diff --git a/packages/orm/tasks.html b/packages/orm/tasks.html new file mode 100644 index 000000000..99da602c1 --- /dev/null +++ b/packages/orm/tasks.html @@ -0,0 +1,102 @@ + + + + + + + + + + + Tasks - Orm Package - FuelPHP Documentation + + +
+ + +
+

Tasks

+ +

+ The ORM package comes with tasks that automate things, thus making your life just that little bit easier. +

+ +

+

ModelDBCheck

+ +

The modeldbcheck task allows you to check your application model definition against the current database schema.

+ +

+ The task will scan your application "classes" directories, in the app, and all defined model and package + paths, for model classes that extend "\Orm\Model", either directly, or via a base class. +

+ +

+ For every ORM model found, it will connect to the database using the models' defined table name, and retrieve + the database schema for that table. It will then verify if all columns defined in the schema are present + in the model properties list. If not, it will generate a basic array for that column, which you can simply + copy and paste into the model. It will also warn you if you have columns defined that are no longer + present in the schema. +

+ +
$ php oil refine modeldbcheck
+
+ +

+ Which will return something like +

+ +
Enumerate application models...
+10 ORM database models found to verify
+
+Verifying model \Auth\Model\Auth_Group
+Verifying model \Auth\Model\Auth_User
+Verifying model \Auth\Model\Auth_Role
+Verifying model \Auth\Model\Auth_Grouppermission
+Verifying model \Auth\Model\Auth_Metadata
+Verifying model \Auth\Model\Auth_Permission
+Verifying model \Auth\Model\Auth_Provider
+Verifying model \Auth\Model\Auth_Rolepermission
+Verifying model \Auth\Model\Auth_Userpermission
+
+ +

+ If you define and run your own migrations, there is usually no mismatch detected. But if your database is + designed and managed by a third party, or is part of another application, you might find the schema changed + since the last time you checked or worked on the application. +

+

+ In these cases it is important to check, and missing columns in your model properties definition aren't + detected until you want to access a column, or you do an INSERT or UPDATE and a missing column is defined + in the schame as "NOT NULL", in which case the query will fail. +

+ +
+ +
+ + +
+ + diff --git a/packages/orm/views.html b/packages/orm/views.html new file mode 100644 index 000000000..c76e61ff4 --- /dev/null +++ b/packages/orm/views.html @@ -0,0 +1,156 @@ + + + + + + + + + + + View support - Orm Package - FuelPHP Documentation + + +
+ + +
+

View support

+ +

+ You can also use the ORM to query database views instead of tables. +

+

+ Two different ways of accessing views are supported: +

+

+ +
+

View model

+ +

+ A view model is simply a model that uses a view instead of a table for selection.
+ The resulting model objects will be read-only, any attempt to call save() or update() will return false. +

+ +
class Model_Patient extends \Orm\Model
+
+	/**
+	 * @var  string  define the name of the view this model should use
+	 *
+	 * @var string
+	 */
+	protected static $_table_name = 'vw_PatientView';
+
+	/**
+	 * @var  bool  the given table name is actually a view
+	 */
+	protected static $_table_is_view = true;
+
+}
+ +

And that is it. Define the rest of the model as usual, and use is as a standard model:

+
// get the patient record for John Doe
+$patient = Model_Patient::query()->where('name', '=', 'John Doe')->get_one();
+
+// will return false, not throw an error !
+$patient->save();
+
+ +

+ Note that for models, it is required to define a primary key, which by default is 'id'. If your view does not have + an 'id' column, make sure the view has one or more fields which can be defined as primary key. +

+ +
+ +
+

Table model with views

+ +

+ This is a standard ORM model, with the addition that you can define views to return table data. This can be useful in organisations + where views are used by the database administrator to hide the selection SQL from the application developers. +

+ +
class Model_Patient extends \Orm\Model
+
+	/**
+	 * @var  string  define the name of the view this model should use
+	 *
+	 * @var string
+	 */
+	protected static $_table_name = 'Patients';
+
+	/**
+	 * @var  bool  define the available views on this table
+	 */
+	protected static $_views = array(
+		// use the alias 'discharged' for the view 'vw_PatientsDischargedToday'
+		// view returns all columns defined in the model properties
+		'discharged' => array(
+			'view' => 'vw_PatientsDischargedToday',
+			'columns' => true,
+		),
+		// use the view name directly
+		// and returns a specific set of columns
+		'vw_PatientsAdmittedToday'  => array(
+			'columns' => array(
+				'id',
+				'name',
+				'dob',
+				'room',
+			),
+		),
+	);
+}
+ +

You can now query the model the usual way, or define you want to use one of the views:

+
// check if John Doe was discharged today
+$patient = Model_Patient::query()->use_view('discharged')->where('name', '=', 'John Doe')->get_one();
+
+if ($patient)
+{
+	// yes, John Doe is discharged today
+}
+
+// check if John Doe was admitted today
+$patient = Model_Patient::query()->use_view('vw_PatientsAdmittedToday')->where('name', '=', 'John Doe')->get_one();
+
+if ($patient)
+{
+	// yes, John Doe is admitted today
+	// note the patient object will contain a subset of data due to the column list defined
+}
+ +
+ +
+ + +
+ + diff --git a/packages/parser/intro.html b/packages/parser/intro.html index 3da3e9066..4609b2d04 100644 --- a/packages/parser/intro.html +++ b/packages/parser/intro.html @@ -70,7 +70,7 @@

Installation

"smarty/smarty" : "*", "twig/twig" : "*", "mthaml/mthaml": "*", - "pyrocms/lex": "*" + "8ctopus/lex": "*" } } @@ -136,7 +136,7 @@

Runtime Configuration

diff --git a/requirements.html b/requirements.html index f709d07da..389c3c1a5 100644 --- a/requirements.html +++ b/requirements.html @@ -41,7 +41,7 @@
Web Server
PHP Version
PHP Extensions

@@ -92,7 +92,7 @@

diff --git a/templates/index.html b/templates/index.html index 3f8f51c53..74fc07a49 100644 --- a/templates/index.html +++ b/templates/index.html @@ -44,7 +44,7 @@

diff --git a/toc.html b/toc.html index 9d68c9223..1880912d4 100644 --- a/toc.html +++ b/toc.html @@ -307,6 +307,8 @@

Orm Package

Parser Package

@@ -344,7 +347,7 @@

Parser Package

diff --git a/vendor/htmlawed.html b/vendor/htmlawed.html index b14204fc0..d1456367e 100644 --- a/vendor/htmlawed.html +++ b/vendor/htmlawed.html @@ -69,7 +69,7 @@

Application Usage

diff --git a/vendor/intro.html b/vendor/intro.html index e22c1c10f..05b21a1c5 100644 --- a/vendor/intro.html +++ b/vendor/intro.html @@ -45,23 +45,23 @@

Introduction

Pre-installed packages

Composer packages

diff --git a/vendor/markdown.html b/vendor/markdown.html index b483ed964..01dddb55e 100644 --- a/vendor/markdown.html +++ b/vendor/markdown.html @@ -66,7 +66,7 @@

Application Usage

diff --git a/vendor/phpquickprofiler.html b/vendor/phpquickprofiler.html index 31842885b..e1c6bafb2 100644 --- a/vendor/phpquickprofiler.html +++ b/vendor/phpquickprofiler.html @@ -46,7 +46,9 @@

Introduction

Documentation

-

You can find the original documentation here.

+

+ PHPQuickProfiler was originally made by ParticleTree, which no longer exists.
+ You may find an old version of the original documentation in the Way Back Machine, here.

FuelPHP Usage

@@ -57,6 +59,9 @@

FuelPHP Usage

Application Usage

+ The profiler is active by default for applications running in DEVELOPMENT mode. It can manually be switched on or off in your code by setting the + boolean \Fuel::$profiler. Is is only active if the output content type is detected to be "text/html". +

You can add data to PHPQuickProfiler through the Profiler class methods.

@@ -64,7 +69,7 @@

Application Usage

diff --git a/vendor/phpseclib.html b/vendor/phpseclib.html index 45f2a5a14..5f50c222a 100644 --- a/vendor/phpseclib.html +++ b/vendor/phpseclib.html @@ -44,7 +44,7 @@

Introduction

Documentation

-

You can find the original PHPSecLib documentation here.

+

You can find the original PHPSecLib documentation here.

FuelPHP Usage

@@ -55,7 +55,6 @@

FuelPHP Usage

Application Usage

- For use in your application the PHPSecLib package has been converted to a namespaced, autoloader friendly library. You can use it directly from your application.

<?php
@@ -68,7 +67,7 @@ 

Application Usage

public function action_index() { // connect to my server - $ssh = new \phpseclib\Net\SSH2('myserver.example.org'); + $ssh = new \phpseclib3\Net\SSH2('myserver.example.org'); // login if ( ! $ssh->login('username', 'password')) @@ -85,11 +84,18 @@

Application Usage

}
+

+ Note that the current version of PHPSecLib defined in Fuel's composer.json is version 3. If you want to change the version, + please be aware that it may have tighter PHP version restrictions than Fuel itself has. Also, Fuel's Crypt class references + the phpseclib3 namespace, so you might want to alias either the PHPSecLib 3 namespace, or overload the Crypt class, to + address this problem. +

+ diff --git a/vendor/spyc.html b/vendor/spyc.html index 8e650ccb6..16827eca1 100644 --- a/vendor/spyc.html +++ b/vendor/spyc.html @@ -68,7 +68,7 @@

Application Usage