Skip to content

Commit 961630b

Browse files
committed
first
0 parents  commit 961630b

File tree

10 files changed

+524
-0
lines changed

10 files changed

+524
-0
lines changed

composer.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "jdion84/lucid",
3+
"description": "Declare database schemas inside Laravel models.",
4+
"keywords": [
5+
"lucid",
6+
"database",
7+
"schemas",
8+
"laravel",
9+
"models"
10+
],
11+
"license": "MIT",
12+
"support": {
13+
"source": "https://github.com/jdion84/lucid"
14+
},
15+
"authors": [
16+
{
17+
"name": "Kevin Dion",
18+
"email": "[email protected]"
19+
}
20+
],
21+
"require": {
22+
"doctrine/dbal": "^3.7"
23+
},
24+
"autoload": {
25+
"psr-4": {
26+
"Jdion84\\Lucid\\": "src/"
27+
}
28+
},
29+
"extra": {
30+
"laravel": {
31+
"providers": [
32+
"Jdion84\\Lucid\\LucidServiceProvider"
33+
]
34+
}
35+
}
36+
}

license.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) Kevin Dion
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

readme.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Lucid
2+
3+
Declare database schemas inside Laravel models.
4+
5+
## Installation
6+
7+
Require this package via composer:
8+
9+
```console
10+
composer require jdion84/lucid
11+
```
12+
13+
## Usage
14+
15+
Create a new model class with a schema method:
16+
17+
```console
18+
php artisan make:schema Post
19+
```
20+
21+
Or, add a schema method to an existing model:
22+
23+
```php
24+
namespace App\Models;
25+
26+
use Illuminate\Database\Eloquent\Factories\HasFactory;
27+
use Illuminate\Database\Eloquent\Model;
28+
use Jdion84\Lucid\Table;
29+
30+
class Post extends Model
31+
{
32+
use HasFactory;
33+
34+
public function schema(Table $table)
35+
{
36+
$table->id();
37+
$table->string('title')->index();
38+
$table->text('body');
39+
$table->timestamp('created_at');
40+
$table->timestamp('updated_at');
41+
}
42+
}
43+
```
44+
45+
Migrate & sync model schema methods with the database:
46+
47+
```console
48+
php artisan migrate:schemas
49+
```
50+
51+
## Commands
52+
53+
Create a new model class with a schema method:
54+
55+
```console
56+
php artisan make:schema {name} {--p|pivot} {--force}
57+
```
58+
59+
Migrate & sync model schema methods with the database:
60+
61+
```console
62+
migrate:schemas {--f|fresh} {--s|seed} {--force}
63+
```

src/Console/MakeSchemaCommand.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace Jdion84\Lucid\Console;
4+
5+
use Illuminate\Console\GeneratorCommand;
6+
use Symfony\Component\Console\Input\InputOption;
7+
8+
class MakeSchemaCommand extends GeneratorCommand
9+
{
10+
protected $name = 'make:schema';
11+
12+
protected $description = 'Create a new model class with a schema method';
13+
14+
protected $type = 'Model';
15+
16+
public function handle()
17+
{
18+
if (parent::handle() === false && !$this->option('force')) {
19+
return false;
20+
}
21+
22+
if ($this->argument('name') == 'User') {
23+
$this->backupUserMigration();
24+
}
25+
26+
if (!$this->option('pivot')) {
27+
$this->createFactory();
28+
}
29+
}
30+
31+
protected function backupUserMigration()
32+
{
33+
$file = database_path('migrations/2014_10_12_000000_create_users_table.php');
34+
35+
if (file_exists($file)) {
36+
rename($file, "$file.bak");
37+
}
38+
}
39+
40+
protected function createFactory()
41+
{
42+
$this->call('make:factory', [
43+
'name' => "{$this->argument('name')}Factory",
44+
]);
45+
}
46+
47+
protected function getStub()
48+
{
49+
if ($this->argument('name') == 'User') {
50+
return __DIR__ . '/../../stubs/User.php';
51+
}
52+
53+
if ($this->option('pivot')) {
54+
return __DIR__ . '/../../stubs/Pivot.php';
55+
}
56+
57+
return __DIR__ . '/../../stubs/Model.php';
58+
}
59+
60+
protected function getDefaultNamespace($rootNamespace)
61+
{
62+
return $rootNamespace . '\\Models';
63+
}
64+
65+
protected function getOptions()
66+
{
67+
return [
68+
['pivot', 'p', InputOption::VALUE_NONE, 'Indicates if the generated model should be a pivot'],
69+
['force', null, InputOption::VALUE_NONE, 'Create the class even if the model already exists'],
70+
];
71+
}
72+
}

