laragear/meta-model

允许其他开发者定制您的包模型和迁移

v1.1.0 2024-03-15 23:27 UTC

README

Latest Version on Packagist Latest stable test run Codecov coverage Maintainability Sonarcloud Status Laravel Octane Compatibility

允许其他开发者定制您的包模型和迁移。

use Illuminate\Database\Eloquent\Model;
use Laragear\MetaModel\CustomizableModel;
use MyVendor\MyPackage\Migrations\MyMigration;

class MyPackageModel extends Model
{
    use CustomizableModel;
    
    protected static function migrationClass(): string
    {
        return MyMigration::class    
    }
}

提示

您是从包中过来的吗?您可能想阅读 MIGRATIONS.md 文件。

保持此包免费

您的支持使我能够保持此包免费、最新和维护。或者,您也可以 传播信息!

要求

  • Laravel 10 或更高版本

安装

启动 Composer 并将其要求添加到您的包中

composer require laragear/meta-model

定制模型

大多数时候,您的用户会希望定制包中的模型和迁移。例如,他们可能希望添加列并将它们转换为特定的数据类型,或者修改哪些属性被隐藏。这可以通过包含 CustomizableModel 特质的模型来实现。

namespace Vendor\Package\Models;

use Illuminate\Database\Eloquent\Model;
use Laragear\MetaModel\CustomizableModel;
use Vendor\Package\Migrations\CarMigration;

class Car extends Model
{
    use CustomizableModel;
    
    protected static function migrationClass(): string
    {
        return CarMigration::class;
    }
}

从那里,最终开发者可以使用可用的静态属性来定制模型

  • $useConnection: 要使用的自定义连接名称。
  • $useTable: 要使用的自定义表名称。
  • $useCasts: 要合并的铸造属性。
  • $useFillable: 要合并的填充属性。
  • $useGuarded: 要合并的受保护属性。
  • $useHidden: 要合并的隐藏属性。
  • $useVisible: 要合并的可见属性。
  • $useAppends: 要合并的追加属性。

除了 $useTable 之外的所有静态属性都接受一个闭包,该闭包接收模型并返回一个属性数组。最终开发者应在 AppServiceProviderboot() 方法中修改这些属性。

namespace App\Providers;

use MyVendor\MyPackage\Models\Car;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Car::$useCasts = ['is_new' => 'boolean'];
    }
}

追加

正如你所猜测的,useAppend 只在您的模型有属性访问器时才工作。如果您期望用户在模型序列化时追加属性,确保您有适当的访问器。

例如,我们可以在我们的 Car 模型中添加 colorchassis 属性访问器。

namespace Vendor\Package\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Laragear\MetaModel\CustomizableModel;
use Vendor\Package\Migrations\ModelMigration;

class Car extends Model
{
    use CustomizableModel;
    
    // ...
    
    protected function getColorAttribute()
    {
        return $this->metadata->color;
    }
    
    protected function chassis(): Attribute
    {
        return Attribute::get(fn() => (string) $this->metadata->chassis)
    }
}

之后,最终开发者可以在运行时追加这些属性。

namespace App\Providers;

use MyVendor\MyPackage\Models\Car;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Car::$useAppends = ['color', 'chassis'];
    }
}

可定制迁移

要允许可定制迁移,创建一个标准的迁移文件,但不是返回一个扩展默认 Migration 类的类,而是返回一个调用您的模型类的 migration() 调用。

让我们解释一下这是多么神奇。

例如,假设我们想要为 Car 模型创建一个迁移。我们将创建一个扩展 CustomizableMigration 类的类。从那里,表模式将在 create() 方法中处理。

namespace MyVendor\MyPackage\Migrations;

use Illuminate\Database\Schema\Blueprint;
use Laragear\MetaModel\CustomizableMigration;
use MyVendor\MyPackage\Models\Car;

class CarsMigration extends CustomizableMigration
{
    protected function create(Blueprint $table)
    {
        $table->id();
        
        $table->string('manufacturer');
        $table->string('model');
        $table->tinyInteger('year');
        
        $table->timestamps();
    }
}

定义我们的默认迁移类后,我们需要在模型的 $migration 静态属性中指定它所在的位置

namespace MyVendor\MyPackage\Models;

use Illuminate\Database\Eloquent\Model;
use Laragear\MetaModel\CustomizableModel;
use MyVendor\MyPackage\Migrations\CarsMigration;

class Car extends Model
{
    use CustomizableModel;
    
    protected static function migrationClass(): string
    {
        reutnr CarsMigration::class
    };
}

一旦这样,我们就可以创建迁移文件 0000_00_00_000000_create_cars_table.php。我们不返回一个扩展默认 Laravel 迁移的类,而是使用我们的模型和 migration() 方法。

// database/migrations/0000_00_00_000000_create_cars_table.php

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

return Car::migration();

启动

