From b2173fb1c0ee1c652ef01df08f85d6bfc1e1fdd2 Mon Sep 17 00:00:00 2001 From: Tim Kelty Date: Tue, 6 Feb 2024 00:37:20 -0500 Subject: [PATCH 1/5] Convert columns to json --- .../m240206_035135_convert_json_columns.php | 36 +++++++++++++++++++ src/models/DeprecationError.php | 10 ++++++ src/records/GqlSchema.php | 10 ++++++ src/services/Dashboard.php | 5 +++ src/services/Deprecator.php | 2 +- src/services/Entries.php | 4 ++- src/services/Fields.php | 2 +- src/services/Gql.php | 2 +- src/services/Users.php | 8 +++-- 9 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 src/migrations/m240206_035135_convert_json_columns.php diff --git a/src/migrations/m240206_035135_convert_json_columns.php b/src/migrations/m240206_035135_convert_json_columns.php new file mode 100644 index 00000000000..b79128e4f18 --- /dev/null +++ b/src/migrations/m240206_035135_convert_json_columns.php @@ -0,0 +1,36 @@ +alterColumn(Table::DEPRECATIONERRORS, 'traces', $this->json()); + $this->alterColumn(Table::FIELDLAYOUTS, 'config', $this->json()); + $this->alterColumn(Table::GQLSCHEMAS, 'scope', $this->json()); + $this->alterColumn(Table::SECTIONS, 'previewTargets', $this->json()); + $this->alterColumn(Table::USERPREFERENCES, 'preferences', $this->json()); + $this->alterColumn(Table::WIDGETS, 'settings', $this->json()); + + return true; + } + + /** + * @inheritdoc + */ + public function safeDown(): bool + { + echo "m240206_035135_convert_json_columns cannot be reverted.\n"; + return false; + } +} diff --git a/src/models/DeprecationError.php b/src/models/DeprecationError.php index 4c23174d1af..b9833fa20df 100644 --- a/src/models/DeprecationError.php +++ b/src/models/DeprecationError.php @@ -8,6 +8,7 @@ namespace craft\models; use craft\base\Model; +use craft\helpers\Json; use craft\validators\DateTimeValidator; use DateTime; @@ -59,6 +60,15 @@ class DeprecationError extends Model */ public ?array $traces = null; + public function __construct($config = []) + { + if (is_string($config['traces'] ?? null)) { + $config['traces'] = Json::decode($config['traces']); + } + + parent::__construct($config); + } + /** * @inheritdoc */ diff --git a/src/records/GqlSchema.php b/src/records/GqlSchema.php index c9573d25d56..e3f8cdbd31b 100644 --- a/src/records/GqlSchema.php +++ b/src/records/GqlSchema.php @@ -9,6 +9,7 @@ use craft\db\ActiveRecord; use craft\db\Table; +use craft\helpers\Json; /** * Class GqlSchema record. @@ -30,4 +31,13 @@ public static function tableName(): string { return Table::GQLSCHEMAS; } + + public function __construct($config = []) + { + if (is_string($config['scope'] ?? null)) { + $config['scope'] = Json::decode($config['scope']); + } + + parent::__construct($config); + } } diff --git a/src/services/Dashboard.php b/src/services/Dashboard.php index ce5e2284d02..01c07a62b53 100644 --- a/src/services/Dashboard.php +++ b/src/services/Dashboard.php @@ -17,6 +17,7 @@ use craft\events\WidgetEvent; use craft\helpers\Component as ComponentHelper; use craft\helpers\Db; +use craft\helpers\Json; use craft\records\Widget as WidgetRecord; use craft\widgets\CraftSupport as CraftSupportWidget; use craft\widgets\Feed as FeedWidget; @@ -122,6 +123,10 @@ public function createWidget(mixed $config): WidgetInterface $config = ['type' => $config]; } + if (is_string($config['settings'] ?? null)) { + $config['settings'] = Json::decode($config['settings']); + } + try { $widget = ComponentHelper::createComponent($config, WidgetInterface::class); } catch (MissingComponentException $e) { diff --git a/src/services/Deprecator.php b/src/services/Deprecator.php index d7c794ae6de..ce99fcb75c6 100644 --- a/src/services/Deprecator.php +++ b/src/services/Deprecator.php @@ -149,7 +149,7 @@ public function storeLogs(): void 'file' => $log->file, 'line' => $log->line, 'message' => $log->message, - 'traces' => Json::encode($log->traces), + 'traces' => $log->traces, ]); $log->id = (int)$db->getLastInsertID(); } catch (Exception $e) { diff --git a/src/services/Entries.php b/src/services/Entries.php index 07f548e15ce..b285d2b8231 100644 --- a/src/services/Entries.php +++ b/src/services/Entries.php @@ -228,7 +228,9 @@ private function _sections(): MemoizableArray $this->_sections = new MemoizableArray($results, function(array $result) use (&$siteSettingsBySection) { if (!empty($result['previewTargets'])) { - $result['previewTargets'] = Json::decode($result['previewTargets']); + $result['previewTargets'] = is_string($result['previewTargets']) + ? Json::decode($result['previewTargets']) + : $result['previewTargets']; } else { $result['previewTargets'] = []; } diff --git a/src/services/Fields.php b/src/services/Fields.php index 63b1aba9ec9..91a02fa5346 100644 --- a/src/services/Fields.php +++ b/src/services/Fields.php @@ -757,7 +757,7 @@ private function _layouts(): MemoizableArray if (array_key_exists('config', $config)) { $nestedConfig = ArrayHelper::remove($config, 'config'); if ($nestedConfig) { - $config += Json::decode($nestedConfig); + $config += is_string($nestedConfig) ? Json::decode($nestedConfig) : $nestedConfig; } $loadTabs = false; } else { diff --git a/src/services/Gql.php b/src/services/Gql.php index 3b8d42b4e88..ed49681a594 100644 --- a/src/services/Gql.php +++ b/src/services/Gql.php @@ -992,7 +992,7 @@ public function handleChangedSchema(ConfigEvent $event): void $schemaRecord->uid = $schemaUid; $schemaRecord->name = $data['name']; $schemaRecord->isPublic = (bool)($data['isPublic'] ?? false); - $schemaRecord->scope = (!empty($data['scope']) && is_array($data['scope'])) ? Json::encode($data['scope']) : []; + $schemaRecord->scope = empty($data['scope']) ? [] : $data['scope']; // Save the schema record $schemaRecord->save(false); diff --git a/src/services/Users.php b/src/services/Users.php index 269605df270..9ade36eb34f 100644 --- a/src/services/Users.php +++ b/src/services/Users.php @@ -369,9 +369,11 @@ public function getUserPreferences(int $userId): array ->select(['preferences']) ->from([Table::USERPREFERENCES]) ->where(['userId' => $userId]) - ->scalar(); + ->scalar() ?: []; - $this->_userPreferences[$userId] = $preferences ? Json::decode($preferences) : []; + $this->_userPreferences[$userId] = is_string($preferences) + ? Json::decode($preferences) + : $preferences; } return $this->_userPreferences[$userId]; @@ -390,7 +392,7 @@ public function saveUserPreferences(User $user, array $preferences): void Db::upsert(Table::USERPREFERENCES, [ 'userId' => $user->id, - 'preferences' => Json::encode($preferences), + 'preferences' => $preferences, ]); $this->_userPreferences[$user->id] = $preferences; From 6a71bfa19f958cd5cc66191d03393f755f6f711f Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Tue, 6 Feb 2024 08:39:02 -0800 Subject: [PATCH 2/5] Fixed PHPStan issue --- src/services/Dashboard.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/Dashboard.php b/src/services/Dashboard.php index 01c07a62b53..830bbeec1e5 100644 --- a/src/services/Dashboard.php +++ b/src/services/Dashboard.php @@ -27,6 +27,7 @@ use craft\widgets\QuickPost as QuickPostWidget; use craft\widgets\RecentEntries as RecentEntriesWidget; use craft\widgets\Updates as UpdatesWidget; +use DateTime; use Throwable; use yii\base\Component; use yii\base\Exception; @@ -114,7 +115,7 @@ public function getAllWidgetTypes(): array * * @template T of WidgetInterface * @param string|array $config The widget’s class name, or its config, with a `type` value and optionally a `settings` value. - * @phpstan-param class-string|array{type:class-string} $config + * @phpstan-param class-string|array{type:class-string,id?:int,dateCreated?:DateTime,dateUpdated?:DateTime,colspan?:int,settings?:array|string} $config * @return T */ public function createWidget(mixed $config): WidgetInterface From 1ecb86fa7416a21d94129ddadad9ba1705223526 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Tue, 6 Feb 2024 09:20:43 -0800 Subject: [PATCH 3/5] Bump the schema version and update the Install migration --- src/config/app.php | 2 +- src/migrations/Install.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/config/app.php b/src/config/app.php index e65d168e444..eefc2110bfd 100644 --- a/src/config/app.php +++ b/src/config/app.php @@ -4,7 +4,7 @@ 'id' => 'CraftCMS', 'name' => 'Craft CMS', 'version' => '5.0.0-alpha.12', - 'schemaVersion' => '5.0.0.17', + 'schemaVersion' => '5.0.0.18', 'minVersionRequired' => '4.4.0', 'basePath' => dirname(__DIR__), // Defines the @app alias 'runtimePath' => '@storage/runtime', // Defines the @runtime alias diff --git a/src/migrations/Install.php b/src/migrations/Install.php index 84f383a1c27..11910af402e 100644 --- a/src/migrations/Install.php +++ b/src/migrations/Install.php @@ -288,7 +288,7 @@ public function createTables(): void 'file' => $this->string()->notNull(), 'line' => $this->smallInteger()->unsigned(), 'message' => $this->text(), - 'traces' => $this->text(), + 'traces' => $this->json(), 'dateCreated' => $this->dateTime()->notNull(), 'dateUpdated' => $this->dateTime()->notNull(), 'uid' => $this->uid(), @@ -424,7 +424,7 @@ public function createTables(): void $this->createTable(Table::FIELDLAYOUTS, [ 'id' => $this->primaryKey(), 'type' => $this->string()->notNull(), - 'config' => $this->text(), + 'config' => $this->json(), 'dateCreated' => $this->dateTime()->notNull(), 'dateUpdated' => $this->dateTime()->notNull(), 'dateDeleted' => $this->dateTime()->null(), @@ -471,7 +471,7 @@ public function createTables(): void $this->createTable(Table::GQLSCHEMAS, [ 'id' => $this->primaryKey(), 'name' => $this->string()->notNull(), - 'scope' => $this->text(), + 'scope' => $this->json(), 'isPublic' => $this->boolean()->notNull()->defaultValue(false), 'dateCreated' => $this->dateTime()->notNull(), 'dateUpdated' => $this->dateTime()->notNull(), @@ -558,7 +558,7 @@ public function createTables(): void 'maxAuthors' => $this->smallInteger()->unsigned()->defaultValue(1)->notNull(), 'propagationMethod' => $this->string()->defaultValue(PropagationMethod::All->value)->notNull(), 'defaultPlacement' => $this->enum('defaultPlacement', [Section::DEFAULT_PLACEMENT_BEGINNING, Section::DEFAULT_PLACEMENT_END])->defaultValue('end')->notNull(), - 'previewTargets' => $this->text(), + 'previewTargets' => $this->json(), 'dateCreated' => $this->dateTime()->notNull(), 'dateUpdated' => $this->dateTime()->notNull(), 'dateDeleted' => $this->dateTime()->null(), @@ -714,7 +714,7 @@ public function createTables(): void ]); $this->createTable(Table::USERPREFERENCES, [ 'userId' => $this->primaryKey(), - 'preferences' => $this->text(), + 'preferences' => $this->json(), ]); $this->createTable(Table::USERS, [ 'id' => $this->integer()->notNull(), @@ -792,7 +792,7 @@ public function createTables(): void 'type' => $this->string()->notNull(), 'sortOrder' => $this->smallInteger()->unsigned(), 'colspan' => $this->tinyInteger(), - 'settings' => $this->text(), + 'settings' => $this->json(), 'enabled' => $this->boolean()->defaultValue(true)->notNull(), 'dateCreated' => $this->dateTime()->notNull(), 'dateUpdated' => $this->dateTime()->notNull(), From 585a13189a1e2d4309237bf39c282995216a208d Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Tue, 6 Feb 2024 09:21:59 -0800 Subject: [PATCH 4/5] Cleanup + MariaDB fixes --- src/models/DeprecationError.php | 10 ---------- src/models/GqlSchema.php | 10 ---------- src/services/Dashboard.php | 5 ----- src/services/Deprecator.php | 4 ++-- src/services/Entries.php | 6 ++---- src/services/Gql.php | 3 +-- src/services/Users.php | 17 ++++++++++++----- 7 files changed, 17 insertions(+), 38 deletions(-) diff --git a/src/models/DeprecationError.php b/src/models/DeprecationError.php index b9833fa20df..4c23174d1af 100644 --- a/src/models/DeprecationError.php +++ b/src/models/DeprecationError.php @@ -8,7 +8,6 @@ namespace craft\models; use craft\base\Model; -use craft\helpers\Json; use craft\validators\DateTimeValidator; use DateTime; @@ -60,15 +59,6 @@ class DeprecationError extends Model */ public ?array $traces = null; - public function __construct($config = []) - { - if (is_string($config['traces'] ?? null)) { - $config['traces'] = Json::decode($config['traces']); - } - - parent::__construct($config); - } - /** * @inheritdoc */ diff --git a/src/models/GqlSchema.php b/src/models/GqlSchema.php index 36488300bb8..865ddd842a2 100644 --- a/src/models/GqlSchema.php +++ b/src/models/GqlSchema.php @@ -8,7 +8,6 @@ namespace craft\models; use craft\base\Model; -use craft\helpers\Json; use craft\helpers\StringHelper; use craft\records\GqlSchema as GqlSchemaRecord; use craft\validators\UniqueValidator; @@ -52,15 +51,6 @@ class GqlSchema extends Model */ private array $_cachedPairs = []; - public function __construct($config = []) - { - if (isset($config['scope']) && is_string($config['scope'])) { - $config['scope'] = Json::decode($config['scope']); - } - - parent::__construct($config); - } - /** * @inheritdoc */ diff --git a/src/services/Dashboard.php b/src/services/Dashboard.php index 830bbeec1e5..ca487bc3d95 100644 --- a/src/services/Dashboard.php +++ b/src/services/Dashboard.php @@ -17,7 +17,6 @@ use craft\events\WidgetEvent; use craft\helpers\Component as ComponentHelper; use craft\helpers\Db; -use craft\helpers\Json; use craft\records\Widget as WidgetRecord; use craft\widgets\CraftSupport as CraftSupportWidget; use craft\widgets\Feed as FeedWidget; @@ -124,10 +123,6 @@ public function createWidget(mixed $config): WidgetInterface $config = ['type' => $config]; } - if (is_string($config['settings'] ?? null)) { - $config['settings'] = Json::decode($config['settings']); - } - try { $widget = ComponentHelper::createComponent($config, WidgetInterface::class); } catch (MissingComponentException $e) { diff --git a/src/services/Deprecator.php b/src/services/Deprecator.php index ce99fcb75c6..a0ef1df1ea0 100644 --- a/src/services/Deprecator.php +++ b/src/services/Deprecator.php @@ -13,7 +13,6 @@ use craft\elements\db\ElementQuery; use craft\errors\DeprecationException; use craft\helpers\Db; -use craft\helpers\Json; use craft\helpers\StringHelper; use craft\helpers\Template; use craft\models\DeprecationError; @@ -139,6 +138,7 @@ private function _storeLogsInDb(): bool public function storeLogs(): void { $db = Craft::$app->getDb(); + $tableSchema = $db->getSchema()->getTableSchema(Table::DEPRECATIONERRORS); foreach ($this->_pendingRequestLogs as $log) { try { @@ -149,7 +149,7 @@ public function storeLogs(): void 'file' => $log->file, 'line' => $log->line, 'message' => $log->message, - 'traces' => $log->traces, + 'traces' => Db::prepareValueForDb($log->traces, $tableSchema->columns['traces']->dbType), ]); $log->id = (int)$db->getLastInsertID(); } catch (Exception $e) { diff --git a/src/services/Entries.php b/src/services/Entries.php index b285d2b8231..2fbdd01938e 100644 --- a/src/services/Entries.php +++ b/src/services/Entries.php @@ -227,10 +227,8 @@ private function _sections(): MemoizableArray } $this->_sections = new MemoizableArray($results, function(array $result) use (&$siteSettingsBySection) { - if (!empty($result['previewTargets'])) { - $result['previewTargets'] = is_string($result['previewTargets']) - ? Json::decode($result['previewTargets']) - : $result['previewTargets']; + if (!empty($result['previewTargets']) && is_string($result['previewTargets'])) { + $result['previewTargets'] = Json::decode($result['previewTargets']); } else { $result['previewTargets'] = []; } diff --git a/src/services/Gql.php b/src/services/Gql.php index ed49681a594..95836c57ff9 100644 --- a/src/services/Gql.php +++ b/src/services/Gql.php @@ -68,7 +68,6 @@ use craft\helpers\DateTimeHelper; use craft\helpers\Db; use craft\helpers\Gql as GqlHelper; -use craft\helpers\Json; use craft\helpers\ProjectConfig as ProjectConfigHelper; use craft\helpers\StringHelper; use craft\models\FieldLayout; @@ -992,7 +991,7 @@ public function handleChangedSchema(ConfigEvent $event): void $schemaRecord->uid = $schemaUid; $schemaRecord->name = $data['name']; $schemaRecord->isPublic = (bool)($data['isPublic'] ?? false); - $schemaRecord->scope = empty($data['scope']) ? [] : $data['scope']; + $schemaRecord->scope = ($data['scope'] ?? false) ?: []; // Save the schema record $schemaRecord->save(false); diff --git a/src/services/Users.php b/src/services/Users.php index 9ade36eb34f..f21bf80b4d1 100644 --- a/src/services/Users.php +++ b/src/services/Users.php @@ -369,11 +369,17 @@ public function getUserPreferences(int $userId): array ->select(['preferences']) ->from([Table::USERPREFERENCES]) ->where(['userId' => $userId]) - ->scalar() ?: []; + ->scalar(); - $this->_userPreferences[$userId] = is_string($preferences) - ? Json::decode($preferences) - : $preferences; + if ($preferences) { + if (is_string($preferences)) { + $preferences = Json::decode($preferences); + } + } else { + $preferences = []; + } + + $this->_userPreferences[$userId] = $preferences; } return $this->_userPreferences[$userId]; @@ -389,10 +395,11 @@ public function saveUserPreferences(User $user, array $preferences): void { // Merge in any other saved preferences $preferences += $this->getUserPreferences($user->id); + $tableSchema = Craft::$app->getDb()->getSchema()->getTableSchema(Table::USERPREFERENCES); Db::upsert(Table::USERPREFERENCES, [ 'userId' => $user->id, - 'preferences' => $preferences, + 'preferences' => Db::prepareValueForDb($preferences, $tableSchema->columns['preferences']->dbType), ]); $this->_userPreferences[$user->id] = $preferences; From b0ebbfdd479a2aaaca6b5facb40d4f7ac572feab Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Tue, 6 Feb 2024 09:26:17 -0800 Subject: [PATCH 5/5] More cleanup --- src/records/GqlSchema.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/records/GqlSchema.php b/src/records/GqlSchema.php index e3f8cdbd31b..c9573d25d56 100644 --- a/src/records/GqlSchema.php +++ b/src/records/GqlSchema.php @@ -9,7 +9,6 @@ use craft\db\ActiveRecord; use craft\db\Table; -use craft\helpers\Json; /** * Class GqlSchema record. @@ -31,13 +30,4 @@ public static function tableName(): string { return Table::GQLSCHEMAS; } - - public function __construct($config = []) - { - if (is_string($config['scope'] ?? null)) { - $config['scope'] = Json::decode($config['scope']); - } - - parent::__construct($config); - } }