How do I change the automatically generated migration page in Laravel? - php

When I genarate a migration in Laravel, it automatically looks like this:
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
But I want to work more convenient, I want to let the database handle when I create and update a row instead of doing it myself everytime.
I found a way to make this possible like this:
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
});
}
But now everytime I make a migration, I manually have to change this and I don't think that's very convenient. Does anybody know how I can change the automatically generated migration in Laravel?

If you dig around in the source code you will find that:
There's a file called create.stub in the framework.
This file is used by the MigrationCreator to make migrations.
In principle you can do the following:
Grab the built-in migration file and move it in another folder in your project (e.g. resouces/stubs probably) Note that you should copy the other stubs in that folder too even if you won't modify them.
Then, override the default migration creator to use this file instead, this should work:
class MyMigrationCreator extends MigrationCreator {
protected function stubPath() {
return base_path("resources"); //Or something valid
}
}
Then in your application service provider you can do:
$this->app->instance(MigrationCreator::class, resolve(MyMigrationCreator::class));
This will (hopefully) "trick" laravel into using your migration creator than the default one. However, creating tables is not something that happens so often to justify all this trouble.
Update: It should extend the migration creator.

In Laravel 5.6 there seems to be no possibility to override MigrationCreator class because it is used directly in MigrationServiceProvider:
protected function registerCreator() {
$this->app->singleton('migration.creator', function ($app) {
return new MigrationCreator($app['files']);
});
}
But you can hack MigrationServiceProvider next way:
Create directory in your project root: overrides/Illuminate/Database
Copy file vendor/laravel/framework/src/Illuminate/Database/MigrationServiceProvider.php into overrides/Illuminate/Database folder (note that class namespace will remain same)
Modify overrides/Illuminate/Database/MigrationServiceProvider.php:
protected function registerCreator() {
$this->app->singleton('migration.creator', function ($app) {
return new MyMigrationCreator($app['files']);
});
}
Modify your composer.json ("Illuminate\\"):
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/",
"Illuminate\\": "overrides/Illuminate"
}
},
Run composer dump-autoload. Composer will say
`Warning: Ambiguous class resolution, "Illuminate\Database\MigrationServiceProvider" was found in both "$baseDir . '/overrides/Illuminate/Database/MigrationServiceProvider.php" and "/vendor/laravel/framework/src/Illuminate\Database\MigrationServiceProvider.php", the first will be used.`
Now your fake MigrationServiceProvider will be used instead of Laravel's but you won't be able to use original one. That's why we copied whole file.
Actually this way you can override MigrationCreator class but amount of code there is high and you actually will need to extend it with your MyMigrationCreator class and override several methods like stubPath() and maybe create(). But MigrationServiceProvider can be quite safely overriden because it contains several small methods that are unlikely will be changed until Laravel 6

Related

Read-only: No corresponding table column in Laravel PHP artisan

I'm developing a web using Laravel 9. I used the command sail php make:migration add_bought_to_products_table to add a boolean column called "bought" in a products table. When trying to modify the value using Eloquent helpers (Product::where('id', $product->id)->update(array('bought'=>true)) the value is not updated in the database. When looking at it, I se that the new field "bought" created by the migration is marked as Read-only: No corresponding table column.
The migration code is the following:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::table('products', function (Blueprint $table) {
$table->boolean('bought');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('products', function (Blueprint $table) {
$table->dropColumn('bought');
});
}
};
Here a screenshot of the database:
I have already tried to clean the cache and rebuild many times the database doing a rollback and migrating again. The curious thing is that I previously added the field "visibility" which works perfectly with the exact same code and steps as the field that is giving the problem.
How can I solve it?
After breaking my head a lot, I solved it by simply doing a clean restart of the docker containers. It seems that the issue had nothing to do with Laravel but with Docker!
For anyone experiencing similar issues: make sure to end Docker and all containers and do a clean restart.
I simply restarted my database tool and it was gone.

Can you delete models referenced in migrations with foreignIdFor()?

