Một số vấn đề gặp phải khi deploy project Laravel lên Heroku và cách giải quyết

Khi deploy một project Laravel lên Heroku, thường thì nó sẽ không thể chạy ngon ngay từ lần đầu được mà chắc chắn sẽ dính lỗi tùm lum, cho dù là các host thông thường cũng sẽ dính lỗi, lý do vì sao thì có lẽ do Laravel hơi khác biệt một chút so với các framework khác.

Dưới đây là một số lỗi khó chịu mình gặp phải khi deploy project Laravel lên Heroku, cách giải quyết mình đưa ra có lẽ cũng không được mượt mà cho lắm nhưng ít nhất làm cho nó chạy được 😄 Các bạn có thể google với các keyword mình cung cấp có lẽ sẽ tìm được cách giải quyết "ngon" hơn 😄

Trước hết bạn cần có project và một tài khoản Heroku để deploy đã, còn làm như thế nào thì bạn có thể tham khảo các hướng dẫn dưới đây:

URL các file CSS, Javascript, ảnh,... đã khai báo bị đổi sang http:// dẫn đến mất style (Lỗi Mixed content):

Đây có lẽ là lỗi gây hoang mang nhiều nhất khi mà bạn vừa chắc mẩm deploy project xong, cứ tưởng được tận hưởng thành quả thì * BOOM * cả trang web chỉ toàn text, không style, không màu, trông như tờ giấy văn bản, khi bật cửa sổ console lên thì đỏ lòm như thế này:

Bình tĩnh, nhìn vậy nhưng đây lại là lỗi đơn giản và dễ giải quyết (gần) nhất.

Lỗi này xảy ra do url được render từ Laravel ra không phát hiện được kết nối HTTPS nên đã lấy mặc định đường dẫn HTTP như trên máy local. Cách đơn giản để fix lỗi này là vào file master.blade.php và sửa lại các đường dẫn từ:

<link rel="stylesheet" href="{{ asset('/assets/bootstrap/dist/css/bootstrap.min.css') }}" >

thành

<link rel="stylesheet" href="/assets/bootstrap/dist/css/bootstrap.min.css" >

Heroku sẽ tự thêm phần tên miền vào trước phía trước nên các đường dẫn này sẽ hoạt động bình thường.

Tuy nhiên cách này khá là tá điền vì bạn sẽ phải tìm và sửa toàn bộ đường dẫn của từng file một, thậm chí cả các file ảnh mà bạn lấy từ project ra, vậy nên cách này tuy đơn giản nhưng không hay.

Không lâu sau đó mình tìm ra một các khác gọn hơn rất rất nhiều đó là sửa một chút tại AppServiceProvider trong project Laravel của mình:

Từ

public function boot()
{
    //
}

Thành

use URL;
//
public function boot()
{
    URL::forceScheme('https');
}

Các đường dẫn khi bạn gọi bằng phương thức url() hoặc asset() hoặc route() sẽ được ép về https://

Bằng việc thêm dòng URL::forceScheme('https') như trên, bạn sẽ tránh được việc request post bị mặc định gửi về đường dẫn http:// gây ra lỗi Mixed content.

Request Ajax bị lỗi Mixed content:

Một số request ajax của bạn có thể sẽ gặp phải lỗi này khiến URL request bị đổi về http://. Nguyên nhân chính xác thì mình không rõ, nhưng cách giải quyết thì có, bạn chỉ cần chú ý vào phần gạch chéo cuối của URL mà bạn cần request đến. Nếu như có gạch chéo thì URL sẽ bị đổi về http:// gây ra lỗi. Ví dụ:

https://someApp.heroku.com/findSomeThing/
-> Mixed Content: The page at 'https://someApp.herokuapp.com/' was loaded over HTTPS .... 
-> URL request bị đổi thành: http://someApp.heroku.com/findSomeThing

Vậy nên chỉ cần chú ý bỏ dấu gạch chéo cuối cùng đó đi trong trường hợp không cần thiết.

//this will work good
https://someApp.heroku.com/findSomeThing

Một vài vấn đề gặp phải với Database:

Gần như chắc chắn khi bạn deploy một project Laravel lên Heroku thì bạn sẽ phải sử dụng một Add-on Database đi kèm, thường sẽ là PostgresSQL, dưới đây là một số vấn đề mình gặp phải khi lần đầu sử dụng PostgresSQL.

Ngoài lề một chút, nếu bạn đã quen với việc sử dụng MySQL và phpmyadmin để quản lý CSDL và cảm thấy "khó thở" khi phải chuyển qua dùng giao diện dòng lệnh của PostgressSQL thì bạn vẫn có thể tự tạo cho mình một CSDL MySQL bên ngoài, sau đó cài đặt lại trong file .env, việc này hoàn toàn khả thi, miễn là CSDL đó được đặt trong một host có đường truyền ổn định, có thêm bảo mật SSL càng tốt.

3.1 Khởi tạo các bảng trên PostgresSQL:

Một khi bạn đã sử dụng Laravel làm nền tảng phát triển cho trang web của mình, bạn chắc chắn sẽ phải học sử dụng Migration để khởi tạo các bảng trong CSDL.

