How to add a constraint to a table with Laravel's migrations? - laravel

Let's say I have a table periods with columns start_date and end_date and I want to add a constraint start_date < end_date.
The PostgreSQL query would be:
ALTER TABLE periods ADD CONSTRAINT check_dates CHECK ("start_date" < "end_date");
But I want to do it as a migration with PHP. I guess it will look something like this:
Schema::table('periods', function (Blueprint $table) { $table->something(); });
...but even with IntelliSense I couldn't guess what to write instead of "something()".
I'm not using the newest version of Laravel so it would be nice if you add from which version your code works and what can you do in older versions.

You can run some raw SQL like below as constraints are still not supported by the Blueprint class.
public function up()
{
DB::statement('ALTER TABLE periods ADD CONSTRAINT check_dates CHECK ("start_date" < "end_date")');
}

Related

How can I set index for json column in mysql 8 with laravel migration

I'm creating a project with laravel 6. One of my table column type is json.
The data format in the table column is like this:{age:30, gender:male, nation:china,...}. I am wondering if there is a way for me to set index for this column with laravel migration. my database version is mysql 8.0.21.
thank you!
I found this article very helpful for figuring this out. So for your example structure above, you might have a migration that looks like the following:
public function up(){
Schema::create('my_table', function(Blueprint $table){
$table->bigIncrements('id');
$table->json('my_json_col')->nullable();
$table->timestamps();
// add stored columns with an index
// index in this is optional, but recommended if you will be filtering/sorting on these columns
$table->unsignedInteger('age')->storedAs('JSON_UNQUOTE(my_json_col->>"$.age")')->index();
$table->string('gender')->storedAs('JSON_UNQUOTE(my_json_col->>"$.gender")')->index();
$table->string('nation')->storedAs('JSON_UNQUOTE(my_json_col->>"$.nation")')->index();
});
}
And this is equivalent to the following mysql statement:
create table my_table
(
id bigint unsigned auto_increment primary key,
my_json_col json null,
created_at timestamp null,
updated_at timestamp null,
age int unsigned as (json_unquote(json_unquote(json_extract(`my_json_col`, _utf8mb4'$.age')))) stored,
gender varchar(255) as (json_unquote(json_unquote(json_extract(`my_json_col`, _utf8mb4'$.gender')))) stored,
nation varchar(255) as (json_unquote(json_unquote(json_extract(`my_json_col`, _utf8mb4'$.nation')))) stored
)
collate = utf8mb4_unicode_ci;
create index my_table_age_index
on my_table (age);
create index my_table_gender_index
on my_table (gender);
create index my_table_nation_index
on my_table (nation);
And a simple view of the table after creation:
This example created actual stored columns, which for this scenario i think is what you would want. But you can also make virtual columns, which are created at query time instead of persistent columns, and you would just use the virtualAs function instead of the storedAs function in the migration.
These functions are documented in the Column Modifiers section of the Laravel migration docs, but it doesn't go into detail on JSON columns, this requires a bit more mysql knowledge.
I also found this article helpful for the mysql side of things for the JSON columns (SemiSQL).

Why I can't do a migration with constraint foreign key

for a homework i have to do a todolist in laravel 7 i do my db but when i try to create a foreign key to the table task
and a table categories
the program fail with the formula :"SQLSTATE[HY000]: General error: 1824 Failed to open the referenced table 'categories' (SQL: alter table tasks add constraint tasks_category_id_foreign foreign key (categ
ory_id) references categories (id) on delete cascade)"
i've done it a lot of time but i don't understand why i doesn't work if someone could explains
the right way to do it
The migration is trying to create the tasks table before categories, the categories table still doesn't exist at the time it tries to create the FK.
Swap the order of your migrations changing its filename. Each migration file at database/migrations has a date-time before the name. Alter it so the tasks migration has a date greater than the categories one.
will Gus Costa is right and you should do as he says but i want to add that your
foreign key should be unsigned
and here you will find some good answers
"General error: 1005 Can't create table" Using Laravel Schema Build and Foreign Keys
// CREATING TABLE
Schema::create('table_name', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->integer('field_id')->unsigned; //for field that contains foreign key constraint
});
// FOREIGN KEY CONSTRAINT
Schema::table('stock', function ($table) {
$table->foreign('field_id')->references('id')->on('some_table')->onDelete('cascade')->onUpdate('cascade');
});

Laravel auto-increment on non-primary field

