Hello everyone.
For the first five years of my career as a programmer, I worked on an in-house project, for the next seven years I worked in various startups, with a maximum of five developers.
Now I have been working for a couple of months on a project with 20+ developers, work is simultaneously carried out in about 30 branches, there are five environments for code development (draft, dev, testing, hotfix, prod), each environment has its own database (before rolling out the kamit to stand / environment, a test rollout takes place using a separate database, that is, for five environments we have 10 separate databases).
It's not new to me to develop in multiple branches, I've always done it. The discovery for me was that the version of the code and the version of the database schema are not synchronized in any way. In a small project, it is not a problem to drop the entire scheme and roll it in its entirety, it takes a few minutes; in this project, rolling a scheme from scratch with seeding takes from an hour.
There is a big problem with how to synchronize the code version and the database schema version.
Below I will tell you about the rules that I have accepted for myself and I will be glad if you share your techniques and techniques that help you cope with this disaster.
Disclaimer
The code presented below is an obfuscated combat code, I have not debugged it, it may need to be modified with a file. I only share ideas with you.
Description of the problem
, , , - -. .
, - , , , , , ? , ?
, , . , , , . , .
.
: " "
, . , migrations, , , .
, .
:
#
php artisan migrate --path="services/best-team-servise/database/migrations/2021_02_04_240000_alter_data_model_table_add_unique_index.php" --pretend
# --pretend SQL ,
#
php artisan migrate:rollback --step=1
# ,
,
php artisan ide-helper:models "Project\Models\DataModel"
:
php artisan db:seed --class=DataModelSeeder
? up() down() , , .
, , , .
Builder :
$conn = (new DataModel())->connection;
$builder = Schema::connection($conn);
( ):
$isExists = $builder->hasColumn(
'data_model',
'deleted_at'
);
, :
if (!$isExists) {
$builder->table(
'data_model',
function (Blueprint $table) {
$table->softDeletesTz();
}
);
}
- , - , , , :
$alias = (new DataModel())->connection;
$builder = Schema
::connection($alias)
->getConnection()
->getDoctrineSchemaManager();
$existingIndexes = $builder->listTableIndexes('data_model');
Laravel , :
Blueprint::unique('index_name');
:
Blueprint::dropUnique('index_name');
Laravel , , , SQL, Laravel ? , !
SQL, :
DROP TRIGGER IF EXISTS trigger_name
ON public.data_model;
CREATE TRIGGER trigger_name
BEFORE INSERT
ON public.data_model
FOR EACH ROW
EXECUTE PROCEDURE public.function_name();
, :
DROP TRIGGER IF EXISTS trigger_name
ON public.data_model;
: " "
, 1000+ . 1000 , .
, 50+ , "" .
, create, alter, , drop.
.
alter_data_model_add_property_column alter_data_model_alter_property_column_to_text alter_data_model_alter_property_column_set_default_value alter_data_model_create_index_on_code_type_columns alter_data_model_create_unique_index_on_code_column
, , MVP.
:
#
php artisan make:migration create_profile_table --create=profile
#
php artisan make:migration add_confirmed_to_profile --table=profile
database/migrations , .
: , nullable()
, NOT NULL, , , , .
, .
nullable(), , .
, , - :
$columns = Schema
::connection((new DataModel())->connection)
->getConnection()
->getDoctrineSchemaManager()
->listTableColumns($(new DataModel())->getTable());
$data = [];
foreach ($columns as $column) {
$name = $column->getName();
/* @var array[] $record */
$exists = key_exists($name, $record);
if ($exists) {
$data[$name] = $record[$name];
}
}
$isSuccess = DataModel
::withTrashed()
->updateOrCreate(
['uniqe_index_column' => $data['uniqe_index_column'],],
$data
)->exists;
: , null
, , , , , .
Or you can use some default value in the code, but I don't like this method, because this is hard code, and this kills the flexibility of our application. Application operation should be configured either through environment variables, or config files, or database records.
Conclusion
This set of rules is certainly not absolute, first of all we turn on the head and use common sense.
Let's discuss in the comments. Please share your experience.