Tạo function với dynamic parameter trong PHP
Thực sự thì mình vẫn chưa hiểu rõ câu hỏi của bạn lắm Có vẻ như bạn đang hỏi làm thế nào mà có thể thay đổi nội dung mảng $columns
mà không ảnh hưởng đến phần code phía dưới đúng không nhỉ
Nếu vậy, bạn có thể khai báo mảng $columns
, rồi chạy vòng for
với mảng đó thì có lẽ là sẽ được.
Kiểu như:
$columns = ["level_id", "level_name"];
while ($row = mysqli_fetch_assoc($data)) {
$data = [];
foreach ($columns as $column) {
$data = $row[$column];
}
array_push($arrayData, new Obj($data)); // Truyền cả mảng $data vào trong constructor của class Obj của bạn
}
Với đoạn code như trên thì bạn có đổi tên các trường trong mảng $columns
, hay thêm trường mới, xoá trường cũ thì cũng không ảnh hưởng gì đến xử lý phía dưới.
Có điều bạn cũng cần phải chỉnh sửa lại cả constructor
của class Obj
của bạn nữa, để nó cũng nhận vào một mảng dynamic, thay vì fix cứng thứ tự level_id
hay level_name
như code hiện tại.
Vị trí đặt hàm xử lý với project Laravel
Em nên có một bảng là results
nữa
Khi tỉ số của trận đấu A được admin cập nhật, thì hệ thống sẽ chạy một job để tính toán toàn bộ kết quả bet của người chơi có liên liên quan đến trận đấu A đó. Lượng tiền thay đổi như thế nào cũng được tính toán trong job này, vừa lưu vào bảng results
như là một record log. Đồng thời, cũng trong job này, em cũng cần cập nhật vào trường money
của user luôn.
Khi hiển thị ra thì em chỉ cần query trong bảng resutls
theo users, và hiển thị theo đúng match_id
là được
Nhờ mọi người giải thích giúp mình ý nghĩa của dòng lệnh này trong JavaScript
Tên hàm createGuid
có nghĩa là create global unique id, tức ta kỳ vọng vào việc có thể generate ra những id
khác nhau, ở những lần gọi khác nhau.
Hàm s4
, theo mình đoán, thì là hàm generate ra 4 ký tự, thuộc hệ 16 (tức bao gồm 0 - 9
và a, b, c, d, e, f
)
Giải thích từng bước 1 trong hàm s4
thì ta có:
Math.random()
, là random ra một số trong khoảng từ0 <= x < 1
0x10000
là số trong hệ16
. Tính ra sẽ có giá trị là 164=65536 trong hệ 10- Lúc này phép toán
(1 + Math.random()) * 0x10000
sẽ cho ra một kết quả trong đoạn0x10000 <= x < 0x20000
- Dùng thêm hàm
Math.floor()
nữa bạn sẽ có một số nguyên trong khoảng0x10000 <= y <= 0x1ffff
- Đến đây ta convert về string bằng hàm
toString
với base là16
, thì sẽ được một chuỗi trong khoảng10000
và1ffff
- Dùng
substring(1)
để bỏ ký tự đầu tiên đi, kết quả sẽ là một chuỗi gồm 4 ký tự, trong khoảng0000
đếnffff
Lặp lại quá trình gọi hàm s4()
này 4 lần, bạn sẽ có chuỗi 16 ký tự, các ký tự nằm trong khoảng từ 0
đến f
của hệ 16.
Khả năng bị trùng lặp sẽ rơi vào 6553641 , tức cự kỳ nhỏ =))
Crypto Currency
Cụ thể không biết bạn đang muốn làm gì, và đang gặp vấn đề ở phần nào nhỉ?
Nếu là tạo ra một đồng tiền mã hoá, thì bạn có thể tham khảo series Thử tạo Blockchain và tiền ảo BitCoin bằng HTML và Javascript trên Viblo của tác giả @chungminhtu xem sao
Hỏi cách import file trong React
Hai dòng đầu tiên là import các class : React và ReactDOM từ 2 file: react và react-dom : ở đây vì là file index.js này cùng loại file (cùng đuôi .js) với 2 file kia nên ta không cần viết thêm đuôi .js vào sau 2 file đó nữa => mình suy đoán vậy không biết có đúng không ?
Không phải bạn ạ, không phải từ 2 file, mà là từ 2 package mang tên react
và react-dom
. Đây là 2 npm packages
mà bạn cài đặt bằng npm
hoặc yarn
đấy, nó có trong thư mục node_modules
.
Hai dòng tiếp theo (3,4) lần lượt import file index.css (cái này khác loại file nên phải thêm đuôi .css) và Class App từ file App.js . Cái mà mình không hiểu ở đây là tại sao trước tên các file lại có thêm ./ mặc dù 2 file này nằm cùng cấp với file index.js (mình vẽ mũi tên đỏ ở trên hình ) , trong khi đó cách import ở 2 dòng đầu tiên thì các file import không nằm cùng cấp với file index.js nhưng lại không có ./ vào phía trước tên file.
Như đã giải thích ở trên, thì nếu không có gì ở đầu, ta sẽ hiểu là import bằng tên package
trong thư mục node_modules
, còn muốn import từ chỗ khác, bạn cần chỉ đường dẫn tương đối, hoặc tuyệt đối. Đó là do tại sao lại cần có ./
ở đây
Tại sao Var và Let khác nhau trong trường hợp này
Vậy chúng chỉ thực sự khác nhau khi được định nghĩa bên trong cặp { }.
Chính xác là dùng {}
có thể tạo ra một block, đối với các hàm if
, while
, switch
, case
...
còn với vòng lặp (for
, for-in
, for-of
) thì bạn cũng có thể tạo ra một biến với block scope thông qua việc khai báo bên trong ()
.
Với mỗi một iteration
(lần lặp), sẽ có một lần binding được diễn ra, hay nói cách khác có một block scope được tạo ra, và phần initialization
của vòng for
được thực thi trong scope đó.
Bạn cứ coi đó là một công dụng đặc biệt của let
khi dùng với vòng lặp đi
upload file excel to mongodb with php
@catbuidem Có vẻ bạn đang gặp vấn đề ở việc đọc nội dung file excel đúng không nhỉ
Bạn có thể sử dụng package PHPOffice/PhpSpreadsheet, một thư viện rất mạnh mẽ trong việc hỗ trợ thao tác với file excel.
ví dụ
$inputFileName = './sampleData/example.xls';
/** Create a new Xlsx Reader **/
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$spreadsheet = $reader->load($inputFileName);
sau đó bạn chỉ cần xử lý data, rồi lưu vào trong DB theo cấu trúc mà mình mong muốn thôi
Về document của PHPOffice/PhpSpreadsheet
, bạn có thể xem thêm ở trang https://phpspreadsheet.readthedocs.io
Tại sao Laravel ServiceProvider gọi phương thức $this->app->rebinding() "không tồn tại"?
Theo anh mọi chuyện khá là đơn giản, chứ không quá phức tạp như em nghĩ đâu
Em có thể tham khảo file public/index.php
để thấy biến $app
được tạo ra như thế nào
$app = require_once __DIR__.'/../bootstrap/app.php';
cụ thể hơn, trong file bootstrap/app.php
thì
$app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);
Như vậy thì $app
là một instance của class Illuminate\Foundation\Application
, và sẽ luôn luôn là như vậy, không hề có chuyện $app
được resolve ra từ một class khác có implement Illuminate\Contracts\Container\Container
đâu em. (Đương nhiên rồi, khi mà $app
chính là service container, nó không thể tự bind
hay resolve
chính nó (^^;))
Thế nên việc trong class Illuminate\Foundation\Application
có những hàm mà trong interface Illuminate\Contracts\Container\Container
không có thì theo anh nghĩ cũng không phải là vấn đề gì cả đâu
Nó chỉ trở thành vấn đề, khi mà như em nói, ta type hint một interface, nhưng lại sử dụng hàm không có trong interface đó thôi Ví dụ như
interface A
{
function a () {}
}
class B
{
function b (A $a)
{
$a->c(); // $a gọi đến hàm không có trong interface A
}
}
Code website bán hàng
Nếu mục đích chính của bạn là có một trang web bán hàng, chứ không phải để biết code trang web đó như thế nào, thì mình khuyên bạn nên dùng Wordpress, dựng một trang web rất đơn giản và dễ dàng, mất có vài phút. Sau đó thì cài thêm plugin WooCommerce nữa là có ngay một nền tảng eCommerce rồi. WooCommerce hiện là nền tảng eCommerce nổi tiếng và phổ biến nhất thế giới, với khoảng 28% các online store đang sử dụng nó, thế nên bạn hoàn toàn có thể yên tâm về chất lượng Ngoài ra nó cũng có rất nhiều các plugin khác, bổ sung thêm nhiều tính năng hữu ích khi cần.
Cuối cùng, nếu cần thì bạn có thể tải thêm các theme cho WooCommerce về nữa là có ngay một trang web bắt mắt, mang cá tính riêng.
Website:
Góp ý ý tưởng về chức năng LIKE bài viết và comment trong Laravel.
My idea: Vì một bài viết, comment có mỗi quan hệ
N- N
với bảngUsers
nên e sẽ tạo một bảng phụ có tên làcomment_user
,posts_user
và query như bình thường ạ, nếu xét cóuser
đang đăng nhập trùng với 1user_id
ở 2 bảng trên thì chứng tỏ có LIKE rồi..
Anh không hiểu rõ chỗ này của em lắm. Bài viết và comment là có quan hệ 1 - n
với user (1 bài viết, hay 1 comment chỉ thuộc về 1 user, 1 user có nhiều bài viết hay comment), chứ sao lại quan hệ n - n
được
Để thực hiện chức năng LIKE, ta cần một bảng trung gian giữa user
và post
, cũng như user
và comment
. Em nên dùng Polymorphic Relations trong trường hợp này, khi đó chỉ cần có một bảng trung gian, gọi là likes
chẳng hạn.
Bảng likes
sẽ có cấu trúc như sau:
likes
id - integer
user_id - integer
likeable_id - integer
likeable_type - string
mỗi khi users like một comment, hay post, thì em insert một bản ghi vào trong bảng likes
ở trên, như vậy sẽ lưu được thông tin về việc ai đã từng like những cái gì. Ví dụ như em có 2 bản ghi:
1, 2, 3, "COMMENT" // => user 2, like comment có id là 3
2, 3, 4, "POST" // => user 3, like post có id là 4
Laravel có support sẵn khai báo và query Polymorphic Relations rồi nên implement cũng rất dễ em ạ
Append không hiển thị source code HTML trong 'View Source Code'
Câu hỏi của em khó hiểu quá, anh phải đọc các comments khác xong mãi mới hiểu em muốn hỏi gì
Ở đoạn code này của em:
$(document).ready(function() {
$('.image_to_add_post').on('click', function() {
var source = $(this).attr('data-href');
simplemde.codemirror.replaceSelection("![](" + source + ")");
$('.modal').modal('toggle');
});
});
thì sự kiện là click
, và nó được bind vào các element có class là .image_to_add_post
từ ngay lúc đầu, khi docment được load xong. Thế nên đương nhiên là khi element mới được tạo ra, dù có class là .image_to_add_post
đi chăng nữa, nó cũng không thể có event click
được
Để giải quyết vấn đề này, thì thay vì bind event click
vào .image_to_add_post
, em có thể gắn event vào element cha của .image_to_add_post
, ví dụ ở đây là một div
có id
là #image-user
chẳng hạn.
Lúc đó code của em sẽ có dạng như sau:
$(document).ready(function() {
$('#image-user').on('click', '.image_to_add_post', function() {
var source = $(this).attr('data-href');
simplemde.codemirror.replaceSelection("![](" + source + ")");
$('.modal').modal('toggle');
});
});
như vậy, event click
thay vì được bind vào tất cả các element có class là .image_to_add_post
như trước nữa, mà nó được bind vào element có id là #image-user
, khi đó những element có class là .image_to_add_post
nằm bên trong element có id là #image-user
sẽ có event click
, nên dù có .image_to_add_post
mới được thêm vào đi chăng nữa, thì click
sẽ vẫn hoạt động. Em thử xem sao
Vấn đề về truyền giá trị config từ Laravel sang VueJs
Sao bạn lại nghĩ rằng sẽ có vấn đề về bảo mật ở đây nhỉ
Dữ liệu mà cần xử lý ở client thì chỉ cần users muốn trông thấy, họ sẽ có thể lấy được, bằng cách này hay cách khác, chỉ là họ có muốn lấy nó hay không mà thôi
Nếu lấy được nó xong mà cũng chỉ giúp users tương tác được với giữ liệu của chính họ thì mình không nghĩ nó là một vấn đề bảo mật gì cả (bởi họ rõ ràng có quyền làm việc đó thông qua các chức năng mà service cung cấp, chỉ có điều giờ họ làm theo cách của họ mà thôi ), nó chỉ là vấn đề khi mà users lấy được key
xong thì có quyền làm những việc mà vốn họ không được phép làm thôi.
Như vậy thì:
- Nếu bạn coi việc lộ thông tin cho users là vấn đề bảo mật, thì không truyền những thông tin đó xuống phía client
- Nếu bạn đã truyền thông tin xuống phía client, đừng quá lo sợ việc họ sẽ biết được thông tin đó :v
Lựa chọn technical stack cho dự án chat
Firebase thì cũng chỉ support bạn được một phần thôi, còn rất nhiều logic backend bạn vẫn phải xử lý chứ nhỉ
Theo mình thì bạn nên theo cách 1, tự dùng của mình xem sao Nhiều thử thách hơn, nhưng sẽ học hỏi được nhiều hơn
Đánh index để tăng performance khi search like trong Laravel
Theo mình biết thì với câu truy vấn like
, index chỉ có hiệu quả khi target không bắt đầu bằng %
Tức với câu truy vấn LIKE 'text'
hoặc LIKE 'text%'
thì sẽ nhanh hơn thông qua việc đánh index, còn LIKE '%text'
thì sẽ không có hiệu quả.
Ở trường hợp của bạn thì khó mà chỉ sử dụng LIKE 'text%'
không thôi được, mà cần dùng LIKE '%text%'
để search nên chắc là không ổn lắm
Một giải pháp khác là bạn nên dùng Full Text Search thay vì câu truy vấn LIKE
. Vừa cho ra kết quả nhanh, vừa chính xác hơn
Lấy Google email address từ access token
Đầu tiên, bạn cần chắc chắn rằng lúc users thực thiện việc authentication ở trang của Google, bạn có request scope cho phép bạn lấy email
của user.
Danh sách các scope của Google APIs bạn có thể check ở đây https://developers.google.com/identity/protocols/googlescopes
Như bạn thấy thì có riêng một scope là userinfo.email
, và bạn cần phải request scope đó thì mới có thể lấy được thông tin email của users sau này.
Sau khi đã request đủ scope
, và đã có access token rồi thì bạn chỉ cần gửi request đến endpoint /userinfo/v2/me
(mình không rõ trong thư viện Google API Client thì nó là hàm gì nữa, bạn thử check code hay documents xem sao nhé ) là có thể lấy được thông tin profile của user, kiểu như sau:
{
"family_name": "Thang",
"name": "Tran Duc Thang",
"picture": "link/to/avatar.jpg",
"gender": "male",
"email": "email@gmail.com",
"link": "https://plus.google.com/thangtd",
"given_name": "Tran Duc",
"id": "1234123123123123123123",
"verified_email": true
}
Bạn có thể vào trang https://developers.google.com/oauthplayground để test các scope
cũng như endpoint
của Google APIs Ở phần 1 (Select & authorize APIs) thì nhớ chọn scope https://www.googleapis.com/auth/userinfo.email
là được
Vấn đề build lại project nuxtjs khi đang triển khai trên server?
Bạn có thể tham khảo chiến lược deploy của Capistrano
Cấu trúc thư mục của bạn sẽ có dạng kiểu như sau:
releases
shared
current -> /deploy/app/releases/20180605125001
Cụ thể thì
- Thư mục
releases
có chứa source codes. Mỗi lần bạn muốn deploy code mới là bạn sẽ tạo một thư mục mới để clone code vào đó. Chẳng hạn như giờ bạn cần deploy, bạn sẽ clone code vào thư mụcreleases/20180605125001
, ngày mai bạn deploy tiếp, và lưu code mới tại thư mục mới làreleases/20180606091511
, chứ không phải pull code về ở thư mục cũ - Bạn có một symbolic link, để trỏ đến thư mục chứa code cần deploy. Symbolic link này sẽ được thay đổi khi bạn deploy code mới. Chẳng hạn hiện thì symbolic link
current
trỏ đến thư mục/deploy/app/releases/20180605125001
, ngày mai bạn deploy tiếp, và sau đó sẽ cần đổi symbolic link sang/deploy/app/releases/20180606091511
. Cuối cùng bạn restart lạipm2
là được. Bởipm2
vốn là chạy với script ở trong thư mục/deploy/app/current
, bạn có deploy bao nhiêu lần đi chăng nữa thì thư mục deploy vẫn là/deploy/app/current
, chỉ có điềucurrent
sẽ trỏ đến các thư mục khác nhau mà thôi. - Thư mục
shared
để chứa những file, hay thư mục cần phải được giữ lại khi swap symbolic link (chẳng hạn như thư mục chứa sessions, file upload, logs ...), bạn cũng dùng sybolic link để link thư mục, file từ releases vào đây.
Khi thực hiện deploy, mà cụ thể ở đây là bạn cần build code nuxjs, thì bạn cứ build ở thư mục mới của bạn (trong lúc này pm2
vẫn chạy với thư mục release cũ nên sẽ không ảnh hưởng gì), sau khi build xong, bạn swap symbolic link, và restart lại pm2
để nó dựng server nodejs mới là được
Bản thân pm2
cũng có cơ chế zero downtime (trong lúc dựng server node mới thì nó vẫn giữ server node cũ chạy, dựng xong thì mới tắt cái cũ đi), nên bạn sẽ có thể deploy mà gần như không hề bị gián đoạn dịch vụ.
Cấu hình slave-master database trong Laravel
Hướng dẫn trong docs của Laravel chỉ là hướng dẫn config để Laravel có thể kết nối đến DB thôi bạn, và bạn vẫn cần phải tự thiết lập ở phía MySQL để các server của bạn hoạt động theo mô hình master slave nữa.
Bạn có thể tham khảo một số bài viết trên Viblo như dưới đây
- https://viblo.asia/p/huong-dan-cai-dat-va-cau-hinh-co-ban-mysql-replication-JQVkVQgNeyd
- https://viblo.asia/p/gioi-thieu-ve-mysql-replication-master-slave-bxjvZYwNkJZ
Nhìn chung mô hình master-slave làm việc theo nguyên lý những dữ liệu sẽ được ghi vào server master, sau đó các server slave sẽ thực hiện lại đúng câu lệnh đã được chạy ở master để có được dữ liệu giống thế. Do đó nó hoàn toàn không phụ thuộc gì vào config ở phía Laravel cả. Bạn thử test bằng cách vào server master insert một row, sau đó sang slave check nếu thấy cũng có data là được
Font tiếng Nhật trong việc tải file trên Laravel
File load_font chỉ có thể chạy file .ttf mặc dù trong code có ghi hỗ trợ cả .ttf lẫn .otf.
Cái này có vẻ là vấn đề của package dompdf
, trước mình có dùng cũng bị như vậy, một số font thì dùng được, một số font thì không
Dung lượng file lớn ( > 20MB) cho một file chỉ có 1 hoặc 2 trang.
Dung lượng file của bạn lớn là do dompdf
đã nhét cả file font tiếng Nhật của bạn vào trong đó. Bạn cứ thử để ý dung lượng file pdf của mình sẽ gần tương đương với dung lượng của file font
Về các font tiếng Anh thì không sao, do nó vốn khá nhẹ, có nhét cả vào file pdf cũng không gây ảnh hưởng lớn lắm. Nhưng đối với font tiếng Nhật thì đây đúng là một vấn đề, khi mà bản thân file font đã rất nặng rồi.
Bạn có thể khắc phục việc này bằng cách set option isFontSubsettingEnabled
bằng true
. Mình không chắc lắm, nhưng check qua code ở file https://github.com/barryvdh/laravel-dompdf/blob/master/src/PDF.php thì có vẻ bạn có thể thực hiện thông qua hàm setOptions
, kiểu như setOptions(['isFontSubsettingEnabled' => true])
, hoặc là sửa trong file config option enable_font_subsetting
từ false
sang true
(https://github.com/barryvdh/laravel-dompdf/blob/master/config/dompdf.php)
Với việc enable_font_subsetting
thì dompdf
sẽ chỉ đưa những ký tự được dùng vào trong file pdf, thay vì đưa toàn bộ nội dung font như trước, qua đó sẽ giúp file pdf của bạn nhẹ đi rất nhiều.
Tuy nhiên, trước đây khi mình dùng cách đó, thì lại gặp một vấn đề khác là file pdf xuất ra thỉnh thoảng lại không mở được trên một vài trình pdf viewer Không rõ giờ vấn đề đã được khắc phục chưa, bạn cứ thử xem sao
Cách deploy APIs và trang admin viết trong cùng một bộ source ở 2 host với 2 domain khác nhau
Chào mọi người, mình có chút vấn đề khi deploy mong mọi người giúp đỡ. Hiện tại bộ source dự án của mình đang viết chung cho cả APIs và trang quản lý, bây giờ mình muốn deploy 2 cái trên 2 host khác nhau và 2 tên miền khác nhau và cả hai đều cùng trỏ chung vào một database (đặt ở host deploy phần API) thì có được không nhỉ.
Hoàn toàn được nhé bạn
Có cách nào đảm bảo là domain cho API thì chỉ gọi dc API và domain cho trang quản trị thì chỉ được vào trang quản trị thôi.
Ý bạn là ở phần server API thì không cho phép người dùng vào trang quản trị, còn ở trang quản trị thì không cho phép người dùng gọi API đúng không nhỉ Không rõ bạn đang sử dụng ngôn ngữ gì để viết backend, nên mình cũng không dám đưa ra hướng dẫn chi tiết, nhưng bạn có thể tìm cách disable hết các route liên quan đến trang admin khi deploy ở server API, và ngược lại
Nếu trang quản trị gọi database đặt ở host đang deploy phần API thì có bị chậm đi nhiều không nhỉ.
Tuỳ thuộc vào 2 server bạn sử dụng ở trường hợp này là như thế nào. Nếu bạn dùng 2 server thuộc cùng một nhà cung cấp, cùng một vùng địa lý thì tốc độ mạng giữa chúng chắc sẽ rất tốt. Bạn cứ thử vào một trong 2 server để ping đến server kia xem sao. Ping time giữa chúng rơi vào hàng 1ms, hoặc thấp hơn, thì bạn có thể yên tâm là tốc độ service cũng không bị ảnh hưởng nhiều đâu, bởi thời gian đó nhỏ hơn nhiều so với thời gian Database của bạn thực hiện truy vấn để trả về dữ liệu
Nếu bây giờ viết lại phần quản trị ở bộ source khác thì hơi thốn nên mong mọi người giúp đỡ, cho mình chút lời khuyên ạ, cảm ơn ạ
Bạn cũng không cần phải viết lại toàn bộ phần quản trị mà, chỉ là tách riêng nó ra để quản lý ở một repository khác, và remove các phần code không cần thiết (của API) đi Phần source code của API bạn cũng làm tương tự, như vậy thì cũng có thể giải quyết được vấn đề bạn đề cập ở trên, là không cho truy cập phần API từ service quản trị ... Có thể nó sẽ khiến bạn tốn nhiều công sức ban đầu để chỉnh sửa lại code, nhưng đó là cách mà mình nghĩ bạn nên cân nhắc thực hiện sau này, để có thể quản lý, và maintain codes tốt hơn
Lấy ảnh trên S3 Amazon chậm
Đối với dịch vụ hướng đến người dùng là users ở Việt Nam thì bạn chọn AWS region Singapore là tốt nhất rồi
Việc hiển thị ảnh thì là ở phía người dùng, nhanh hay chậm thì còn phụ thuộc vào tốc độ mạng của người dùng nên mình cũng không rõ là vấn đề của bạn về việc "request lấy hình rất chậm" là bạn test ở trên máy nào, với đường truyền ra sao. Bạn nên thử ở nhiều máy khác, với những đường truyền khác xem. Về server EC2 cũng tương tự như vậy.
Đương nhiên là về tốc độ sẽ không thể nào so sánh với việc lưu trữ trên một server đặt ở Việt Nam được. Hơn nữa, chi phí bỏ ra để sử dụng EC2 của AWS cũng đắt hơn nhiều so với việc thuê server ở Việt Nam.
Tuy nhiên đổi lại bạn nhận được tính khả dụng, tính ổn định, và khả năng mở rộng rất cao đến từ các dịch vụ của AWS (trong khi dùng server ở Việt Nam thì bạn phải lo rất nhiều thứ về infrastructure). Nếu dịch vụ của bạn coi trọng những thứ đó thì mình khuyên bạn vẫn hãy nên gắn bó với sự lựa chọn hiệu tại của mình