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

[1.x] Adds support for multiple closures #1

Merged
merged 3 commits into from
Mar 15, 2024
Merged
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
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,22 @@ return Car::migration(function (Blueprint $table) {
})
```

An end-developer can also add multiple callbacks programmatically if needed, which are great to separate concerns.

```php
use MyVendor\MyPackage\Models\Car;
use Illuminate\Database\Schema\Blueprint;

return Car::migration(
fn ($table) => /* ... */,
fn ($table) => /* ... */,
fn ($table) => /* ... */,
);
```

> [!TIP]
>
> If you don't want to support additional columns, it's fine. If the end-developer adds a callback, it won't be executed regardless.
> You can omit the `addColumns()` call if you don't want to support additional columns, as any added callback won't be executed.

### Morphs

Expand Down Expand Up @@ -234,7 +247,7 @@ return Car::migration()->morph('ulid', 'custom_index_name');

### After Up & Before Down

The `CustomizableMigration` contains two methods, `afterUp()` and `beforeDown()`. The first is executed after the table is created, while the latter is executed before the table is dropped. This allows the developer to run custom logic to enhance its migrations, or avoid failing migrations.
An end-developer can execute logic after the table is created, and before the table is dropped, using the `afterUp()` and `beforeDown()` methods, respectively. This allows the developer to run enhance the table, or avoid failing migrations.

For example, the end-developer can use these methods to create foreign column references, and remove them before dropping the table.

Expand All @@ -251,6 +264,10 @@ return Car::migration()
});
```

> [!IMPORTANT]
>
> The `afterUp()` and `beforeDown()` adds callbacks to the migration, it doesn't replace them.

## Package documentation

If you plan to add this to your package, you may also want to copy-and-paste the [MIGRATIONS.md](MIGRATIONS.md) file in your package. This way developers will know how to use your model and migrations. Alternatively, you may also just copy its contents, or link back to this repository.
Expand Down
45 changes: 24 additions & 21 deletions src/CustomizableMigration.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use function array_push;
use function sprintf;
use function strtolower;

Expand All @@ -28,17 +29,17 @@ abstract class CustomizableMigration extends Migration
* Create a new Customizable Migration instance.
*
* @param class-string<\Illuminate\Database\Eloquent\Model> $model
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void)|null $with
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void)|null $afterUp
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void)|null $beforeDown
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void)[] $with
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void)[] $afterUp
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void)[] $beforeDown
* @param "numeric"|"uuid"|"ulid"|"" $morphType
* @param string|null $morphIndexName
*/
public function __construct(
string $model,
protected ?Closure $with = null,
protected ?Closure $afterUp = null,
protected ?Closure $beforeDown = null,
protected array $with = [],
protected array $afterUp = [],
protected array $beforeDown = [],
protected string $morphType = '',
protected ?string $morphIndexName = null,
)
Expand All @@ -52,14 +53,16 @@ public function __construct(
abstract public function create(Blueprint $table): void;

/**
* Execute a callback from the developer to add more columns in the table, if any.
* Execute stored callbacks using the table Blueprint instance.
*
* @param \Illuminate\Database\Schema\Blueprint $table
* @return void
*/
protected function addColumns(Blueprint $table): void
{
with($table, $this->with);
foreach ($this->with as $callback) {
$callback($table);
}
}

/**
Expand All @@ -80,38 +83,38 @@ public function morph(string $type, string $indexName = null): static
/**
* Add additional columns to the table.
*
* @param \Closure(\Illuminate\Database\Schema\Blueprint $table):void $callback
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void) ...$callbacks
* @return $this
*/
public function with(Closure $callback): static
public function with(Closure ...$callbacks): static
{
$this->with = $callback;
array_push($this->with, ...$callbacks);

return $this;
}

/**
* Execute the callback after the "up" method.
*
* @param \Closure(\Illuminate\Database\Schema\Blueprint $table):void $callback
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void) ...$callbacks
* @return $this
*/
public function afterUp(Closure $callback): static
public function afterUp(Closure ...$callbacks): static
{
$this->afterUp = $callback;
array_push($this->afterUp, ...$callbacks);

return $this;
}

/**
* Execute the callback before the "down" method.
*
* @param \Closure(\Illuminate\Database\Schema\Blueprint $table):void $callback
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void) ...$callbacks
* @return $this
*/
public function beforeDown(Closure $callback): static
public function beforeDown(Closure ...$callbacks): static
{
$this->beforeDown = $callback;
array_push($this->beforeDown, ...$callbacks);

return $this;
}
Expand Down Expand Up @@ -168,8 +171,8 @@ public function up(): void
{
Schema::create($this->table, $this->create(...));

if ($this->afterUp) {
Schema::table($this->table, $this->afterUp);
foreach ($this->afterUp as $callback) {
Schema::table($this->table, $callback);
}
}

Expand All @@ -180,8 +183,8 @@ public function up(): void
*/
public function down(): void
{
if ($this->beforeDown) {
Schema::table($this->table, $this->beforeDown);
foreach ($this->beforeDown as $callback) {
Schema::table($this->table, $callback);
}

Schema::dropIfExists($this->table);
Expand Down
6 changes: 3 additions & 3 deletions src/CustomizableModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ abstract protected static function migrationClass(): string;
/**
* Return a new customizable migration instance.
*
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void)|null $with
* @param (\Closure(\Illuminate\Database\Schema\Blueprint $table):void) ...$with
*/
public static function migration(Closure $with = null): CustomizableMigration
public static function migration(Closure ...$with): CustomizableMigration
{
return new (static::migrationClass())(static::class, $with);
return (new (static::migrationClass())(static::class, $with));
}
}
50 changes: 34 additions & 16 deletions tests/CustomizableMigrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ public function creates_columns(): void
return true;
});



TestModel::migration()->up();
}

