Laravel PHPUnit failing on ALTER TABLE using SQLite - php

I have a migration which I made at the beginning of my project, basically adding a TEXT column called 'description' which is set to NOT NULL.
Now several months down the track I need to change that to allow null.
I can't use Laravel 5.5 change() function as I have a enum in my column list and it bugs out, so i need to add it as a raw query in a migration like so;
DB::statement('ALTER TABLE `galleries` MODIFY `description` TEXT NULL;');
When i do a php artisan migrate against my local mysql database it all works great, BUT when i try to run my test suite, it all breaks.
Im using SQLite for my test suite, and the error im getting is as follows;
PDOException: SQLSTATE[HY000]: General error: 1 near "MODIFY": syntax error
If anyone else has come up against this issue and fixed it, i would love to hear how you did it.
Thanks

SQLite only allows you to rename the table or add a column. The ALTER TABLE statement cannot change or remove columns.
In order to change or remove a column in SQLite, you need to create a new table with the desired schema, copy the data from the original table to the new table, delete the original table, and then rename the new table to the original name.
This is all abstracted out for you by Laravel and DBAL, so your best bet may be to get help with figuring out the issue with your enum column (though that would be a separate question).
You can read more about altering tables in the SQLite docs here.

Related

When a new db is imported in a Laravel project it can't run new migrations properly

I recently imported new db to my local server and now when I created a new table and tried to migrate, it shows
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '3' for key 'migrations.PRIMARY' (SQL: insert into migrations (migration, batch) values (2022_10_17_071124_create_team_histories_table, 11))
So, basically the system has started inserting the migration table id(pk) from 1 but right now my migration table has 39 rows(1-39 filled up). As a result when I run php artisan migrate it migrates the table but doesn't add the table record in the migrations table, thus any further migrations will fail with a error saying "the last_table_name already exists"
Now one way to solve this problem is to migrate the table then drop it and keep doing it until the system tries to insert 40 as the id of migration table.
So, I have have to migrate and drop my table 39 times to get there. This trick may work for very small db, but when it gets larger it's not really a solution rather it's another problem :v.
Any efficient solution will be a big help. Thanks!
Your scenario is not very clear.
Manage application base data
If your application need seeders for some table (ie populating a table that contains basic data), make seeders part of the migrations.
Data Import from another application
If you need to do a complete data import from another application, then do a complete database dump including migration table.
mysqldump --opt
With this option, mysqldump will include in your dump also the table creation and drop table statement
Then after data import, run migrations.
Migrations will run only if the codebase you are using has more recent migrations than the application from where you have dumped data
None of the above is an option
If this is not an option, maybe you can't do a dump again, you can try to fix the counter column by running
alter table migration AUTO_INCREMENT=39;
Of course adjust the 39 to your need

PHP create a copy command like phpmyadmin

I am new with PHP development and just wondering if theres a existing function on PHP than duplicate the copy command on phpmyadmin, i know that the query sequence is below, but this is like a long query/code since the table has alot of columns. i mean if phpmyadmin has this feature maybe its calling a build in function?
SELECT * FROM table where id = X
INSERT INTO table (XXX)VALUES(XXX)
Where the information is based from the SELECT query
Note: The id is primary and auto increment.
Here is the copy command on phpmyadmin
i mean if phpmyadmin has this feature maybe its calling a build in function?
There is no built-in functionality in MySQL to duplicate a row other than an INSERT statement of the form: INSERT INTO tableName ( columns-specification ) SELECT columns-specification FROM tableName WHERE primaryKeyColumns = primaryKeyValue.
The problem is you need to know the names of the columns beforehand, you also need to exclude auto_increment columns, as well as primary-key columns, and know how to come up with "smart defaults" for non-auto_increment primary key columns, especially composite keys. You'll also need to consider if any triggers should be executed too - and how to handle any constraints and indexes that may be designed to prevent duplicate values that a "copy" operation might introduce.
You can still do it in PHP, or even pure-MySQL (inside a sproc, using Dynamic SQL) but you'll need to query information_schema to get metadata about your database - which may be more trouble than it's worth.

How to add a column after another column in sqlite using Laravel migrations?

Is there a method in Laravel 5.4 to add a column after/before another column inside a sqlite table? Like after('column') for MySql?
Why I'm in a hurry is because whenever I tried to rename a column using Laravel migration, it jumps and stick to the last / end of a table!
Is there a way to get rid out of this?
Short answer: no.
However there is apparently a workaround:
Insert new column into table in sqlite?
Disclaimer: I rarely use sqlite and cannot verify this works. Good luck.

Renaming a column in a migration script in Doctrine2

