laravel generate database from model

12,048

Solution 1

If you are looking to generate these tables automagically, then no, Laravel doesn't really have a way to do that. In theory, you could write your own command to generate migration files for each of your models, but it will still require you to provide all the column names, data types, etc. anyways. Take a look at Laravel's make:migration command for instance. It just uses stub files and replaces key words when generating (vendor/laravel/framework/src/Illuminate/Database/Migrations/MigrationCreator.php). If you have a ton of models that need database tables, then maybe this would be a good approach for you to dive into.

If not, however, you're probably best off generating a migration using the standard command and just supply it with the --create tag. Afterwards, you would just have to define your table in your model (or use the naming convention so it finds it automatically, see: https://laravel.com/docs/5.6/eloquent#defining-models for more info on the naming conventions).

Example:

php artisan make:migration create_my_model_table --create=my_model_table_name

If you don't use the naming convention, add your table name to your model:

class PdTcountry extends Eloquent {

    protected $table = "my_model_table_name"

    ...
}

Solution 2

Is there a way via php artisan to create a database table from the model?

It sounds like what you want is "Code-First Design". This is supported in Microsoft's Entity Framework however in Laravel the way things work is somewhat different. In C# with Entity framework one would create properties (basically getter methods) to correspond with each database column. With Eloquent (Laravel's 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 package.

Alternatively some database clients like Sequel Pro have a plugin to export the existing database schema into a Laravel migration which I have found very quick and helpful in the past and might be the closest thing you can get to the workflow you're looking for. Good luck!

Exporting a database schema as a Laravel migration

I have (a set of migration files) in my database folder, but I don't know what they do.

You should check out the relevant documentation on the Laravel website. The migrations have already been generated so you need to configure a local database and run the migrate command. This will create the tables and columns necessary for your application. As you make changes to the schema you should add more migrations and rerun the migrate command.

php artisan migrate

Solution 3

As others have pointed out: this is not how Laravel, or the Eloquent ORM to be exact, works. Normally you'd first create a table (i.e. make a migration) and then create your model. A lot of time can be saved by adhering to Laravels model conventions.

However, you already have one or more models that seem to be missing a migration. I'd advice you to simply add migrations for these models. Unfortunately this will be a lot of work if you have a lot of models and, especially (as is the case with your example model) when you didn't adhere to the table name, and other, conventions.

On the other hand, you already have a lot of the information that's going to go into your migration, available in your Model. You could extract this information from the DocComments and properties like $table, $primaryKey, $fillable, etc. This could be done automatically. I've put together an example that's far from complete, but should at least get you started with the base of your migration. You can then decide to do the remaining parts manually or add functionality to the automatic process. I'd personally only do the latter if I had a lot of models.


Example

I've based this example of the model included in your question.

As I said; it's far from complete, the following additions/improvements come to mind:

  1. Determine relations and base foreign keys on that. (Take a look at this post to get some inspiration.)
  2. Add more data types to the switch.
  3. ...

Save the following trait as 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;
    }
}

Create the following controller and register a route so that you can access it:

<?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();


    }
}
Share:
12,048
user979331
Author by

user979331

Updated on June 07, 2022

Comments

  • user979331
    user979331 almost 2 years

    I am using an existing project of Laravel and this existing project already has models, here is an example of one:

    <?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');
        }
    }
    

    My question is, with this Model is there away via php artisan to create a database table from the model? If there is a php artisan command to do it for all my models that would be super.

    In my database folder I have these, but I don't know what they do.

    enter image description here

  • Aakash Tushar
    Aakash Tushar almost 6 years
    @SimoneCabrino He said, he has an existing project with models and migration files. What else does he mean by generating tables?
  • abr
    abr almost 6 years
    He means code-first database, using the models to create the database and not the migrations
  • Muhammad Raheel
    Muhammad Raheel over 2 years
    the OP is asking exactly the opposite
  • Arkemlar
    Arkemlar about 2 years
    Actually what you said about Microsoft's Entity Framework exists in PHP and called Doctrine 2 ORM. And it was terrible for me to switch into Laravel ecosystem because that Eloquent is so ugly compared to Doctrine. In Doctrine you can run command to generate Up and Down migrations with SQL code automatically. Also you can run command that actually prints alter SQL querries or executes them in database. And all of these is out of box.