mirror of
https://github.com/flarum/framework.git
synced 2024-11-23 16:23:40 +08:00
feat: add support for SQLite
(#3984)
* feat: add support for sqlite * chore: add warning on install * fix: ignore constraints before transaction begins * chore: update workflow * Apply fixes from StyleCI * chore: generate sqlite dump and manually add foreign keys * chore: fix actions * chore: fix actions * chore: fix actions * chore: fix actions * chore: fix actions * chore: fix actions * test: fix * Apply fixes from StyleCI * fix: sqlite with db prefix * Apply fixes from StyleCI * fix: statistics sqlite
This commit is contained in:
parent
5ce1aeab47
commit
eb6e599df1
|
@ -23,3 +23,6 @@ indent_size = 2
|
|||
|
||||
[*.neon]
|
||||
indent_style = tab
|
||||
|
||||
[{install,update}.php]
|
||||
indent_size = 2
|
||||
|
|
35
.github/workflows/REUSABLE_backend.yml
vendored
35
.github/workflows/REUSABLE_backend.yml
vendored
|
@ -44,7 +44,7 @@ on:
|
|||
description: Versions of databases to test with. Should be array of strings encoded as JSON array
|
||||
type: string
|
||||
required: false
|
||||
default: '["mysql:5.7", "mysql:8.0.30", "mysql:8.1.0", "mariadb"]'
|
||||
default: '["mysql:5.7", "mysql:8.0.30", "mysql:8.1.0", "mariadb", "sqlite:3"]'
|
||||
|
||||
php_ini_values:
|
||||
description: PHP ini values
|
||||
|
@ -85,32 +85,49 @@ jobs:
|
|||
# Expands the matrix by naming DBs.
|
||||
- service: 'mysql:5.7'
|
||||
db: MySQL 5.7
|
||||
driver: mysql
|
||||
- service: 'mysql:8.0.30'
|
||||
db: MySQL 8.0
|
||||
driver: mysql
|
||||
- service: mariadb
|
||||
db: MariaDB
|
||||
driver: mysql
|
||||
- service: 'mysql:8.1.0'
|
||||
db: MySQL 8.1
|
||||
driver: mysql
|
||||
- service: 'sqlite:3'
|
||||
db: SQLite
|
||||
driver: sqlite
|
||||
|
||||
# Include Database prefix tests with only one PHP version.
|
||||
- php: ${{ fromJSON(inputs.php_versions)[0] }}
|
||||
service: 'mysql:5.7'
|
||||
db: MySQL 5.7
|
||||
driver: mysql
|
||||
prefix: flarum_
|
||||
prefixStr: (prefix)
|
||||
- php: ${{ fromJSON(inputs.php_versions)[0] }}
|
||||
service: 'mysql:8.0.30'
|
||||
db: MySQL 8.0
|
||||
driver: mysql
|
||||
prefix: flarum_
|
||||
prefixStr: (prefix)
|
||||
- php: ${{ fromJSON(inputs.php_versions)[0] }}
|
||||
service: mariadb
|
||||
db: MariaDB
|
||||
driver: mysql
|
||||
prefix: flarum_
|
||||
prefixStr: (prefix)
|
||||
- php: ${{ fromJSON(inputs.php_versions)[0] }}
|
||||
service: 'mysql:8.1.0'
|
||||
db: MySQL 8.1
|
||||
driver: mysql
|
||||
prefix: flarum_
|
||||
prefixStr: (prefix)
|
||||
- php: ${{ fromJSON(inputs.php_versions)[0] }}
|
||||
service: 'sqlite:3'
|
||||
db: SQLite
|
||||
driver: sqlite
|
||||
prefix: flarum_
|
||||
prefixStr: (prefix)
|
||||
|
||||
|
@ -118,10 +135,22 @@ jobs:
|
|||
exclude:
|
||||
- php: ${{ fromJSON(inputs.php_versions)[1] }}
|
||||
service: 'mysql:8.0.30'
|
||||
- php: ${{ fromJSON(inputs.php_versions)[0] }}
|
||||
service: mariadb
|
||||
- php: ${{ fromJSON(inputs.php_versions)[1] }}
|
||||
service: mariadb
|
||||
- php: ${{ fromJSON(inputs.php_versions)[0] }}
|
||||
service: 'mysql:8.1.0'
|
||||
- php: ${{ fromJSON(inputs.php_versions)[1] }}
|
||||
service: 'mysql:8.1.0'
|
||||
- php: ${{ fromJSON(inputs.php_versions)[0] }}
|
||||
service: 'sqlite:3'
|
||||
- php: ${{ fromJSON(inputs.php_versions)[1] }}
|
||||
service: 'sqlite:3'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: ${{ matrix.service }}
|
||||
image: ${{ matrix.service != 'sqlite:3' && matrix.service || '' }}
|
||||
ports:
|
||||
- 13306:3306
|
||||
|
||||
|
@ -144,6 +173,7 @@ jobs:
|
|||
ini-values: ${{ matrix.php_ini_values }}
|
||||
|
||||
- name: Create MySQL Database
|
||||
if: ${{ matrix.service != 'sqlite:3' }}
|
||||
run: |
|
||||
sudo systemctl start mysql
|
||||
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306
|
||||
|
@ -173,6 +203,7 @@ jobs:
|
|||
DB_PORT: 13306
|
||||
DB_PASSWORD: root
|
||||
DB_PREFIX: ${{ matrix.prefix }}
|
||||
DB_DRIVER: ${{ matrix.driver }}
|
||||
COMPOSER_PROCESS_TIMEOUT: 600
|
||||
|
||||
phpstan:
|
||||
|
|
|
@ -14,6 +14,7 @@ use Flarum\Database\AbstractModel;
|
|||
use Flarum\Database\ScopeVisibilityTrait;
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
|
@ -30,6 +31,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||
class Flag extends AbstractModel
|
||||
{
|
||||
use ScopeVisibilityTrait;
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = ['created_at' => 'datetime'];
|
||||
|
||||
|
|
30
extensions/flags/src/FlagFactory.php
Normal file
30
extensions/flags/src/FlagFactory.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Flags;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class FlagFactory extends Factory
|
||||
{
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'type' => 'user',
|
||||
'post_id' => Post::factory(),
|
||||
'user_id' => User::factory(),
|
||||
'reason' => $this->faker->sentence,
|
||||
'reason_detail' => $this->faker->sentence,
|
||||
'created_at' => Carbon::now(),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
namespace Flarum\Flags\Tests\integration\api\flags;
|
||||
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Flags\Flag;
|
||||
use Flarum\Group\Group;
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||
|
@ -55,7 +56,7 @@ class ListTest extends TestCase
|
|||
['id' => 2, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||
['id' => 3, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||
],
|
||||
'flags' => [
|
||||
Flag::class => [
|
||||
['id' => 1, 'post_id' => 1, 'user_id' => 1],
|
||||
['id' => 2, 'post_id' => 1, 'user_id' => 2],
|
||||
['id' => 3, 'post_id' => 1, 'user_id' => 3],
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
namespace Flarum\Flags\Tests\integration\api\flags;
|
||||
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Flags\Flag;
|
||||
use Flarum\Group\Group;
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\Tags\Tag;
|
||||
|
@ -83,7 +84,7 @@ class ListWithTagsTest extends TestCase
|
|||
['id' => 6, 'discussion_id' => 4, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||
['id' => 7, 'discussion_id' => 5, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||
],
|
||||
'flags' => [
|
||||
Flag::class => [
|
||||
// From regular ListTest
|
||||
['id' => 1, 'post_id' => 1, 'user_id' => 1],
|
||||
['id' => 2, 'post_id' => 1, 'user_id' => 2],
|
||||
|
|
|
@ -16,10 +16,9 @@ return [
|
|||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE `{$prefix}post_mentions_post` MODIFY created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
$schema->table('post_mentions_post', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->nullable()->useCurrent()->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
|
|
|
@ -16,10 +16,9 @@ return [
|
|||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE `{$prefix}post_mentions_user` MODIFY created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
$schema->table('post_mentions_user', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->nullable()->useCurrent()->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
|
|
|
@ -32,7 +32,7 @@ class LoadMentionedByRelationship
|
|||
$query
|
||||
->with(['mentionsPosts', 'mentionsPosts.user', 'mentionsPosts.discussion', 'mentionsUsers'])
|
||||
->whereVisibleTo($actor)
|
||||
->oldest()
|
||||
->oldest('posts.created_at')
|
||||
// Limiting a relationship results is only possible because
|
||||
// the Post model uses the \Staudenmeir\EloquentEagerLimit\HasEagerLimit
|
||||
// trait.
|
||||
|
|
|
@ -130,12 +130,16 @@ class ShowStatisticsData implements RequestHandlerInterface
|
|||
$endDate = new DateTime();
|
||||
}
|
||||
|
||||
// if within the last 24 hours, group by hour
|
||||
$format = 'CASE WHEN '.$column.' > ? THEN \'%Y-%m-%d %H:00:00\' ELSE \'%Y-%m-%d\' END';
|
||||
$dbFormattedDatetime = match ($query->getConnection()->getDriverName()) {
|
||||
'sqlite' => 'strftime('.$format.', '.$column.')',
|
||||
default => 'DATE_FORMAT('.$column.', '.$format.')',
|
||||
};
|
||||
|
||||
$results = $query
|
||||
->selectRaw(
|
||||
'DATE_FORMAT(
|
||||
@date := '.$column.',
|
||||
IF(@date > ?, \'%Y-%m-%d %H:00:00\', \'%Y-%m-%d\') -- if within the last 24 hours, group by hour
|
||||
) as time_group',
|
||||
$dbFormattedDatetime.' as time_group',
|
||||
[new DateTime('-25 hours')]
|
||||
)
|
||||
->selectRaw('COUNT(id) as count')
|
||||
|
|
|
@ -102,7 +102,7 @@ class CanRequestCustomTimedStatisticsTest extends TestCase
|
|||
|
||||
$body = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals(200, $response->getStatusCode(), $body['errors'][0]['detail'] ?? '');
|
||||
|
||||
$this->assertEquals(
|
||||
$data,
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Flarum\Sticky;
|
|||
use Flarum\Search\Database\DatabaseSearchState;
|
||||
use Flarum\Search\SearchCriteria;
|
||||
use Flarum\Tags\Search\Filter\TagFilter;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
|
||||
class PinStickiedDiscussionsToTop
|
||||
{
|
||||
|
@ -45,22 +46,26 @@ class PinStickiedDiscussionsToTop
|
|||
$sticky->where('is_sticky', true);
|
||||
unset($sticky->orders);
|
||||
|
||||
/** @var Builder $q */
|
||||
foreach ([$sticky, $query] as $q) {
|
||||
$read = $q->newQuery()
|
||||
->selectRaw('1')
|
||||
->from('discussion_user as sticky')
|
||||
->whereColumn('sticky.discussion_id', 'id')
|
||||
->where('sticky.user_id', '=', $state->getActor()->id)
|
||||
->whereColumn('sticky.last_read_post_number', '>=', 'last_post_number');
|
||||
|
||||
// Add the bindings manually (rather than as the second
|
||||
// argument in orderByRaw) for now due to a bug in Laravel which
|
||||
// would add the bindings in the wrong order.
|
||||
$q->selectRaw('(is_sticky and not exists ('.$read->toSql().') and last_posted_at > ?) as is_unread_sticky', array_merge($read->getBindings(), [$state->getActor()->marked_all_as_read_at ?: 0]));
|
||||
}
|
||||
|
||||
$query->union($sticky);
|
||||
|
||||
$read = $query->newQuery()
|
||||
->selectRaw('1')
|
||||
->from('discussion_user as sticky')
|
||||
->whereColumn('sticky.discussion_id', 'id')
|
||||
->where('sticky.user_id', '=', $state->getActor()->id)
|
||||
->whereColumn('sticky.last_read_post_number', '>=', 'last_post_number');
|
||||
$query->orderByDesc('is_unread_sticky');
|
||||
|
||||
// Add the bindings manually (rather than as the second
|
||||
// argument in orderByRaw) for now due to a bug in Laravel which
|
||||
// would add the bindings in the wrong order.
|
||||
$query->orderByRaw('is_sticky and not exists ('.$read->toSql().') and last_posted_at > ? desc')
|
||||
->addBinding(array_merge($read->getBindings(), [$state->getActor()->marked_all_as_read_at ?: 0]), 'union');
|
||||
|
||||
$query->unionOrders = array_merge($query->unionOrders, $query->orders);
|
||||
$query->unionOrders = array_merge($query->unionOrders ?? [], $query->orders ?? []);
|
||||
$query->unionLimit = $query->limit;
|
||||
$query->unionOffset = $query->offset;
|
||||
|
||||
|
|
|
@ -14,9 +14,14 @@ return [
|
|||
'up' => function (Builder $schema) {
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->renameColumn('discussions_count', 'discussion_count');
|
||||
});
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->renameColumn('last_time', 'last_posted_at');
|
||||
});
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->renameColumn('last_discussion_id', 'last_posted_discussion_id');
|
||||
|
||||
});
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->integer('parent_id')->unsigned()->nullable()->change();
|
||||
|
||||
$table->integer('last_posted_user_id')->unsigned()->nullable();
|
||||
|
@ -26,11 +31,17 @@ return [
|
|||
'down' => function (Builder $schema) {
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->dropColumn('last_posted_user_id');
|
||||
|
||||
});
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->integer('parent_id')->nullable()->change();
|
||||
|
||||
});
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->renameColumn('discussion_count', 'discussions_count');
|
||||
});
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->renameColumn('last_posted_at', 'last_time');
|
||||
});
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->renameColumn('last_posted_discussion_id', 'last_discussion_id');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,17 +17,15 @@ return [
|
|||
$table->timestamp('updated_at')->nullable();
|
||||
});
|
||||
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE `{$prefix}tags` MODIFY created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
$connection->statement("ALTER TABLE `{$prefix}tags` MODIFY updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP");
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->nullable()->useCurrent()->change();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrent()->useCurrentOnUpdate()->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('tags', function (Blueprint $table) {
|
||||
$table->dropColumn('created_at');
|
||||
$table->dropColumn('updated_at');
|
||||
$table->dropColumn('created_at', 'updated_at');
|
||||
});
|
||||
}
|
||||
];
|
||||
|
|
|
@ -16,10 +16,9 @@ return [
|
|||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE `{$prefix}discussion_tag` MODIFY created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
$schema->table('discussion_tag', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->nullable()->useCurrent()->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
|
|
|
@ -34,7 +34,7 @@ export default class StatusWidget extends DashboardWidget {
|
|||
|
||||
items.add('version-flarum', [<strong>Flarum</strong>, <br />, app.forum.attribute('version')], 100);
|
||||
items.add('version-php', [<strong>PHP</strong>, <br />, app.data.phpVersion], 90);
|
||||
items.add('version-mysql', [<strong>MySQL</strong>, <br />, app.data.mysqlVersion], 80);
|
||||
items.add('version-db', [<strong>{app.data.dbDriver}</strong>, <br />, app.data.dbVersion], 80);
|
||||
if (app.data.schedulerStatus) {
|
||||
items.add(
|
||||
'schedule-status',
|
||||
|
|
|
@ -34,7 +34,10 @@ return [
|
|||
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement('ALTER TABLE '.$prefix.'posts ADD FULLTEXT content (content)');
|
||||
|
||||
if ($connection->getDriverName() !== 'sqlite') {
|
||||
$connection->statement('ALTER TABLE '.$prefix.'posts ADD FULLTEXT content (content)');
|
||||
}
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
|
|
|
@ -12,10 +12,12 @@ use Illuminate\Database\Schema\Builder;
|
|||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->dropColumn('created_at', 'expires_at');
|
||||
});
|
||||
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->string('id', 40)->change();
|
||||
$table->dropColumn('created_at');
|
||||
$table->dropColumn('expires_at');
|
||||
$table->integer('last_activity');
|
||||
$table->integer('lifetime');
|
||||
});
|
||||
|
@ -24,8 +26,7 @@ return [
|
|||
'down' => function (Builder $schema) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->string('id', 100)->change();
|
||||
$table->dropColumn('last_activity');
|
||||
$table->dropColumn('lifetime');
|
||||
$table->dropColumn('last_activity', 'lifetime');
|
||||
$table->timestamp('created_at');
|
||||
$table->timestamp('expires_at');
|
||||
});
|
||||
|
|
|
@ -14,14 +14,20 @@ return [
|
|||
'up' => function (Builder $schema) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->renameColumn('id', 'token');
|
||||
});
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->renameColumn('lifetime', 'lifetime_seconds');
|
||||
});
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->renameColumn('last_activity', 'last_activity_at');
|
||||
});
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->dateTime('created_at');
|
||||
$table->integer('user_id')->unsigned()->change();
|
||||
});
|
||||
|
||||
// Use a separate schema instance because this column gets renamed
|
||||
// in the first one.
|
||||
// in the previous one.
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->dateTime('last_activity_at')->change();
|
||||
});
|
||||
|
@ -31,12 +37,19 @@ return [
|
|||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->integer('last_activity_at')->change();
|
||||
});
|
||||
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->renameColumn('token', 'id');
|
||||
});
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->renameColumn('lifetime_seconds', 'lifetime');
|
||||
});
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->renameColumn('last_activity_at', 'last_activity');
|
||||
});
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->dropColumn('created_at');
|
||||
});
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->integer('user_id')->change();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,13 +12,7 @@ use Illuminate\Database\Schema\Builder;
|
|||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$table->dropPrimary(['id']);
|
||||
$table->renameColumn('id', 'key');
|
||||
$table->unique('key');
|
||||
});
|
||||
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$definition = function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('allowed_ips')->nullable();
|
||||
$table->string('scopes')->nullable();
|
||||
|
@ -27,7 +21,23 @@ return [
|
|||
$table->dateTime('last_activity_at')->nullable();
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
});
|
||||
};
|
||||
|
||||
if ($schema->getConnection()->getDriverName() !== 'sqlite') {
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$table->dropPrimary(['id']);
|
||||
$table->renameColumn('id', 'key');
|
||||
$table->unique('key');
|
||||
});
|
||||
|
||||
$schema->table('api_keys', $definition);
|
||||
} else {
|
||||
$schema->drop('api_keys');
|
||||
$schema->create('api_keys', function (Blueprint $table) use ($definition) {
|
||||
$table->string('key', 100)->unique();
|
||||
$definition($table);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
|
@ -36,10 +46,13 @@ return [
|
|||
$table->dropColumn('id', 'allowed_ips', 'user_id', 'scopes', 'created_at');
|
||||
});
|
||||
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
|
||||
$table->dropUnique(['key']);
|
||||
$table->renameColumn('key', 'id');
|
||||
$table->primary('id');
|
||||
|
||||
if ($schema->getConnection()->getDriverName() !== 'sqlite') {
|
||||
$table->primary('id');
|
||||
}
|
||||
});
|
||||
}
|
||||
];
|
||||
|
|
|
@ -7,19 +7,19 @@
|
|||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE {$prefix}registration_tokens MODIFY created_at DATETIME");
|
||||
$schema->table('registration_tokens', function (Blueprint $table) {
|
||||
$table->dateTime('created_at')->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE {$prefix}registration_tokens MODIFY created_at TIMESTAMP");
|
||||
$schema->table('registration_tokens', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->change();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
|
|
@ -12,13 +12,19 @@ use Illuminate\Database\Schema\Builder;
|
|||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement('ALTER TABLE '.$prefix.'posts ENGINE = InnoDB');
|
||||
|
||||
if ($connection->getDriverName() === 'mysql') {
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement('ALTER TABLE '.$prefix.'posts ENGINE = InnoDB');
|
||||
}
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement('ALTER TABLE '.$prefix.'posts ENGINE = MyISAM');
|
||||
|
||||
if ($connection->getDriverName() === 'mysql') {
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement('ALTER TABLE '.$prefix.'posts ENGINE = MyISAM');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
@ -7,19 +7,19 @@
|
|||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE {$prefix}email_tokens MODIFY created_at DATETIME");
|
||||
$schema->table('email_tokens', function (Blueprint $table) {
|
||||
$table->dateTime('created_at')->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE {$prefix}email_tokens MODIFY created_at TIMESTAMP");
|
||||
$schema->table('email_tokens', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->change();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
|
|
@ -15,10 +15,14 @@ return [
|
|||
'up' => function (Builder $schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->dropColumn('subject_type');
|
||||
|
||||
});
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->renameColumn('time', 'created_at');
|
||||
});
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->renameColumn('sender_id', 'from_user_id');
|
||||
|
||||
});
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->dateTime('read_at')->nullable();
|
||||
});
|
||||
|
||||
|
@ -36,8 +40,11 @@ return [
|
|||
$table->string('subject_type', 200)->nullable();
|
||||
|
||||
$table->renameColumn('created_at', 'time');
|
||||
});
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->renameColumn('from_user_id', 'sender_id');
|
||||
|
||||
});
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->boolean('is_read');
|
||||
});
|
||||
|
||||
|
|
|
@ -7,19 +7,19 @@
|
|||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE {$prefix}password_tokens MODIFY created_at DATETIME");
|
||||
$schema->table('password_tokens', function (Blueprint $table) {
|
||||
$table->dateTime('created_at')->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE {$prefix}password_tokens MODIFY created_at TIMESTAMP");
|
||||
$schema->table('password_tokens', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->change();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
|
|
@ -13,7 +13,10 @@ return [
|
|||
'up' => function (Builder $schema) {
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement('ALTER TABLE '.$prefix.'discussions ADD FULLTEXT title (title)');
|
||||
|
||||
if ($connection->getDriverName() !== 'sqlite') {
|
||||
$connection->statement('ALTER TABLE '.$prefix.'discussions ADD FULLTEXT title (title)');
|
||||
}
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
|
|
|
@ -12,24 +12,42 @@ use Illuminate\Database\Schema\Builder;
|
|||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
// Replace primary key with unique index so we can create a new primary
|
||||
$table->dropPrimary('token');
|
||||
$table->unique('token');
|
||||
});
|
||||
if ($schema->getConnection()->getDriverName() !== 'sqlite') {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
// Replace primary key with unique index so we can create a new primary
|
||||
$table->dropPrimary('token');
|
||||
$table->unique('token');
|
||||
});
|
||||
|
||||
// This needs to be done in a second statement because of the order Laravel runs operations in
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
// Introduce new increment-based ID
|
||||
$table->increments('id')->first();
|
||||
});
|
||||
// This needs to be done in a second statement because of the order Laravel runs operations in
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
// Introduce new increment-based ID
|
||||
$table->increments('id')->first();
|
||||
});
|
||||
} else {
|
||||
$schema->drop('access_tokens');
|
||||
$schema->create('access_tokens', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('token', 100)->unique();
|
||||
$table->integer('user_id')->unsigned();
|
||||
$table->dateTime('last_activity_at')->nullable();
|
||||
$table->dateTime('created_at');
|
||||
$table->string('type', 100)->nullable();
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->index('type');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) use ($schema) {
|
||||
$table->dropColumn('id');
|
||||
$table->dropIndex('token');
|
||||
$table->primary('token');
|
||||
|
||||
if ($schema->getConnection()->getDriverName() !== 'sqlite') {
|
||||
$table->primary('token');
|
||||
}
|
||||
});
|
||||
}
|
||||
];
|
||||
|
|
|
@ -17,17 +17,15 @@ return [
|
|||
$table->timestamp('updated_at')->nullable();
|
||||
});
|
||||
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE `${prefix}groups` MODIFY created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
$connection->statement("ALTER TABLE `${prefix}groups` MODIFY updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP");
|
||||
$schema->table('groups', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->nullable()->useCurrent()->change();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate()->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('groups', function (Blueprint $table) {
|
||||
$table->dropColumn('created_at');
|
||||
$table->dropColumn('updated_at');
|
||||
$table->dropColumn('created_at', 'updated_at');
|
||||
});
|
||||
}
|
||||
];
|
||||
|
|
|
@ -16,10 +16,9 @@ return [
|
|||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE `${prefix}group_user` MODIFY created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
$schema->table('group_user', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->nullable()->useCurrent()->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
|
|
|
@ -16,10 +16,9 @@ return [
|
|||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
// do this manually because dbal doesn't recognize timestamp columns
|
||||
$connection = $schema->getConnection();
|
||||
$prefix = $connection->getTablePrefix();
|
||||
$connection->statement("ALTER TABLE `${prefix}group_permission` MODIFY created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
$schema->table('group_permission', function (Blueprint $table) {
|
||||
$table->timestamp('created_at')->nullable()->useCurrent()->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
if ($schema->getConnection()->getDriverName() === 'sqlite') {
|
||||
$schema->getConnection()->statement('PRAGMA foreign_keys = OFF');
|
||||
$schema->getConnection()->statement('PRAGMA writable_schema = ON');
|
||||
|
||||
$prefix = $schema->getConnection()->getTablePrefix();
|
||||
|
||||
$foreignKeysSqlite = [
|
||||
'discussions' => <<<SQL
|
||||
foreign key("user_id") references "{$prefix}users"("id") on delete set null,
|
||||
foreign key("last_posted_user_id") references "{$prefix}users"("id") on delete set null,
|
||||
foreign key("hidden_user_id") references "{$prefix}users"("id") on delete set null,
|
||||
foreign key("first_post_id") references "{$prefix}posts"("id") on delete set null,
|
||||
foreign key("last_post_id") references "{$prefix}posts"("id") on delete set null
|
||||
SQL,
|
||||
'posts' => <<<SQL
|
||||
foreign key("discussion_id") references "{$prefix}discussions"("id") on delete cascade,
|
||||
foreign key("user_id") references "{$prefix}users"("id") on delete set null,
|
||||
foreign key("edited_user_id") references "{$prefix}users"("id") on delete set null,
|
||||
foreign key("hidden_user_id") references "{$prefix}users"("id") on delete set null
|
||||
SQL,
|
||||
];
|
||||
|
||||
foreach (['discussions', 'posts'] as $table) {
|
||||
$tableDefinition = $schema->getConnection()->select('SELECT sql FROM sqlite_master WHERE type = "table" AND name = "'.$prefix.$table.'"')[0]->sql;
|
||||
$modifiedTableDefinition = str($tableDefinition)->beforeLast(')')->append(', '.$foreignKeysSqlite[$table].')')->toString();
|
||||
$modifiedTableDefinitionWithEscapedQuotes = str($modifiedTableDefinition)->replace('"', '""')->toString();
|
||||
$schema->getConnection()->statement('UPDATE sqlite_master SET sql = "'.$modifiedTableDefinitionWithEscapedQuotes.'" WHERE type = "table" AND name = "'.$prefix.$table.'"');
|
||||
}
|
||||
|
||||
$schema->getConnection()->statement('PRAGMA writable_schema = OFF');
|
||||
$schema->getConnection()->statement('PRAGMA foreign_keys = ON');
|
||||
}
|
||||
},
|
||||
];
|
132
framework/core/migrations/sqlite-install.dump
Normal file
132
framework/core/migrations/sqlite-install.dump
Normal file
|
@ -0,0 +1,132 @@
|
|||
CREATE TABLE IF NOT EXISTS "db_prefix_migrations" ("id" integer primary key autoincrement not null, "migration" varchar not null, "extension" varchar);
|
||||
CREATE TABLE IF NOT EXISTS "db_prefix_groups" ("id" integer primary key autoincrement not null, "name_singular" varchar not null, "name_plural" varchar not null, "color" varchar, "icon" varchar, "is_hidden" tinyint(1) not null default '0', "created_at" datetime, "updated_at" datetime);
|
||||
CREATE TABLE IF NOT EXISTS "db_prefix_group_permission" ("group_id" integer not null, "permission" varchar not null, "created_at" datetime, primary key ("group_id", "permission"));
|
||||
CREATE TABLE IF NOT EXISTS "db_prefix_group_user" ("user_id" integer not null, "group_id" integer not null, "created_at" datetime, primary key ("user_id", "group_id"));
|
||||
CREATE TABLE db_prefix_settings ("key" VARCHAR(255) NOT NULL, value CLOB DEFAULT NULL, PRIMARY KEY("key"));
|
||||
CREATE TABLE IF NOT EXISTS "db_prefix_api_keys" ("key" varchar not null, "id" integer primary key autoincrement not null, "allowed_ips" varchar, "scopes" varchar, "user_id" integer, "created_at" datetime not null, "last_activity_at" datetime, foreign key("user_id") references "db_prefix_users"("id") on delete cascade);
|
||||
CREATE UNIQUE INDEX "db_prefix_api_keys_key_unique" on "db_prefix_api_keys" ("key");
|
||||
CREATE TABLE db_prefix_discussion_user (user_id INTEGER NOT NULL, discussion_id INTEGER NOT NULL, last_read_at DATETIME DEFAULT NULL, last_read_post_number INTEGER DEFAULT NULL, PRIMARY KEY(user_id, discussion_id));
|
||||
CREATE TABLE db_prefix_email_tokens (token VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL COLLATE "BINARY", user_id INTEGER NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY(token));
|
||||
CREATE TABLE db_prefix_notifications (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER NOT NULL, from_user_id INTEGER DEFAULT NULL, type VARCHAR(255) NOT NULL COLLATE "BINARY", subject_id INTEGER DEFAULT NULL, data BLOB DEFAULT NULL, created_at DATETIME NOT NULL, is_deleted BOOLEAN DEFAULT 0 NOT NULL, read_at DATETIME DEFAULT NULL);
|
||||
CREATE TABLE db_prefix_password_tokens (token VARCHAR(255) NOT NULL, user_id INTEGER NOT NULL, created_at DATETIME NOT NULL, PRIMARY KEY(token));
|
||||
CREATE TABLE IF NOT EXISTS "db_prefix_post_user" ("post_id" integer not null, "user_id" integer not null, foreign key("post_id") references "db_prefix_posts"("id") on delete cascade, foreign key("user_id") references "db_prefix_users"("id") on delete cascade, primary key ("post_id", "user_id"));
|
||||
CREATE TABLE db_prefix_users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username VARCHAR(255) NOT NULL COLLATE "BINARY", email VARCHAR(255) NOT NULL COLLATE "BINARY", is_email_confirmed BOOLEAN DEFAULT 0 NOT NULL, password VARCHAR(255) NOT NULL COLLATE "BINARY", avatar_url VARCHAR(255) DEFAULT NULL, preferences BLOB DEFAULT NULL, joined_at DATETIME DEFAULT NULL, last_seen_at DATETIME DEFAULT NULL, marked_all_as_read_at DATETIME DEFAULT NULL, read_notifications_at DATETIME DEFAULT NULL, discussion_count INTEGER DEFAULT 0 NOT NULL, comment_count INTEGER DEFAULT 0 NOT NULL);
|
||||
CREATE UNIQUE INDEX db_prefix_users_email_unique ON db_prefix_users (email);
|
||||
CREATE UNIQUE INDEX db_prefix_users_username_unique ON db_prefix_users (username);
|
||||
CREATE INDEX "db_prefix_users_joined_at_index" on "db_prefix_users" ("joined_at");
|
||||
CREATE INDEX "db_prefix_users_last_seen_at_index" on "db_prefix_users" ("last_seen_at");
|
||||
CREATE INDEX "db_prefix_users_discussion_count_index" on "db_prefix_users" ("discussion_count");
|
||||
CREATE INDEX "db_prefix_users_comment_count_index" on "db_prefix_users" ("comment_count");
|
||||
CREATE INDEX "db_prefix_notifications_user_id_index" on "db_prefix_notifications" ("user_id");
|
||||
CREATE TABLE db_prefix_registration_tokens (token VARCHAR(255) NOT NULL, payload CLOB DEFAULT NULL, created_at DATETIME NOT NULL, "provider" varchar not null, "identifier" varchar not null, "user_attributes" text, PRIMARY KEY(token));
|
||||
CREATE TABLE IF NOT EXISTS "db_prefix_login_providers" ("id" integer primary key autoincrement not null, "user_id" integer not null, "provider" varchar not null, "identifier" varchar not null, "created_at" datetime, "last_login_at" datetime, foreign key("user_id") references "db_prefix_users"("id") on delete cascade);
|
||||
CREATE UNIQUE INDEX "db_prefix_login_providers_provider_identifier_unique" on "db_prefix_login_providers" ("provider", "identifier");
|
||||
CREATE TABLE IF NOT EXISTS "db_prefix_access_tokens" ("id" integer primary key autoincrement not null, "token" varchar not null, "user_id" integer not null, "last_activity_at" datetime, "created_at" datetime not null, "type" varchar, "title" varchar, "last_ip_address" varchar, "last_user_agent" varchar, foreign key("user_id") references "db_prefix_users"("id") on delete cascade);
|
||||
CREATE INDEX "db_prefix_access_tokens_type_index" on "db_prefix_access_tokens" ("type");
|
||||
CREATE UNIQUE INDEX "db_prefix_access_tokens_token_unique" on "db_prefix_access_tokens" ("token");
|
||||
CREATE TABLE db_prefix_posts (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, discussion_id INTEGER NOT NULL, number INTEGER DEFAULT NULL, created_at DATETIME NOT NULL, user_id INTEGER DEFAULT NULL, type VARCHAR(255) DEFAULT NULL, content CLOB DEFAULT NULL --
|
||||
, edited_at DATETIME DEFAULT NULL, edited_user_id INTEGER DEFAULT NULL, hidden_at DATETIME DEFAULT NULL, hidden_user_id INTEGER DEFAULT NULL, ip_address VARCHAR(255) DEFAULT NULL, is_private BOOLEAN DEFAULT 0 NOT NULL, foreign key("discussion_id") references "db_prefix_discussions"("id") on delete cascade,
|
||||
foreign key("user_id") references "db_prefix_users"("id") on delete set null,
|
||||
foreign key("edited_user_id") references "db_prefix_users"("id") on delete set null,
|
||||
foreign key("hidden_user_id") references "db_prefix_users"("id") on delete set null);
|
||||
CREATE INDEX db_prefix_posts_user_id_created_at_index ON db_prefix_posts (user_id, created_at);
|
||||
CREATE INDEX db_prefix_posts_discussion_id_created_at_index ON db_prefix_posts (discussion_id, created_at);
|
||||
CREATE INDEX db_prefix_posts_discussion_id_number_index ON db_prefix_posts (discussion_id, number);
|
||||
CREATE UNIQUE INDEX db_prefix_posts_discussion_id_number_unique ON db_prefix_posts (discussion_id, number);
|
||||
CREATE INDEX "db_prefix_posts_type_index" on "db_prefix_posts" ("type");
|
||||
CREATE INDEX "db_prefix_posts_type_created_at_index" on "db_prefix_posts" ("type", "created_at");
|
||||
CREATE TABLE IF NOT EXISTS "db_prefix_unsubscribe_tokens" ("id" integer primary key autoincrement not null, "user_id" integer not null, "email_type" varchar not null, "token" varchar not null, "unsubscribed_at" datetime, "created_at" datetime, "updated_at" datetime, foreign key("user_id") references "db_prefix_users"("id") on delete cascade);
|
||||
CREATE INDEX "db_prefix_unsubscribe_tokens_user_id_index" on "db_prefix_unsubscribe_tokens" ("user_id");
|
||||
CREATE INDEX "db_prefix_unsubscribe_tokens_email_type_index" on "db_prefix_unsubscribe_tokens" ("email_type");
|
||||
CREATE INDEX "db_prefix_unsubscribe_tokens_token_index" on "db_prefix_unsubscribe_tokens" ("token");
|
||||
CREATE INDEX "db_prefix_unsubscribe_tokens_user_id_email_type_index" on "db_prefix_unsubscribe_tokens" ("user_id", "email_type");
|
||||
CREATE UNIQUE INDEX "db_prefix_unsubscribe_tokens_token_unique" on "db_prefix_unsubscribe_tokens" ("token");
|
||||
CREATE TABLE db_prefix_discussions (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, title VARCHAR(255) NOT NULL COLLATE "BINARY", comment_count INTEGER DEFAULT 1 NOT NULL, participant_count INTEGER DEFAULT 0 NOT NULL, created_at DATETIME NOT NULL, user_id INTEGER DEFAULT NULL, first_post_id INTEGER DEFAULT NULL, last_posted_at DATETIME DEFAULT NULL, last_posted_user_id INTEGER DEFAULT NULL, last_post_id INTEGER DEFAULT NULL, last_post_number INTEGER DEFAULT NULL, hidden_at DATETIME DEFAULT NULL, hidden_user_id INTEGER DEFAULT NULL, slug VARCHAR(255) NOT NULL COLLATE "BINARY", is_private BOOLEAN DEFAULT 0 NOT NULL, foreign key("user_id") references "db_prefix_users"("id") on delete set null,
|
||||
foreign key("last_posted_user_id") references "db_prefix_users"("id") on delete set null,
|
||||
foreign key("hidden_user_id") references "db_prefix_users"("id") on delete set null,
|
||||
foreign key("first_post_id") references "db_prefix_posts"("id") on delete set null,
|
||||
foreign key("last_post_id") references "db_prefix_posts"("id") on delete set null);
|
||||
CREATE INDEX db_prefix_discussions_last_posted_at_index ON db_prefix_discussions (last_posted_at);
|
||||
CREATE INDEX db_prefix_discussions_last_posted_user_id_index ON db_prefix_discussions (last_posted_user_id);
|
||||
CREATE INDEX db_prefix_discussions_created_at_index ON db_prefix_discussions (created_at);
|
||||
CREATE INDEX db_prefix_discussions_user_id_index ON db_prefix_discussions (user_id);
|
||||
CREATE INDEX db_prefix_discussions_comment_count_index ON db_prefix_discussions (comment_count);
|
||||
CREATE INDEX db_prefix_discussions_participant_count_index ON db_prefix_discussions (participant_count);
|
||||
CREATE INDEX db_prefix_discussions_hidden_at_index ON db_prefix_discussions (hidden_at);
|
||||
INSERT INTO db_prefix_migrations VALUES(1,'2015_02_24_000000_create_access_tokens_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(2,'2015_02_24_000000_create_api_keys_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(3,'2015_02_24_000000_create_config_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(4,'2015_02_24_000000_create_discussions_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(5,'2015_02_24_000000_create_email_tokens_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(6,'2015_02_24_000000_create_groups_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(7,'2015_02_24_000000_create_notifications_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(8,'2015_02_24_000000_create_password_tokens_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(9,'2015_02_24_000000_create_permissions_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(10,'2015_02_24_000000_create_posts_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(11,'2015_02_24_000000_create_users_discussions_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(12,'2015_02_24_000000_create_users_groups_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(13,'2015_02_24_000000_create_users_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(14,'2015_09_15_000000_create_auth_tokens_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(15,'2015_09_20_224327_add_hide_to_discussions',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(16,'2015_09_22_030432_rename_notification_read_time',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(17,'2015_10_07_130531_rename_config_to_settings',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(18,'2015_10_24_194000_add_ip_address_to_posts',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(19,'2015_12_05_042721_change_access_tokens_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(20,'2015_12_17_194247_change_settings_value_column_to_text',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(21,'2016_02_04_095452_add_slug_to_discussions',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(22,'2017_04_07_114138_add_is_private_to_discussions',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(23,'2017_04_07_114138_add_is_private_to_posts',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(24,'2018_01_11_093900_change_access_tokens_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(25,'2018_01_11_094000_change_access_tokens_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(26,'2018_01_11_095000_change_api_keys_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(27,'2018_01_11_101800_rename_auth_tokens_to_registration_tokens',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(28,'2018_01_11_102000_change_registration_tokens_rename_id_to_token',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(29,'2018_01_11_102100_change_registration_tokens_created_at_to_datetime',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(30,'2018_01_11_120604_change_posts_table_to_innodb',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(31,'2018_01_11_155200_change_discussions_rename_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(32,'2018_01_11_155300_change_discussions_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(33,'2018_01_15_071700_rename_users_discussions_to_discussion_user',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(34,'2018_01_15_071800_change_discussion_user_rename_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(35,'2018_01_15_071900_change_discussion_user_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(36,'2018_01_15_072600_change_email_tokens_rename_id_to_token',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(37,'2018_01_15_072700_change_email_tokens_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(38,'2018_01_15_072800_change_email_tokens_created_at_to_datetime',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(39,'2018_01_18_130400_rename_permissions_to_group_permission',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(40,'2018_01_18_130500_change_group_permission_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(41,'2018_01_18_130600_rename_users_groups_to_group_user',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(42,'2018_01_18_130700_change_group_user_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(43,'2018_01_18_133000_change_notifications_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(44,'2018_01_18_133100_change_notifications_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(45,'2018_01_18_134400_change_password_tokens_rename_id_to_token',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(46,'2018_01_18_134500_change_password_tokens_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(47,'2018_01_18_134600_change_password_tokens_created_at_to_datetime',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(48,'2018_01_18_135000_change_posts_rename_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(49,'2018_01_18_135100_change_posts_add_foreign_keys',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(50,'2018_01_30_112238_add_fulltext_index_to_discussions_title',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(51,'2018_01_30_220100_create_post_user_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(52,'2018_01_30_222900_change_users_rename_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(55,'2018_09_15_041340_add_users_indicies',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(56,'2018_09_15_041828_add_discussions_indicies',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(57,'2018_09_15_043337_add_notifications_indices',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(58,'2018_09_15_043621_add_posts_indices',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(59,'2018_09_22_004100_change_registration_tokens_columns',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(60,'2018_09_22_004200_create_login_providers_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(61,'2018_10_08_144700_add_shim_prefix_to_group_icons',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(62,'2019_10_12_195349_change_posts_add_discussion_foreign_key',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(63,'2020_03_19_134512_change_discussions_default_comment_count',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(64,'2020_04_21_130500_change_permission_groups_add_is_hidden',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(65,'2021_03_02_040000_change_access_tokens_add_type',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(66,'2021_03_02_040500_change_access_tokens_add_id',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(67,'2021_03_02_041000_change_access_tokens_add_title_ip_agent',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(68,'2021_04_18_040500_change_migrations_add_id_primary_key',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(69,'2021_04_18_145100_change_posts_content_column_to_mediumtext',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(70,'2021_05_10_000000_rename_permissions',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(71,'2022_05_20_000000_add_timestamps_to_groups_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(72,'2022_05_20_000001_add_created_at_to_group_user_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(73,'2022_05_20_000002_add_created_at_to_group_permission_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(74,'2022_07_14_000000_add_type_index_to_posts',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(75,'2022_07_14_000001_add_type_created_at_composite_index_to_posts',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(76,'2022_08_06_000000_change_access_tokens_last_activity_at_to_nullable',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(77,'2023_08_19_000000_create_unsubscribe_tokens_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(78,'2023_10_23_000000_drop_post_number_index_column_from_discussions_table',NULL);
|
||||
INSERT INTO db_prefix_migrations VALUES(79,'2024_05_05_000000_add_sqlite_keys',NULL);
|
|
@ -60,7 +60,8 @@ class AdminPayload
|
|||
$document->payload['searchDrivers'] = $this->getSearchDrivers();
|
||||
|
||||
$document->payload['phpVersion'] = $this->appInfo->identifyPHPVersion();
|
||||
$document->payload['mysqlVersion'] = $this->appInfo->identifyDatabaseVersion();
|
||||
$document->payload['dbDriver'] = $this->appInfo->identifyDatabaseDriver();
|
||||
$document->payload['dbVersion'] = $this->appInfo->identifyDatabaseVersion();
|
||||
$document->payload['debugEnabled'] = Arr::get($this->config, 'debug');
|
||||
|
||||
if ($this->appInfo->scheduledTasksRegistered()) {
|
||||
|
|
|
@ -31,13 +31,14 @@ class Index
|
|||
$extensionsEnabled = json_decode($this->settings->get('extensions_enabled', '{}'), true);
|
||||
$csrfToken = $request->getAttribute('session')->token();
|
||||
|
||||
$mysqlVersion = $document->payload['mysqlVersion'];
|
||||
$dbDriver = $document->payload['dbDriver'];
|
||||
$dbVersion = $document->payload['dbVersion'];
|
||||
$phpVersion = $document->payload['phpVersion'];
|
||||
$flarumVersion = Application::VERSION;
|
||||
|
||||
$document->content = $this->view->make(
|
||||
'flarum.admin::frontend.content.admin',
|
||||
compact('extensions', 'extensionsEnabled', 'csrfToken', 'flarumVersion', 'phpVersion', 'mysqlVersion')
|
||||
compact('extensions', 'extensionsEnabled', 'csrfToken', 'flarumVersion', 'phpVersion', 'dbVersion', 'dbDriver')
|
||||
);
|
||||
|
||||
return $document;
|
||||
|
|
|
@ -68,6 +68,13 @@ abstract class AbstractModel extends Eloquent
|
|||
*/
|
||||
protected ?string $tableAlias = null;
|
||||
|
||||
/**
|
||||
* If a model has unique keys, they should be defined here.
|
||||
*
|
||||
* @var array<string>|null
|
||||
*/
|
||||
public ?array $uniqueKeys = null;
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
namespace Flarum\Database\Console;
|
||||
|
||||
use Flarum\Console\AbstractCommand;
|
||||
use Flarum\Foundation\Config;
|
||||
use Flarum\Foundation\Paths;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\MySqlConnection;
|
||||
|
@ -19,6 +20,7 @@ class GenerateDumpCommand extends AbstractCommand
|
|||
{
|
||||
public function __construct(
|
||||
protected Connection $connection,
|
||||
protected Config $config,
|
||||
protected Paths $paths
|
||||
) {
|
||||
parent::__construct();
|
||||
|
@ -33,7 +35,8 @@ class GenerateDumpCommand extends AbstractCommand
|
|||
|
||||
protected function fire(): int
|
||||
{
|
||||
$dumpPath = __DIR__.'/../../../migrations/install.dump';
|
||||
$driver = $this->config['database.driver'];
|
||||
$dumpPath = __DIR__."/../../../migrations/$driver-install.dump";
|
||||
/** @var Connection&MySqlConnection $connection */
|
||||
$connection = resolve('db.connection');
|
||||
|
||||
|
|
|
@ -49,6 +49,26 @@ class DatabaseMigrationRepository implements MigrationRepositoryInterface
|
|||
$query->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the migration repository data store.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function createRepository(): void
|
||||
{
|
||||
if ($this->repositoryExists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$schema = $this->connection->getSchemaBuilder();
|
||||
|
||||
$schema->create($this->table, function ($table) {
|
||||
$table->increments('id');
|
||||
$table->string('migration');
|
||||
$table->string('extension')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function repositoryExists(): bool
|
||||
{
|
||||
$schema = $this->connection->getSchemaBuilder();
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Flarum\Database;
|
|||
use Faker\Factory as FakerFactory;
|
||||
use Faker\Generator as FakerGenerator;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\Paths;
|
||||
use Illuminate\Container\Container as ContainerImplementation;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Database\Capsule\Manager;
|
||||
|
@ -32,7 +33,13 @@ class DatabaseServiceProvider extends AbstractServiceProvider
|
|||
$manager = new Manager($container);
|
||||
|
||||
$config = $container['flarum']->config('database');
|
||||
$config['engine'] = 'InnoDB';
|
||||
|
||||
if ($config['driver'] === 'mysql') {
|
||||
$config['engine'] = 'InnoDB';
|
||||
} elseif ($config['driver'] === 'sqlite' && ! file_exists($config['database'])) {
|
||||
$config['database'] = $container->make(Paths::class)->base.'/'.$config['database'];
|
||||
}
|
||||
|
||||
$config['prefix_indexes'] = true;
|
||||
|
||||
$manager->addConnection($config, 'flarum');
|
||||
|
|
|
@ -101,18 +101,18 @@ abstract class Migration
|
|||
{
|
||||
return [
|
||||
'up' => function (Builder $schema) use ($tableName, $columnNames) {
|
||||
$schema->table($tableName, function (Blueprint $table) use ($columnNames) {
|
||||
foreach ($columnNames as $from => $to) {
|
||||
foreach ($columnNames as $from => $to) {
|
||||
$schema->table($tableName, function (Blueprint $table) use ($from, $to) {
|
||||
$table->renameColumn($from, $to);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
'down' => function (Builder $schema) use ($tableName, $columnNames) {
|
||||
$schema->table($tableName, function (Blueprint $table) use ($columnNames) {
|
||||
foreach ($columnNames as $to => $from) {
|
||||
foreach ($columnNames as $to => $from) {
|
||||
$schema->table($tableName, function (Blueprint $table) use ($from, $to) {
|
||||
$table->renameColumn($from, $to);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
|
||||
namespace Flarum\Database;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Flarum\Database\Exception\MigrationKeyMissing;
|
||||
use Flarum\Extension\Extension;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Database\MySqlConnection;
|
||||
use Illuminate\Database\DBAL\TimestampType;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
|
@ -31,12 +31,14 @@ class Migrator
|
|||
protected ConnectionInterface $connection,
|
||||
protected Filesystem $files
|
||||
) {
|
||||
if (! ($connection instanceof MySqlConnection)) {
|
||||
throw new InvalidArgumentException('Only MySQL connections are supported');
|
||||
$doctrine = $connection->getDoctrineConnection()->getDatabasePlatform();
|
||||
|
||||
if (! Type::hasType('timestamp')) {
|
||||
Type::addType('timestamp', TimestampType::class);
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/laravel/framework/issues/1186
|
||||
$connection->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
|
||||
$doctrine->registerDoctrineTypeMapping('enum', 'string');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,9 +209,13 @@ class Migrator
|
|||
*
|
||||
* @param string $path to the directory containing the dump.
|
||||
*/
|
||||
public function installFromSchema(string $path): void
|
||||
public function installFromSchema(string $path, string $driver): bool
|
||||
{
|
||||
$schemaPath = "$path/install.dump";
|
||||
$schemaPath = "$path/$driver-install.dump";
|
||||
|
||||
if (! file_exists($schemaPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
|
@ -236,6 +242,8 @@ class Migrator
|
|||
|
||||
$runTime = number_format((microtime(true) - $startTime) * 1000, 2);
|
||||
$this->note('<info>Loaded stored database schema.</info> ('.$runTime.'ms)');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setOutput(OutputInterface $output): static
|
||||
|
@ -250,6 +258,16 @@ class Migrator
|
|||
$this->output?->writeln($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the migration repository instance.
|
||||
*
|
||||
* @return MigrationRepositoryInterface
|
||||
*/
|
||||
public function getRepository()
|
||||
{
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
public function repositoryExists(): bool
|
||||
{
|
||||
return $this->repository->repositoryExists();
|
||||
|
|
|
@ -109,6 +109,9 @@ class Discussion extends AbstractModel
|
|||
$discussion->raise(new Deleted($discussion));
|
||||
|
||||
Notification::whereSubject($discussion)->delete();
|
||||
|
||||
// SQLite foreign constraints don't work since they were added *after* the table creation.
|
||||
$discussion->posts()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,33 @@ class FulltextFilter extends AbstractFulltextFilter
|
|||
{
|
||||
public function search(SearchState $state, string $value): void
|
||||
{
|
||||
/** @var Builder $query */
|
||||
$query = $state->getQuery();
|
||||
|
||||
if ($query->getConnection()->getDriverName() === 'sqlite') {
|
||||
$query->where(function (Builder $query) use ($state, $value) {
|
||||
$query->where('discussions.title', 'like', "%$value%")
|
||||
->orWhereExists(function (Builder $query) use ($state, $value) {
|
||||
$query->selectRaw('1')
|
||||
->from(
|
||||
Post::whereVisibleTo($state->getActor())
|
||||
->whereColumn('discussion_id', 'discussions.id')
|
||||
->where('type', 'comment')
|
||||
->where('content', 'like', "%$value%")
|
||||
->limit(1)
|
||||
->toBase()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace all non-word characters with spaces.
|
||||
// We do this to prevent MySQL fulltext search boolean mode from taking
|
||||
// effect: https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
|
||||
$value = preg_replace('/[^\p{L}\p{N}\p{M}_]+/u', ' ', $value);
|
||||
|
||||
$query = $state->getQuery();
|
||||
$grammar = $query->getGrammar();
|
||||
|
||||
$discussionSubquery = Discussion::select('id')
|
||||
|
|
|
@ -70,7 +70,20 @@ class ApplicationInfoProvider
|
|||
|
||||
public function identifyDatabaseVersion(): string
|
||||
{
|
||||
return $this->db->selectOne('select version() as version')->version;
|
||||
return match ($this->config['database.driver']) {
|
||||
'mysql' => $this->db->selectOne('select version() as version')->version,
|
||||
'sqlite' => $this->db->selectOne('select sqlite_version() as version')->version,
|
||||
default => 'Unknown',
|
||||
};
|
||||
}
|
||||
|
||||
public function identifyDatabaseDriver(): string
|
||||
{
|
||||
return match ($this->config['database.driver']) {
|
||||
'mysql' => 'MySQL',
|
||||
'sqlite' => 'SQLite',
|
||||
default => $this->config['database.driver'],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,7 +45,7 @@ class InfoCommand extends AbstractCommand
|
|||
$this->output->writeln("<info>Flarum core:</info> $coreVersion");
|
||||
|
||||
$this->output->writeln('<info>PHP version:</info> '.$this->appInfo->identifyPHPVersion());
|
||||
$this->output->writeln('<info>MySQL version:</info> '.$this->appInfo->identifyDatabaseVersion());
|
||||
$this->output->writeln('<info>'.$this->appInfo->identifyDatabaseDriver().' version:</info> '.$this->appInfo->identifyDatabaseVersion());
|
||||
|
||||
$phpExtensions = implode(', ', get_loaded_extensions());
|
||||
$this->output->writeln("<info>Loaded extensions:</info> $phpExtensions");
|
||||
|
|
|
@ -50,7 +50,7 @@ class UserDataProvider implements DataProviderInterface
|
|||
}
|
||||
|
||||
return new DatabaseConfig(
|
||||
'mysql',
|
||||
$this->ask('Database driver (mysql, sqlite) (Default: mysql):', 'mysql'),
|
||||
$host,
|
||||
intval($port),
|
||||
$this->ask('Database name (required):'),
|
||||
|
|
|
@ -84,10 +84,10 @@ class InstallController implements RequestHandlerInterface
|
|||
}
|
||||
|
||||
return new DatabaseConfig(
|
||||
'mysql',
|
||||
Arr::get($input, 'dbDriver'),
|
||||
$host,
|
||||
intval($port),
|
||||
Arr::get($input, 'mysqlDatabase'),
|
||||
Arr::get($input, 'dbName'),
|
||||
Arr::get($input, 'mysqlUsername'),
|
||||
Arr::get($input, 'mysqlPassword'),
|
||||
Arr::get($input, 'tablePrefix')
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
namespace Flarum\Install;
|
||||
|
||||
use Flarum\Foundation\Paths;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
|
||||
class DatabaseConfig implements Arrayable
|
||||
|
@ -17,7 +18,7 @@ class DatabaseConfig implements Arrayable
|
|||
private readonly string $driver,
|
||||
private readonly string $host,
|
||||
private readonly int $port,
|
||||
private readonly string $database,
|
||||
private string $database,
|
||||
private readonly string $username,
|
||||
private readonly string $password,
|
||||
private readonly string $prefix
|
||||
|
@ -27,20 +28,12 @@ class DatabaseConfig implements Arrayable
|
|||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
return array_merge([
|
||||
'driver' => $this->driver,
|
||||
'host' => $this->host,
|
||||
'port' => $this->port,
|
||||
'database' => $this->database,
|
||||
'username' => $this->username,
|
||||
'password' => $this->password,
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'prefix' => $this->prefix,
|
||||
'strict' => false,
|
||||
'engine' => 'InnoDB',
|
||||
'prefix_indexes' => true
|
||||
];
|
||||
], $this->driverOptions());
|
||||
}
|
||||
|
||||
private function validate(): void
|
||||
|
@ -49,15 +42,15 @@ class DatabaseConfig implements Arrayable
|
|||
throw new ValidationFailed('Please specify a database driver.');
|
||||
}
|
||||
|
||||
if ($this->driver !== 'mysql') {
|
||||
throw new ValidationFailed('Currently, only MySQL/MariaDB is supported.');
|
||||
if (! in_array($this->driver, ['mysql', 'sqlite'])) {
|
||||
throw new ValidationFailed('Currently, only MySQL/MariaDB and SQLite are supported.');
|
||||
}
|
||||
|
||||
if (empty($this->host)) {
|
||||
if ($this->driver === 'mysql' && empty($this->host)) {
|
||||
throw new ValidationFailed('Please specify the hostname of your database server.');
|
||||
}
|
||||
|
||||
if ($this->port < 1 || $this->port > 65535) {
|
||||
if ($this->driver === 'mysql' && ($this->port < 1 || $this->port > 65535)) {
|
||||
throw new ValidationFailed('Please provide a valid port number between 1 and 65535.');
|
||||
}
|
||||
|
||||
|
@ -65,7 +58,7 @@ class DatabaseConfig implements Arrayable
|
|||
throw new ValidationFailed('Please specify the database name.');
|
||||
}
|
||||
|
||||
if (empty($this->username)) {
|
||||
if ($this->driver === 'mysql' && empty($this->username)) {
|
||||
throw new ValidationFailed('Please specify the username for accessing the database.');
|
||||
}
|
||||
|
||||
|
@ -79,4 +72,32 @@ class DatabaseConfig implements Arrayable
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function prepare(Paths $paths): void
|
||||
{
|
||||
if ($this->driver === 'sqlite' && ! file_exists($this->database)) {
|
||||
$this->database = str_replace('.sqlite', '', $this->database).'.sqlite';
|
||||
touch($paths->base.'/'.$this->database);
|
||||
}
|
||||
}
|
||||
|
||||
private function driverOptions(): array
|
||||
{
|
||||
return match ($this->driver) {
|
||||
'mysql' => [
|
||||
'host' => $this->host,
|
||||
'port' => $this->port,
|
||||
'username' => $this->username,
|
||||
'password' => $this->password,
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'engine' => 'InnoDB',
|
||||
'strict' => false,
|
||||
],
|
||||
'sqlite' => [
|
||||
'foreign_key_constraints' => true,
|
||||
],
|
||||
default => []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,12 +116,15 @@ class Installation
|
|||
{
|
||||
$pipeline = new Pipeline;
|
||||
|
||||
$this->dbConfig->prepare($this->paths);
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\ConnectToDatabase(
|
||||
$this->dbConfig,
|
||||
function ($connection) {
|
||||
$this->db = $connection;
|
||||
}
|
||||
},
|
||||
$this->paths->base
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -135,7 +138,7 @@ class Installation
|
|||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\RunMigrations($this->db, $this->getMigrationPath());
|
||||
return new Steps\RunMigrations($this->db, $this->dbConfig->toArray()['driver'], $this->getMigrationPath());
|
||||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
|
|
|
@ -13,15 +13,19 @@ use Closure;
|
|||
use Flarum\Install\DatabaseConfig;
|
||||
use Flarum\Install\Step;
|
||||
use Illuminate\Database\Connectors\MySqlConnector;
|
||||
use Illuminate\Database\Connectors\SQLiteConnector;
|
||||
use Illuminate\Database\MySqlConnection;
|
||||
use Illuminate\Database\SQLiteConnection;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
use RangeException;
|
||||
|
||||
class ConnectToDatabase implements Step
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DatabaseConfig $dbConfig,
|
||||
private readonly Closure $store
|
||||
private readonly Closure $store,
|
||||
private readonly string $basePath
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -33,17 +37,27 @@ class ConnectToDatabase implements Step
|
|||
public function run(): void
|
||||
{
|
||||
$config = $this->dbConfig->toArray();
|
||||
|
||||
match ($config['driver']) {
|
||||
'mysql' => $this->mysql($config),
|
||||
'sqlite' => $this->sqlite($config),
|
||||
default => throw new InvalidArgumentException('Unsupported database driver: '.$config['driver']),
|
||||
};
|
||||
}
|
||||
|
||||
private function mysql(array $config): void
|
||||
{
|
||||
$pdo = (new MySqlConnector)->connect($config);
|
||||
|
||||
$version = $pdo->query('SELECT VERSION()')->fetchColumn();
|
||||
|
||||
if (Str::contains($version, 'MariaDB')) {
|
||||
if (version_compare($version, '10.0.5', '<')) {
|
||||
if (version_compare($version, '10.10.0', '<')) {
|
||||
throw new RangeException('MariaDB version too low. You need at least MariaDB 10.0.5');
|
||||
}
|
||||
} else {
|
||||
if (version_compare($version, '5.6.0', '<')) {
|
||||
throw new RangeException('MySQL version too low. You need at least MySQL 5.6.');
|
||||
if (version_compare($version, '5.7.0', '<')) {
|
||||
throw new RangeException('MySQL version too low. You need at least MySQL 5.7');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,4 +70,28 @@ class ConnectToDatabase implements Step
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function sqlite(array $config): void
|
||||
{
|
||||
if (! file_exists($config['database'])) {
|
||||
$config['database'] = $this->basePath.'/'.$config['database'];
|
||||
}
|
||||
|
||||
$pdo = (new SQLiteConnector())->connect($config);
|
||||
|
||||
$version = $pdo->query('SELECT sqlite_version()')->fetchColumn();
|
||||
|
||||
if (version_compare($version, '3.8.8', '<')) {
|
||||
throw new RangeException('SQLite version too low. You need at least SQLite 3.8.8');
|
||||
}
|
||||
|
||||
($this->store)(
|
||||
new SQLiteConnection(
|
||||
$pdo,
|
||||
$config['database'],
|
||||
$config['prefix'],
|
||||
$config
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ class RunMigrations implements Step
|
|||
{
|
||||
public function __construct(
|
||||
private readonly ConnectionInterface $database,
|
||||
private readonly string $driver,
|
||||
private readonly string $path
|
||||
) {
|
||||
}
|
||||
|
@ -32,7 +33,10 @@ class RunMigrations implements Step
|
|||
{
|
||||
$migrator = $this->getMigrator();
|
||||
|
||||
$migrator->installFromSchema($this->path);
|
||||
if (! $migrator->installFromSchema($this->path, $this->driver)) {
|
||||
$migrator->getRepository()->createRepository();
|
||||
}
|
||||
|
||||
$migrator->run($this->path);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,8 +95,7 @@ class Post extends AbstractModel
|
|||
$post->number = new Expression('('.
|
||||
$db->table('posts', 'pn')
|
||||
->whereRaw($db->getTablePrefix().'pn.discussion_id = '.intval($post->discussion_id))
|
||||
// IFNULL only works on MySQL/MariaDB
|
||||
->selectRaw('IFNULL(MAX('.$db->getTablePrefix().'pn.number), 0) + 1')
|
||||
->selectRaw('COALESCE(MAX('.$db->getTablePrefix().'pn.number), 0) + 1')
|
||||
->toSql()
|
||||
.')');
|
||||
});
|
||||
|
|
|
@ -37,7 +37,6 @@ class GroupFilter implements FilterInterface
|
|||
protected function constrain(Builder $query, User $actor, string|array $rawQuery, bool $negate): void
|
||||
{
|
||||
$groupIdentifiers = $this->asStringArray($rawQuery);
|
||||
$groupQuery = Group::whereVisibleTo($actor);
|
||||
|
||||
$ids = [];
|
||||
$names = [];
|
||||
|
@ -49,11 +48,15 @@ class GroupFilter implements FilterInterface
|
|||
}
|
||||
}
|
||||
|
||||
$groupQuery->whereIn('groups.id', $ids)
|
||||
->orWhereIn('name_singular', $names)
|
||||
->orWhereIn('name_plural', $names);
|
||||
$groupQuery = Group::whereVisibleTo($actor)
|
||||
->join('group_user', 'groups.id', 'group_user.group_id')
|
||||
->where(function (\Illuminate\Database\Eloquent\Builder $query) use ($ids, $names) {
|
||||
$query->whereIn('groups.id', $ids)
|
||||
->orWhereIn($query->raw('lower(name_singular)'), $names)
|
||||
->orWhereIn($query->raw('lower(name_plural)'), $names);
|
||||
});
|
||||
|
||||
$userIds = $groupQuery->join('group_user', 'groups.id', 'group_user.group_id')
|
||||
$userIds = $groupQuery
|
||||
->pluck('group_user.user_id')
|
||||
->all();
|
||||
|
||||
|
|
|
@ -73,6 +73,10 @@ class ListWithFulltextSearchTest extends TestCase
|
|||
*/
|
||||
public function can_search_for_word_or_title_in_post()
|
||||
{
|
||||
if ($this->database()->getDriverName() === 'sqlite') {
|
||||
return $this->markTestSkipped('No fulltext search in SQLite.');
|
||||
}
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
|
@ -94,6 +98,10 @@ class ListWithFulltextSearchTest extends TestCase
|
|||
*/
|
||||
public function ignores_non_word_characters_when_searching()
|
||||
{
|
||||
if ($this->database()->getDriverName() === 'sqlite') {
|
||||
return $this->markTestSkipped('No fulltext search in SQLite.');
|
||||
}
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
|
@ -115,6 +123,10 @@ class ListWithFulltextSearchTest extends TestCase
|
|||
*/
|
||||
public function can_search_telugu_like_languages()
|
||||
{
|
||||
if ($this->database()->getDriverName() === 'sqlite') {
|
||||
return $this->markTestSkipped('No fulltext search in SQLite.');
|
||||
}
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
|
@ -137,6 +149,10 @@ class ListWithFulltextSearchTest extends TestCase
|
|||
*/
|
||||
public function can_search_cjk_languages()
|
||||
{
|
||||
if ($this->database()->getDriverName() === 'sqlite') {
|
||||
return $this->markTestSkipped('No fulltext search in SQLite.');
|
||||
}
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
|
@ -159,6 +175,10 @@ class ListWithFulltextSearchTest extends TestCase
|
|||
*/
|
||||
public function search_for_special_characters_gives_empty_result()
|
||||
{
|
||||
if ($this->database()->getDriverName() === 'sqlite') {
|
||||
return $this->markTestSkipped('No fulltext search in SQLite.');
|
||||
}
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/discussions')
|
||||
->withQueryParams([
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<td>{{ $phpVersion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MySQL</td>
|
||||
<td>{{ $mysqlVersion }}</td>
|
||||
<td>{{ $dbDriver }}</td>
|
||||
<td>{{ $dbVersion }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
padding: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
body, input, button {
|
||||
body, .FormControl, button {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 16px;
|
||||
color: #7E96B3;
|
||||
|
@ -50,15 +50,19 @@
|
|||
.FormGroup {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.FormGroup .FormField:first-child input {
|
||||
.FormGroup .FormField:first-child .FormControl {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.FormGroup .FormField:last-child input {
|
||||
.FormGroup .FormField:last-child .FormControl {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
.FormField input {
|
||||
.FormField select.FormControl {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
.FormField .FormControl {
|
||||
background: #EDF2F7;
|
||||
margin: 0 0 1px;
|
||||
border: 2px solid transparent;
|
||||
|
@ -67,7 +71,7 @@
|
|||
padding: 15px 15px 15px 180px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.FormField input:focus {
|
||||
.FormField .FormControl:focus {
|
||||
border-color: #e7652e;
|
||||
background: #fff;
|
||||
color: #444;
|
||||
|
@ -106,6 +110,17 @@
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.Alert {
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.Alert--warning {
|
||||
background: #fff2ae;
|
||||
color: #ad6c00;
|
||||
}
|
||||
|
||||
.animated {
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
|
|
|
@ -8,56 +8,74 @@
|
|||
<div class="FormGroup">
|
||||
<div class="FormField">
|
||||
<label>Forum Title</label>
|
||||
<input name="forumTitle">
|
||||
<input class="FormControl" name="forumTitle">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="FormGroup">
|
||||
<div data-group="sqlite" style="display:none">
|
||||
<div class="Alert Alert--warning">
|
||||
<strong>Warning:</strong> Please keep in mind that while Flarum supports SQLite, not all ecosystem extensions do. If you're planning to install extensions, you should expect some of them to not work properly or at all.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="FormGroup">
|
||||
<div class="FormField">
|
||||
<label>MySQL Host</label>
|
||||
<input name="mysqlHost" value="localhost">
|
||||
<label>Database Driver</label>
|
||||
<select class="FormControl" name="dbDriver">
|
||||
<option value="mysql">MySQL</option>
|
||||
<option value="sqlite">SQLite</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="FormField">
|
||||
<label>MySQL Database</label>
|
||||
<input name="mysqlDatabase">
|
||||
<label>Database</label>
|
||||
<input class="FormControl" name="dbName" value="flarum">
|
||||
</div>
|
||||
|
||||
<div class="FormField">
|
||||
<label>MySQL Username</label>
|
||||
<input name="mysqlUsername">
|
||||
</div>
|
||||
<div data-group="mysql">
|
||||
<div class="FormField">
|
||||
<label>MySQL Host</label>
|
||||
<input class="FormControl" name="mysqlHost" value="localhost">
|
||||
</div>
|
||||
|
||||
<div class="FormField">
|
||||
<label>MySQL Password</label>
|
||||
<input type="password" name="mysqlPassword">
|
||||
<div class="FormField">
|
||||
<label>MySQL Username</label>
|
||||
<input class="FormControl" name="mysqlUsername">
|
||||
</div>
|
||||
|
||||
<div class="FormField">
|
||||
<label>MySQL Password</label>
|
||||
<input class="FormControl" type="password" name="mysqlPassword">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="FormField">
|
||||
<label>Table Prefix</label>
|
||||
<input type="text" name="tablePrefix">
|
||||
<input class="FormControl" type="text" name="tablePrefix">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="FormGroup">
|
||||
<div class="FormField">
|
||||
<label>Admin Username</label>
|
||||
<input name="adminUsername">
|
||||
<input class="FormControl" name="adminUsername">
|
||||
</div>
|
||||
|
||||
<div class="FormField">
|
||||
<label>Admin Email</label>
|
||||
<input name="adminEmail">
|
||||
<input class="FormControl" name="adminEmail">
|
||||
</div>
|
||||
|
||||
<div class="FormField">
|
||||
<label>Admin Password</label>
|
||||
<input type="password" name="adminPassword">
|
||||
<input class="FormControl" type="password" name="adminPassword">
|
||||
</div>
|
||||
|
||||
<div class="FormField">
|
||||
<label>Confirm Password</label>
|
||||
<input type="password" name="adminPasswordConfirmation">
|
||||
<input class="FormControl" type="password" name="adminPasswordConfirmation">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -70,6 +88,18 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelector('form input').select();
|
||||
|
||||
document.querySelector('select[name="dbDriver"]').addEventListener('change', function() {
|
||||
document.querySelectorAll('[data-group]').forEach(function(group) {
|
||||
group.style.display = 'none';
|
||||
});
|
||||
|
||||
const groups = document.querySelectorAll('[data-group="' + this.value + '"]');
|
||||
|
||||
groups.forEach(function(group) {
|
||||
group.style.display = 'block';
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector('form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<div class="FormGroup">
|
||||
<div class="FormField">
|
||||
<label>Database Password</label>
|
||||
<input type="password" name="databasePassword">
|
||||
<input class="FormControl" type="password" name="databasePassword">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Flarum\Testing\integration\Extend;
|
|||
use Flarum\Extend\ExtenderInterface;
|
||||
use Flarum\Extension\Extension;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
|
||||
class BeginTransactionAndSetDatabase implements ExtenderInterface
|
||||
|
@ -28,8 +29,14 @@ class BeginTransactionAndSetDatabase implements ExtenderInterface
|
|||
|
||||
public function extend(Container $container, Extension $extension = null): void
|
||||
{
|
||||
/** @var Connection $db */
|
||||
$db = $container->make(ConnectionInterface::class);
|
||||
|
||||
// SQLite requires this be done outside a transaction.
|
||||
if ($db->getDriverName() === 'sqlite') {
|
||||
$db->getSchemaBuilder()->disableForeignKeyConstraints();
|
||||
}
|
||||
|
||||
$db->beginTransaction();
|
||||
|
||||
($this->setDbOnTestCase)($db);
|
||||
|
|
|
@ -21,61 +21,24 @@ class SetupScript
|
|||
{
|
||||
use UsesTmpDir;
|
||||
|
||||
/**
|
||||
* Test database host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
protected string $driver;
|
||||
protected string $host;
|
||||
protected int $port;
|
||||
protected string $name;
|
||||
protected string $user;
|
||||
protected string $pass;
|
||||
protected string $pref;
|
||||
|
||||
/**
|
||||
* Test database port.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $port;
|
||||
|
||||
/**
|
||||
* Test database name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Test database username.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Test database password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pass;
|
||||
|
||||
/**
|
||||
* Test database prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pref;
|
||||
|
||||
/**
|
||||
* @var DatabaseConfig
|
||||
*/
|
||||
protected $dbConfig;
|
||||
protected DatabaseConfig $dbConfig;
|
||||
|
||||
/**
|
||||
* Settings to be applied during installation.
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = ['mail_driver' => 'log'];
|
||||
protected array $settings = ['mail_driver' => 'log'];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->driver = getenv('DB_DRIVER') ?: 'mysql';
|
||||
$this->host = getenv('DB_HOST') ?: 'localhost';
|
||||
$this->port = intval(getenv('DB_PORT') ?: 3306);
|
||||
$this->name = getenv('DB_DATABASE') ?: 'flarum_test';
|
||||
|
@ -88,7 +51,12 @@ class SetupScript
|
|||
{
|
||||
$tmp = $this->tmpDir();
|
||||
|
||||
echo "Connecting to database $this->name at $this->host:$this->port.\n";
|
||||
if ($this->driver === 'sqlite') {
|
||||
echo "Connecting to SQLite database at $this->name.\n";
|
||||
} else {
|
||||
echo "Connecting to database $this->name at $this->host:$this->port.\n";
|
||||
}
|
||||
|
||||
echo "Warning: all tables will be dropped to ensure clean state. DO NOT use your production database!\n";
|
||||
echo "Logging in as $this->user with password '$this->pass'.\n";
|
||||
echo "Table prefix: '$this->pref'\n";
|
||||
|
@ -103,22 +71,31 @@ class SetupScript
|
|||
|
||||
echo "\nOff we go...\n";
|
||||
|
||||
$this->dbConfig = new DatabaseConfig('mysql', $this->host, $this->port, $this->name, $this->user, $this->pass, $this->pref);
|
||||
$this->dbConfig = new DatabaseConfig(
|
||||
$this->driver,
|
||||
$this->host,
|
||||
$this->port,
|
||||
$this->name,
|
||||
$this->user,
|
||||
$this->pass,
|
||||
$this->pref
|
||||
);
|
||||
|
||||
echo "\nWiping DB to ensure clean state\n";
|
||||
$this->wipeDb();
|
||||
echo "Success! Proceeding to installation...\n";
|
||||
$paths = new Paths([
|
||||
'base' => $tmp,
|
||||
'public' => "$tmp/public",
|
||||
'storage' => "$tmp/storage",
|
||||
'vendor' => getenv('FLARUM_TEST_VENDOR_PATH') ?: getcwd().'/vendor',
|
||||
]);
|
||||
|
||||
$this->setupTmpDir();
|
||||
$this->dbConfig->prepare($paths);
|
||||
|
||||
$installation = new Installation(
|
||||
new Paths([
|
||||
'base' => $tmp,
|
||||
'public' => "$tmp/public",
|
||||
'storage' => "$tmp/storage",
|
||||
'vendor' => getenv('FLARUM_TEST_VENDOR_PATH') ?: getcwd().'/vendor',
|
||||
])
|
||||
);
|
||||
echo "\nWiping DB to ensure clean state\n";
|
||||
$this->wipeDb($paths);
|
||||
echo "Success! Proceeding to installation...\n";
|
||||
|
||||
$installation = new Installation($paths);
|
||||
|
||||
$pipeline = $installation
|
||||
->configPath('config.php')
|
||||
|
@ -140,7 +117,7 @@ class SetupScript
|
|||
echo "Installation complete\n";
|
||||
}
|
||||
|
||||
protected function wipeDb()
|
||||
protected function wipeDb(Paths $paths)
|
||||
{
|
||||
// Reuse the connection step to include version checks
|
||||
(new ConnectToDatabase($this->dbConfig, function ($db) {
|
||||
|
@ -149,7 +126,7 @@ class SetupScript
|
|||
|
||||
$builder->dropAllTables();
|
||||
$builder->dropAllViews();
|
||||
}))->run();
|
||||
}, $paths->base))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -193,7 +193,12 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
|
|||
|
||||
protected function populateDatabase(): void
|
||||
{
|
||||
// We temporarily disable foreign key checks to simplify this process.
|
||||
/**
|
||||
* We temporarily disable foreign key checks to simplify this process.
|
||||
* SQLite ignores this statement since we are inside a transaction.
|
||||
* So we do that before starting a transaction.
|
||||
* @see BeginTransactionAndSetDatabase
|
||||
*/
|
||||
$this->database()->getSchemaBuilder()->disableForeignKeyConstraints();
|
||||
|
||||
$databaseContent = [];
|
||||
|
|
Loading…
Reference in New Issue
Block a user