Skip to content

Commit

Permalink
Merge pull request #1 from Laragear/feat/multiple-closures
Browse files Browse the repository at this point in the history
[1.x] Adds support for multiple closures
  • Loading branch information
DarkGhostHunter committed Mar 15, 2024
2 parents 3f733e7 + b3e36a3 commit 1881649
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 40 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,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 @@ -322,7 +335,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 @@ -339,6 +352,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 @@ -64,14 +65,16 @@ protected function boot(): void
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 @@ -92,38 +95,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 @@ -180,8 +183,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 @@ -192,8 +195,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 @@ -121,10 +121,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));
}
}
48 changes: 34 additions & 14 deletions tests/CustomizableMigrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,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 @@ -146,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 @@ -169,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 @@ -191,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 @@ -206,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 @@ -235,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 @@ -247,7 +264,10 @@ 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();
}

#[Test]
Expand Down

0 comments on commit 1881649

Please sign in to comment.