src/Console/MigrateSchemasCommand.php

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
3+
namespace Jdion84\Lucid\Console;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Console\ConfirmableTrait;
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Database\Eloquent\Relations\Pivot;
9+
use Illuminate\Database\Schema\Blueprint;
10+
use Illuminate\Database\Schema\Builder;
11+
use Illuminate\Support\Str;
12+
use Jdion84\Lucid\Table;
13+
use Symfony\Component\Finder\Finder;
14+
15+
class MigrateSchemasCommand extends Command
16+
{
17+
use ConfirmableTrait;
18+
19+
protected $signature = 'migrate:schemas {--f|fresh} {--s|seed} {--force}';
20+
21+
protected $description = 'Migrate & sync model schema methods with the database';
22+
23+
public function handle()
24+
{
25+
if (!$this->confirmToProceed()) {
26+
return 1;
27+
}
28+
29+
$this->migrate();
30+
31+
$this->syncModelSchemaMethods();
32+
33+
if ($this->option('seed')) {
34+
$this->seed();
35+
}
36+
37+
return 0;
38+
}
39+
40+
protected function migrate()
41+
{
42+
$command = $this->option('fresh')
43+
? 'migrate:fresh'
44+
: 'migrate';
45+
46+
$this->call($command, [
47+
'--force' => true,
48+
]);
49+
}
50+
51+
protected function syncModelSchemaMethods()
52+
{
53+
$this->components->info('Syncing model schema methods.');
54+
55+
$path = app_path('Models');
56+
57+
$namespace = app()->getNamespace();
58+
59+
foreach ((new Finder)->in($path)->files() as $file) {
60+
$model = $namespace . str_replace(
61+
['/', '.php'],
62+
['\\', ''],
63+
Str::after($file->getRealPath(), realpath(app_path()) . DIRECTORY_SEPARATOR)
64+
);
65+
66+
if (method_exists($model, 'schema')) {
67+
$this->syncModelSchemaMethod(app($model));
68+
}
69+
}
70+
71+
$this->newLine();
72+
}
73+
74+
protected function syncModelSchemaMethod(Model|Pivot $model)
75+
{
76+
$builder = $model->getConnection()->getSchemaBuilder();
77+
78+
$temporaryTable = $this->createTemporaryTable($model, $builder);
79+
80+
if (!$builder->hasTable($model->getTable())) {
81+
$this->createTable($model, $builder, $temporaryTable);
82+
} else {
83+
$this->updateTable($model, $builder, $temporaryTable);
84+
}
85+
}
86+
87+
protected function createTemporaryTable(Model|Pivot $model, Builder $builder)
88+
{
89+
$temporaryTable = "{$model->getTable()}_table";
90+
91+
$builder->dropIfExists($temporaryTable);
92+
93+
$builder->create($temporaryTable, function (Blueprint $table) use ($model) {
94+
$model->schema(new Table($table));
95+
});
96+
97+
return $temporaryTable;
98+
}
99+
100+
protected function createTable(Model|Pivot $model, Builder $builder, $temporaryTable)
101+
{
102+
$this->components->task(
103+
"Creating {$model->getTable()} table",
104+
function () use ($builder, $temporaryTable, $model) {
105+
$builder->rename($temporaryTable, $model->getTable());
106+
}
107+
);
108+
}
109+
110+
protected function updateTable(Model|Pivot $model, Builder $builder, $temporaryTable)
111+
{
112+
$manager = $model->getConnection()->getDoctrineSchemaManager();
113+
114+
$tableDifference = $manager->createComparator()->compareTables(
115+
$manager->introspectTable($model->getTable()),
116+
$manager->introspectTable($temporaryTable),
117+
);
118+
119+
if (!$tableDifference->isEmpty()) {
120+
$this->components->task(
121+
"Updating {$model->getTable()} table",
122+
function () use ($manager, $tableDifference) {
123+
$manager->alterTable($tableDifference);
124+
}
125+
);
126+
}
127+
128+
$builder->drop($temporaryTable);
129+
}
130+
131+
protected function seed()
132+
{
133+
$this->call('db:seed', [
134+
'--force' => true,
135+
]);
136+
}
137+
}

src/LucidServiceProvider.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Jdion84\Lucid;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Support\ServiceProvider;
7+
use Jdion84\Lucid\Console\MakeSchemaCommand;
8+
use Jdion84\Lucid\Console\MigrateSchemasCommand;
9+
10+
class LucidServiceProvider extends ServiceProvider
11+
{
12+
public function boot()
13+
{
14+
if ($this->app->runningInConsole()) {
15+
$this->commands([
16+
MakeSchemaCommand::class,
17+
MigrateSchemasCommand::class,
18+
]);
19+
}
20+
21+
Model::unguard();
22+
}
23+
}

0 commit comments

Comments
 (0)