Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/rename migrations #17

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Contracts/RenamesMigrations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace LaravelEnso\Upgrade\Contracts;

interface RenamesMigrations
{
public function from(): array;

public function to(): array;
}
43 changes: 43 additions & 0 deletions src/Services/CustomUpgrade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace LaravelEnso\Upgrade\Services;

use LaravelEnso\Upgrade\Contracts\Applicable;
use LaravelEnso\Upgrade\Contracts\MigratesData;
use LaravelEnso\Upgrade\Contracts\MigratesPostDataMigration;
use LaravelEnso\Upgrade\Contracts\MigratesStructure;
use LaravelEnso\Upgrade\Contracts\Prioritization;
use LaravelEnso\Upgrade\Contracts\RenamesMigrations;
use LaravelEnso\Upgrade\Contracts\Upgrade;

abstract class CustomUpgrade implements Upgrade, MigratesData, Prioritization, MigratesPostDataMigration, Applicable
{
public function __construct(protected MigratesStructure|RenamesMigrations $upgrade)
{
}

public function class(): MigratesStructure|RenamesMigrations
{
return $this->upgrade;
}

public function priority(): int
{
return $this->upgrade instanceof Prioritization
? $this->upgrade->priority()
: Prioritization::Default;
}

public function migratePostDataMigration(): void
{
if ($this->upgrade instanceof MigratesPostDataMigration) {
$this->upgrade->migratePostDataMigration();
}
}

public function applicable(): bool
{
return ! $this->upgrade instanceof Applicable
|| $this->upgrade->applicable();
}
}
20 changes: 15 additions & 5 deletions src/Services/Finder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\File;
use LaravelEnso\Upgrade\Contracts\MigratesStructure;
use LaravelEnso\Upgrade\Contracts\RenamesMigrations;

class Finder
{
Expand All @@ -30,10 +31,19 @@ private function upgradePackages(): Collection

private function upgradeClasses(Package $package): Collection
{
return $package->upgradeClasses()
->map(fn ($class) => new $class)
->map(fn ($upgrade) => $upgrade instanceof MigratesStructure
? new Structure($upgrade)
: new $upgrade);
return $package->upgradeClasses()->map(fn ($class) => $this->upgrade($class));
}

private function upgrade(string $class)
{
$upgrade = new $class();

if ($upgrade instanceof MigratesStructure) {
return new Structure($upgrade);
} elseif ($upgrade instanceof RenamesMigrations) {
return new Migrations($upgrade);
}

return $upgrade;
}
}
38 changes: 38 additions & 0 deletions src/Services/Migrations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace LaravelEnso\Upgrade\Services;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use LaravelEnso\Helpers\Exceptions\EnsoException;

class Migrations extends CustomUpgrade
{
public function isMigrated(): bool
{
return Collection::wrap($this->class()->to())
->every(fn ($to) => DB::table('migrations')->whereMigration($to)->exists());
}

public function migrateData(): void
{
$to = Collection::wrap($this->class()->to())->sortKeys();
$from = Collection::wrap($this->class()->from())->sortKeys();

$invalidMapping = $to->count() !== $from->count()
|| $to->keys()->diff($from->keys())->isNotEmpty();

if ($invalidMapping) {
$message = 'Invalid number of elements or distinct keys in "from" and "to" arrays';
throw new EnsoException($message);
}

$to->combine($from)
->filter(fn ($from) => DB::table('migrations')
->whereMigration($from)
->exists())
->each(fn ($from, $to) => DB::table('migrations')
->whereMigration($from)
->update(['migration' => $to]));
}
}
2 changes: 2 additions & 0 deletions src/Services/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Support\Str;
use LaravelEnso\Helpers\Services\JsonReader;
use LaravelEnso\Upgrade\Contracts\MigratesStructure;
use LaravelEnso\Upgrade\Contracts\RenamesMigrations;
use LaravelEnso\Upgrade\Contracts\Upgrade;
use ReflectionClass;
use Symfony\Component\Finder\SplFileInfo;
Expand Down Expand Up @@ -87,6 +88,7 @@ private function isUpgrade($class): bool
$reflection = new ReflectionClass($class);

