kksonthomas / kkson-framework
self use framework
Requires
- php: >=8.1.0
- ext-mbstring: *
- ext-mysqli: *
- almasaeed2010/adminlte: 3.*.*
- bootstrap-select/bootstrap-select: 1.12.*
- electrolinux/phpquery: 0.9.6
- filp/whoops: 2.*.*
- fortawesome/font-awesome: 4.*
- gabordemooij/redbean: ^5.7.2
- gammadia/slim-2.x: 2.6.*
- gaomingcode/ckeditor5: ^27.1
- greenlion/php-sql-parser: 4.*
- ircmaxell/password-compat: 1.0.*
- league/plates: ^3.4.0
- league/route: 2.*.*
- moment/moment: ^2.29.3
- php-curl-class/php-curl-class: 4.10.0
- phpmailer/phpmailer: v5.2.14
- phpoffice/phpspreadsheet: ^1.23.0
- respect/validation: 1.1.14
- slim/slim: 2.6.*
- swaggest/json-diff: ^3.5.0
- voku/stringy: ^6.5.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.8
- gd-75/php-cs-fixer-enforce-double-quotes: ^1.0
- phpstan/phpstan: ^1.6
- phpunit/phpunit: ^9.5.20
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-master
- v0.11.2.0
- v0.11.1.1
- v0.11.1.0
- v0.11.0.0
- v0.10.5.0
- v0.10.4.4
- v0.10.4.3
- v0.10.4.2
- v0.10.4.1
- v0.10.4.0
- v0.10.3.2
- v0.10.3.1
- v0.10.3.0
- v0.10.2.0
- v0.10.1.0
- v0.10.0.0
- v0.9.4.1
- v0.9.4.0
- v0.9.3.3
- v0.9.3.2
- v0.9.3.1
- v0.9.3.0
- v0.9.2.0
- v0.9.1.1
- v0.9.1.0
- v0.9.0.0
- v0.8.4.0
- v0.8.3.1
- v0.8.3.0
- v0.8.2.2
- v0.8.2.1
- v0.8.2
- v0.8.1
- v0.8.0
- v0.7.2
- v0.7.1
- v0.7.0
- v0.6.2
- v0.6.1.1
- v0.6.1
- v0.6.0
- v0.5.5
- v0.5.4
- v0.5.3
- v0.5.2
- v0.5.1
- v0.5.0
- v0.4.2
- v0.4.1
- v0.4.0
- v0.3.6
- v0.3.5
- v0.3.4
- v0.3.3
- v0.3.2
- v0.3.1
- v0.3.0
- v0.2.9
- v0.2.8
- v0.2.7
- v0.2.6
- v0.2.5
- v0.2.4
- v0.2.3
- v0.2.2
- v0.2.1
- v0.2.0
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
This package is auto-updated.
Last update: 2026-09-17 11:17:04 UTC
README
PHP admin/CRUD framework built on Slim 2, RedBeanPHP, Plates, and AdminLTE. Includes authentication, CRUD UI, search/export, permissions, and system logging.
Installation
composer require kksonthomas/kkson-framework:^0.11
New blank project: copy the packaged scaffold, configure conf/, and import the greenfield SQL. Step-by-step: docs/getting-started.md.
# After composer require in an empty project directory: # Windows: Copy-Item -Recurse vendor\kksonthomas\kkson-framework\scaffold\* . # bash: cp -a vendor/kksonthomas/kkson-framework/scaffold/. . mysql -u USER -p DATABASE < vendor/kksonthomas/kkson-framework/sql/0000_init_framework.sql
| Material | Path |
|---|---|
| Getting started | docs/getting-started.md |
| Blank app scaffold | scaffold/ |
| Greenfield DB init | sql/0000_init_framework.sql |
Add configuration under your application conf/ directory (scaffold ships .example files):
app.config.ini— environment and app settings (envselects the DB config file)db.config.{env}.ini— database connection
Default seed login after init SQL: sysadmin / sysadmin (change after first login). New databases use the init file only; existing DBs missing system_log.client_ip still use sql/patch-v0.10.4.1-ip-ban.sql.
Database transactions and Writer Cache (v0.11.0.0+)
Updating to v0.11.0.0+ enables transactional CRUD by default and adds DB::begin(), DB::commit(), DB::rollback(), and DB::transaction() with Writer Cache flush on rollback.
composer require kksonthomas/kkson-framework:^0.11.0.0
CRUD insert, update, and delete run inside a DB transaction by default. Opt out on a CRUD instance:
$crud->setIsInsertUpdateUseTransaction(false);
Transactions require frozen RedBean (DB::fixSchema() / R::freeze(true) in normal app bootstrap).
DB transaction API
Use KKsonFramework\App\DB for transaction control instead of importing R for begin / commit / rollback:
use KKsonFramework\App\DB; DB::begin(); try { // ... DB::commit(); } catch (\Throwable $e) { DB::rollback(); // rolls back + flushes RedBean Writer Cache throw $e; } // or DB::transaction(function () { // ... });
Why rollback flushes cache
RedBean 5.7 Writer Cache (default ON) caches R::find, R::findOne, R::load, and related read queries. R::store and R::exec invalidate the cache on the next cached read; R::rollback() does not. After a rollback, cached reads can still return rows from the undone transaction.
DB::rollback() and a failed DB::transaction() call R::getWriter()->flushCache() as RedBean recommends. Successful commits do not flush cache (committed data matches the cache). Direct SQL via R::getCell / R::getAll is not Writer-cached.
Custom code that still uses R::begin() / R::rollback() directly should switch to DB::* or call R::getWriter()->flushCache() after rollback.
IP ban performance (v0.10.4.1+)
Updating the package improves IP ban behavior without any database change. Unauthenticated requests only check existing bans; failed-login counting runs after a failed login.
For faster queries on large system_log tables on databases created before the greenfield init, apply the optional SQL patch once:
vendor/kksonthomas/kkson-framework/sql/patch-v0.10.4.1-ip-ban.sql
Example:
mysql -u USER -p DATABASE < vendor/kksonthomas/kkson-framework/sql/patch-v0.10.4.1-ip-ban.sql
The script adds system_log.client_ip, backfills simple JSON array rows, and creates indexes. Skip it if you do not need indexed IP lookups, or if the database was created with sql/0000_init_framework.sql (already includes client_ip and indexes). The app remains fully functional either way.
On re-run, ignore duplicate column or duplicate index errors. Requires system_log and ban_ip_list tables.
Soft delete (mimic delete)
Models can soft-delete by setting _deleted = 1 instead of removing rows. Enable on a BaseModelBase subclass:
public static function _enabledMimicDelete() { return true; }
The table must have an integer _deleted column (0 = active, 1 = deleted).
Loading
Model::load($id)— active rows only (_deleted = 0).Model::load($id, true)— includes soft-deleted rows.Model::loadForUpdate($id)— same filter asload(); useloadForUpdate($id, true)to lock a deleted row.Model::isActiveInDb()— re-reads_deletedfrom the database for long-running jobs.
Saving and concurrency
R::store() runs the FUSE update() hook on boxed models. For mimic-delete tables, BaseModelBase::update() rejects a stale revive: the database row is soft-deleted (_deleted = 1) while the in-memory bean still has _deleted = 0. This prevents cross-request/cron races from undoing a delete after deleteSelf() or CRUD delete.
- Restore a row with
undeleteSelf()orsave(allowReviveDeleted: true). - Otherwise catch
KKsonFramework\RedBeanPHP\Exception\StaleDeletedModelExceptionand skip the write (typical for background jobs).
Subclasses that override update() must call parent::update() first so the guard runs.
CRUD transactions
CRUD insert, update, and delete are transactional by default (see Database transactions and Writer Cache (v0.11.0.0+)). This wraps one HTTP request only; it does not cover CLI, cron, or a second concurrent request. Long jobs should re-check isActiveInDb() before saving or handle StaleDeletedModelException.
Audit fields (v0.11.2.0+)
BaseModelBase assigns creation_date, creation_user_id, modified_date, and modified_user_id on every R::store() / save(). That is the default. High-volume fact tables that do not have these columns can opt out:
public static function _enabledAuditFields(): bool { return false; }
Default is true, so existing models keep current behaviour. This flag is independent of _enabledMimicDelete() / _deleted: a table can have audit and no soft-delete (the usual case), or neither.
When the flag is false, FUSE update() still runs mimic-delete checks and tempID bookkeeping, but it does not assign the four properties. Do not call parent::update() and then unset() the columns — that still writes them onto the bean first, which errors under a frozen schema and re-creates the columns if freeze is off.
Ship the framework version that contains this flag before (or in the same window as) ALTER TABLE ... DROP of those columns. Never drop them while update() still assigns the properties.
CRUD list UI registers the four fields only when the table’s model has audit enabled (BeanHelper::isCurrentTableEnabledAuditFields()). Unmapped CRUD tables keep the current audit widgets.