- The default setup to use if none is profided.
+ The default setup to use if none is provided.
@@ -209,6 +209,17 @@
Group configuration
Use this to set a default from name.
+
+
force_to
+
null|array
+
null
+
+ Set to an array with email and name (like from) to force all outgoing emails to be
+ send to this address instead.
+ This allows you to test outgoing emails without sending them to real addresses
+ by mistake.
+
+
validate
bool
@@ -341,6 +352,19 @@
Group configuration
https://, since some Email clients (Outlook!) do not support relative protocol URI's.
+
+
+
+
Mandrill configuration
+
+
+
+
+
Param
+
Type
+
Default
+
Description
+
mandrill.key
string
@@ -385,10 +409,9 @@
Group configuration
- Make sure to install mandrill/mandrill composer package if you want to use the mandrill driver.
+ Make sure to install the mandrill/mandrill composer package if you want to use the mandrill driver.
-
Mailgun configuration
@@ -400,7 +423,7 @@
Mailgun configuration
Description
-
key
+
mailgun.key
string
'YOUR KEY'
@@ -408,7 +431,7 @@
Mailgun configuration
-
domain
+
mailgun.domain
string
'YOUR DOMAIN'
@@ -416,17 +439,22 @@
Mailgun configuration
-
endpoint
+
mailgun.endpoint
string|null
'ALTERNATIVE API ENDPOINT URL'
- (OPTIONAL) Omit entirely or set to null to use Mailgun's default endpoint.
- Can be set to match the region in which the domain is provisioned.
+ (OPTIONAL) Omit entirely or set to null to use Mailgun's default endpoint.
+ Can be set to match the region in which the domain is provisioned.
Example: 'https://api.eu.mailgun.net/v3'
+
+
+ Make sure to install the mailgun/mailgun-php composer package if you want to use the mandrill driver.
+
+
From 7e7cd7dfe51c02c60940a705a79a3f20f621aa75 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Thu, 28 Sep 2023 23:22:21 +0100
Subject: [PATCH 02/62] documented Date strptime() and strftime() methods, and
clarified create_from_string() and timezones
---
classes/date.html | 142 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 138 insertions(+), 4 deletions(-)
diff --git a/classes/date.html b/classes/date.html
index 226577ae0..5804f2768 100644
--- a/classes/date.html
+++ b/classes/date.html
@@ -182,9 +182,9 @@
create_from_string($input, $pa
The create_from_string creates a Date object from a named date string format or a
pattern.
-
This function uses strptime()
- for i18n purposes. That method isn't available on Windows hosts however, on those strtotime()
- is used instead and the pattern is ignored.
+
This function uses strptime().
+ This method isn't available on Windows hosts, and deprecated in PHP 8.1. In those cases, we emulate it using Date::strptime().
+ Note that this doesn't have locale support (i.e. pattern values representing locale values, like %c).
+ The Date object created is always in the currently active timezone. If a timezone is detected in the format,
+ its offset will be taken into account when creating the object.
+
@@ -464,6 +478,125 @@
is_valid($date, 'Y-m-d')
+
+
+
strftime($format, $timestamp)
+
The strftime method is a drop-in replacedment for the PHP strftime() function, for use on Windows or PHP 8.1+.
+ Unlike the PHP built-in function, this version has support for timezones. If a timezone is detected in the format, like
+ "%z" in the above example, an additional "tm_zone" value is returned in the array, indicating the offset to UTC, in seconds.
+
+
+
format($pattern_key = 'local', $timezone = null)
@@ -472,7 +605,8 @@
format($pattern_key = 'local', $timezone =
by creating a similar file in app/config.
This method uses patterns for the strftime()
- function instead of date() to allow for i18n.
+ function instead of date() to allow for i18n. On PHP 8.1 or higher, or on platforms not supporting this function,
+ it is emulated by Date::strftime().
If you don't pass a timezone, the timezone set on the current Date object will be used when formatting the result. By default,
this is the system timezone, unless you have altered it by calling the set_timezone() method. You can also pass
From 98e32c57214b537a517f79838a5b8a7621615429 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Thu, 28 Sep 2023 23:23:36 +0100
Subject: [PATCH 03/62] Clarified the side-effect of using Format::from_json()
for input hydration
---
classes/input.html | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/classes/input.html b/classes/input.html
index cf0d8ce09..07229b3b9 100644
--- a/classes/input.html
+++ b/classes/input.html
@@ -99,7 +99,7 @@
json($index = null, $default = null)
Returns
-
array - Format::from_json result array
+
array
Example
@@ -112,6 +112,10 @@
json($index = null, $default = null)
+
+ Note that the hydration code in the Input class uses Format::forge($input, 'json')->to_array(), to be able to access nested data using dot‑notation.
+ This implies that all json objects in the input are converted to arrays instead of StdClass !
+
From e2e4ef5a4b7e39ef48faf0044fba79ed94cef279 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Tue, 3 Oct 2023 17:16:35 +0100
Subject: [PATCH 04/62] to(), cc(), bcc() and reply_to() accept the same
arguments now
from() also accepts an assoc array, but not multiple from addresses
(although techically allowed by RFC5322)
---
packages/email/methods.html | 65 ++++++++++++++++++++++++++++++-------
1 file changed, 53 insertions(+), 12 deletions(-)
diff --git a/packages/email/methods.html b/packages/email/methods.html
index 441714435..14c037b2f 100644
--- a/packages/email/methods.html
+++ b/packages/email/methods.html
@@ -319,7 +319,7 @@
Note that while RFC5322 explicitly allows multiple From addresses, we do not support this, as most email clients can handle this.
+
+
@@ -432,11 +446,17 @@
to($email, $name = false)
// Add a single address
$email->to('me@example.com', 'My Name');
-// Add multiple addresses
+// Using an assoc array
+$email->to(array(
+ 'email' => 'me@example.com',
+ 'name' => 'My Name',
+));
+
+// Or as an key-value array
$email->to(array(
'me@example.com',
// with a name
- 'me@example.com' => 'His/Her name.',
+ 'me@example.com' => 'My Name',
));
@@ -489,11 +509,17 @@
cc($email, $name = false)
// Add a single address
$email->cc('me@example.com', 'My Name');
-// Add multiple addresses
+// Using an assoc array
+$email->cc(array(
+ 'email' => 'me@example.com',
+ 'name' => 'My Name',
+));
+
+// Or as an key-value array
$email->cc(array(
'me@example.com',
// with a name
- 'me@example.com' => 'His/Her name.',
+ 'me@example.com' => 'My Name',
));
@@ -546,11 +572,18 @@
bcc($email, $name = false)
// Add a single address
$email->bcc('me@example.com', 'My Name');
-// Add multiple addresses
+// Using an assoc array
+$email->bcc(array(
+ 'email' => 'me@example.com',
+ 'name' => 'My Name',
+));
+
+// Or as an key-value array
$email->bcc(array(
+ // without a name
'me@example.com',
// with a name
- 'me@example.com' => 'His/Her name.',
+ 'me@example.com' => 'My Name',
));
@@ -603,17 +636,25 @@
reply_to($email, $name = false)
// Add a single address
$email->reply_to('me@example.com', 'My Name');
-// Add multiple addresses
+// Using an assoc array
+$email->reply_to(array(
+ 'email' => 'me@example.com',
+ 'name' => 'My Name',
+));
+
+// Or as an key-value array
$email->reply_to(array(
+ // without a name
'me@example.com',
// with a name
- 'me@example.com' => 'His/Her name.',
+ 'me@example.com' => 'My Name',
));
+
Note that while RFC5322 explicitly allows multiple Reply-To addresses, it is discouraged to use this, as not all email clients can handle this.
@@ -742,7 +783,7 @@
clear_addresses()
$email->to(array(
'me@example.com',
// with a name
- 'me@example.com' => 'His/Her name.',
+ 'me@example.com' => 'My Name',
));
// Reset them
From a67980e8fc1e57da98846ed68be86cbccc40d4e8 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Thu, 12 Oct 2023 19:58:31 +0100
Subject: [PATCH 05/62] "force_to" is no longer an array but a string
---
packages/email/introduction.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/email/introduction.html b/packages/email/introduction.html
index 3efb490e5..6e01a14e8 100644
--- a/packages/email/introduction.html
+++ b/packages/email/introduction.html
@@ -211,10 +211,10 @@
Group configuration
force_to
-
null|array
+
null|string
null
- Set to an array with email and name (like from) to force all outgoing emails to be
+ Set to a valid email address to force all outgoing emails to be
send to this address instead.
This allows you to test outgoing emails without sending them to real addresses
by mistake.
From 121d139c83c8969b68f76733ab4141997fc3a99d Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Tue, 31 Oct 2023 18:14:26 +0000
Subject: [PATCH 06/62] document changes to how framework notices are processed
---
general/configuration.html | 2 +-
general/error.html | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/general/configuration.html b/general/configuration.html
index d3e16e8cc..1e443bda0 100644
--- a/general/configuration.html
+++ b/general/configuration.html
@@ -189,7 +189,7 @@
Configuration options
bool
true
- Whether to show notices.
+ If true, PHP notices are shown inline. If false, an E_USER_NOTICE exception is thrown.
in your applications config/config.php file. Errors defined here will allow your script to continue. No exception
is being thrown for these errors, so you can no longer catch these.
+
+ If you want to continue on PHP notices, you may also want to add E_USER_NOTICE to the array, as the framework uses this code to generate its own notices.
+
It is strongly adviced not to continue on errors, as it might lead to bugs in your code that are
very difficult to find once your application moves into production!
From 2a6338a627fbafe9ff173d97ddca9c8f628abc25 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Wed, 29 Nov 2023 15:23:01 +0000
Subject: [PATCH 07/62] document route recursion
---
general/configuration.html | 9 +++++++++
general/routing.html | 31 +++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/general/configuration.html b/general/configuration.html
index 1e443bda0..fd67d7b31 100644
--- a/general/configuration.html
+++ b/general/configuration.html
@@ -597,6 +597,15 @@
Configuration options
you load the module.
+
+
.recursive
+
bool
+
false
+
+ If true, and the requested URI resolves to a defined route, but that route doesn't resolve
+ to a valid controller action, the resolved route will be processed again by the routing engine.
+
+ 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',
+);
From 81a9d1ff8567dbf0303e871916553ec8f55dde29 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Thu, 23 May 2024 10:38:35 +0100
Subject: [PATCH 08/62] [ftp] all configuration values are now optional and
have defaults
---
classes/ftp.html | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/classes/ftp.html b/classes/ftp.html
index eaa4653bb..a86f41d8c 100644
--- a/classes/ftp.html
+++ b/classes/ftp.html
@@ -64,7 +64,7 @@
Configuration
string
''
- Optional: Name of the user to connect with, if login is required.
+ Name of the user to connect with, if login is required.
@@ -72,7 +72,7 @@
Configuration
string
''
- Optional: Name of the password to connect with, if login is required.
+ Name of the password to connect with, if login is required.
@@ -117,6 +117,10 @@
Configuration
+
+ All configuration values are optional. If you omit one, the default value as given above will be used.
+ This is also the case when you pass a array with custom configuration values when you call forge().
+
- Used for setlocale() required on some php installations, set to false
- to prevent it from being set.
+ Used for set the locale for your application, if it differs from the default server or
+ PHP configuation locale. See setlocale() for more information. The syntax setting for this can differ per OS, Ubuntu for example requires a
- .utf8 (the encoding) suffix.
+ .utf8 (the encoding) suffix.. To pass multiple options, pass an indexed array
+ with the locales in order of preference.
+ If you don't need to use a difffent locale, set to false.
+
+
+
locale_category
+
int | array
+
LC_ALL
+
+ One or more categories for which the defined locale should be set, define either a single
+ category, like the LC_ALL constant, or an indexed array of LC constants.
+ The array is needed because unlike other constants in PHP, these can't be or'd.
From 7e3090151be5a713929d3681896bad2967b4c2a3 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Tue, 30 Jul 2024 00:15:15 +0100
Subject: [PATCH 10/62] updated with the changed to the login system
---
packages/auth/intro.html | 16 +++++++++++++
packages/auth/ormauth/usage.html | 36 ++++++++++++++---------------
packages/auth/simpleauth/usage.html | 24 +++++++++----------
3 files changed, 46 insertions(+), 30 deletions(-)
diff --git a/packages/auth/intro.html b/packages/auth/intro.html
index 7fe4c9d8c..71f38b709 100644
--- a/packages/auth/intro.html
+++ b/packages/auth/intro.html
@@ -138,6 +138,14 @@
Configuration
this makes it possible to login in multiple ways at the same time.
+
+
login_type
+
string
+
'both'
+
+ The type of login you want to utilize. Valid values are "username", "email" or "both".
+
+
salt
string
@@ -163,6 +171,14 @@
Configuration
login drivers, it does not mean "allow a user to be logged-in multiple times"!
+
+ Note that for 'login_type', "both" is the default for compatibility with existing installations, but should be used with
+ caution, as theoretically, you can create a user with as the username the email address from an existing user, and thus
+ potentionally hijack or block that account.
+ Also, even though in most methods both values where checked, not all methods did support checking both, or could find multiple
+ user records and blindly use the first one found. Which is another reason not to use "both" !
+
The change_password method allows you to change a users password.
@@ -461,9 +461,9 @@
change_password($old_password, $n
The users new password
-
$username
-
null
-
The name of the user of whom you want to change the password
+
$username_or_email
+
''
+
Either the users username or email address of the user you want to change the password of (depending on the "login_type" config setting). If not given, the currently logged-in user is assumed.
@@ -493,7 +493,7 @@
change_password($old_password, $n
-
reset_password($username)
+
reset_password($username_or_email)
The reset_password method allows you to assign a new random password to a user.
@@ -513,9 +513,9 @@
reset_password($username)
Description
-
$username
-
required
-
The name of the user for whom you want to reset the password
+
$username_or_email
+
''
+
Either the users username or email address you want to reset the password for (depending on the "login_type" config setting).
@@ -541,7 +541,7 @@
reset_password($username)
-
delete_user($username)
+
delete_user($username_or_email)
The delete_user method allows you to delete a user account.
@@ -561,9 +561,9 @@
delete_user($username)
Description
-
$username
-
required
-
The name of the user account you want to delete
+
$username_or_email
+
''
+
Either the users username or email address of the user you want to delete (depending on the "login_type" config setting).
The change_password method allows you to change a users password.
@@ -453,9 +453,9 @@
change_password($old_password, $n
The users new password
-
$username
+
$username_or_email
null
-
The name of the user of whom you want to change the password
+
The name or the email of the user of whom you want to change the password (depending on the "login_type" config setting).
@@ -485,7 +485,7 @@
change_password($old_password, $n
-
reset_password($username)
+
reset_password($username_or_email)
The reset_password method allows you to assign a new random password to a user.
@@ -505,9 +505,9 @@
reset_password($username)
Description
-
$username
+
$username_or_email
required
-
The name of the user for whom you want to reset the password
+
The name or email of the user for whom you want to reset the password (depending on the "login_type" config setting).
@@ -533,7 +533,7 @@
reset_password($username)
-
delete_user($username)
+
delete_user($username_or_email)
The delete_user method allows you to delete a user account.
@@ -553,9 +553,9 @@
delete_user($username)
Description
-
$username
+
$username_or_email
required
-
The name of the user account you want to delete
+
The name or the email of the user account you want to delete (depending on the "login_type" config setting).
From fb7e1b31937db12dcc6e77fe1ee3274060623967 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Mon, 5 Aug 2024 18:12:46 +0100
Subject: [PATCH 11/62] documented new finder behaviour with "::" file prefix
and added an example of the usage at Lang::load().
---
classes/finder.html | 27 +++++++++++++++++++++++++--
classes/lang.html | 8 +++++++-
2 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/classes/finder.html b/classes/finder.html
index 79e491e00..90232d604 100644
--- a/classes/finder.html
+++ b/classes/finder.html
@@ -189,7 +189,18 @@
search($dir, $file, $ext = '.php', $multip
// will return APPPATH/views/welcome/index.php
$viewfile = Finder::search('views', 'welcome/index');
-
+
+// assuming we're in active module called "test", and requested all files, this could
+// return array("MODULEPATH/test/lang/en/welcome.php", "APPPATH/lang/en/welcome.php")
+$lang = Finder::search('lang/en', 'welcome');
+
+// searching explicitly in the module, this would return
+// array("MODULEPATH/test/lang/en/welcome.php")
+$lang = Finder::search('test::lang/en', 'welcome', '.php', true);
+
+// searching explicitly in the app, this would return
+// array("APPPATH/lang/en/welcome.php")
+$lang = Finder::search('::lang/en', 'welcome', '.php', true);
@@ -445,7 +456,19 @@
locate($dir, $file, $ext = '.php', $multip
// will return APPPATH/views/welcome/index.php
$viewfile = Finder::instance()->locate('views', 'welcome/index');
-
+
+// assuming we're in active module called "test", and requested all files, this could
+// return array("MODULEPATH/test/lang/en/welcome.php", "APPPATH/lang/en/welcome.php")
+$lang = Finder::instance()->locate('lang/en', 'welcome');
+
+// searching explicitly in the module, this would return
+// array("MODULEPATH/test/lang/en/welcome.php")
+$lang = Finder::instance()->locate('test::lang/en', 'welcome', '.php', true);
+
+// searching explicitly in the app, this would return
+// array("APPPATH/lang/en/welcome.php")
+$lang = Finder::instance()->locate('::lang/en', 'welcome', '.php', true);
+
load($file, $group = null, $language = null,
// Will load the example language file in italian.
// If that doesn't exist, it will load the configured language
-Lang::load('foo::example', 'bar', 'it');
+Lang::load('foo::example', 'bar', 'it');
+
+// Special case: if you want to explicitly load a file from the app, even when in
+// an active module, use this. It will not merge the result with a file in the
+// active module, like a standard load would do.
+Lang::load('::example', 'bar', 'it');
+
From a251ef05cc3a2ad00ae9fe4c632f7039d8033fcc Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Tue, 13 Aug 2024 16:24:13 +0100
Subject: [PATCH 12/62] added route statement :decimal
---
general/routing.html | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/general/routing.html b/general/routing.html
index 5cb714c99..81d63f6e0 100644
--- a/general/routing.html
+++ b/general/routing.html
@@ -90,6 +90,7 @@
Slightly Advanced Routing
:everything - Like :any, but also matches "nothing"
:segment - This matches only 1 segment in the URI, but that segment can be anything
:num - This matches any numbers
+
:decimal - This matches decimal values, with support for both decimal point and decimal comma.
:alpha - This matches any alpha characters, including UTF-8
:alnum - This matches any alphanumeric characters, including UTF-8
@@ -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.
+
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:
+
+ 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.
+
+ 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.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$objpk
+
required
+
The 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.
+
+
+
+
+
+
Returns
+
Orm\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.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
Returns
+
array()
+
+
+
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.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
Returns
+
string | null
+
+
+
Example
+
+
// retrieves the MyModel write connection
+$where = MyModel::connection(true);
+
+
+
+
+
+
+
+
find($id = null, $options = null)
+
The find method allows you to retrieve record as a model object or an array of model objects.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
Returns
+
Orm/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.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
+
Returns
+
void
+
+
+
+
+
+
+
primary_key()
+
The primary_key method returns an array with the column names that define the tables primary key.
+
+
+
+
Static
+
Yes
+
+
+
Returns
+
array
+
+
+
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.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$data
+
required
+
+ 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.
+
+
+
+
+
+
+
Returns
+
string | null
+
+
+
Example
+
+
// with PK defined as both columns "A" and "B", this returns "[1][2]"
+$pk = MyModel::implode_pk(array('A' => 1, 'B' => 2));
The forge method allows you to forge a new model instance.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
+
Returns
+
string | 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.
+
+
+
+
+
Static
+
Yes
+
+
+
Returns
+
array
+
+
+
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.
+
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$key
+
null
+
+ Name of a numeric table column. If not given, the primary key column is used.
+
+
+
+
+
+
+
Returns
+
int | 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.
+
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$key
+
null
+
+ Name of a numeric table column. If not given, the primary key column is used.
+
+
+
+
+
+
+
Returns
+
int | 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.
+
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
+
Returns
+
mixed
+
+
+
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.
+
+
+
+
+
Static
+
Yes
+
+
+
Returns
+
array
+
+
+
+
+ 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 through an exception instead.
+
+
+
+
+
property($key, $default = null)
+
+ The property method returns the definition of a single model class property.
+
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$key
+
required
+
+ Name of a property you want to query.
+
+
+
+
$default
+
null
+
+ The value to return is no data was found.
+
+
+
+
+
+
+
Returns
+
mixed
+
+
+
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.
+
+
+
+
+
Static
+
Yes
+
+
+
Returns
+
ORM\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.
+
+
+
+
related_class($relation)
+
+ The related_class method returns the name of the ORM model class a relation maps to.
+
+ 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.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$cascade
+
null
+
+ This argument allows you to control cascading delete behaviour.
null
is the default behavior,
+ configured in the relations of the model.
true
or
false
+ explicitly enables or disables cascading deletes, or you can pass an array with relation names, if you want selective cascading deletes.
+
+
+
+
$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.
+
+
+
+
+
+
+
Returns
+
Model | 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.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$event
+
required
+
+ 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'.
+
+
+
+
+
+
+
Returns
+
void
+
+
+
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().
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$event
+
required
+
+ 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'.
+
+
+
+
+
+
+
Returns
+
void
+
+
+
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.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$values
+
required
+
+ 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.
+
+
+
+
+
+
+
Returns
+
Model, 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.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$property
+
required
+
+ 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.
+
+
+
+
+
+
+
Returns
+
mixed
+
+
+
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.
The is_changed method allows you to test if a model object has been altered since it was loaded.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
+
Returns
+
bool
+
+
+
Example
+
+
// you can use get_diff() to check what has changed
+if ($mymodel->is_changed()
+{
+ echo "something has changed in the object !";
+}
+
+
+
+
+
+
+
+
is_fetched($relation)
+
The is_fetched method allows you check if related objects have been loaded.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$relation
+
required
+
+ The name of the relation you want to check.
+
+
+
+
+
+
+
Returns
+
bool
+
+
+
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.
+
+
+
+
Static
+
No
+
+
+
Returns
+
bool
+
+
+
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.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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).
+
+
+
+
+
+
+
Returns
+
bool | 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.
+
+
+
+
Static
+
No
+
+
+
+
+ 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.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
+
Returns
+
Model | 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 !
+
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.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$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.
+
+
+
+
+
+
+
Returns
+
array
+
+
+
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_array($custom = false)
+
The to_array method allows you to convert the model object into a StdClass object with only the data properties.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$custom
+
false
+
+ Set this to true if you want to include custom or runtime data stored in the model object.
+
+
+
+
+
+
+
Returns
+
StdClass
+
+
+
Example
+
+
// these are identical
+$obj1 = $record->to_object();
+$obj2 = (object) $record->to_array();
The SQL data type, Required to have the typing observer used on a field.
@@ -389,6 +389,10 @@
Observer_Typing
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
From 38695b799332ebb4eff1c744006670cfe890243f Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Fri, 6 Dec 2024 16:32:53 +0000
Subject: [PATCH 16/62] documented new readonly argument to
Fielset::set_tabular_form()
---
classes/fieldset/fieldset.html | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/classes/fieldset/fieldset.html b/classes/fieldset/fieldset.html
index aeb658591..57b7d04df 100644
--- a/classes/fieldset/fieldset.html
+++ b/classes/fieldset/fieldset.html
@@ -1145,7 +1145,7 @@
+ The app_total method returs an array with two values, the number of microseconds Fuel is running processing the current request,
+ and the number of bytes of memory the process has allocated sofar (excluding PHP internal bootstrap memory used).
+
+ The init method initializes the profiler state. It will create a profiler instance, and sets up database query profiling.
+ As long as the profiler is not initialized, all profiler methods apart from app_total() are NOOP's.
+
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
None
+
+
+
Returns
+
void
+
+
+
Example
+
+
Profiler::reset();
+
+
+
+
+
+
+
+
reset()
+
+ The reset method reset the Profiler to its uninitialized state. You will have to call init() again if you want to re-enable the profiler.
+
The version method will move the migrations up by one, or up to a specific version if given.
+
The up method will move the migrations up by one, or up to a specific version if given.
@@ -241,7 +241,7 @@
up($version, $name = 'default', $type = 'app')
down($version, $name = 'default', $type = 'app')
-
The version method will move the migrations down by one, or down to a specific version if given.
+
The down method will move the migrations down by one, or down to a specific version if given.
@@ -296,6 +296,61 @@
down($version, $name = 'default', $type = 'a
If no migrations where found, or if the version specified resulted in no migrations being executed, the array returned is empty.
+
+
has($name, $type, $seq = null)
+
The has method allows you to check if a given migration has already run. This is most useful inside migrations, to check for dependencies.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$type
+
required
+
Type of migration. Valid values are 'app', 'module' and 'package'.
+
+
+
$name
+
required
+
Name of the package or module. In case of app, use 'default' as a placeholder.
+
+
+
$seq
+
null
+
+
+
+
+
+
Returns
+
bool
+
+
+
Example
+
+
// skip further processing if migration 10 of the module 'mymodule' has not already run
+// use this in a migration up() method to postpone that migration
+if ( ! Migrate::has('module', 'mymodule', '010'))
+{
+ return false;
+}
+
+
+
+
+
+
+
+
+
has_any_access($conditions, Array $entity)
+
The has_any_access method uses the defined ACL drivers to check the users access according to the given array of conditions.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$conditions
+
required
+
takes a array of locations and the required rights on the location as either a string or array
+
+
+
$entity
+
required
+
user or group identifier in the form of array(driver_id, id).
+
+
+
+
+
+
Returns
+
boolean, true if access was granted to ANY of the given conditions, false if not.
+
+
+
Example
+
+
// check if user is allowed to comment when one right is granted
+if ( ! Auth::acl()->has_any_access(array('comments.create', 'comments.edit')))
+{
+ Response::redirect('no_admin');
+}
+
+
+
+
+
+
+
+
has_all_access($conditions, Array $entity)
+
The has_all_access method uses the defined ACL drivers to check the users access according to the given array of conditions.
+
+
+
+
Static
+
No
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$conditions
+
required
+
takes a array of locations and the required rights on the location as either a string or array
+
+
+
$entity
+
required
+
user or group identifier in the form of array(driver_id, id).
+
+
+
+
+
+
Returns
+
boolean, true if access was granted to ALL of the given conditions, false if not.
+
+
+
Example
+
+
// check if user is allowed to comment when both rights are granted
+if ( ! Auth::acl()->has_all_access(array('comments.create', 'comments.edit')))
+{
+ Response::redirect('no_admin');
+}
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:
+ 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"):
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 +141,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:
+
+ The SQL data type, Required to have the typing observer used on a field.
+
+
+
+
db_decimals
+
int
+
Number of decimals the value in the database has, if different from the specification in "data_type".
+
+
+
null
+
bool
+
+ Whether null is allowed as a value, default true
+
+
+
+
character_maximum_length
+
int
+
+ The maximum number of characters allowed for a string data type (varchar, text, serialized or encrypted result)
+
+
+
+
encryption_key
+
string
+
+ A custom encryption key to encrypt and decrypt this value. If given, it will replace the "crypto_key" set in the Crypt config file.
+
+
+
+
min
+
int
+
+ The minimal value for an integer
+
+
+
+
max
+
int
+
+ The maximum value for an integer
+
+
+
+
options
+
array
+
+ 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,122 +448,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:
-
- The SQL data type, Required to have the typing observer used on a field.
-
-
-
-
db_decimals
-
int
-
Number of decimals the value in the database has, if different from the specification in "data_type".
-
-
-
null
-
bool
-
- Whether null is allowed as a value, default true
-
-
-
-
character_maximum_length
-
int
-
- The maximum number of characters allowed for a string data type (varchar, text, serialized or encrypted result)
-
-
-
-
encryption_key
-
string
-
- A custom encryption key to encrypt and decrypt this value. If given, it will replace the "crypto_key" set in the Crypt config file.
-
-
-
-
min
-
int
-
- The minimal value for an integer
-
-
-
-
max
-
int
-
- The maximum value for an integer
-
-
-
-
options
-
array
-
- 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_Slug
From 94aa2464efcda1c05df580d660f94e1a018d8d7b Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Wed, 4 Feb 2026 13:49:10 +0000
Subject: [PATCH 44/62] [orm][audit observer] add the users' IP to the log
---
packages/orm/observers/included.html | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/orm/observers/included.html b/packages/orm/observers/included.html
index f04d19053..9c4c6723f 100644
--- a/packages/orm/observers/included.html
+++ b/packages/orm/observers/included.html
@@ -86,7 +86,8 @@
The is_changed method allows you to test if a model object has been altered since it was loaded.
@@ -1250,6 +1250,14 @@
is_changed($property = null, $observe
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.
+
+
@@ -1264,11 +1272,21 @@
is_changed($property = null, $observe
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.
+
From 8698559dc29f797b44cb7580acdfd8a5dcac4ec3 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Tue, 10 Feb 2026 12:47:22 +0000
Subject: [PATCH 47/62] documented new Arr::is_assoc() flag
to test for non-consecutive or non-zero-based indexed arrays.
---
classes/arr.html | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/classes/arr.html b/classes/arr.html
index ee6d4c8de..7bd89ab0c 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.
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.
From d7938859738089aec826777d39d0a014debb5309 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Tue, 10 Feb 2026 12:58:52 +0000
Subject: [PATCH 48/62] [arr] add option to multisort to prevent re-indexing of
indexed arrays
---
classes/arr.html | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/classes/arr.html b/classes/arr.html
index 7bd89ab0c..366aacb5b 100644
--- a/classes/arr.html
+++ b/classes/arr.html
@@ -1924,7 +1924,7 @@
The sort method sorts a multi-dimensional array by multiple values.
@@ -1952,10 +1952,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.
+
@@ -2043,6 +2048,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.
+
The database_exists method checks if a named database already exists. Will throw a Database_Exception if it cannot.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$database
+
required
+
the database name
+
+
+
$db
+
null
+
The database connection.
+
+
+
+
+
+
Returns
+
true if the database exists, false if not.
+
+
+
Throws
+
\Database_Exception on failure; \Fuel_Exception if not implemented
+
+
+
Example
+
+
// check if the database exists
+if (DBUtil::database_exists('my_database'))
+{
+ // deal with it
+}
+
+
+
+
+
+ This method is currently only available for MySQL/MariaDB, SQLSrv and SQLite.
+ In case of SQLite, you can check on either the attached database name or the FQFN or the attached database file (case-insensitive).
+
The active method returns wether or not the named or default session instance is active (loaded and initialized).
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$instance
+
null
+
Cookiename (as defined in config/session.php) that identifies the required session instance.
+
+
+
+
+
+
Returns
+
bool, true if the session is active and can be used, false if not.
+
+
+
Example
+
+
// Make sure we have an active session before using it
+if (Session::active())
+{
+ $var = Session::get('var');
+}
+
+
+
+
+
+
+ You may need this check if your application has cases where no session is active, and can not be activated, for example because the database session table has not been created yet.
+ Web-based installers as part of your application are a great example where this may be the case.
+
+
+
set($variable, $value = null)
The set method allows you to set a session variable.
The installed method allows you to check if a package is currently installed, i.e. present in a defined package path. If no package name is given then all installed packages are returned.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$package
+
null
+
Name of the package to be checked.
+
+
+
+
+
+
Returns
+
bool|array
+
+
+
Example
+
+
// Check if the orm package is installed
+$loaded = Package::installed('orm');
+
+// Get all installed packages
+$loaded = Package::installed();
+/*
+Returns something like:
+array(
+ 'orm' => '/path/to/orm',
+ 'parser' => '/path/to/parser',
+ 'notloaded' => '/path/to/notloaded',
+)
+*/
+
The to_array method allows you to convert the model object into a StdClass object with only the data properties.
+
to_object($custom = false)
+
The to_object method allows you to convert the model object into a StdClass object with only the data properties.
From 898960d3cb3216ba7deb2a9a7ca003f3430dd609 Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Fri, 21 Aug 2026 13:56:51 +0100
Subject: [PATCH 55/62] [config][finder]add the option to give APP files
precedence over MODULE files
---
classes/config.html | 31 +++++++++++++++++++++----------
classes/finder.html | 26 ++++++++++++++++++++++----
2 files changed, 43 insertions(+), 14 deletions(-)
diff --git a/classes/config.html b/classes/config.html
index fa7fd1873..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:
- PHP. The default type. A PHP file should return an array structure.
+ PHP The default type. A PHP file should return an array structure.
return array('key' => 'value');
- INI. See this page for a definition of the ini-file layout.
+ INI See this page for a definition of the ini-file layout.
[group]
key=value
- YAML. See this page for a definition of the yaml-file layout.
+ YAML See this page for a definition of the yaml-file layout.
group:
key: value
- JSON. See this page for a definition of the json-file layout.
+ JSON See this page for a definition of the json-file layout.
{
"group" :
{
@@ -66,14 +66,14 @@
Config File Types
}
- MEMCACHED. Stores or retrieves the config from a Memcached or Memcachedb store.
+ MEMCACHED Stores or retrieves the config from a Memcached or Memcachedb store.
It will use the config.memcached section of the app/config/config.php configuration
file to configure the memcached class used to connect to the store.
- DB. Use the following table structure:
+ DB Use the following table structure:
CREATE TABLE IF NOT EXISTS `config` (
`identifier` char(100) NOT NULL,
`config` longtext NOT NULL,
@@ -268,7 +268,7 @@
The load method reads in a config file into the system. It searches through the config directories for the requested file. You can optionally group the config files to avoid naming collisions.
@@ -292,19 +292,24 @@
load($file, $group = null, $reload = false,
$group
-
null
+
null
(Optional) A group name to use. If set to true then a group of the same name as $file will be created. If this is not set or is null then the loaded config will be merged with the root config.
$reload
-
false
+
false
(Optional) If set to true a reload of the requested configuration is forced, erasing cached configuration items related to the config file to be loaded.
$overwrite
-
false
+
false
(Optional) If set to true the loaded configuration items will be merged with the items already loaded in a non-recursive manner, with will overwrite array values in a multidimensional array, rather then merging them.
+
+
$appfirst
+
false
+
By default, module files override global files (app, core, packages). By setting this to true, files in the app will have precedence over module files.
+
@@ -333,6 +338,12 @@
load($file, $group = null, $reload = false,
+
+ When loading configuration files from a module, whatever the type, files found are loaded in the following order:
+ MODULE, APP, PACKAGES (in order of definition/loading), CORE
+ If you have a modules that require customisation in the app in which the module is used, without altering the code in the module (for examples modules that are distributed), you need to set $appfirst to true when loading the configuration, which alters the load order to:
+ APP, MODULE, PACKAGES (in order of definition/loading), CORE.
+
The search method is a static alias for locate() on the default finder instance.
@@ -175,6 +175,11 @@
search($dir, $file, $ext = '.php', $multip
true
If true, cache the results for subsequent requests to prevent unnecessary disk access.
+
+
$appfirst
+
false
+
By default, module files override global files (app, core, packages). By setting this to true, files in the app will have precedence over module files.
+
@@ -192,7 +197,11 @@
search($dir, $file, $ext = '.php', $multip
// assuming we're in active module called "test", and requested all files, this could
// return array("MODULEPATH/test/lang/en/welcome.php", "APPPATH/lang/en/welcome.php")
-$lang = Finder::search('lang/en', 'welcome');
+$lang = Finder::search('lang/en', 'welcome', true, false);
+
+// assuming we're in active module called "test", and requested all files, this could
+// return array("APPPATH/lang/en/welcome.php", "MODULEPATH/test/lang/en/welcome.php")
+$lang = Finder::search('lang/en', 'welcome', true, false, true);
// searching explicitly in the module, this would return
// array("MODULEPATH/test/lang/en/welcome.php")
@@ -400,7 +409,7 @@
The locate method locates files in the defined search paths.
@@ -442,6 +451,11 @@
locate($dir, $file, $ext = '.php', $multip
true
If true, cache the results for subsequent requests to prevent unnecessary disk access.
+
+
$appfirst
+
false
+
By default, module files override global files (app, core, packages). By setting this to true, files in the app will have precedence over module files.
+
@@ -459,7 +473,11 @@
locate($dir, $file, $ext = '.php', $multip
// assuming we're in active module called "test", and requested all files, this could
// return array("MODULEPATH/test/lang/en/welcome.php", "APPPATH/lang/en/welcome.php")
-$lang = Finder::instance()->locate('lang/en', 'welcome');
+$lang = Finder::instance()->locate('lang/en', 'welcome', true, false);
+
+// assuming we're in active module called "test", and requested all files, this could
+// return array("APPPATH/lang/en/welcome.php", "MODULEPATH/test/lang/en/welcome.php")
+$lang = Finder::instance()->locate('lang/en', 'welcome', true, false, true);
// searching explicitly in the module, this would return
// array("MODULEPATH/test/lang/en/welcome.php")
From 649cc3a8dda7f96bc8672ecb57c7b4d79dd50f8c Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Tue, 1 Sep 2026 15:42:25 +0100
Subject: [PATCH 56/62] small layout fix
---
assets/css/combined.css | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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;
}
From bf6d0e7fdfb8647e10a74b1757e971982c181d1b Mon Sep 17 00:00:00 2001
From: WanWizard
Date: Tue, 1 Sep 2026 15:42:38 +0100
Subject: [PATCH 57/62] documented File::relativepath()
---
classes/file/usage.html | 56 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/classes/file/usage.html b/classes/file/usage.html
index 6e8443bf9..c491e7169 100644
--- a/classes/file/usage.html
+++ b/classes/file/usage.html
@@ -1425,6 +1425,62 @@
download($path, $name = null, $mime = nu
+
+
relativepath($reference, $to)
+
Converts an absolute path to a path relative to the given reference path.
+
+
+
+
Static
+
Yes
+
+
+
Parameters
+
+
+
+
Param
+
Default
+
Description
+
+
+
$reference
+
required
+
Path to be used a a referencde
+
+
+
$to
+
required
+
Absolute path, to be converted to a relative path
+
+
+
+
+
+
Returns
+
string the path for $to, relative to $reference
+
+
+
Example
+
+
// get the relative path on *nix
+$relpath = File::relativepath('/a/b/c/d', '/a/b/e/f'); // returns "../e/f"
+$relpath = File::relativepath('/a/b/c/d', '/e/f/g/h'); // returns "../../../e/f/g/h"
+// get the relative path on Windows
+$relpath = File::relativepath('C:\\a\\b\\c\\d', 'C:\\a\\b\\e\\f'); // returns "..\e\f"
+$relpath = File::relativepath('C:\\a\\b\\c\\d', 'D:\\a\\b\\e\\f'); // throws an exception
+
+
+
+
+
+
+ Note that on Windows, if only one of the paths is prefixed with a drive letter, it is assumed that the other path
+ is on the same drive. When both are prefixed with a drive letter, it MUST be the same drive, if not, an exception
+ is thrown, as you can't create relative paths between different drives.
+