return $reflection->implementsInterface(MigratesStructure::class)
|| $reflection->implementsInterface(RenamesMigrations::class)
|| $reflection->implementsInterface(Upgrade::class);
}
}
4 changes: 2 additions & 2 deletions src/Services/Reflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class Reflection
{
public static function reflection(Upgrade $upgrade): ReflectionClass
{
return $upgrade instanceof Structure
? $upgrade->reflection()
return $upgrade instanceof Structure || $upgrade instanceof Migrations
? new ReflectionClass($upgrade->class())
: new ReflectionClass($upgrade);
}

Expand Down
49 changes: 4 additions & 45 deletions src/Services/Structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,11 @@
use Illuminate\Support\Facades\Config;
use LaravelEnso\Permissions\Models\Permission;
use LaravelEnso\Roles\Models\Role;
use LaravelEnso\Upgrade\Contracts\Applicable;
use LaravelEnso\Upgrade\Contracts\MigratesData;
use LaravelEnso\Upgrade\Contracts\MigratesPostDataMigration;
use LaravelEnso\Upgrade\Contracts\MigratesStructure;
use LaravelEnso\Upgrade\Contracts\Prioritization;
use LaravelEnso\Upgrade\Contracts\Upgrade;
use ReflectionClass;

class Structure implements Upgrade, MigratesData, Prioritization, MigratesPostDataMigration, Applicable
class Structure extends CustomUpgrade
{
private MigratesStructure $upgrade;
private Collection $existing;
private Collection $roles;
private string $defaultRole;

public function __construct(MigratesStructure $upgrade)
{
$this->upgrade = $upgrade;
$this->defaultRole = Config::get('enso.config.defaultRole');
}

public function isMigrated(): bool
{
Expand All @@ -50,36 +35,11 @@ public function migrateData(): void

if (App::isLocal()) {
$this->roles()
->reject(fn ($role) => $role->name === $this->defaultRole)
->reject(fn ($role) => $role->name === Config::get('enso.config.defaultRole'))
->each->writeConfig();
}
}

public function reflection()
{
return new ReflectionClass($this->upgrade);
}

public function priority(): int
{
return $this->upgrade instanceof Prioritization
? $this->upgrade->priority()
: Prioritization::Default;
}

public function migratePostDataMigration(): void
{
if ($this->upgrade instanceof MigratesPostDataMigration) {
$this->upgrade->migratePostDataMigration();
}
}

public function applicable(): bool
{
return ! $this->upgrade instanceof Applicable
|| $this->upgrade->applicable();
}

private function storeWithRoles(array $permission): void
{
$permission = Permission::create($permission);
Expand All @@ -90,9 +50,8 @@ private function storeWithRoles(array $permission): void

private function syncRoles(Permission $permission): Collection
{
return $this->roles()->when(! $permission->is_default, fn ($roles) => $roles
->filter(fn ($role) => in_array($role->name, $this->upgrade->roles())
|| $role->name === $this->defaultRole));
return $this->roles()->when(! $permission->is_default, fn ($roles) => $roles->filter(fn ($role) => in_array($role->name, $this->upgrade->roles())
|| $role->name === Config::get('enso.config.defaultRole')));
}

private function roles(): Collection
Expand Down
86 changes: 86 additions & 0 deletions tests/units/MigrationsUpgradeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

use Illuminate\Foundation\Testing\RefreshDatabase;
use LaravelEnso\Helpers\Exceptions\EnsoException;
use LaravelEnso\Upgrade\Contracts\RenamesMigrations;
use LaravelEnso\Upgrade\Services\Database;
use LaravelEnso\Upgrade\Services\Migrations;
use Tests\TestCase;

class MigrationsUpgradeTest extends TestCase
{
use RefreshDatabase;

protected function setUp(): void
{
parent::setUp();

$this->mock = $this->createMock(TestRenamesMigrations::class);
}

/** @test */
public function can_rename_migrations()
{
$this->createMigration('bar');

$this->mock->method('from')->willReturn(['bar']);
$this->mock->method('to')->willReturn(['foo']);

$this->migrateStructure();

$this->assertTrue(DB::table('migrations')->whereMigration('foo')->exists());
}

/** @test */
public function will_throw_exception_on_argument_count_mismatch()
{
$this->expectException(EnsoException::class);

$this->mock->method('to')->willReturn(['foo']);
$this->mock->method('from')->willReturn(['foo', 'bar']);

$this->migrateStructure();
}

/** @test */
public function will_not_migrate_data_if_all_to_migrations_exist()
{
$this->createMigration('foo');
$this->createMigration('qux');

$this->mock->method('from')->willReturn(['bar', 'baz']);
$this->mock->method('to')->willReturn(['foo', 'qux']);

$service = Mockery::mock(Migrations::class, [$this->mock]);
$service->expects()->class()->andReturn($this->mock)->twice();
$service->expects()->isMigrated()->andReturn(true);

(new Database($service))->handle();
}

private function migrateStructure()
{
(new Database(new Migrations($this->mock)))->handle();
}

private function createMigration(string $name): void
{
DB::table('migrations')->insert([
'migration' => $name,
'batch' => 1,
]);
}
}

class TestRenamesMigrations implements RenamesMigrations
{
public function from(): array
{
return [];
}

public function to(): array
{
return [];
}
}
22 changes: 10 additions & 12 deletions tests/units/StructureUpgradeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ protected function setUp(): void
{
parent::setUp();

$this->upgrade = new TestStructureMigration();
$this->mock = $this->createMock(TestStructureMigration::class);

$this->defaultRole = $this->role(Config::get('enso.config.defaultRole'));

Expand All @@ -32,9 +32,9 @@ protected function setUp(): void
/** @test */
public function can_migrate()
{
$this->upgrade->permissions = [
$this->mock->method('permissions')->willReturn([
['name' => 'test', 'description' => 'test', 'is_default' => true],
];
]);

$this->migrateStructure();

Expand All @@ -44,9 +44,9 @@ public function can_migrate()
/** @test */
public function can_migrate_default_permission()
{
$this->upgrade->permissions = [
$this->mock->method('permissions')->willReturn([
['name' => 'test', 'description' => 'test', 'is_default' => true],
];
]);

$this->migrateStructure();

Expand All @@ -57,9 +57,9 @@ public function can_migrate_default_permission()
/** @test */
public function can_migrate_non_default_permission()
{
$this->upgrade->permissions = [
$this->mock->method('permissions')->willReturn([
['name' => 'test', 'description' => 'test', 'is_default' => false],
];
]);

$this->migrateStructure();

Expand All @@ -70,9 +70,9 @@ public function can_migrate_non_default_permission()
/** @test */
public function skips_existing_permissions()
{
$this->upgrade->permissions = [
$this->mock->method('permissions')->willReturn([
['name' => 'test', 'description' => 'test', 'is_default' => true],
];
]);

$this->migrateStructure();
$this->migrateStructure();
Expand All @@ -89,13 +89,11 @@ protected function role($name)

private function migrateStructure()
{
(new Database(new Structure($this->upgrade)))->handle();
(new Database(new Structure($this->mock)))->handle();
}
}

class TestStructureMigration implements MigratesStructure
{
use StructureMigration;

public $permissions = [];
}