. // }}} /** * Compiler pass which triggers Symfony to tell Doctrine to * use our `SchemaDef` metadata driver * * @package GNUsocial * @category DB * * @author Hugo Sales * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ namespace App\DependencyInjection\Compiler; use App\Core\Log; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Mapping\Driver\StaticPHPDriver; use Functional as F; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** * Register a new ORM driver to allow use to use the old (and better) schemaDef format */ class SchemaDefDriver extends StaticPHPDriver implements CompilerPassInterface { /** * Register `app.schemadef_driver` (this class instantiated with argument src/Entity) as a metadata driver */ public function process(ContainerBuilder $container) { $container->findDefinition('doctrine.orm.default_metadata_driver') ->addMethodCall('addDriver', [new Reference('app.schemadef_driver'), 'App\\Entity'] ); } /** * V2 DB type => Doctrine type */ private const types = [ 'varchar' => 'string', 'char' => 'string', // char is a fixed witdh varchar 'int' => 'integer', 'serial' => 'integer', 'tinyint' => 'smallint', // no portable tinyint 'bigint' => 'bigint', 'bool' => 'boolean', 'numeric' => 'decimal', 'text' => 'text', 'datetime' => 'datetime', 'timestamp' => 'datetime', 'phone_number' => 'phone_number', // 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 $class_name * * @param string $class_name * @param ClassMetadata $metadata */ public function loadMetadataForClass($class_name, ClassMetadata $metadata) { $schema = $class_name::schemaDef(); Log::emergency($class_name); $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) { // TODO // 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 field doesn't exist otherwise : self::types[($opts['size'] ?? '') . $opts['type']]; $unique = null; foreach ($schema['unique keys'] ?? [] as $key => $uniq_arr) { if (in_array($name, $uniq_arr)) { $unique = $key; break; } } $default = $opts['default'] ?? null; $field = [ // boolean, optional 'id' => in_array($name, $schema['primary key']), // string 'fieldName' => $name, // string 'type' => $type, // stringn, optional 'unique' => $unique, // String length, ignored if not a string // int, optional 'length' => $opts['length'] ?? null, // boolean, optional 'nullable' => !($opts['not null'] ?? false), // Numeric precision and scale, ignored if not a number // integer, optional 'precision' => $opts['precision'] ?? null, // integer, optional 'scale' => $opts['scale'] ?? null, 'options' => [ 'comment' => $opts['description'] ?? null, 'default' => $default, 'unsigned' => $opts['unsigned'] ?? null, // bool, optional 'fixed' => $opts['type'] === 'char', // 'collation' => string, unused // 'check', unused ], // 'columnDefinition', unused ]; // The optional feilds from earlier were populated with null, remove them $field = array_filter($field, F\not('is_null')); $field['options'] = array_filter($field['options'], F\not('is_null')); $metadata->mapField($field); if ($opts['type'] === 'serial') { $metadata->setIdGeneratorType($metadata::GENERATOR_TYPE_AUTO); } } // TODO foreign keys } /** * Override StaticPHPDriver's method, * we care about classes that have the method `schemaDef`, * instead of `loadMetadata`. * * @param string $class_name * * @return bool */ public function isTransient($class_name) { return !method_exists($class_name, 'schemaDef'); } /** * Convert [$key => $val] to ['name' => $key, 'columns' => $val] * * @param array $arr * * @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; } }