I'm fairly new to Doctrine Migrations so apologies if this is really obvious.
Updated to provide more information
I have a Symfony2 entity mapping as below:
/**
* #ORM\Column(type="string")
*/
private $name;
This was added to a migration and deployed with everything working as it should. The column then needed to be updated to accept null values so it was changed to:
/**
* #ORM\Column(type="string", nullable=true)
*/
private $name;
The problem is that this has no impact on the resulting migration file:
$ php app/console cache:clear
$ php app/console doctrine:migrations:diff
$ tail -50 app/DoctrineMigrations/Version20131028205742.php
<?php
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20131028205742 extends AbstractMigration
{
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != "mysql", "Migration can only be executed safely on 'mysql'.");
}
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != "mysql", "Migration can only be executed safely on 'mysql'.");
}
}
No ALTER statements have been added to handle null values. I could drop and rebuild the database but as this specific migration has already been deployed this will result in problems in the production database. I can see this situation occurring again in the future so would like to understand it a bit more.
Is this a limitation of Doctrine and/or the Symfony2 Doctrine Migrations bundle? Is there a way to get around this without writing a custom migration?
Please note, all my other migrations are working fine, the only issue is adding nullable options to existing fields.
Doctrine's diff works in that way it compares your entities metadata with existing database schema. So if you run doctrine:schema:update after you've ran the migration - doctrine doesn't notice any change because db schema is already changed (and therefore fits to entites metadata). And basically after you run the migration you don't need to run update anymore.
Related
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.
Is there a way how I can change the migrations order without remaking them all? Because now I have a problem with my foreign keys -_- (working with laravel)
Roll back all the migrations (or start with a fresh database);
Change the dates that form the first part of the migration filenames so they're in the order you want (eg. for 2014_06_24_134109_update_database.php, the date & time is 2014-06-24, 13:41:09);
Run the migrations again.
With respect to your comment about foreign keys... I'm not sure that the problem is with Laravel. More likely, it's just MySQL.
I avoid foreign keys because once you get a moderately complicated set of relations, you start to run into problems with database consistency like you're seeing - it's hard for the server to figure out what order to create the tables & relationships in, and it starts to cause difficulties with things like dump files (for backups).
You have to create a custom command that executes
php artisan migrate:refresh --path=/database/migrations/name_migration.php repeately with the migrations's name in the order you want.
Like this:
Create Command class with: php artisan make:command NameClass
Go to app/Console/Commands/ and find the class file NameClass.php
In the NameClass.php you have two attributes $signature (the name of the command) and $description (Information about what your command does).
Set the name and the description of your command.Ex: protected $signature='namecommand'; protected $descripton = 'This method migrate tables in order'
Inside the NameClass.php you have a method called handle(), here you have to declare the code you want to be executed when you write the command.
Register your command. Go to app/Console/Kernel.php and add your class to the list of Command Classes.
protected $commands = [
Commands\NameClass::class,
];
Write the command in the terminal. php artisan namecommand
Example:
php artisan make:command MigrateInOrder
app/Console/Commands/MigrateInOrder.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class MigrateInOrder extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'migrate_in_order';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Execute the migrations in the order specified in the file app/Console/Comands/MigrateInOrder.php \n Drop all the table in db before execute the command.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
/** Specify the names of the migrations files in the order you want to
* loaded
* $migrations =[
* 'xxxx_xx_xx_000000_create_nameTable_table.php',
* ];
*/
$migrations = [
'2020_04_18_005024_create_users_types.php',
'2014_10_12_000000_create_users_table.php',
'2014_10_12_100000_create_password_resets_table.php',
'2019_08_19_000000_create_failed_jobs_table.php'
];
foreach($migrations as $migration)
{
$basePath = 'database/migrations/';
$migrationName = trim($migration);
$path = $basePath.$migrationName;
$this->call('migrate:refresh', [
'--path' => $path ,
]);
}
}
}
Go to app/Console/Kernel.php and register your command
protected $commands = [
Commands\MigrateInOrder::class,
];
Excute the command
php artisan migrate_in_order
Taking inspiration from PhpMyAdmin, I put all foreign keys definitions in a specific far future file, eg : 2999_12_31_235959_foreign_key.php
<?php
use App\Models\Post;
use App\Models\Tag;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ForeignKeys extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
// Post_tag
Schema::table(Post::NOM, function (Blueprint $table) {
$table->foreign('id_post')
->references('id_post')
->on(Post::NOM);
$table->foreign('id_tag')
->references('id_tag')
->on(Tag::NOM);
});
}
}
The only con I see is not having foreign keys definition in migration.
For the pros :
Keeping database relations
Do not care of table creation order
The best and easiest thing would be to just rename the migration yyyy_mm_dd_hhmmss_migration_name. If your migration follows this sequence, Laravel will ensure to run the migration is sorted form of date,
Building on the answer of Galeokerdo which suggests creating a separate migration file for the foreign keys, and putting the date in the far future, I tried it and it worked great. But then I started thinking of the rollback. It turned out that Laravel takes the reverse order when rolling back migrations. That is, the latest file is rolled back first.
Since the rollback will not work with the foreign key constraints in place, I tried putting my foreign-key-removal code in the "down" method of the separate foreign key migration, having found out that the file will execute first before all other migration files. Like thus:
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('tablename', function (Blueprint $table) {
$table->dropForeign('tablename_foreignkey_foreign');
});
}
"tablename_foreignkey_foreign" is the name of the foreign key constraint. By default, it is
"nameofthetable_foreignkeycolumn_foreign"
I just wanted to share this in case anybody is struggling with it the way I did.
You only need to change migrations order. if bands or stage tables are below users table, MySQL don't find the references. =)
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..)
I'm growing desperate, I need help! I've been at this all day!
I want to configure a DB in Laravel. I have all my migrations (in order) so no foreign key will conflict and hit a table that would only be created later.
But no matter what I try I keep hitting an error that says this,
http://prntscr.com/6z23nw
EDIT: now it says the exact same thing but that the class was 'not found'... I'm totally clueless here....
A bit more information, (I was going to post a screenshot but I can't)... searching for usages of GroupUser all I can find is the one I posted and entries in the laravel.log (listed as non-code usage). So there really is no other "groupUser" class
Here's a screenshot of my migrations folder.
These are my migrations they run in order and all tables until GroupUser are properly created
the migration that's causing all this mess
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class GroupUser extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('group_user', function(Blueprint $table)
{
$table->integer('user_id')->unsigned();
$table->integer('group_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('group_id')->references('id')->on('groups');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('group_user');
}}
One final note I completely erased all the previous migrations and even tried reinstalling with the php artisan migrate:install
Run composer dump-autoload. After creating the migration files via artisan, you need to re-run composer to have them be autoloaded automatically.
Solved the issue,
. migrate:reset to set everything from blank
. created migrations with the tables but no foreign keys and dependencies
. php artisan migrate (all worked out fine)
. created new migrations altering the tables required with the foreign keys.
. again 'php artisan migrate'
I think it all worked fine. Thank you for your help and ideas everyone!
Is it possible to copy data from old table into new table instead of rename? We are planning a major database schema upgrade and would like to preserve current data tables, so the migration down() can be as simple as dropping newly created tables.
we realize this breaks backward compatibility as migrate:rollback doesn't really rollback any new data into previous state; but enabling such thing will be very costly due to the scale of schema update, we are content with a simple 1-way migration, as long as it preserves old tables.
Can this be done within Laravel's migration and schema alone?
Thanks to suggestion from #TonyArra and #Fractaliste, we now do something like following, this allow us to test run migration and rollback without worrying about data lost.
use Illuminate\Database\Migrations\Migration;
use MyNewModel;
class DataConvert extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
foreach(MyOldModel::all() as $item)
{
MyNewModel::create(array(...));
}
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
MyNewModel::truncate();
}
}
As far as I know, there's no copy function in Laravel to do this, but it's fairly easy with models. For example, if you wanted to move data from the table users to newusers, you could add the following to the NewusersTableSeeder run function:
$users = User::all()->toArray();
foreach ($users as $user) {
$user['newField'] = "data";
Newuser::create($user);
}
(recommend that this be done in the seeder, as you should already have Eloquent::unguard(); in DatabaseSeeder.php)
Into the down() function and just before dropping your tables, I think you can perform an export of your data.
The basic use of migration is to create/drop tables. But nothing prevents you to make a more complex one. And the artisan tool provides access to any Laravel's functionality (except network one's like Input or Cookies I think)
Not sure it helps but i wrote a small class that helps copying data between two databases with totally different structure according to rules you provide on a xml file, see https://github.com/Binternet/redb
So maybe you can fire this up when you finish the last migration