Expand Down Expand Up @@ -100,19 +98,24 @@ public function creates_column_with_callback(): void
TestMigration::$callMethod = true;

$blueprint = m::mock(Blueprint::class);
$blueprint->expects('createCall')->twice();
$blueprint->expects('addCall')->twice();
$blueprint->expects('createCall')->once();
$blueprint->expects('firstCall')->once();
$blueprint->expects('secondCall')->once();
$blueprint->expects('thirdCall')->once();
$blueprint->expects('fourthCall')->once();

$this->container->instance('db.schema', $schema = m::mock(SchemaBuilder::class));
$schema->expects('create')->twice()->withArgs(function (string $table, Closure $closure) use ($blueprint): bool {
$schema->expects('create')->once()->withArgs(function (string $table, Closure $closure) use ($blueprint): bool {
static::assertSame('test_models', $table);
$closure($blueprint);

return true;
});

TestModel::migration()->with(fn($table) => $table->addCall())->up();
TestModel::migration(fn($table) => $table->addCall())->up();
TestModel::migration(fn($table) => $table->firstCall())
->with(fn($table) => $table->secondCall())
->with(fn($table) => $table->thirdCall(), fn($table) => $table->fourthCall())
->up();
}

#[Test]
Expand Down Expand Up @@ -148,7 +151,8 @@ public function morphs_to_numeric(): void
$blueprint->expects('nullableNumericMorphs')->with('bar', null)->twice();

$this->container->instance('db.schema', $schema = m::mock(SchemaBuilder::class));
$schema->expects('create')->twice()->withArgs(function (string $table, Closure $closure) use ($blueprint): bool {
$schema->expects('create')->twice()->withArgs(function (string $table, Closure $closure) use ($blueprint
): bool {
static::assertSame('test_models', $table);

$closure($blueprint);
Expand All @@ -171,7 +175,8 @@ public function morphs_to_uuid(): void
$blueprint->expects('nullableUuidMorphs')->with('bar', null)->twice();

$this->container->instance('db.schema', $schema = m::mock(SchemaBuilder::class));
$schema->expects('create')->twice()->withArgs(function (string $table, Closure $closure) use ($blueprint): bool {
$schema->expects('create')->twice()->withArgs(function (string $table, Closure $closure) use ($blueprint
): bool {
static::assertSame('test_models', $table);
$closure($blueprint);

Expand All @@ -193,7 +198,8 @@ public function morphs_to_ulid(): void
$blueprint->expects('nullableUlidMorphs')->with('bar', null)->twice();

$this->container->instance('db.schema', $schema = m::mock(SchemaBuilder::class));
$schema->expects('create')->twice()->withArgs(function (string $table, Closure $closure) use ($blueprint): bool {
$schema->expects('create')->twice()->withArgs(function (string $table, Closure $closure) use ($blueprint
): bool {
static::assertSame('test_models', $table);
$closure($blueprint);

Expand All @@ -208,18 +214,24 @@ public function morphs_to_ulid(): void
public function calls_after_up(): void
{
$blueprint = m::mock(Blueprint::class);
$blueprint->expects('createCall')->once();
$blueprint->expects('firstCall')->once();
$blueprint->expects('secondCall')->once();
$blueprint->expects('thirdCall')->once();

$this->container->instance('db.schema', $schema = m::mock(SchemaBuilder::class));
$schema->expects('create')->once();
$schema->expects('table')->once()->withArgs(function (string $table, Closure $closure) use ($blueprint): bool {
$schema->expects('table')->times(3)->withArgs(function (string $table, Closure $closure) use ($blueprint
): bool {
static::assertSame('test_models', $table);
$closure($blueprint);

return true;
});

TestModel::migration()->afterUp(fn($table) => $table->createCall())->up();
TestModel::migration()
->afterUp(fn($table) => $table->firstCall())
->afterUp(fn($table) => $table->secondCall(), fn($table) => $table->thirdCall())
->up();
}

#[Test]
Expand All @@ -237,10 +249,13 @@ public function drops_table(): void
public function calls_before_down(): void
{
$blueprint = m::mock(Blueprint::class);
$blueprint->expects('afterDownCall')->once();
$blueprint->expects('firstCall')->once();
$blueprint->expects('secondCall')->once();
$blueprint->expects('thirdCall')->once();

$this->container->instance('db.schema', $schema = m::mock(SchemaBuilder::class));
$schema->expects('table')->once()->withArgs(function (string $table, Closure $closure) use ($blueprint): bool {
$schema->expects('table')->times(3)->withArgs(function (string $table, Closure $closure) use ($blueprint
): bool {
static::assertSame('test_models', $table);
$closure($blueprint);

Expand All @@ -249,6 +264,9 @@ public function calls_before_down(): void

$schema->expects('dropIfExists')->with('test_models')->once();

TestModel::migration()->beforeDown(fn($table) => $table->afterDownCall())->down();
TestModel::migration()
->beforeDown(fn($table) => $table->firstCall())
->beforeDown(fn($table) => $table->secondCall(), fn($table) => $table->thirdCall())
->down();
}
}
Loading