When I have a Table reference and I call renameColumn() on it, I get the following pair of errors:
Migration 20130725141653 failed during Execution. Error Table#renameColumn() was removed, because it drops and recreates the column instead. There is no fix available, because a schema diff cannot reliably detect if a column was renamed or one column was created and another one dropped.
[Doctrine\DBAL\DBALException]
Table#renameColumn() was removed, because it drops and recreates the column instead. There is no fix available, because a schema diff cannot reliably detect if a column was renamed or one column was created and another one dropped.
(executed via doctrine migrations:migrate --dry-run)
Which makes sense... for a diff program.
I'm writing a migration. I know that I want the column renamed (preserving data).
Is there any pure Doctrine method (I don't want to write a raw query) to acheive this?
The 'diff' that the exception is talking about is the diff between your original Schema and your updated Schema. This is how Doctrine generates the SQL statements -- by comparing the current Schema to the modified Schema.
There is no way for Doctrine to reliably figure out that someColumn in $originalTable is now someRenamedColumn in $modifiedTable (pardon the bad psuedocode) and so the functionality was removed .

Create Table Else Alter Table

This seems to be a simple problem, but after a while of searching I can't figure out the answer.
I currently have a MySQL table in my local database used by a webapp, and them same table on a database in a remote server. Right now, I'm using the CREATE TABLE IF NOT EXISTS command through PHP to create the table on the databases:
CREATE TABLE IF NOT EXISTS users (
`id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(18) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
However, let's say I make a modification to the local database, adding a collumn, for example. It would be really annoying to have to go and change the remote database every time I change the local one. Is there an easier way to run code to create a table if it doesn't exist, and if it does exist, make sure it's structure matches that of the create table structure?
Here's an example, to make what I'm trying to convey a little clearer. Let's say on the local database I have a users table, and I decide that in my webapp I want to have another collumn, password. So I go to the local database and add a password collumn. Is there PHP/MySQL code I can run to check if the users table exists, and if it does, make sure it has a password collumn, and if not, add it?
What you are actually looking for are Migrations, e.g. you are looking for a Schema Management Tool that lets you manage your Database structure in versioned code diffs.
For instance, for your described scenario you would first create a script to create the table, e.g. 001_create_user_table.sql. Then you'd use the schema manager to connect and deploy these changes to your databases.
When you want to change or add something, you just write another script, for instance, 002_Add_Password_Column_To_User_Table.sql. Fill in just the code to do that change. Then run the schema manager again.
Typically, you tell the Schema Manager to go through all existing migrations files. On each run, the Schema manager will update a changelog table in the database, so when you run it, it will know which of your scripts it should apply.
The good thing is, you can add these migrations to your regular VCS, so you will always know which database schema you had at which version of your application. And you will have a proper changelog for them.
To directly answer your question you can create temporary procedures to detect field existence like using a query like this:
SHOW COLUMNS FROM table_name LIKE 'column_name';
However in the real world, database changes are general rolled into three scripts. A create script and two deltas one up and one down. Then the database is versioned so that you know at what state the database is in at any given time.
To specifically check for a password column you can use DESCRIBE:
$colExists = false;
$res = mysql_query('DESCRIBE `users`');
while ($row = mysql_fetch_assoc($res)) {
if ($row['Field'] == 'password') {
$colExists = true;
break;
}
}
if (!$colExists) {
// create column
}
However, you should check into replication or some other automated tool to see if they would be a better solution for you.
Follow these steps (you can easily implement this in PHP, I assumed that the name of the table is Foo)
1.) Run the following code:
desc Foo
2.) Based on the result of the first step you can make your create table command (and you should)
3.) Store your data from the existing table which will be replaced in a variable (Optional, you only need this if you can potentially use data from the old table)
4.) Modify the extracted rows from step 3.) so they will be compatible with your new definition (Optional, you only need this if you can potentially use data from the old table)
5.) Get the rows from your new Foo table
6.) Merge the results got in steps 4.) an 5.) (Optional, you only need this if you can potentially use data from the old table)
7.) Run a drop table for the old table
8.) Generate a replace into command to insert all your rows into the newly created Foo table (you can read more about this here)
After these steps, as a result, you will have the new version of the table. If your tables are too large, you can do a CREATE TABLE IF NOT EXISTS command and if that was not successful, run the alter command.
Also, you can make a library to do these steps and will use that in the future instead of solving the same problem several times.
EDIT:
You can connect the database using this function: mysql-connect (documentation here)
You can run a query using this function: mysql-query (documentation here)
Based on the first step you will get the field names (let's assume you store it in a variable called $bar) and you can use your result to generate your select command (connecting to the database where you have important data. It may be both):
$field_list = "1";
foreach ($bar as $key => $value)
$field_list.= ",".$bar[$key];
mysql_connect(/*connection data*/);
mysql_query("select ".$field_list." from Foo");
You can use your new resource to build up an insert command to insert all your important data after deletion recreation (about resources read more here, about how you can generate your insert you can read here, but I suggest that you should use replace into instead of insert which works like the insert, except that it replaces the row if it already exists, it's better here than an insert, read more here)
So, use mysql_connect and mysql_query, and the resource returned by the mysql_query function can be used for replace into later (I've linked now the URL's for everything you need, so I'm pretty sure you'll solve the problem.), apologies for being not specific enough before.

Categories