I have an old migration where I used the foreignIdFor() method to create a column.
I later decided to delete the model which was referenced and now, when I do a migrate:refresh locally, that migration triggers an error saying that the model doesn't exist, of course.
So, should one never delete models referenced with foreignIdFor() and use unsignedBigInteger()instead ?
EDIT:
I know foreignIdFor and unsignedBigInteger will create the same column. When I say I deleted the model, I mean the model class, not a model.
My migration file :
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\Event;
use App\Models\OutlookCategory;
class CreateEventOutlookCategoryTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('event_outlook_category', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(Event::class);
$table->foreignIdFor(OutlookCategory::class);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('event_outlook_category');
}
}
The foreignIdFor method adds a {column}_id UNSIGNED BIGINT equivalent column for a given model class, so using unsignedBigInteger() doesn't make any difference.
Resources:
https://laravel.com/docs/9.x/migrations#column-method-foreignIdFor
https://laravel.com/docs/9.x/migrations#column-method-unsignedBigInteger
What you could/needed to do is to add cascade delete, and make sure that whenever associated model is deleted, all related models are deleted too.
You could do it like this:
table->foreignId('column_name_id')
->constrained()
->onDelete('cascade');
Resources:
https://laravel.com/docs/9.x/migrations#foreign-key-constraints
Answer to the edit:
If you delete Model that was used in foreignIdFor, that method will not be able to understand what you are referencing, and therefore it will fail. So, the answer to your question is YES and NO.
Let me elaborate. If your migrations will be run just once, on the production environment, for example, then you will be able to delete the model you've been referencing in the previous migrations and just create a new migration that will cleanup those columns.
In all other cases, when your migrations will be run for a few times (locally when you use migrate:fresh), you need to have Model that was referenced in them in your code base in order for it to work properly.
If you want to avoid these kind of problems that you are experiencing right now, just use unsignedBigInteger and pass to it string that is the name of the column, and you don't have to worry about deleting the model. However, you will still need to pay attention not to delete the column that is referenced there, because you will get another error for missing column.

Edit database column parameters from laravel (tinker)

So I have database with table, where I need to edit one column, make it nullable, to be specific. How can I access it from php artisan tinker, or maybe somehow re-run migration on one table without losing data from it?
With tinker you cannot modify schema table. You need to create a migration like this:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AlterTableUsers extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::table('users', function ($table) {
$table->string('name', 50)->nullable()->default(null)->change();
});
}
}
In this case, we make name nullable with default value null.
More info: https://laravel.com/docs/master/migrations#modifying-columns
You can just run the suggested code:
Schema::table('users', function ($table) {
$table->string('name', 50)->nullable()->default(null)->change();
});
directly from tinker
If you want to re-run migration or add new migration that alters the existing table depends on whether you are coding on production mode or development mode. Generally on development mode re-running migration may be best option(due to dummy data)..On other hand; on production mode, if the data is important ..we generally add the new migration that updates the existing table (because data may be important to us..)

How to run laravel migration and DB seeder except one