Chúng ta thường dùng MySQLphpmyadmin để làm CSDL cho ứng dụng trong quá trình phát triển ứng dụng trên môi trường Local. Vậy dĩ nhiên các mã lệnh migration mà bạn tạo ra cũng sẽ dành cho HQT CSDL MySQL, vậy giờ chuyển qua dùng PostgresSQL có phải thay đổi gì không? Câu trả lời là không, bạn có thể giữ nguyên mã lệnh đó và tiến hành tạo bảng bằng Migration.

Đầu tiên bạn sẽ phải truy cập vào database mà bạn đã đính kèm khi deploy project (nếu bạn chưa tạo có thể xem lại hướng dẫn tại đây)

$ heroku pg:psql --app <heroku app name>
//$ heroku pg:psql --app someApp

Chạy lệnh Migration:

$ heroku run php /app/artisan migrate

Thêm --seed đằng sau nếu muốn khởi tạo các Seeder:

$ heroku run php /app/artisan migrate --seed

Vậy là các bảng và seed của bạn sẽ được tạo đúng như những gì bạn code 😄

Giả sử như bạn muốn nhiều hơn thế (hoặc đơn giản là do lười 😄 ) bạn muốn import các dữ liệu từ CSDL MySQL cũ vào Postgres, vậy phải làm thế nào?

3.2 Import các dữ liệu từ các file SQL:

Công đoạn này khá là nhọc, mình chưa tìm ra cách khác nhanh hơn, nhưng thôi có còn hơn không. Đây là cách mình dùng để import các dữ liệu từ CSDL MySQL cũ vào Postgres.

Đầu tiên các bạn cần liệt kê thứ tự các bảng cần import, tất nhiên là các bảng một trước rồi mới đến bảng nhiều, sau đó export từng bảng ra. Tại sao lại phải làm từng bảng mất công vậy? Không export cả DB ra cho nhanh??

Lý do là định dạng import của Postgres khác so với MySQL, nếu các bạn cứ thế export cả DB ra rồi ném vào Postgres chắc chắn sẽ nhận một đống lỗi syntax.

Sau khi export xong, các bạn mở từng file đó lên (khuyến khích dùng Sublime 3 hoặc Visual Studio Code), chỉ giữ lại phần code INSERT còn lại xoá hết. Vì chúng ta đã có bảng và các ràng buộc từ khi migrate rồi nên không cần phần này nữa, xoá hết các dấu " ` " (nếu có):

INSERT INTO table_1 (`col_1`, `col_2`,...)

=> INSERT INTO table_1 (col_1, col_2,...)

Xoá xong thì lưu lại, gõ lệnh:

$ heroku pg:psql appName::DATABASE < fileToImport.sql

Hoặc coppy toàn bộ phần code insert đó, paste vào cửa sổ command (đang kết nối với Postgres, trông nó sẽ như thế này:

appName::DATABASE => INSERT INTO table_1 .... ; //đừng quên dấu " ; "

Vậy là bạn đã import xong, nhưng đừng vội mừng, vì rất có thể khi bạn chạy thử ứng dụng, sẽ có một số chức năng dính lỗi Duplicate id for primary key (chủ yếu là các chức năng thêm)

3.3 Khắc phục lỗi trùng khoá chính sau khi import dữ liệu trên trường khoá chính tự tăng:

Lỗi này xảy ra sau khi bạn import dữ liệu cũ vào với một trường khoá chính tự tăng, ví dụ trong CSDL Postgres của bạn đang có table_1 với sẵn 3 bản ghi có cột ID (Auto increment) làm khoá chính lần lượt là 1, 2, 3.

ID | Col_1 | ...
1  | abc   | ...
2  | def   | ...
3  | ghj   | ...

Bạn vừa import thêm vào table_1 hai bản ghi có các khoá chính là 4, 5:

ID | Col_1 | ...
4  | klm   | ...
5  | zxc   | ...

Việc import diễn ra ngon lành không sao cả, nhưng đến khi bạn dùng chức năng thêm vào bảng này thì dính lỗi Duplicate ID for primary key.

Lỗi này xảy ra do giá trị của cột ID không được tự động tăng lên thành giá trị mới lớn nhất sau khi import ( ở đây là 5 -> 6 ) mà chỉ tăng lên 4 ( từ 3 -> 4 ) Do đó khi dùng câu lệnh insert và để ID tự động tăng thì sẽ dính lỗi Duplicate khoá chính.

Do vậy chúng ta cần đặt lại giá trị tiếp theo cho cột khoá chính này.

Create sequence <seq name>;
Alter table <table name> alter <primary key name> set default nextval('<seq name>');
Select setval('<seq name>', <number> ); // number là giá trị mà bạn muốn ID bắt đầu tăng từ đó, theo ví dụ kia là: 6

Đôi khi bạn sẽ gặp phải lỗi relation seq_name has been exist đó là do sequence đã được khởi tạo trước, bạn chỉ cần dùng chạy từ câu lệnh Alter table ... trở đi là được.

Làm tiếp tục như vậy với các bảng có ID tự tăng và vừa được import dữ liệu, cách này tốn khá nhiều công sức, nhưng ít nhất là giúp ứng dụng hoạt động lại bình thường 😄

Cám ơn các bạn đã đọc bài viết!


All Rights Reserved