Database Migrations in PHP With Phinx
Bài đăng này đã không được cập nhật trong 3 năm
1. Migration là gì
Database Migration là một trong những công việc thường gặp trong mỗi dự án. Chúng ta muốn chia sẻ cấu trúc dữ liệu, chia sẻ data? Nếu phải export dữ liệu, cấu trúc DB rồi chờ import thì chắc chắn sẽ tốn rất nhiều thời gian. Vì thế, migration trở nên thiết yếu trong các project php. Và đối với những project không sử dụng framework, đó là một giải pháp hữu hiệu để quản lý cập nhật database.
Bạn quản lí code bằng git như database, một thành phần quan trọng không kém là database thì không được như vậy. Vì vậy migration được tạo ra để quản lí sự thay đổi cấu trúc dữ liệu. Việc sử dụng migration sẽ giúp bạn giảm nhẹ công việc khi deploy ứng dụng.
Migration là một tính năng được "vay mượn" từ Rails - Ruby framework, rất hữu dụng khi chúng ta thay đổi database (thường là MySQL) và muốn quản lý version của những lần thay đổi đó.
Đa phần các framework lớn đều tích hợp hoặc dựng sẵn một migration chẳng hạn:
Hôm nay chúng ta sẽ tìm hiểu về Phinx Migration. Phinx được tạo ra bởi Rob Morgan. Hiện nay, Phinx được chuyển sang quyền sở hữu của CakePHP.
2. Mục tiêu của Phinx :
- Dễ dàng, linh hoạt chuyển đổi.
- Sử dụng độc lập.
- Cài đặt đơn giản.
- Dễ dàng sử dụng qua cửa sổ dòng lệnh.
- Dễ dàng tích hợp với các công cụ khác như Phing, PHPUnit hoặc vào các framework khác.
3. Cài đặt phinx.
phinx có thể cài đặt dễ dàng bằng composer. Bạn có thể thêm những dòng sau vào composer.json và chạy composer install
{
"require": {
"robmorgan/phinx": "*"
},
}
Hoặc gọi trực tiếp bằng lệnh:
composer require robmorgan/phinx --dev
Kiểm tra xem đã cài đặt thành công chưa nào:
php vendor/bin/phinx
Kết quả hiện lên như thế này là chúng ta đã cài đặt thành công rồi
Phinx by Rob Morgan - https://phinx.org. 0.8.1
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
breakpoint Manage breakpoints
create Create a new migration
help Displays help for a command
init Initialize the application for Phinx
list Lists commands
migrate Migrate the database
rollback Rollback the last or to a specific migration
status Show migration status
test Verify the configuration file
seed
seed:create Create a new database seeder
seed:run Run database seeders
4. Cấu hình cho Phinx.
Phinx khá linh hoạt cho việc hỗ trợ các loại cơ sở dữ liệu khác nhau:
- MySQL: specify the mysql adapter.
- PostgreSQL: specify the pgsql adapter.
- SQLite: specify the sqlite adapter.
- SQL Server: specify the sqlsrv adapter.
Để có thể config cho phinx, ta gõ lệnh:
php vendor/bin/phinx init
mặc định phinx sẽ tạo ra file phinx.yml
ở ngay root source. Thử mở file này lên xem nào:
paths:
migrations: %%PHINX_CONFIG_DIR%%/db/migrations
seeds: %%PHINX_CONFIG_DIR%%/db/seeds
environments:
default_migration_table: phinxlog
default_database: development
production:
adapter: mysql
host: localhost
name: production_db
user: root
pass: ''
port: 3306
charset: utf8
development:
adapter: mysql
host: localhost
name: development_db
user: root
pass: ''
port: 3306
charset: utf8
testing:
adapter: mysql
host: localhost
name: testing_db
user: root
pass: ''
port: 3306
charset: utf8
version_order: creation
File cấu hình của phinx sử dụng định dạng YAML, ngoài ra chúng ta có thể sử dụng định dạng là json hoặc php. Có vẻ khá linh hoạt cho chúng ta đúng không nào.
Ở đây, chúng ta tập trung chủ yếu vào định dạng YAML
thôi nhé .
Bây giờ cùng tìm hiểu cấu trúc file cấu hình nào.
4.1. Paths
a. Migration Paths:
- Tùy chọn đầu tiên là cấu hình đường dẫn đến nơi chứa file migrations:
%%PHINX_CONFIG_DIR%%/db/migrations
%%PHINX_CONFIG_DIR%% được thay thế bằng đường dẫn thư mục gốc, hoặc nơi đặt tệp tin
phinx.yml
.
Để cấu hình đường dẫn, chúng ta có thể chọn đặt một đường dẫn duy nhất:
paths:
migrations: /your/full/path
hoặc cấu hình nhiều đường dẫn:
paths:
migrations:
- application/module1/migrations
- application/module2/migrations
b. Seed Paths
Tùy chọn này cho phép chúng ta đặt đường dẫn tới thư mục chứa các file seed. Tương tự như cấu hình path, chúng ta có thể cấu hình 1 hoặc nhiều đường dẫn chứa các file seed:
# cấu hình 1 đường dẫn.
paths:
seeds: /your/full/path
# cấu hình nhiều đường dẫn.
paths:
seeds:
- /your/full/path1
- /your/full/path2
# hoặc sử dụng config dir:
paths:
seeds: %%PHINX_CONFIG_DIR%%/your/relative/path
4.2. Environments:
Một trong những điểm khác biệt của phinx đó là có thể cấu hình cho nhiều môi trường khác nhau.
production:
adapter: mysql
host: localhost
name: production_db
user: root
pass: ''
port: 3306
charset: utf8
development:
adapter: mysql
host: localhost
name: development_db
user: root
pass: ''
port: 3306
charset: utf8
testing:
adapter: mysql
host: localhost
name: testing_db
user: root
pass: ''
port: 3306
charset: utf8
a. Tiền tố, hậu tố cho bảng
Nếu muốn đặt tiền tố, hậu tố cho các bảng thì sao? Phinx có thể đáp ứng dễ dàng ví dụ:
Table Prefix and Suffix
You can define a table prefix and table suffix:
environments:
development:
....
table_prefix: dev_
table_suffix: _v1
testing:
....
table_prefix: test_
table_suffix: _v2
b. Kết nối qua sockets
Để có thể kết nối qua sockets, chúng ta có thể config như sau:
production:
adapter: mysql
name: production_db
user: root
pass: ''
unix_socket: /var/run/mysql/mysql.sock
charset: utf8
Để tìm hiểu thêm những config đặc biệt hơn hay áp dụng cho các kiểu cơ sở dữ liệu khác nhau, chúng ta có thể tìm hiểu thêm tại Configurations.
#5. Viết file Migrations. Để kiểm tra xem chúng ta có config đúng hay không, chúng ta sử dụng lệnh:
php vendor/bin/phinx status
5.1. Tạo file migrations
Để tạo một file migration, chúng ta sử dụng lệnh create
của phinx:
php vendor/bin/phinx create MyNewMigration
Chạy câu lệnh trên sẽ tạo một file migration theo định dạng: YYYYMMDDHHMMSS_my_new_migration.php
. Trường hợp đặt nhiều đường dẫn, chúng ta cần chọn đường dẫn để có thể tiếp tục tạo file migration.
Cấu trúc file migration được tạo ra:
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
}
}
Tất cả các file migration đều được kế thừa từ class AbstractMigration
. Chúng ta có thể sửa đổi thành class khác trong phần Configurations.
5.2. Các phương thức của file migration.
Một file migrations bao gồm các phương thức:
up()
, dùng để chạy các thay đổi database lần đầu được thực hiện, như là thêm một cột hoàn toàn mới, bảng hoàn toàn mới ...down()
, dùng cho những lúc bạn muốn "undo", "rollback" lại các thay đổi lần trước.change()
, phinx sẽ ignore hết các đoạn code viết vàoup
haydown
ở trên cùng một file.
5.3. Các phương thức của phinx.
a. sử dụng trong file migration để tương tác với DB.
createTable
renameTable
addColumn
renameColumn
addIndex
addForeignKey
Ví dụ:
<?php
use Phinx\Migration\AbstractMigration;
class CityMigration extends AbstractMigration {
public function up() {
$table = $this->table('city');
$table
->addColumn('name', 'string', array('limit' => 20))
->addColumn('country', 'string', array('limit' => 20))
->addColumn('population', 'integer', ['limit'=>5000000])
->save();
}
public function down() {}
}
b. Thực thi câu lệnh SQL
.
Chúng ta có thể thực thi câu lệnh truy vấn bằng các phương thức:
execute()
trả về số hàng ảnh hưởng.query()
trả về kết quả củaPDOStatement
Ví dụ:
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
// execute()
$count = $this->execute('DELETE FROM users'); // returns the number of affected rows
// query()
$rows = $this->query('SELECT * FROM users'); // returns the result as an array
}
/**
* Migrate Down.
*/
public function down()
{
}
}
c. Fetching Rows.
Chúng ta có thể fetch
database bằng các phương thức:
fetchRow()
sẽ trả về một hàng duy nhất.fetchAll()
sẽ trả về tất cả các hàng.
Ví dụ:
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
// fetch a user
$row = $this->fetchRow('SELECT * FROM users');
// fetch an array of messages
$rows = $this->fetchAll('SELECT * FROM messages');
}
/**
* Migrate Down.
*/
public function down()
{
}
}
d. Chèn dữ liệu.
Chúng ta có thể dễ dàng chèn dữ liệu không chỉ ở file seed mà còn có thể chèn dữ liệu ngay trong file migration. Ví dụ:
<?php
use Phinx\Migration\AbstractMigration;
class NewStatus extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
// inserting only one row
$singleRow = [
'id' => 1,
'name' => 'In Progress'
];
$table = $this->table('status');
$table->insert($singleRow);
$table->saveData();
// inserting multiple rows
$rows = [
[
'id' => 2,
'name' => 'Stopped'
],
[
'id' => 3,
'name' => 'Queued'
]
];
// this is a handy shortcut
$this->insert('status', $rows);
}
/**
* Migrate Down.
*/
public function down()
{
$this->execute('DELETE FROM status');
}
}
5.4. Làm việc với bảng.
Phinx cung cấp hệ thống API tương đối dễ dàng để thao tác với các bảng:
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$table = $this->table('tableName');
}
/**
* Migrate Down.
*/
public function down()
{
}
}
Ở trên, chúng ta chỉ định rõ tên bảng cần làm việc để xác định đối tượng bảng trong cơ sở dữ liệu.
a. Các kiểu dữ liệu. Trong phinx, chúng ta cần chỉ định rõ kiểu dữ liệu cho các cột:
- biginteger
- binary
- boolean
- date
- datetime
- decimal
- float
- integer
- string
- text
- time
- timestamp
- uuid
Ngoài ra, kiểu cơ sở dữ liệu MySQL hỗ trợ các kiểu cột enum, set, blob và json. (Json trong MySQL 5,7 và ở trên)
Ngoài ra, kiểu cơ sở dữ liệu Postgres hỗ trợ các loại cột nhỏ, json, jsonb và uuid (PostgreSQL 9.3 trở lên).
Chúng ta có thể tìm hiểu kỹ hơn về các thao tác ở tài liệu của phinx:
6. Database Seeding.
6.1 Tạo file seed.
Để tạo một file seeder ta sử dụng lệnh:
php vendor/bin/phinx seed:create UserSeeder
Nếu chúng ta đặt nhiều đường dẫn chứa các file seed, chúng ta cần xác định đường dẫn nào sẽ lưu các file seeder thì mới có thể tiếp tục.
File seeder được tạo ra có dạng:
<?php
use Phinx\Seed\AbstractSeed;
class MyNewSeeder extends AbstractSeed
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* http://docs.phinx.org/en/latest/seeding.html
*/
public function run()
{
}
}
6.2 Chèn dữ liệu.
Để chèn dữ liệu vào bảng, phinx có thể tích hợp linh hoạt vào các framework, các công cụ khác. Vì thế chúng ta có rất nhiều các để có thể chèn dữ liệu vào cơ sở dữ liệu.
- Sử dụng đối tượng bảng:
<?php
use Phinx\Seed\AbstractSeed;
class PostsSeeder extends AbstractSeed
{
public function run()
{
$data = array(
array(
'body' => 'foo',
'created' => date('Y-m-d H:i:s'),
),
array(
'body' => 'bar',
'created' => date('Y-m-d H:i:s'),
)
);
$posts = $this->table('posts');
$posts->insert($data)
->save();
}
}
- Sử dụng thư viện
Faker
.
<?php
use Phinx\Seed\AbstractSeed;
class UserSeeder extends AbstractSeed
{
public function run()
{
$faker = Faker\Factory::create();
$data = [];
for ($i = 0; $i < 100; $i++) {
$data[] = [
'username' => $faker->userName,
'password' => sha1($faker->password),
'password_salt' => sha1('foo'),
'email' => $faker->email,
'first_name' => $faker->firstName,
'last_name' => $faker->lastName,
'created' => date('Y-m-d H:i:s'),
];
}
$this->insert('users', $data);
}
}
6.3. Hay truncate
dữ liệ của bảng.
<?php
use Phinx\Seed\AbstractSeed;
class UserSeeder extends AbstractSeed
{
public function run()
{
$data = [
[
'body' => 'foo',
'created' => date('Y-m-d H:i:s'),
],
[
'body' => 'bar',
'created' => date('Y-m-d H:i:s'),
]
];
$posts = $this->table('posts');
$posts->insert($data)
->save();
// empty the table
$posts->truncate();
}
}
6.4. Thực thi seeder.
Sau khi tạo xong migration, tạo các file seed, chúng ta cần seed
để thao tác với dữ liệu bằng nhiều cách:
- Thực thi tất cả các file seed:
php vendor/bin/phinx seed:run
- Thực thi một file seed cụ thể:
php vendor/bin/phinx seed:run -s UserSeeder
- Hay thực thi nhiều file seed:
php vendor/bin/phinx seed:run -s UserSeeder -s PermissionSeeder -s LogSeeder
Còn rất nhiều thứ hay ho mà chúng ta còn chưa khám phá hết với phinx. Các bạn có thể tìm hiểu thêm tại Phinx.
Kết luận.
Phinx rất dễ sử dụng. Với khả năng dễ dàng tích hợp với những hệ thống đã có sẵn, hay bắt đầu xây dựng, tích hợp vào framework hay dự án không theo framework đều khá dễ dạng. Với phinx, việc testing với cơ sở dữ liệu cũng khá dễ dàng phải không nào. Không những thế, việc thao tác với nhiều kiểu cơ sở dữ liệu khác nhau, chúng ta cần phải biết những câu lệnh khác nhau, với Phinx thì xử lý việc đó khá dễ dàng phải không nào.
Migration thực sự làm nhẹ gánh bớt nhức đầu rất nhiều cho lập trình backend bằng PHP. Vậy sao chúng ta không thử áp dụng vào dự án của mình đi nào
Với một ngôi nhà mới, Phinx gia nhập với gia đình CakePHP, hi vọng rằng Phinx sẽ ngày càng tối ưu, phát triển mạnh mẽ hơn nữa. Cảm ơn các bạn đã đọc tới đây. Hi vong bài viết hữu ích đối với các bạn nhé
Tìm hiểu thêm:
All rights reserved