From 3863061ea390a892de157d2b8be67de5bd23a5a9 Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 28 Sep 2026 22:35:10 +0200 Subject: [PATCH 1/3] upd(install): seed minimal template with database source A fresh install left the Minimal Template on the legacy "automatic" source, the only template in the system showing that option, while every template created in the manager starts as "database". Seed it as "db" so it skips the view-file probes like any other database template. Co-Authored-By: Claude Opus 5.5 --- .github/docker/ci/smoke.php | 2 + .../seeders/SiteTemplatesTableSeeder.php | 1 + .../default_site_template_seeder_worker.php | 63 +++++++++++++++++++ .../Install/DefaultSiteTemplateSeederTest.php | 25 ++++++++ .../install/SiteTemplatesTableSeeder.php | 1 + 5 files changed, 92 insertions(+) create mode 100644 core/tests/Mocks/default_site_template_seeder_worker.php create mode 100644 core/tests/Unit/Install/DefaultSiteTemplateSeederTest.php diff --git a/.github/docker/ci/smoke.php b/.github/docker/ci/smoke.php index a65b353208..a43a064dc5 100644 --- a/.github/docker/ci/smoke.php +++ b/.github/docker/ci/smoke.php @@ -257,6 +257,8 @@ function setting(string $name) check('one document seeded', count_rows('site_content') === 1, 'rows: ' . count_rows('site_content')); check('the document is the install success page', ($home['alias'] ?? '') === 'minimal-base', 'alias: ' . ($home['alias'] ?? 'none')); check('the document uses the seeded template', (int) ($home['template'] ?? 0) === 1); + $templateSource = $pdo->query('SELECT templatesource FROM ' . t('site_templates') . ' WHERE id = 1')->fetchColumn(); + check('the minimal template uses database rendering', $templateSource === 'db', 'source: ' . ($templateSource ?: 'empty')); } check('the document is published', (int) ($home['published'] ?? 0) === 1); check('a template was seeded', count_rows('site_templates') >= 1); diff --git a/core/database/seeders/SiteTemplatesTableSeeder.php b/core/database/seeders/SiteTemplatesTableSeeder.php index a2848cc0ef..977e803bda 100644 --- a/core/database/seeders/SiteTemplatesTableSeeder.php +++ b/core/database/seeders/SiteTemplatesTableSeeder.php @@ -31,6 +31,7 @@ public function run(): void [ 'templatename' => 'Minimal Template', 'templatealias' => '', + 'templatesource' => 'db', 'description' => 'Default minimal empty template (content returned only)', 'editor_type' => 0, 'category' => 0, diff --git a/core/tests/Mocks/default_site_template_seeder_worker.php b/core/tests/Mocks/default_site_template_seeder_worker.php new file mode 100644 index 0000000000..e469dd2b8d --- /dev/null +++ b/core/tests/Mocks/default_site_template_seeder_worker.php @@ -0,0 +1,63 @@ +name] = []; + + return 0; + } + + public function insert(array $rows): void + { + DB::$inserted[$this->name] = $rows; + } + } +} + +namespace { + require $argv[1]; + + $seederClass = $argv[2]; + (new $seederClass())->run(); + + echo json_encode( + \Illuminate\Support\Facades\DB::$inserted['site_templates'][0] ?? [], + JSON_THROW_ON_ERROR + ); +} diff --git a/core/tests/Unit/Install/DefaultSiteTemplateSeederTest.php b/core/tests/Unit/Install/DefaultSiteTemplateSeederTest.php new file mode 100644 index 0000000000..aed01b7523 --- /dev/null +++ b/core/tests/Unit/Install/DefaultSiteTemplateSeederTest.php @@ -0,0 +1,25 @@ +toBe(0) + ->and($template['templatesource'])->toBe('db'); + } +}); diff --git a/install/stubs/seeds/install/SiteTemplatesTableSeeder.php b/install/stubs/seeds/install/SiteTemplatesTableSeeder.php index da4ee10e86..7b76098acd 100644 --- a/install/stubs/seeds/install/SiteTemplatesTableSeeder.php +++ b/install/stubs/seeds/install/SiteTemplatesTableSeeder.php @@ -18,6 +18,7 @@ public function run(): void [ 'templatename' => 'Minimal Template', 'templatealias' => '', + 'templatesource' => 'db', 'description' => 'Default minimal empty template (content returned only)', 'editor_type' => 0, 'category' => 0, From 59ac33c4d674fa54834cd92ca455664f23425b3d Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 28 Sep 2026 22:35:11 +0200 Subject: [PATCH 2/3] fix(models): keep deletedon as a unix timestamp in document arrays The base soft-delete trait casts deletedon to datetime, so toArray() built a Carbon instance only to print it as an ISO string. Every uncached page loaded Carbon (about 40 files) for it, and [*deletedon*] rendered as 1970-01-01T00:00:00.000000Z instead of a timestamp like every other date field. Array form now keeps the stored value; reading the attribute still returns Carbon. Co-Authored-By: Claude Opus 5.5 --- core/src/Traits/Models/SoftDeletes.php | 41 +++++++++++++ .../Mocks/site_content_to_array_worker.php | 20 +++++++ .../Models/SiteContentDeletedOnArrayTest.php | 58 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 core/tests/Mocks/site_content_to_array_worker.php create mode 100644 core/tests/Unit/Models/SiteContentDeletedOnArrayTest.php diff --git a/core/src/Traits/Models/SoftDeletes.php b/core/src/Traits/Models/SoftDeletes.php index cae753a189..2afbd8507c 100644 --- a/core/src/Traits/Models/SoftDeletes.php +++ b/core/src/Traits/Models/SoftDeletes.php @@ -1,5 +1,6 @@ $attributes + * @param array $mutatedAttributes + * @return array + */ + protected function addCastAttributesToArray(array $attributes, array $mutatedAttributes) + { + $column = $this->getDeletedAtColumn(); + if (!array_key_exists($column, $attributes) + || in_array($column, $mutatedAttributes, true) + || ($this->getCasts()[$column] ?? null) !== 'datetime' + ) { + return parent::addCastAttributesToArray($attributes, $mutatedAttributes); + } + + $raw = $attributes[$column]; + if ($raw instanceof DateTimeInterface) { + $raw = $raw->getTimestamp(); + } elseif (is_numeric($raw)) { + $raw = (int) $raw; + } + + // The parent only rewrites keys it is given, so the column goes back + // where it was and the array keeps its order. + $position = array_search($column, array_keys($attributes), true); + unset($attributes[$column]); + $attributes = parent::addCastAttributesToArray($attributes, $mutatedAttributes); + + return array_slice($attributes, 0, $position, true) + + [$column => $raw] + + array_slice($attributes, $position, null, true); + } } diff --git a/core/tests/Mocks/site_content_to_array_worker.php b/core/tests/Mocks/site_content_to_array_worker.php new file mode 100644 index 0000000000..ad350e5b37 --- /dev/null +++ b/core/tests/Mocks/site_content_to_array_worker.php @@ -0,0 +1,20 @@ +newFromBuilder([ + 'id' => 1, + 'pagetitle' => 'Home', + 'template' => 1, + 'createdon' => '1790625541', + 'deleted' => '0', + 'deletedon' => '0', +]); + +echo json_encode([ + 'deletedon' => $document->toArray()['deletedon'], + 'carbonLoaded' => class_exists(\Carbon\Carbon::class, false), +], JSON_THROW_ON_ERROR); diff --git a/core/tests/Unit/Models/SiteContentDeletedOnArrayTest.php b/core/tests/Unit/Models/SiteContentDeletedOnArrayTest.php new file mode 100644 index 0000000000..3be3570aa8 --- /dev/null +++ b/core/tests/Unit/Models/SiteContentDeletedOnArrayTest.php @@ -0,0 +1,58 @@ +newFromBuilder($row); +} + +test('toArray keeps deletedon as the stored unix timestamp', function () { + $never = siteContentFromRow(['id' => 1, 'deletedon' => '0'])->toArray(); + $deleted = siteContentFromRow(['id' => 2, 'deletedon' => '1790625541'])->toArray(); + $missing = siteContentFromRow(['id' => 3, 'deletedon' => null])->toArray(); + + expect($never['deletedon'])->toBe(0) + ->and($deleted['deletedon'])->toBe(1790625541) + ->and($missing['deletedon'])->toBeNull(); +}); + +test('toArray keeps the column order and still casts the other attributes', function () { + $array = siteContentFromRow([ + 'id' => 1, + 'published' => '1', + 'deletedon' => '0', + 'template' => '4', + 'hidemenu' => '0', + ])->toArray(); + + expect(array_keys($array))->toBe(['id', 'published', 'deletedon', 'template', 'hidemenu']) + ->and($array['published'])->toBe(1) + ->and($array['template'])->toBe(4) + ->and($array['hidemenu'])->toBeFalse(); +}); + +test('reading deletedon as an attribute still returns a date', function () { + $document = siteContentFromRow(['id' => 1, 'deletedon' => '1790625541']); + + expect($document->deletedon)->toBeInstanceOf(Carbon::class) + ->and($document->deletedon->getTimestamp())->toBe(1790625541); +}); + +test('a deletion time set in memory is arrayed as a unix timestamp', function () { + $document = siteContentFromRow(['id' => 1, 'deletedon' => '0']); + $document->deletedon = Carbon::createFromTimestamp(1790625541); + + expect($document->toArray()['deletedon'])->toBe(1790625541); +}); + +test('arraying a document does not load Carbon', function () { + $exitCode = null; + $output = evoRunPhp(dirname(__DIR__, 2) . '/Mocks/site_content_to_array_worker.php', [], $exitCode); + $result = json_decode($output, true, flags: JSON_THROW_ON_ERROR); + + expect($exitCode)->toBe(0) + ->and($result['deletedon'])->toBe(0) + ->and($result['carbonLoaded'])->toBeFalse(); +}); From 8166e8f01d5e64cd07d2790b2f86fea0c6e67111 Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 28 Sep 2026 22:35:11 +0200 Subject: [PATCH 3/3] ref(models): declare table names on all core models Models without $table made Eloquent pluralise the class name on the first query of a request, which loads Doctrine Inflector (about 20 files) on every uncached front-end page. Each name is the one Eloquent derived before, so no query changes. Co-Authored-By: Claude Opus 5.5 --- core/src/Models/ActiveUser.php | 2 + core/src/Models/ActiveUserLock.php | 2 + core/src/Models/ActiveUserSession.php | 2 + core/src/Models/Category.php | 2 + core/src/Models/DocumentGroup.php | 2 + core/src/Models/DocumentgroupName.php | 2 + core/src/Models/FileGroup.php | 2 + core/src/Models/MemberGroup.php | 2 + core/src/Models/MembergroupName.php | 2 + core/src/Models/Permissions.php | 2 + core/src/Models/PermissionsGroups.php | 2 + core/src/Models/RolePermissions.php | 1 + core/src/Models/SiteHtmlsnippet.php | 2 + core/src/Models/SiteModule.php | 2 + core/src/Models/SitePlugin.php | 2 + core/src/Models/SitePluginEvent.php | 2 + core/src/Models/SiteSnippet.php | 2 + core/src/Models/SiteTemplate.php | 2 + core/src/Models/SiteTmplvar.php | 2 + core/src/Models/SiteTmplvarContentvalue.php | 2 + core/src/Models/SiteTmplvarTemplate.php | 2 + core/src/Models/SystemEventname.php | 2 + core/src/Models/SystemSetting.php | 2 + core/src/Models/User.php | 2 + core/src/Models/UserAttribute.php | 2 + core/src/Models/UserRole.php | 2 + core/src/Models/UserRoleVar.php | 2 + core/src/Models/UserSetting.php | 2 + core/src/Models/UserValue.php | 2 + .../tests/Unit/Models/ModelTableNamesTest.php | 44 +++++++++++++++++++ 30 files changed, 101 insertions(+) create mode 100644 core/tests/Unit/Models/ModelTableNamesTest.php diff --git a/core/src/Models/ActiveUser.php b/core/src/Models/ActiveUser.php index 5a46a38cbb..b03560f94d 100644 --- a/core/src/Models/ActiveUser.php +++ b/core/src/Models/ActiveUser.php @@ -18,6 +18,8 @@ */ class ActiveUser extends Eloquent\Model { + protected $table = 'active_users'; + protected $primaryKey = 'sid'; public $incrementing = false; public $timestamps = false; diff --git a/core/src/Models/ActiveUserLock.php b/core/src/Models/ActiveUserLock.php index 38f784e7d7..2cfedc7bec 100644 --- a/core/src/Models/ActiveUserLock.php +++ b/core/src/Models/ActiveUserLock.php @@ -16,6 +16,8 @@ */ class ActiveUserLock extends Eloquent\Model { + protected $table = 'active_user_locks'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/ActiveUserSession.php b/core/src/Models/ActiveUserSession.php index f6acc6d850..e34c208aef 100644 --- a/core/src/Models/ActiveUserSession.php +++ b/core/src/Models/ActiveUserSession.php @@ -14,6 +14,8 @@ */ class ActiveUserSession extends Eloquent\Model { + protected $table = 'active_user_sessions'; + protected $primaryKey = 'sid'; public $incrementing = false; public $timestamps = false; diff --git a/core/src/Models/Category.php b/core/src/Models/Category.php index 17c7b28b6d..bc4c4549b3 100644 --- a/core/src/Models/Category.php +++ b/core/src/Models/Category.php @@ -27,6 +27,8 @@ class Category extends Eloquent\Model { use Traits\Models\ManagerActions; + protected $table = 'categories'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/DocumentGroup.php b/core/src/Models/DocumentGroup.php index 2955c75480..e8c5c72388 100644 --- a/core/src/Models/DocumentGroup.php +++ b/core/src/Models/DocumentGroup.php @@ -13,6 +13,8 @@ */ class DocumentGroup extends Eloquent\Model { + protected $table = 'document_groups'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/DocumentgroupName.php b/core/src/Models/DocumentgroupName.php index 08d98f928c..0597bfe3f4 100644 --- a/core/src/Models/DocumentgroupName.php +++ b/core/src/Models/DocumentgroupName.php @@ -16,6 +16,8 @@ */ class DocumentgroupName extends Eloquent\Model { + protected $table = 'documentgroup_names'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/FileGroup.php b/core/src/Models/FileGroup.php index 5a4a508dbd..91e9130b5a 100644 --- a/core/src/Models/FileGroup.php +++ b/core/src/Models/FileGroup.php @@ -13,6 +13,8 @@ */ class FileGroup extends Eloquent\Model { + protected $table = 'file_groups'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/MemberGroup.php b/core/src/Models/MemberGroup.php index 278b37a667..2e4cbeb9ea 100644 --- a/core/src/Models/MemberGroup.php +++ b/core/src/Models/MemberGroup.php @@ -16,6 +16,8 @@ class MemberGroup extends Eloquent\Model { use Traits\Models\ManagerActions; + protected $table = 'member_groups'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/MembergroupName.php b/core/src/Models/MembergroupName.php index 651ef7477e..e40e93587b 100644 --- a/core/src/Models/MembergroupName.php +++ b/core/src/Models/MembergroupName.php @@ -16,6 +16,8 @@ */ class MembergroupName extends Eloquent\Model { + protected $table = 'membergroup_names'; + public $timestamps = false; protected $fillable = [ diff --git a/core/src/Models/Permissions.php b/core/src/Models/Permissions.php index 00a57fc2d4..fd558e0992 100644 --- a/core/src/Models/Permissions.php +++ b/core/src/Models/Permissions.php @@ -24,6 +24,8 @@ class Permissions extends Eloquent\Model { use ManagerActions; + protected $table = 'permissions'; + protected $managerActionsMap = [ 'actions.cancel' => 86, 'actions.new' => 135, diff --git a/core/src/Models/PermissionsGroups.php b/core/src/Models/PermissionsGroups.php index 7e56337aa4..f4acc22a0c 100644 --- a/core/src/Models/PermissionsGroups.php +++ b/core/src/Models/PermissionsGroups.php @@ -22,6 +22,8 @@ class PermissionsGroups extends Eloquent\Model { use ManagerActions; + protected $table = 'permissions_groups'; + protected $managerActionsMap = [ 'actions.cancel' => 86, 'actions.new' => 136, diff --git a/core/src/Models/RolePermissions.php b/core/src/Models/RolePermissions.php index 4a3eaf3562..b29ea46a21 100644 --- a/core/src/Models/RolePermissions.php +++ b/core/src/Models/RolePermissions.php @@ -19,6 +19,7 @@ */ class RolePermissions extends Eloquent\Model { + protected $table = 'role_permissions'; protected $fillable = [ 'permission', diff --git a/core/src/Models/SiteHtmlsnippet.php b/core/src/Models/SiteHtmlsnippet.php index 50f40917e5..11291ce327 100644 --- a/core/src/Models/SiteHtmlsnippet.php +++ b/core/src/Models/SiteHtmlsnippet.php @@ -43,6 +43,8 @@ class SiteHtmlsnippet extends Eloquent\Model use Traits\Models\ManagerActions, Traits\Models\TimeMutator; + protected $table = 'site_htmlsnippets'; + const CREATED_AT = 'createdon'; const UPDATED_AT = 'editedon'; protected $dateFormat = 'U'; diff --git a/core/src/Models/SiteModule.php b/core/src/Models/SiteModule.php index a0334a238b..363ba130fc 100644 --- a/core/src/Models/SiteModule.php +++ b/core/src/Models/SiteModule.php @@ -47,6 +47,8 @@ class SiteModule extends Eloquent\Model use Traits\Models\ManagerActions, Traits\Models\TimeMutator; + protected $table = 'site_modules'; + const CREATED_AT = 'createdon'; const UPDATED_AT = 'editedon'; protected $dateFormat = 'U'; diff --git a/core/src/Models/SitePlugin.php b/core/src/Models/SitePlugin.php index 523dba7e52..12e2f0f1a1 100644 --- a/core/src/Models/SitePlugin.php +++ b/core/src/Models/SitePlugin.php @@ -45,6 +45,8 @@ class SitePlugin extends Eloquent\Model use Traits\Models\ManagerActions, Traits\Models\TimeMutator; + protected $table = 'site_plugins'; + const CREATED_AT = 'createdon'; const UPDATED_AT = 'editedon'; protected $dateFormat = 'U'; diff --git a/core/src/Models/SitePluginEvent.php b/core/src/Models/SitePluginEvent.php index 9deec20f6e..59e5f7baf7 100644 --- a/core/src/Models/SitePluginEvent.php +++ b/core/src/Models/SitePluginEvent.php @@ -16,6 +16,8 @@ class SitePluginEvent extends Eloquent\Model { use Traits\Models\ManagerActions; + protected $table = 'site_plugin_events'; + public $incrementing = false; public $timestamps = false; diff --git a/core/src/Models/SiteSnippet.php b/core/src/Models/SiteSnippet.php index bd09a51b1a..07cf86c299 100644 --- a/core/src/Models/SiteSnippet.php +++ b/core/src/Models/SiteSnippet.php @@ -45,6 +45,8 @@ class SiteSnippet extends Eloquent\Model use Traits\Models\ManagerActions, Traits\Models\TimeMutator; + protected $table = 'site_snippets'; + const CREATED_AT = 'createdon'; const UPDATED_AT = 'editedon'; protected $dateFormat = 'U'; diff --git a/core/src/Models/SiteTemplate.php b/core/src/Models/SiteTemplate.php index 2dfdc7eccc..b3fe0f4761 100644 --- a/core/src/Models/SiteTemplate.php +++ b/core/src/Models/SiteTemplate.php @@ -46,6 +46,8 @@ class SiteTemplate extends Eloquent\Model use Traits\Models\ManagerActions, Traits\Models\TimeMutator; + protected $table = 'site_templates'; + const CREATED_AT = 'createdon'; const UPDATED_AT = 'editedon'; protected $dateFormat = 'U'; diff --git a/core/src/Models/SiteTmplvar.php b/core/src/Models/SiteTmplvar.php index c4d04f73f6..d51b7218f3 100644 --- a/core/src/Models/SiteTmplvar.php +++ b/core/src/Models/SiteTmplvar.php @@ -45,6 +45,8 @@ class SiteTmplvar extends Eloquent\Model use Traits\Models\ManagerActions, Traits\Models\TimeMutator; + protected $table = 'site_tmplvars'; + const CREATED_AT = 'createdon'; const UPDATED_AT = 'editedon'; protected $dateFormat = 'U'; diff --git a/core/src/Models/SiteTmplvarContentvalue.php b/core/src/Models/SiteTmplvarContentvalue.php index 11320eddcc..5384c1c5a3 100644 --- a/core/src/Models/SiteTmplvarContentvalue.php +++ b/core/src/Models/SiteTmplvarContentvalue.php @@ -14,6 +14,8 @@ */ class SiteTmplvarContentvalue extends Eloquent\Model { + protected $table = 'site_tmplvar_contentvalues'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/SiteTmplvarTemplate.php b/core/src/Models/SiteTmplvarTemplate.php index 9eb1369e8f..12af2fddf3 100644 --- a/core/src/Models/SiteTmplvarTemplate.php +++ b/core/src/Models/SiteTmplvarTemplate.php @@ -13,6 +13,8 @@ */ class SiteTmplvarTemplate extends Eloquent\Model { + protected $table = 'site_tmplvar_templates'; + public $incrementing = false; public $timestamps = false; diff --git a/core/src/Models/SystemEventname.php b/core/src/Models/SystemEventname.php index 2406ec1128..9f321cdc42 100644 --- a/core/src/Models/SystemEventname.php +++ b/core/src/Models/SystemEventname.php @@ -17,6 +17,8 @@ */ class SystemEventname extends Eloquent\Model { + protected $table = 'system_eventnames'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/SystemSetting.php b/core/src/Models/SystemSetting.php index 79725d472c..74cf1eae7c 100644 --- a/core/src/Models/SystemSetting.php +++ b/core/src/Models/SystemSetting.php @@ -15,6 +15,8 @@ class SystemSetting extends Eloquent\Model { use Traits\Models\ManagerActions; + protected $table = 'system_settings'; + protected $primaryKey = 'setting_name'; public $incrementing = false; public $timestamps = false; diff --git a/core/src/Models/User.php b/core/src/Models/User.php index c9dc1357bb..6236d64784 100644 --- a/core/src/Models/User.php +++ b/core/src/Models/User.php @@ -20,6 +20,8 @@ class User extends Eloquent\Model { use Traits\Models\ManagerActions; + protected $table = 'users'; + public $timestamps = false; protected $hidden = [ diff --git a/core/src/Models/UserAttribute.php b/core/src/Models/UserAttribute.php index dda5b8aeea..ed99cbd4f9 100644 --- a/core/src/Models/UserAttribute.php +++ b/core/src/Models/UserAttribute.php @@ -49,6 +49,8 @@ class UserAttribute extends Eloquent\Model { use Traits\Models\TimeMutator; + protected $table = 'user_attributes'; + const CREATED_AT = 'createdon'; const UPDATED_AT = 'editedon'; protected $dateFormat = 'U'; diff --git a/core/src/Models/UserRole.php b/core/src/Models/UserRole.php index 84c0ef7514..dcf9ef7d95 100644 --- a/core/src/Models/UserRole.php +++ b/core/src/Models/UserRole.php @@ -93,6 +93,8 @@ class UserRole extends Eloquent\Model { use Traits\Models\ManagerActions; + protected $table = 'user_roles'; + public $timestamps = false; protected $casts = [ diff --git a/core/src/Models/UserRoleVar.php b/core/src/Models/UserRoleVar.php index 2d602e5060..d20aa56ba1 100644 --- a/core/src/Models/UserRoleVar.php +++ b/core/src/Models/UserRoleVar.php @@ -15,6 +15,8 @@ */ class UserRoleVar extends Eloquent\Model { + protected $table = 'user_role_vars'; + public $incrementing = false; public $timestamps = false; diff --git a/core/src/Models/UserSetting.php b/core/src/Models/UserSetting.php index 449cde50a0..e85e225e8e 100644 --- a/core/src/Models/UserSetting.php +++ b/core/src/Models/UserSetting.php @@ -13,6 +13,8 @@ */ class UserSetting extends Eloquent\Model { + protected $table = 'user_settings'; + protected $primaryKey = null; public $incrementing = false; public $timestamps = false; diff --git a/core/src/Models/UserValue.php b/core/src/Models/UserValue.php index db2f0c868d..f9c7f679a6 100644 --- a/core/src/Models/UserValue.php +++ b/core/src/Models/UserValue.php @@ -16,6 +16,8 @@ */ class UserValue extends Model { + protected $table = 'user_values'; + public $timestamps = false; protected $casts = [ diff --git a/core/tests/Unit/Models/ModelTableNamesTest.php b/core/tests/Unit/Models/ModelTableNamesTest.php new file mode 100644 index 0000000000..e777c5c74e --- /dev/null +++ b/core/tests/Unit/Models/ModelTableNamesTest.php @@ -0,0 +1,44 @@ +isAbstract() || !$class->isSubclassOf(Model::class)) { + continue; + } + $declaring = $class->getProperty('table')->getDeclaringClass()->getName(); + $table = $class->newInstanceWithoutConstructor()->getTable(); + if ($declaring === Model::class || $table === '') { + $missing[] = $class->getShortName(); + } + } + + expect($missing)->toBe([]); +}); + +test('models that used to derive their table name keep that exact name', function () { + $models = [ + 'ActiveUser', 'ActiveUserLock', 'ActiveUserSession', 'Category', 'DocumentGroup', + 'DocumentgroupName', 'FileGroup', 'MemberGroup', 'MembergroupName', 'Permissions', + 'PermissionsGroups', 'RolePermissions', 'SiteHtmlsnippet', 'SiteModule', 'SitePlugin', + 'SitePluginEvent', 'SiteSnippet', 'SiteTemplate', 'SiteTmplvar', 'SiteTmplvarContentvalue', + 'SiteTmplvarTemplate', 'SystemEventname', 'SystemSetting', 'User', 'UserAttribute', + 'UserRole', 'UserRoleVar', 'UserSetting', 'UserValue', + ]; + + foreach ($models as $model) { + $class = new ReflectionClass('EvolutionCMS\\Models\\' . $model); + // The rule Model::getTable() applies when $table is not set. + $derived = Str::snake(Str::pluralStudly($model)); + + expect($class->newInstanceWithoutConstructor()->getTable())->toBe($derived, $model); + } +});