I am having many migration and seeder files to run, Although I will need to run all files but currently I need to skip one migration and seeder.
How could I skip one file from laravel migration and db seeder command.
I do not want to delete files from the migrations or seeds folder to skip the file.
Laravel doesn't give you a default method to do it. However, you can create your own console commands and seeder to achieve it.
Let's say you have this default DatabaseSeeder class:
class DatabaseSeeder extends Seeder
{
public function run()
{
$this->call(ExampleTableSeeder::class);
$this->call(UserSamplesTableSeeder::class);
}
}
the goal is to create a new command overriding "db:seed" and pass a new parameter, an "except" parameter, to the DatabaseSeeder class.
This is the final code, I created on my Laravel 5.2 instance and tried:
Command, put in app/Console/Commands, don't forget to update your Kernel.php:
namespace App\Console\Commands;
use Illuminate\Console\Command;
class SeedExcept extends Command
{
protected $signature = 'db:seed-except {--except=class name to jump}';
protected $description = 'Seed all except one';
public function handle()
{
$except = $this->option('except');
$seeder = new \DatabaseSeeder($except);
$seeder->run();
}
}
DatabaseSeeder
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
protected $except;
public function __construct($except = null) {
$this->except = $except;
}
public function call($class)
{
if ($class != $this->except)
{
echo "calling $class \n";
//parent::call($class); // uncomment this to execute after tests
}
}
public function run()
{
$this->call(ExampleTableSeeder::class);
$this->call(UserSamplesTableSeeder::class);
}
}
It the code, you'll find that I commented the line that calls the seed and added an echo for testing purposes.
Executing this command:
php artisan db:seed-except
will give you:
calling ExampleTableSeeder
calling UserSamplesTableSeeder
However, adding "except":
php artisan db:seed-except --except=ExampleTableSeeder
will give you
calling UserSamplesTableSeeder
This works overriding the default call method of your DatabaseSeeder class and calling the parent only if the name of the class is not in the $except variable. The variable is populated by the SeedExcept custom command.
Regarding migrations, the thing is similar but a little bit more difficult.
I can't give you tested code for this by now, but the thing is:
you create a migrate-except command that overrides the MigrateCommand class (namespace Illuminate\Database\Console\Migrations, located in vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php).
the MigrateCommand takes a Migrator object (namespace Illuminate\Database\Migrations, path vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php) in the constructor (injected via IoC). The Migrator class owns the logic that reads all the migrations inside the folder and execute it. This logic is inside the run() method
create a subclass of Migrator, for example MyMigrator, and override the run() method to skip the files passed with the special option
override the __construct() method of your MigrateExceptCommand and pass your MyMigrator: public function __construct(MyMigrator $migrator)
If I have time I'll add the code for an example before the bounty ends
EDIT
as promised, here's an example for migrations:
MyMigrator class, extends Migrator and contains the logic to skip files:
namespace App\Helpers;
use Illuminate\Database\Migrations\Migrator;
class MyMigrator extends Migrator
{
public $except = null;
// run() method copied from it's superclass adding the skip logic
public function run($path, array $options = [])
{
$this->notes = [];
$files = $this->getMigrationFiles($path);
// skip logic
// remove file from array
if (isset($this->except))
{
$index = array_search($this->except,$files);
if($index !== FALSE){
unset($files[$index]);
}
}
var_dump($files); // debug
$ran = $this->repository->getRan();
$migrations = array_diff($files, $ran);
$this->requireFiles($path, $migrations);
//$this->runMigrationList($migrations, $options); // commented for debugging purposes
}
}
The MigrateExcept custom command
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Database\Console\Migrations\MigrateCommand;
use App\Helpers\MyMigrator;
use Illuminate\Database\Migrations\Migrator;
use Symfony\Component\Console\Input\InputOption;
class MigrateExcept extends MigrateCommand
{
protected $name = 'migrate-except';
public function __construct(MyMigrator $migrator)
{
parent::__construct($migrator);
}
public function fire()
{
// set the "except" param, containing the name of the file to skip, on our custom migrator
$this->migrator->except = $this->option('except');
parent::fire();
}
// add the 'except' option to the command
protected function getOptions()
{
return [
['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'],
['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'],
['path', null, InputOption::VALUE_OPTIONAL, 'The path of migrations files to be executed.'],
['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run.'],
['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run.'],
['step', null, InputOption::VALUE_NONE, 'Force the migrations to be run so they can be rolled back individually.'],
['except', null, InputOption::VALUE_OPTIONAL, 'Files to jump'],
];
}
}
Last, you need to add this to a service provider to permit the Laravel IoC resolve the dependencies
namespace App\Providers;
use App\Helpers\MyMigrator;
use App\Console\Commands\MigrateExcept;
class CustomServiceProvider extends ServiceProvider
{
public function boot()
{
parent::boot($events);
$this->app->bind('Illuminate\Database\Migrations\MigrationRepositoryInterface', 'migration.repository');
$this->app->bind('Illuminate\Database\ConnectionResolverInterface', 'Illuminate\Database\DatabaseManager');
$this->app->singleton('MyMigrator', function ($app) {
$repository = $app['migration.repository'];
return new MyMigrator($repository, $app['db'], $app['files']);
});
}
}
Don't forget to add Commands\MigrateExcept::class in the Kernel.php
Now, if you execute
php artisan migrate-except
you have:
array(70) {
[0] =>
string(43) "2014_04_24_110151_create_oauth_scopes_table"
[1] =>
string(43) "2014_04_24_110304_create_oauth_grants_table"
[2] =>
string(49) "2014_04_24_110403_create_oauth_grant_scopes_table"
...
but adding the except param:
php artisan migrate-except --except=2014_04_24_110151_create_oauth_scopes_table
array(69) {
[1] =>
string(43) "2014_04_24_110304_create_oauth_grants_table"
[2] =>
string(49) "2014_04_24_110403_create_oauth_grant_scopes_table"
So, recap:
we create a custom migrate-except command, MigrateExcept class, extending MigrateCommand
we create a custom migrator class, MyMigrator, extending the behavior of the standard Migrator
when MigrateExcept is fire(), pass the name of the file to skip to our MyMigrator class
MyMigrator overrides the run() method of Migrator and skip the passed migration
More: since we need to instruct Laravel IoC about the new created classes, so it can inject them correctly, we create a Service Provider
The code is tested so it should work correctly on Laravel 5.2 (hoping that cut&paste worked correctly :-) ...if anyone has any doubt leave a comment
Skipping seeds are very simple, migrations not so much. To skip a seed, remove the following from your DatabaseSeeder class.
$this->call(TableYouDontWantToSeed::class);
For migrations, There are three ways you can do it:
Put the class you don't want to migrate into a different folder.
Insert your migrations into the database manually (Bindesh Pandya's answer elaborated).
Rename the file that you don't want to migrate to something like UsersTableMigration.dud.
Hope this helps
I also faced the same problem in my project but after long time wasting in R & D i have found that Laravel does not provide any way to do this with migration and seeding but you have 2 ways to do this.
1) you'll save a lot of time just putting them into different folders.
You could theoretically make your own artisan command that does what
you want, or spoofs its by making directories, moving files, and running
php artisan migrate.
For the seeders, just make a seeder and call the others seeders you want to run from with in it. Then just be explicit about what seeder you want to run. Try php artisan db:seed --help for more details there.
2) you can create a table Manually (which has same name as migration table is creating in you db) and insert the values of migration like this
insert into migrations(migration, batch) values('2015_12_08_134409_create_tables_script',1);
so migrate command will not create table which is already exist in migration table.
If you want just omit (but keep) migration and seeder:
Rename your migration by removing .php extension: mv your_migration_file.php your_migration_file
Go to: DatabaseSeeder.php and comment out line with your unwanted seeder: //$this->call('YourSeeder');.
Run: php artisan migrate --seed
Execute below sql query on db (be careful, there should be migration file name WITHOUT extension) (this will prevent artisan migrate to execute your_migration_file in future):
INSERT INTO migrations (migration, batch) VALUES (your_migration_file, 1)
Rename back your migration file: mv your_migration_file your_migration_file.php
Uncomment your seeder in DatabaseSeeder.php
And you are done. Now when you run php artisan migrate any migration should be executed (except new one if you add some new migration files).
just an idea comment seeder and schema. this is the way i guess
//$this->call(HvAccountsSeeder::class);
//Schema::create('users', function (Blueprint $table) {
// $table->increments('id');
// $table->string('name');
// $table->string('email')->unique();
// $table->string('password');
// $table->rememberToken();
// $table->timestamps();
// });
// Schema::drop('users');
To directly answer your question, Laravel does not have a way to do this currently.
If I understand you correctly, I assume you're looking for a way to temporarily disable/skip a specific class from the default DatabaseSeeder.
You can easily create your own command which will accept a string such as a model/table name and attempt to run the migration and seed for that particular table. You will simply need something like the following:
public function handle(){ //fire for Laravel 4.*
$tables = explode(',', $this->option('tables'));//default []
$skip = explode(',', $this->option('skip'));//default []
$migrations = glob("*table*.php");//get all migrations
foreach($migrations as $migrate){
//if tables argument is set, check to see if part of tables
//if file name not like any in skip.. you get the point

Laravel 4 Migration Error

I'm getting the following error when trying to create migrations for my laravel 4 installation. The file gets created but it outputs the following error.
Created Migration: 2014_07_06_073213_create-users-table
Generating optimized class loader
Compiling common classes
{"error":
{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Class 'ClassPreloader\\Command\\PreCompileCommand' not found","file":"\/home\/name123\/domain.com\/laravel\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/Console\/OptimizeCommand.php","line":113}}[warehouse]
$ php artisan migrate:make create-users-table
It doesn't seem that there are any other people having the same problem.
Also getting this error when i commit the migration
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Call to a member function increments() on a non-object","file":"\/home\/dandel26\/danieldelcore.com\/laravel\/app\/database\/migrations\/2014_06_29_092641_create_users_table.php","line":15}}
Thanks in advance.
In you're migration you have the function up. In this function there will be something like:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
});
It would seem that you did not do define the callback with the variable $table. Please add Blueprint $table and it should work.
In that case remember to import the right namespaces:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

Categories