laravel 从模型生成数据库

laravel generate database from model

我正在使用 Laravel 的一个现有项目,这个现有项目已经有了模型,下面是一个示例:

<?php

/**
 * Created by Reliese Model.
 * Date: Fri, 20 Apr 2018 08:56:36 +0000.
 */

namespace App\Models;

use Reliese\Database\Eloquent\Model as Eloquent;

/**
 * Class PdTcountry
 * 
 * @property int $pkcountry
 * @property string $country_code
 * @property string $country_name
 * @property string $country_localName
 * @property string $country_webCode
 * @property string $country_region
 * @property string $country_continent
 * @property float $country_latitude
 * @property float $country_longitude
 * @property string $country_surfaceArea
 * @property string $country_population
 * @property string $country_postcodeexpression
 * @property \Carbon\Carbon $create_at
 * @property \Carbon\Carbon $update_at
 * 
 * @property \Illuminate\Database\Eloquent\Collection $pd_tregions
 *
 * @package App\Models
 */
class PdTcountry extends Eloquent
{
    protected $table = 'pd_tcountry';
    protected $primaryKey = 'pkcountry';
    public $timestamps = false;

    protected $casts = [
        'country_latitude' => 'float',
        'country_longitude' => 'float'
    ];

    protected $dates = [
        'create_at',
        'update_at'
    ];

    protected $fillable = [
        'country_code',
        'country_name',
        'country_localName',
        'country_webCode',
        'country_region',
        'country_continent',
        'country_latitude',
        'country_longitude',
        'country_surfaceArea',
        'country_population',
        'country_postcodeexpression',
        'create_at',
        'update_at'
    ];

    public function pd_tregions()
    {
        return $this->hasMany(\App\Models\PdTregion::class, 'fkcountry');
    }
}

我的问题是,这个模型是否可以通过 php artisan 从模型中创建数据库 table?如果有一个 php artisan 命令可以为我所有的模型执行此操作,那就太棒了。

在我的数据库文件夹中有这些,但我不知道它们的作用。

如果您希望自动生成这些 table,那么不,Laravel 确实没有办法做到这一点。理论上,您可以编写自己的命令来为每个模型生成迁移文件,但无论如何它仍然需要您提供所有列名、数据类型等。例如,看一下 Laravel 的 make:migration 命令。它只是使用存根文件并在生成时替换关键字(vendor/laravel/framework/src/Illuminate/Database/Migrations/MigrationCreator.php)。如果您有大量需要数据库 table 的模型,那么这可能是您深入研究的好方法。

