4

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

user979331
  • 11,039
  • 73
  • 223
  • 418
  • Dublicate [try this](https://stackoverflow.com/questions/30560485/create-models-from-database-in-laravel-5) – Stranger Jul 03 '18 at 15:58
  • 1
    Does the project have migration as well? This should be where the tables that makes up the database are. – Oluwatobi Samuel Omisakin Jul 03 '18 at 16:02
  • 1
    You can probably install [Doctrine](https://www.doctrine-project.org/) to do this. – apokryfos Jul 03 '18 at 16:03
  • @Stranger, that's the opposite. Generating models from a database is fine, generating a database from models is a bad idea. There is nothing in the model that would determine an exact data type (BLOB vs CHAR vs VARCHAR vs TEXT), nothing that determines nullable fields, etc. – Devon Bessemer Jul 03 '18 at 16:08
  • Possible duplicate of [Create Models from database in Laravel 5](https://stackoverflow.com/questions/30560485/create-models-from-database-in-laravel-5) – Simas Joneliunas Jul 03 '18 at 16:09
  • @OluwatobiSamuelOmisakin I updated my question with a screenshot of the database folder. – user979331 Jul 03 '18 at 17:33
  • @Stranger I tried this: php artisan krlove:generate:model ClassName, but got this error There are no commands defined in the "krlove:generate" namespace. – user979331 Jul 03 '18 at 17:35
  • you need `php artisan migrate` You need to read about Laravel migration: https://laravel.com/docs/5.4/migrations#running-migrations – Oluwatobi Samuel Omisakin Jul 03 '18 at 18:00
  • @OluwatobiSamuelOmisakin I just tried php artisan mirgrate and got this error `Base table or view not found: 1146 Table 'forge.pd_tlogin' doesn't exist (SQL: alter table `pd_tlogin` add `fkprofessional` int null)` My goal is with this exisiting project is to create a database from the models instead of creating a database with all the tables (there is a few of them) – user979331 Jul 04 '18 at 03:22
  • You need to observe how it is, something are not automatic for every steps. – Oluwatobi Samuel Omisakin Jul 04 '18 at 07:15
  • You have to write migrations to create that table `forge.pd_tlogin` – Oluwatobi Samuel Omisakin Jul 04 '18 at 07:43
  • @OluwatobiSamuelOmisakin Can you give me an example of what the migrations would look like? – user979331 Jul 04 '18 at 23:30

5 Answers5

7

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"

    ...
}
JPark
  • 779
  • 4
  • 6
3

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
Erik Berkun-Drevnig
  • 2,306
  • 23
  • 38
  • 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. – Arkemlar Apr 04 '22 at 17:01
1

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


    }
}
Niellles
  • 868
  • 10
  • 27
-2

Here is a composer package that you can install that creates database tables from your models. It's called reliese.

https://github.com/reliese/laravel

Hope this helps and is what you are looking for.

Polaris
  • 1,219
  • 8
  • 14
-3

Run php artisan migrate in the console. This will generate tables for definition existing in your database/migrations folder as shown in the pic in the question.

Aakash Tushar
  • 504
  • 6
  • 21
  • @SimoneCabrino He said, he has an existing project with models and migration files. What else does he mean by generating tables? – Aakash Tushar Jul 06 '18 at 15:31
  • He means code-first database, using the models to create the database and not the migrations – abr Jul 07 '18 at 14:56