diff --git a/migrations/v0.1/2021_05_30_000000_wrap_migration_beta_path.php b/migrations/v1.0/2021_05_31_000000_rename_beta_migrations.php similarity index 80% rename from migrations/v0.1/2021_05_30_000000_wrap_migration_beta_path.php rename to migrations/v1.0/2021_05_31_000000_rename_beta_migrations.php index 70ed8452d..129ce79ed 100644 --- a/migrations/v0.1/2021_05_30_000000_wrap_migration_beta_path.php +++ b/migrations/v1.0/2021_05_31_000000_rename_beta_migrations.php @@ -22,7 +22,7 @@ return [ $db = $schema->getConnection(); $db->table('migrations') - ->where('permission', 'LIKE', 'viewForum') - ->update(['permission' => $db->raw("REPLACE(migration, 'v0.1/', '')")]); + ->whereNull('extension') + ->update(['migration' => $db->raw("REPLACE('v0.1/', '')")]); } ]; diff --git a/migrations/v1.0/create_discussions_table.php b/migrations/v1.0/create_discussions_table.php new file mode 100644 index 000000000..13d2a9ef1 --- /dev/null +++ b/migrations/v1.0/create_discussions_table.php @@ -0,0 +1,48 @@ + function (Builder $schema) { + $schema->create('discussions', function (Blueprint $table) { + $table->increments('id'); + $table->string('title', 200); + $table->unsignedInteger('comments_count')->default(0); + $table->unsignedInteger('participants_count')->default(0); + $table->unsignedInteger('post_number_index')->default(0); + + $table->dateTime('created_at'); + $table->foreignIdFor(User::class, 'user_id')->nullable(); + $table->foreignIdFor(Post::class, 'first_post_id')->nullable(); + + $table->dateTime('last_posted_at')->nullable(); + $table->foreignIdFor(User::class, 'last_posted_user_id')->nullable(); + $table->foreignIdFor(Post::class, 'last_post_id')->nullable(); + $table->unsignedInteger('last_post_number')->nullable(); + + $table->dateTime('hidden_at')->nullable(); + $table->foreignIdFor(User::class, 'hidden_user_id')->nullable(); + + $table->string('slug', 200); + $table->boolean('is_private')->default(0); + }); + + $connection = $schema->getConnection(); + $prefix = $connection->getTablePrefix(); + $connection->statement('ALTER TABLE '.$prefix.'discussions ADD FULLTEXT title (title)'); + }, + + 'down' => function (Builder $schema) { + $schema->drop('discussions'); + }, +]; diff --git a/migrations/v1.0/create_posts_table.php b/migrations/v1.0/create_posts_table.php new file mode 100644 index 000000000..2ff73d617 --- /dev/null +++ b/migrations/v1.0/create_posts_table.php @@ -0,0 +1,48 @@ + function (Builder $schema) { + $schema->create('posts', function (Blueprint $table) { + $table->increments('id'); + $table->foreignIdFor(Discussion::class, 'discussion_id'); + $table->unsignedInteger('number')->nullable(); + + $table->dateTime('created_at'); + $table->foreignIdFor(User::class, 'user_id')->nullable(); + $table->string('type', 100)->nullable(); + $table->mediumText('content')->nullable(); + + $table->dateTime('edited_at')->nullable(); + $table->foreignIdFor(User::class, 'edited_user_id')->nullable(); + $table->dateTime('hidden_at')->nullable(); + $table->foreignIdFor(User::class, 'hidden_user_id')->nullable(); + + $table->string('ip_address', 45)->nullable(); + $table->boolean('is_private'); + + $table->unique(['discussion_id', 'number']); + }); + + $connection = $schema->getConnection(); + $prefix = $connection->getTablePrefix(); + $connection->statement('ALTER TABLE '.$prefix.'posts ADD FULLTEXT content (content)'); + }, + + 'down' => function (Builder $schema) { + $schema->drop('posts'); + } +]; diff --git a/migrations/v1.0/create_settings_table.php b/migrations/v1.0/create_settings_table.php new file mode 100644 index 000000000..e7ed5857f --- /dev/null +++ b/migrations/v1.0/create_settings_table.php @@ -0,0 +1,19 @@ +string('key', 100)->primary(); + $table->binary('value')->nullable(); + } +); diff --git a/migrations/v1.0/create_users_table.php b/migrations/v1.0/create_users_table.php new file mode 100644 index 000000000..8c91a2a88 --- /dev/null +++ b/migrations/v1.0/create_users_table.php @@ -0,0 +1,35 @@ +increments('id'); + $table->string('username', 100)->unique(); + $table->string('email', 150)->unique(); + $table->boolean('is_email_confirmed')->default(0); + $table->string('password', 100); + $table->string('avatar_url', 100)->nullable(); + $table->text('preferences')->nullable(); + $table->dateTime('join_time')->nullable(); + $table->dateTime('last_seen_time')->nullable(); + $table->dateTime('read_time')->nullable(); + $table->dateTime('notification_read_time')->nullable(); + $table->integer('discussions_count')->unsigned()->default(0); + $table->integer('comments_count')->unsigned()->default(0); + + $table->index('joined_at'); + $table->index('last_seen_at'); + $table->index('discussion_count'); + $table->index('comment_count'); + } +); diff --git a/src/Database/MigrationSourceRepository.php b/src/Database/MigrationSourceRepository.php index 228dad138..02a322661 100644 --- a/src/Database/MigrationSourceRepository.php +++ b/src/Database/MigrationSourceRepository.php @@ -12,6 +12,7 @@ namespace Flarum\Database; use Flarum\Extension\Extension; use Flarum\Foundation\Application; use Illuminate\Database\ConnectionInterface; +use Illuminate\Support\Str; class MigrationSourceRepository { @@ -25,31 +26,32 @@ class MigrationSourceRepository public function flarum(): array { if (! $this->databaseVersion()) { - return $this->install(); + return $this->wrap($this->install()); } - return $this->upgrade(); + return $this->wrap($this->upgrade()); } - public function extension(Extension $extension): ?array + public function extension(Extension $extension): array { if (! $extension->hasMigrations()) { return []; } - return $extension->getMigrations(); + return $this->wrap(glob($extension->getPath().'/migrations/*_*.php')); } protected function install(): array { // We read every file from the latest major/minor version migrations directory. // Including the create__table statements. - $files = glob(__DIR__.'/../../migrations/'.$this->installedVersion(true).'/[0-9_]{15}_*.php'); + $files = glob(__DIR__.'/../../migrations/'.$this->installedVersion(true).'/[0-9_]**.php'); // Sort by timestamp. sort($files); - $create = glob(__DIR__.'/../../migrations/'.$this->installedVersion(true).'/create_*.php'); + // Read and prepend the create_
_table statements. + $create = glob(__DIR__.'/../../migrations/'.$this->installedVersion(true).'/create_*_table.php'); return array_merge($create, $files); } @@ -59,26 +61,28 @@ class MigrationSourceRepository $files = []; $add = false; - $directories = glob(__DIR__.'/../../migrations/', GLOB_ONLYDIR); + $directories = glob(__DIR__.'/../../migrations/*', GLOB_ONLYDIR); sort($directories, SORT_NATURAL); // Upgrade // Loop over all version migrations directory until we find the version that is currently active. foreach ($directories as $directory) { + $directoryVersion = substr(basename($directory), 1); + // We have found the directory matching the version database version. Start adding files. - if (substr($directory, 1) === $this->databaseVersion(true)) { + if ($directoryVersion === $this->databaseVersion(true)) { $add = true; } if ($add) { // Selectively add files, but only include those matching the format YYYY_MM_DD_HHIISS_.php // This excludes the create_
_table. - $files = array_merge($files, glob(__DIR__."/../../migrations/$directory/[0-9_]{15}_*.php")); + $files = array_merge($files, glob(realpath($directory) . "/[0-9_]**.php")); } // Once we found the version that is installed, we can quit. // Theoretically this should never be necessary, it could just loop over all remaining ones. - if (substr($directory, 1) === $this->installedVersion(true)) { + if ($directoryVersion === $this->installedVersion(true)) { break; } } @@ -89,6 +93,15 @@ class MigrationSourceRepository return $files; } + protected function shortVersion(string $version): string + { + if (preg_match('~(?^[0-9]+\.[0-9]+)~', $version, $m)) { + return $m['version']; + } + + return $version; + } + protected function installedVersion(bool $short = false): string { $version = Application::VERSION; @@ -100,15 +113,6 @@ class MigrationSourceRepository return $version; } - protected function shortVersion(string $version): string - { - if (preg_match('~(?^[0-9]+\.[0-9]+)~', $version, $m)) { - return $m['version']; - } - - return $version; - } - protected function databaseVersion(bool $short = false): ?string { $version = $this->connection->getSchemaBuilder()->hasTable('settings') @@ -121,4 +125,16 @@ class MigrationSourceRepository return $version; } + + protected function wrap(array $migrations): array + { + return collect($migrations) + ->mapWithKeys(function (string $path) { + $path = realpath($path); + $path = Str::after($path, 'migrations/'); + $basename = Str::before($path, '.php'); + return [$path => $basename]; + }) + ->toArray(); + } } diff --git a/src/Database/Migrator.php b/src/Database/Migrator.php index 96a2678fb..ccce54a30 100644 --- a/src/Database/Migrator.php +++ b/src/Database/Migrator.php @@ -11,7 +11,6 @@ namespace Flarum\Database; use Exception; use Flarum\Extension\Extension; -use Flarum\Foundation\Application; use Illuminate\Database\ConnectionInterface; use Illuminate\Database\Schema\Builder; use Illuminate\Filesystem\Filesystem; @@ -79,13 +78,13 @@ class Migrator /** * Run the outstanding migrations at a given path. * - * @param string $path - * @param Extension $extension + * @param string $path + * @param Extension|null $extension * @return void */ - public function run($path, Extension $extension = null) + public function run(string $path, Extension $extension = null) { - $files = $this->getMigrationFiles($path, $extension); + $files = $this->getMigrationFiles($extension); $ran = $this->repository->getRan($extension ? $extension->getId() : null); @@ -97,12 +96,12 @@ class Migrator /** * Run an array of migrations. * - * @param string $path - * @param array $migrations - * @param Extension $extension + * @param string $path + * @param array $migrations + * @param Extension|null $extension * @return void */ - public function runMigrationList($path, $migrations, Extension $extension = null) + public function runMigrationList(string $path, array $migrations, Extension $extension = null) { // First we will just make sure that there are any migrations to run. If there // aren't, we will just make a note of it to the developer so they're aware @@ -124,13 +123,13 @@ class Migrator /** * Run "up" a migration instance. * - * @param string $path - * @param string $file - * @param string $path - * @param Extension $extension + * @param string $path + * @param string $file + * @param Extension|null $extension * @return void + * @throws Exception */ - protected function runUp($path, $file, Extension $extension = null) + protected function runUp(string $path, string $file, Extension $extension = null) { $migration = $this->resolve($path, $file); @@ -209,14 +208,9 @@ class Migrator } } - public function getMigrationFiles(string $path, Extension $extension = null): array + public function getMigrationFiles(Extension $extension = null): array { - $files = $extension ? $this->source->extension($extension) : $this->source->flarum(); - dd($files); - - return array_map(function ($file) { - return str_replace('.php', '', basename($file)); - }, $files); + return $extension ? $this->source->extension($extension) : $this->source->flarum(); } /** diff --git a/src/Extension/Extension.php b/src/Extension/Extension.php index 0700ce5ba..7bfe9f405 100644 --- a/src/Extension/Extension.php +++ b/src/Extension/Extension.php @@ -9,6 +9,8 @@ namespace Flarum\Extension; +use Flarum\Database\MigrationSourceRepository; +use Flarum\Database\Migrator; use Flarum\Extend\LifecycleInterface; use Flarum\Extension\Exception\ExtensionBootError; use Illuminate\Contracts\Container\Container; @@ -458,15 +460,17 @@ class Extension implements Arrayable } } - /** - * Retrieves the list of migrations of this extension. - * - * @return array - * @internal - */ - public function getMigrations(): array + public function migrate(Migrator $migrator, string $direction) { - return glob($this->path.'/migrations/*_*.php'); + if (! $this->hasMigrations()) { + return; + } + + if ($direction == 'up') { + return $migrator->run($this->getPath().'/migrations', $this); + } else { + return $migrator->reset($this->getPath().'/migrations', $this); + } } /**