您可以使用 boot() 方法在迁移实例化时运行自定义逻辑。

namespace MyVendor\MyPackage\Migrations;

use Illuminate\Database\Schema\Blueprint;
use Laragear\MetaModel\CustomizableMigration;
use MyVendor\MyPackage\Models\Car;

class CarsMigration extends CustomizableMigration
{
    protected function boot() : void
    {
        if (app()->isUnitTesting()) {
            Car::$useConnection = env('DB_CONNECTION');        
        }
    }

    protected function create(Blueprint $table)
    {
        $table->id();
        
        $table->string('manufacturer');
        $table->string('model');
        $table->tinyInteger('year');
        
        $table->timestamps();
    }
}

注意

每次实例化迁移时都会运行 boot() 方法。确保当需要时,该方法的效果是幂等的。

添加自定义列

您可能希望让最终开发者在迁移中添加额外的列。为了实现这一点,只需在 create() 方法内部任何位置调用 addColumns(),确保传递 Blueprint 实例。在调用此方法的一个好位置是在 timestamps() 之前或主键之后。

namespace MyVendor\MyPackage\Migrations;

use Illuminate\Database\Schema\Blueprint;
use Laragear\MetaModel\CustomizableMigration;

abstract class CarsMigration extends CustomizableMigration
{
    protected function create(Blueprint $table)
    {
        $table->id();
        
        $table->string('manufacturer');
        $table->string('model');
        $table->tinyInteger('year');
        
        $this->addColumns($table);
        
        $table->timestamps();
    }
}

之后,在您的迁移文件中,向 migration() 方法添加一个空的回调,或使用 with() 方法,让最终开发者知道他可以扩展表模式。

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

return Car::migration(function (Blueprint $table) {
    // Add your custom columns here.
    // Don't forget to add casts to the model if necessary!
})

最终开发者也可以根据需要以编程方式添加多个回调,这对于分离关注点非常有用。

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

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

提示

如果您不想支持额外的列,则可以省略 addColumns() 调用,因为任何添加的回调都不会执行。

多态

注意

多态仅支持单一关系。在一个表上存在多个多态关系被强烈不建议。

如果您的迁移需要多态关系,您会发现最终开发者在其应用程序中不一定有相同的键类型。这个问题可以通过使用 createMorph()createNullableMorph() 方法并传递 Blueprint 实例和多态类型的名称来解决。

protected function create(Blueprint $table)
{
    $table->id();
    
    $this->createMorphRelation($table, 'ownable');
    
    $table->string('manufacturer');
    $table->string('model');
    $table->tinyInteger('year');
    
    $table->timestamps();
}

这将允许最终开发者在需要时通过 morph() 方法更改多态类型。例如,如果他在目标模型中使用 ULID 多态,他可能只需一行代码即可设置它

use MyVendor\MyPackage\Models\Car;

return Car::migration()->morph('ulid', 'custom_index_name');

默认索引名称

您也可以为多态设置自定义的索引名称。除非用户手动覆盖,否则它将作为默认值使用。

protected function create(Blueprint $table)
{
    $this->createMorphRelation($table, 'ownable', 'ownable_table_index');
    
    // ...
}
use MyVendor\MyPackage\Models\Car;

// Uses "custom_index_name" as index name
return Car::migration()->morph('ulid', 'custom_index_name');

// Uses "ownable_table_index" as index name
return Car::migration()->morph('ulid');

在升级后和降级前

最终开发者可以使用 afterUp()beforeDown() 方法分别执行逻辑,在表创建后,在表删除前。这允许开发者增强表或避免失败的迁移。

例如,最终开发者可以使用这些方法创建外键列引用,并在删除表之前删除它们。

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

return Car::migration()
    ->afterUp(function (Blueprint $table) {
        $table->foreign('manufacturer')->references('name')->on('manufacturers');
    })
    ->beforeDown(function (Blueprint $table) {
         $table->dropForeign('manufacturer');
    });

重要

afterUp()beforeDown() 为迁移添加回调,它不会替换它们。

包文档

如果您计划将其添加到您的包中,您可能还想将 MIGRATIONS.md 文件复制并粘贴到您的包中。这样开发者就会知道如何使用您的模型和迁移。或者,您也可以仅复制其内容,或链接回此存储库。

Laravel Octane 兼容性

  • 没有使用过时的应用程序实例的单例。
  • 没有使用过时的配置实例的单例。
  • 没有使用过时的请求实例的单例。
  • 特质静态属性只被最终开发者写入一次。

使用此包与 Laravel Octane 一起使用应该没有问题。

安全

如果您发现任何与安全相关的问题,请通过电子邮件 darkghosthunter@gmail.com 反馈,而不是使用问题跟踪器。

许可证

在发布时,此特定包版本是根据 MIT 许可证 的条款许可的。

LaravelTaylor Otwell 的商标。版权所有 © 2011-2024 Laravel LLC。