There is my migration. I want id field being auto-incremented but not primary. Is it possible? This migration throws an exception Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key
Schema::create('tests', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('project_id');
$table->unsignedInteger('model_id');
$table->timestamps();
$table->dropPrimary('id');
$table->foreign('project_id')
->references('id')
->on('projects')
->onDelete('cascade');
$table->primary(['project_id', 'model_id']);
});
This is not Laravel error. you can't have two auto increment column in mysql table. (of course if you are using mysql) but I think none of any relational database gives you ability to have two auto increment columns.
But there is another way to do that.
look at this question
This is not a Laravel error but a MySQL error. As the error message says: it must be defined as a key. You are dropping the primaryKey constraint on the id column. You should try setting it as an index.
try the following $table->unsignedInteger('id')->index()->autoIncrement();. This will make it an AI integer and also an index but not a PK.

foreign key constraint: cannot drop table because other objects depend on it

I'm running migrations on Laravel 5.1 and am switching DBs from Mysql to Postgres.
Typically I could set foreign key checks to 0 prior to running down migrations as such:
- DB::statement('SET FOREIGN_KEY_CHECKS = 0');
- Do stuff
- DB::statement('SET FOREIGN_KEY_CHECKS = 1');
Postgres does not offer this.
In running down migrations, I get error:
Dependent objects still exist: 7 ERROR: cannot drop table table2 because other objects depend on it
DETAIL: constraint table1_table2_table1_id_foreign on table table1_table2 depends on table table2
HINT: Use DROP ... CASCADE to drop the dependent objects too. (SQL: drop table "table2")
Question: This complaint is curious to me as I set ->onDelete('cascade'); on the foreign key creations. Why is this happening?
Snippets:
Create Table1 Table:
...
public function down()
{
Schema::drop('table1_table2');
Schema::drop('table1');
}
Create Table2 Table (Called after table 1 migration):
...
public function down()
{
Schema::drop('table2');
}
Create Foreign Keys Table (last migration to be called)
public function up()
{
Schema::table('table1_table2', function(Blueprint $table)
{
$table->foreign('table1_id')->references('id')->on('table1')->onDelete('cascade');
$table->foreign('table2_id')->references('id')->on('table2')->onDelete('cascade');
});
...
}
public function down()
{
...
Schema::table('table1_table2', function(Blueprint $table)
{
$table->dropForeign('table1_id');
$table->dropForeign('table2_id');
});
...
}
This complaint is curious to me as I set ->onDelete('cascade'); on the foreign key creations. Why is this happening?
The key term here is "on delete" - when you delete a row from one table, that option will determine whether rows with foreign keys referencing that row will be deleted as well.
However, your change script is not deleting rows, it is dropping the table. This is therefore a different event, not effected by the ON DELETE option on the foreign key.
The CASCADE mentioned in the hint is a keyword on the DROP TABLE statement, discussed in the manual under "Dependency Tracking":
Key quotes:
When you create complex database structures involving many tables with foreign key constraints, views, triggers, functions, etc. you implicitly create a net of dependencies between the objects. For instance, a table with a foreign key constraint depends on the table it references.
and:
if you do not want to bother deleting all the dependent objects individually, you can run DROP TABLE products CASCADE; and all the dependent objects will be removed, as will any objects that depend on them, recursively. In this case, it doesn't remove the orders table, it only removes the foreign key constraint.
and:
Almost all DROP commands in PostgreSQL support specifying CASCADE.
For my case; you can get this error with this scenario;
If your table seems rollbacked in migration table (ex: maybe forget drop function first time) but table still exists on database you can get this error. migrate:fresh command will fail with given error message for this scenario.
You can drop table manually or add a row to migration table with that migration name and everything will be working normally.

laravel 5: soft delete

I added deleted_at filed in tables and the default value is 0000-00-00 00:00:00(which is must in mysql),now using User::first() can not get the value when deleted_at = 0000-00-00 00:00:00. any idea?
When you call the delete method on the model, the deleted_at column will be set to the current date and time. And, when querying a model that uses soft deletes, the soft deleted models will automatically be excluded from all query results.
To make your code working set deleted_at field to null.
To delete the model you can use below logic
$deletedRows = App\Flight::where('active', 0)->delete();
Reference http://laravel.com/docs/5.1/eloquent#soft-deleting
How did you add the column?
If you use Laravels Schema within a migrations there is a softDeletes() column which defaults to null (you could also do this in your database directly of course).
You can only get the value of record has deleted_at = NULL otherwise row cannot be retrieved. If you want to get the desired row, just set the deleted_at = NULL
You should use softDelete like the code below, it will automatically create proper column with proper default value.
Schema::table('table_name', function(Blueprint $table)
{
$table->softDeletes();
});

Resources