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.
Related
I created a migration file using
php artisan make:migration create_todoapps_table.
The migration file was created with the table name as to_do_apps
(Schema::create('to_do_apps', function (Blueprint $table) {...}).
But I don't want the table name to be to_do_apps .
So I tried to change it to todoapps manually
(Schema::create('todoapps', function (Blueprint $table) {...}).
When doing migration this works fine and table with todoapps is getting created. But when doing the factory and seed, it is trying to add data to the old table name to_do_apps itself, which is causing an error because, that table is not available.
Is there a way to overcome this issue?
How can I change the tablename in migration file without breaking anything?
The name of the table to be used for a model comes from the model itself, not from migrations.
Laravel assumes that the name of the table for a model is the snake_case of the model name. If you want to use the table todoapps, just add the following line to the model that should read data from that table (probably your model is called ToDoApps)
protected $table = 'todoapps';
You can set the table name in the Eloquent model if its not in the default naming convention. Below is from the Laravel Docs
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'my_flights';
}
The seed which is using the model will then use the correct table name.
I just started to work on a laravel project for my school assignment. I just have started it for about a week so my fundamental knowledge about laravel is not complete.
Today I bump into a problem with model many to many relationship in laravel. I create 2 model with migration A and B. In App\A.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class A extends Model
{
//
public function B(){
return $this->belongsToMany('App\B');
}
}
and in App\B.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class B extends Model
{
//
public function A(){
return $this->belongsToMany('App\A');
}
}
I think it should do the job. But when I use seeder to create dummy data, I got the error with is that table A_B is not created. I assume that I must create empty table A_B for 2 pivot columns which is annoying. Is there a better way, a proper way to create many to many relationship without manually create pivot table for them?
I'm afraid not. There are shortcuts to doing it, like the way they show here but you will end up doing some manual work to create the table. You will end up using a migration anyway, but depending on the amount of control you want with the pivot, you might want to use a model for that. Using a model for a pivot table is not mandatory.
https://laravel.com/docs/5.5/migrations#creating-tables
This could be what your migration would end up looking like:
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateABTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('A_B', function (Blueprint $table) {
$table->increments('id');
$table->integer('a_id')->unsigned();
$table->integer('b_id')->unsigned();
$table->foreign('a_id')->references('id')->on('a')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('A_B');
}
}
But if you really wanted to, you could also create a custom table (with a custom name like ABRandomName) using a new model. Just look for the Defining Custom Intermediate Table Models header in the documentation:
https://laravel.com/docs/5.5/eloquent-relationships#many-to-many
I would like to rename a table in the database from topics to galleries and I have created a migration that will rename my table.
Schema::rename('foo', 'bar');
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class RenameTopicsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
//
Schema::rename('topics', 'galleries');
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
//
Schema::rename('galleries', 'topics');
}
}
However will the Topic Model and Topic Controller be automatically renamed? Or will I have to refactor my code? Does Laravel provide a way to do this easily?
In short my question is - How do you change your schema easily in laravel? (models/controllers/database/requests/transformers ect..)
To answer this question. Laravel does not provide a out-of-the-box way to rename your tables at the same time as your Models/Controllers.
You must manually refactor your code after you change your database schema.
An example of this is lets say i have a posts table and i want to rename it to blogs. Well my posts model and posts controller wont be helpful after i update the schema so i will need to change those over as well. Routes will need to be updated. If I am using views those will need to be updated. In my case i was using transformers and requests so those need to be manually updated.
If you can, try to avoid changing your schema :D
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..)
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