Những điều thú vị trong Laravel Phần 1 - Migration

Đây là bài đầu tiên trong series những điều thú vị trong Laravel, mình sẽ nói về những thứ hay ho mà mình gặp phải trong quá trình phát triển ứng dụng trên nền tảng Laravel framework

Những điều thú vị trong Laravel Phần 1 - Migration

Trong bài viết này mình sẽ nói về những thứ không hề có trong docs của Laravel https://laravel.com/docs/9.x/migrations

1. Migration không nhất thiết phải khai báo hàm up() hoặc down()

Trong laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:460 sẽ check nếu có hàm up() thì mới chạy lúc thực hiện migrate, và chỉ chạy hàm down() nếu có lúc thực hiện migrate:rollback.

Nghĩa là migration của bạn hoàn toàn có thể không định nghĩa hàm up() và hàm down()

    protected function getQueries($migration, $method)
    {
        // Now that we have the connections we can resolve it and pretend to run the
        // queries against the database returning the array of raw SQL statements
        // that would get fired against the database system for this migration.
        $db = $this->resolveConnection(
            $migration->getConnection()
        );

        return $db->pretend(function () use ($db, $migration, $method) {
            if (method_exists($migration, $method)) {
                $this->runMethod($db, $migration, $method);
            }
        });
    }

    /**
     * Run a migration method on the given connection.
     *
     * @param  \Illuminate\Database\Connection  $connection
     * @param  object  $migration
     * @param  string  $method
     * @return void
     */
    protected function runMethod($connection, $migration, $method)
    {
        $previousConnection = $this->resolver->getDefaultConnection();

        try {
            $this->resolver->setDefaultConnection($connection->getName());

            $migration->{$method}();
        } finally {
            $this->resolver->setDefaultConnection($previousConnection);
        }
    }
laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:460

2. Migration không chỉ để làm thao tác DB

Đây là 1 cheat, mặc dù nó không phải là cách sử dụng đúng của migration nhưng chúng ta có thể lợi dụng nó để làm 1 số việc khác khi chạy migration 😅

Ví dụ: từ Laravel 9 thư mục resources/lang đã được move ra /lang, thay vì làm thủ công việc copy thư mục này ra root cho toàn bộ projects của mình thì mình viết 1 migration và sử dụng nó cho toàn bộ projects.

<?php

use Illuminate\Database\Migrations\Migration;

return new class () extends Migration {
    public function up(): void
    {
        if (File::isDirectory(resource_path('lang'))) {
            File::moveDirectory(resource_path('lang'), base_path('lang'));
        }
    }
};

Migration có thể để làm nhiều thứ thú vị, không nhất thiết phải viết thao tác DB trong này.

3. Migration có thể để ở bất cứ đâu.

Bạn có thể quăng migration trong folder /acme/abc/dummy cũng được, không nhất thiết phải để trong /database/migrations. Chỉ cần bạn load nó lên là được.

public function boot()
{
    // app/Providers/AppServiceProvider.php
    $this->loadMigrationsFrom(base_path('acme/abc/dummy'));
}

4. Chạy migration trên hosting

Trên hosting thường không hỗ trợ command line, bạn sẽ không thể chạy php artisan migrate trên hosting nếu nó không hỗ trợ ssh.

Mình có thấy một số hướng dẫn thế này.

// routes/web.php
Route::get('artisan/migrate', function () {
   Artisan::call('migrate');
   
   return 'Ok';
});

Cách này sẽ chạy nếu hosting có mở hàm proc_open(), nếu hàm này bị tắt, chúng ta sẽ gặp lỗi The Process class relies on proc_open, which is not available on your PHP installation.

Lý do là Artisan sẽ thực thi command bằng class symfony/process/Process.php của Symfony và class này sử dụng hàm proc_open() để mở ra 1 process.

Trên khá nhiều hosting bạn sẽ gặp lỗi proc_open() has been disabled for security reasons. Bên hosting họ có lý do để khóa hàm này vì thực sự nó không được bảo mật trên môi trường web, khi website của bạn bị chèn mã độc, nếu hosting có mở hàm này thì hacker có thể làm mọi thứ với hosting của bạn.

Vậy nên cách này sẽ không đúng cho toàn bộ hosting, giải pháp ở đây là chúng ta sẽ gọi trực tiếp migrator.

Migration sẽ được chạy thông qua class laravel/framework/src/Illuminate/Database/Migrations/Migrator.php và class này đã được bind vào service container dưới tên migrator, chúng ta chỉ cần xài app('migrator') để resolve nó ra và sử dụng thôi.

Route::get('artisan/migrate', function () {
    app('migrator')->run(database_path('migrations'));

   return 'Ok';
});

Cách này sẽ đảm bảo migration có thể chạy trên toàn bộ hosting/VPS.

Đây là một số điều thú vị về migration trong Laravel. Trong bài viết tới mình sẽ tiếp tục chia sẻ một số điều mình biết về Command, Service Provider, Event, Listener... trong Laravel. Các bạn nhớ subscribe Laravel Việt Nam Blog để nhận thông báo khi có bài mới nhé 😊