From 2c9a732256634d4fbc51feeaa640d90b16baf72f Mon Sep 17 00:00:00 2001 From: Hugo Sales Date: Wed, 18 Mar 2020 10:52:08 +0000 Subject: [PATCH] [CORE][DOCTRINE] Implement SchemaDefDriver, which transforms the old syntax from to doctrine's metadata --- src/Entity/User.php | 51 +++++++++-------- src/Util/SchemaDefDriver.php | 103 ++++++++++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 27 deletions(-) diff --git a/src/Entity/User.php b/src/Entity/User.php index f74ff1b53e..430707c076 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -8,36 +8,35 @@ class User public static function schemaDef() { - intval('37', 16); - return [ + 'name' => 'user', 'description' => 'local users', 'fields' => [ - 'id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'], - 'nickname' => ['type' => 'varchar', 'length' => 64, 'description' => 'nickname or username, duped in profile'], - 'password' => ['type' => 'varchar', 'length' => 191, 'description' => 'salted password, can be null for OpenID users'], - 'email' => ['type' => 'varchar', 'length' => 191, 'description' => 'email address for password recovery etc.'], - 'incomingemail' => ['type' => 'varchar', 'length' => 191, 'description' => 'email address for post-by-email'], - 'emailnotifysub' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of subscriptions'], - 'emailnotifyfav' => ['type' => 'int', 'size' => 'tiny', 'default' => null, 'description' => 'Notify by email of favorites'], - 'emailnotifynudge' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of nudges'], - 'emailnotifymsg' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of direct messages'], - 'emailnotifyattn' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of @-replies'], - 'language' => ['type' => 'varchar', 'length' => 50, 'description' => 'preferred language'], - 'timezone' => ['type' => 'varchar', 'length' => 50, 'description' => 'timezone'], - 'emailpost' => ['type' => 'bool', 'default' => true, 'description' => 'Post by email'], - 'sms' => ['type' => 'varchar', 'length' => 64, 'description' => 'sms phone number'], - 'carrier' => ['type' => 'int', 'description' => 'foreign key to sms_carrier'], - 'smsnotify' => ['type' => 'bool', 'default' => false, 'description' => 'whether to send notices to SMS'], - 'smsreplies' => ['type' => 'bool', 'default' => false, 'description' => 'whether to send notices to SMS on replies'], - 'smsemail' => ['type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier'], - 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'], - 'autosubscribe' => ['type' => 'bool', 'default' => false, 'description' => 'automatically subscribe to users who subscribe to us'], - 'subscribe_policy' => ['type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'], - 'urlshorteningservice' => ['type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'], - 'private_stream' => ['type' => 'bool', 'default' => false, 'description' => 'whether to limit all notices to followers only'], + 'id' => ['type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'], + 'nickname' => ['type' => 'varchar', 'length' => 64, 'description' => 'nickname or username, duped in profile'], + 'password' => ['type' => 'varchar', 'length' => 191, 'description' => 'salted password, can be null for OpenID users'], + 'email' => ['type' => 'varchar', 'length' => 191, 'description' => 'email address for password recovery etc.'], + 'incomingemail' => ['type' => 'varchar', 'length' => 191, 'description' => 'email address for post-by-email'], + 'emailnotifysub' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of subscriptions'], + 'emailnotifyfav' => ['type' => 'int', 'size' => 'tiny', 'default' => null, 'description' => 'Notify by email of favorites'], + 'emailnotifynudge' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of nudges'], + 'emailnotifymsg' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of direct messages'], + 'emailnotifyattn' => ['type' => 'bool', 'default' => true, 'description' => 'Notify by email of @-replies'], + 'language' => ['type' => 'varchar', 'length' => 50, 'description' => 'preferred language'], + 'timezone' => ['type' => 'varchar', 'length' => 50, 'description' => 'timezone'], + 'emailpost' => ['type' => 'bool', 'default' => true, 'description' => 'Post by email'], + 'sms' => ['type' => 'varchar', 'length' => 64, 'description' => 'sms phone number'], + 'carrier' => ['type' => 'int', 'description' => 'foreign key to sms_carrier'], + 'smsnotify' => ['type' => 'bool', 'default' => false, 'description' => 'whether to send notices to SMS'], + 'smsreplies' => ['type' => 'bool', 'default' => false, 'description' => 'whether to send notices to SMS on replies'], + 'smsemail' => ['type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier'], + 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'], + 'autosubscribe' => ['type' => 'bool', 'default' => false, 'description' => 'automatically subscribe to users who subscribe to us'], + 'subscribe_policy' => ['type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'], + 'urlshorteningservice' => ['type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'], + 'private_stream' => ['type' => 'bool', 'default' => false, 'description' => 'whether to limit all notices to followers only'], 'created' => ['type' => 'datetime', 'not null' => true, 'default' => '0000-00-00 00:00:00', 'description' => 'date this record was created'], - 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], + 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], ], 'primary key' => ['id'], 'unique keys' => [ diff --git a/src/Util/SchemaDefDriver.php b/src/Util/SchemaDefDriver.php index af2a84d05e..c2401e65ca 100644 --- a/src/Util/SchemaDefDriver.php +++ b/src/Util/SchemaDefDriver.php @@ -7,9 +7,110 @@ use Doctrine\Persistence\Mapping\Driver\StaticPHPDriver; class SchemaDefDriver extends StaticPHPDriver { + /** + * PEAR DB type => Doctrine type + */ + private const types = [ + 'varchar' => 'string', + 'int' => 'integer', + 'tinyint' => 'smallint', // no portable tinyint + 'bigint' => 'bigint', + 'bool' => 'boolean', + 'numeric' => 'decimal', + 'text' => 'text', + 'datetime' => 'datetime', + // Unused in V2, but might start being used + 'date' => 'date', + 'time' => 'time', + 'datetimez' => 'datetimez', + 'object' => 'object', + 'array' => 'array', + 'simplearray' => 'simplearray', + 'json_array' => 'json_array', + 'float' => 'float', + 'guid' => 'guid', + 'blob' => 'blob', + ]; + + /** + * Fill in the database $metadata for $className + * + * @param string $className + * @param ClassMetadata $metadata + */ public function loadMetadataForClass($className, ClassMetadata $metadata) { $schema = $className::schemaDef(); - $metadata->addField($schema[0]); + + $metadata->setPrimaryTable(['name' => $schema['name'], + 'indexes' => self::kv_to_name_col($schema['indexes']), + 'uniqueConstraints' => self::kv_to_name_col($schema['unique keys']), + 'options' => ['comment' => $schema['description']], ]); + + foreach ($schema['fields'] as $name => $opts) { + // Convert old to new types + $type = $name === 'date' + // Old date fields were stored as int, store as datetime/timestamp + ? 'datetime' + // For ints, prepend the size (smallint) + // The size fields doesn't exist otherwise, suppress error + : self::types[(@$opts['size']) . $opts['type']]; + $field = [ + 'id' => in_array($name, $schema['primary key']), + 'fieldName' => $name, + 'type' => $type, + 'unique' => in_array([$name], $schema['unique keys']) || @$opts['unique'], + // String length, ignored if not a string, suppress error + 'length' => @$opts['length'], + 'nullable' => (@!$opts['not null']), + // Numeric precision and scale, ignored if not a number, suppress errors + 'precision' => @$opts['precision'], + 'scale' => @$opts['scale'], + 'options' => [ + 'comment' => $opts['description'], + 'default' => @$opts['default'], + 'unsigned' => @$opts['unsigned'], + // 'fixed' => bool, unused + // 'collation' => string, unused + // 'check', unused + ], + // 'columnDefinition', unused + ]; + // The optional feilds from earlier were populated with null, remove them + $field = array_filter($field, function ($v) { return !is_null($v); }); + $field['options'] = array_filter($field['options'], function ($v) { return !is_null($v); }); + + $metadata->mapField($field); + } + + // TODO foreign keys + } + + /** + * Override StaticPHPDriver's method, + * we care about classes that have the method `schemaDef`, + * instead of `loadMetadata`. + * + * @param string $className + */ + public function isTransient($className) + { + return !method_exists($className, 'schemaDef'); + } + + /** + * Convert [$key => $val] to ['name' => $key, 'columns' => $val] + * + * @param array + * + * @return array + */ + private static function kv_to_name_col(array $arr): array + { + $res = []; + foreach ($arr as $name => $cols) { + $res[] = ['name' => $name, 'columns' => $cols]; + } + return $res; } }