但是,如果没有,您可能最好使用标准命令生成迁移并为其提供 --create 标记。之后,您只需在模型中定义 table(或使用命名约定以便它自动找到它,请参阅:https://laravel.com/docs/5.6/eloquent#defining-models 了解有关命名约定的更多信息)。

示例:

php artisan make:migration create_my_model_table --create=my_model_table_name

如果您不使用命名约定,请将您的 table 名称添加到您的模型中:

class PdTcountry extends Eloquent {

    protected $table = "my_model_table_name"

    ...
}

运行 php artisan migrate 在控制台中。这将为您的 database/migrations 文件夹中存在的定义生成表,如问题中的图片所示。

有没有办法通过 php artisan 从模型中创建数据库 table?

听起来你想要的是"Code-First Design"。 Microsoft 的 Entity Framework 支持此功能,但 Laravel 的工作方式有所不同。在使用 Entity framework 的 C# 中,可以创建属性(基本上是 getter 方法)来对应每个数据库列。用Eloquent(Laravel的ORM library) it generates these dynamically using PHP magic methods, PHP variables also lack types in the way that C# does. Because of this there is no way to populate the database based on the code in the way you want. The doc comments you posted in your question look like they were generated the other way around from database to code, using the laravel-ide-helper包.

或者一些数据库客户端,如 Sequel Pro 有一个插件可以将现有的数据库模式导出到 Laravel 迁移中,我发现它在过去非常快速和有用,并且可能是最接近你的东西可以找到您正在寻找的工作流程。祝你好运!

我的数据库文件夹中有(一组迁移文件),但我不知道它们的作用。

你应该看看相关的documentation on the Laravel website。迁移已经生成,因此您需要配置本地数据库和 运行 migrate 命令。这将创建您的应用程序所需的 table 和列。当您对架构进行更改时,您应该添加更多迁移并重新运行 migrate 命令。

php artisan migrate

正如其他人所指出的:这不是 Laravel 或 Eloquent ORM 的工作方式。通常你会先创建一个 table (即进行迁移)然后创建你的模型。坚持 Laravels model conventions.

可以节省很多时间

但是,您已经有一个或多个模型似乎缺少迁移。我建议您简单地为这些模型添加迁移。不幸的是,如果您有很多模型,尤其是当您不遵守 table 名称和其他约定时(就像您的示例模型一样),这将是很多工作。

另一方面,您已经拥有大量要进入迁移的信息,这些信息在您的模型中可用。您可以从 DocComments 和 $table$primaryKey$fillable 等属性中提取此信息。这可以自动完成。我整理了一个远未完成的示例,但至少应该让您从迁移的基础开始。然后您可以决定手动完成其余部分或向自动流程添加功能。如果我有很多模型,我个人只会做后者。


例子

我已经基于您问题中包含的模型示例。

如我所说;还远未完成,想到以下 additions/improvements:

  1. 确定关系并以此为基础的外键。 (看看这个 post 以获得一些灵感。)
  2. switch 添加更多数据类型。
  3. ...

将以下特征保存为app/SchemaBuilder.php:

<?php

namespace App;

trait SchemaBuilder
{
    public function printMigration()
    {
        echo '<pre>';
        echo htmlspecialchars($this->generateMigration());
        echo '</pre>';
    }

    public function makeMigrationFile()
    {
        if ($this->migrationFileExists()) {
            die('It appears that a migration for this model already exists. Please check it out.');
        }
        $filename = date('Y_m_t_His') . '_create_' . $this->table . '_table.php';
        if (file_put_contents(database_path('migrations/') . $filename, $this->generateMigration())) {
            return true;
        }

        return false;
    }

    protected function generateMigration()
    {
        return sprintf($this->getSchemaTemplate(),
            ucfirst($this->table), $this->table,
            implode("\n\t\t\t", $this->generateFieldCreationFunctions()),
            $this->table
        );
    }

    protected function getSchemaTemplate()
    {
        $schema = "<?php\n";
        $schema .= "\n";
        $schema .= "use Illuminate\Support\Facades\Schema;\n";
        $schema .= "use Illuminate\Database\Schema\Blueprint;\n";
        $schema .= "use Illuminate\Database\Migrations\Migration;\n";
        $schema .= "\n";
        $schema .= "class Create%sTable extends Migration\n";
        $schema .= "{\n";
        $schema .= "\t/**\n";
        $schema .= "\t* Run the migrations.\n";
        $schema .= "\t*\n";
        $schema .= "\t* @return void\n";
        $schema .= "\t*/\n";
        $schema .= "\tpublic function up()\n";
        $schema .= "\t{\n";
        $schema .= "\t\tSchema::create('%s', function (Blueprint $table) {\n";
        $schema .= "\t\t\t%s\n"; # Actual database fields will be added here.
        $schema .= "\t\t});\n";
        $schema .= "\t}\n";
        $schema .= "\n";
        $schema .= "\t/**\n";
        $schema .= "\t* Reverse the migrations.\n";
        $schema .= "\t*\n";
        $schema .= "\t* @return void\n";
        $schema .= "\t*/\n";
        $schema .= "\tpublic function down()\n";
        $schema .= "\t{\n";
        $schema .= "\t\tSchema::drop('%s');\n";
        $schema .= "\t}\n";
        $schema .= "}";

        return $schema;
    }

    protected function generateFieldCreationFunctions()
    {
        $functions = [];

        if (isset($this->primaryKey)) {
            $functions[] = "$table->increments('$this->primaryKey');";
        }

        $featuresFromDoc = $this->extractFieldDataFromCommentDoc();
        $functions[] = ""; # Hack our way to an empty line.
        foreach ($this->fillable as $fillableField) {
            if (in_array($fillableField, $this->dates)) { # We'll handle fields in $dates later.
                continue;
            }

            if (!isset($featuresFromDoc[$fillableField])) {
                $functions[] = "//Manually do something with $fillableField";
            }

            switch ($featuresFromDoc[$fillableField]) {
                case 'string':
                    $functions[] = "$table->string('$fillableField'); //TODO: check whether varchar is the correct field type.";
                    break;
                case 'int':
                    $functions[] = "$table->integer('$fillableField'); //TODO: check whether integer is the correct field type.";
                    break;
                case 'float':
                    $functions[] = "$table->float('$fillableField', 12, 10);";
                    break;
                default:
                    $functions[] = "//Manually do something with $fillableField";
            }
        }

        $functions[] = ""; # Empty line.
        foreach ($this->dates as $dateField) {
            $functions[] = "$table->dateTime('$dateField');";
        }

        $functions[] = ""; # Empty line.
        if (!empty($this->timestamps)) {

            $functions[] = "$table->timestamps();";
        }
        return $functions;
    }

    protected function extractFieldDataFromCommentDoc()
    {
        $doc_comment = (new \ReflectionClass(get_parent_class($this)))->getDocComment();

        preg_match_all('/@property (.+) $(.+)/', $doc_comment, $matches, PREG_SET_ORDER);

        foreach ($matches as $match) {
            $features[$match[2]] = $match[1];
        }

        return $features;
    }

    protected function migrationFileExists()
    {
        $path = database_path('migrations');

        if ($handle = opendir($path)) {
            while (false !== ($file = readdir($handle))) {
                if (strpos($file, 'create_' . $this->table . '_table') !== false) {
                    return true;
                }
            }
            closedir($handle);
        }

        return false;
    }
}

创建以下控制器并注册一个路由,以便您可以访问它:

<?php

namespace App\Http\Controllers;

use App\PdTcountry;
use Illuminate\Http\Request;

class TestController extends Controller
{
    public function index()
    {
        # Specify for which model you'd like to build a migration.
        $buildSchemaFor = 'App\PdTcountry';

        eval(sprintf('class MigrationBuilder extends %s{use \App\SchemaBuilder;}', $buildSchemaFor));

        if ((new \MigrationBuilder)->makeMigrationFile()) {
            echo 'Migration file successfully created.';
        }
        else {
            echo 'Encountered error while making migration file.';
        }

        # Or alternatively, print the migration file to the browser:
//        (new \MigrationBuilder)->printMigration();


    }
}

这是一个您可以安装的 composer 包,它可以根据您的模型创建数据库表。叫做依赖。

https://github.com/reliese/laravel

希望这对您有所帮助,并且正是您要找的。