Laravel 13 thêm Cache::touch() để gia hạn thời gian sống của một cache item mà không cần đọc rồi ghi lại value. Thao tác nghe nhỏ, nhưng giải quyết đúng trường hợp dữ liệu vẫn còn hợp lệ và chỉ cần kéo dài TTL sau một hoạt động của người dùng.
Bài kiểm tra dùng Laravel 13.25.0 và file cache. Mục tiêu là xác nhận ba việc: value có bị đổi không, key đang tồn tại trả gì và key không tồn tại có bị tạo ngoài ý muốn hay không.
Mục lục [Hiển thị]
touch() khác put() ở đâu?
put() ghi value và TTL mới. Nếu chỉ muốn gia hạn, ứng dụng thường phải get() rồi put() lại, vừa dài vừa có cửa ghi đè dữ liệu vừa được tiến trình khác cập nhật. Tài liệu Laravel 13 mô tả touch() như một thao tác trực tiếp trên TTL của item hiện có; API này cũng được liệt kê trong release notes Laravel 13.
use Illuminate\Support\Facades\Cache;
$extended = Cache::touch('cart:42', 3600);
if (! $extended) {
// Key không tồn tại hoặc store không gia hạn được.
}
Tham số thứ hai nhận số giây, DateTimeInterface, DateInterval hoặc một instance Carbon. Giá trị trả về là boolean, vì vậy không nên bỏ qua nếu logic phía sau phụ thuộc vào việc TTL đã được đổi thật. Có thể đối chiếu cách facade chuyển lời gọi xuống store trong mã nguồn Illuminate Cache.

Kết quả chạy trên Laravel 13.25
Fixture tạo key catchup:touch với value {"version": 1} và TTL 60 giây. Sau đó touch() đổi TTL thành 3.600 giây. Kết quả trả true, còn value trước và sau giống nhau. Khi gọi cùng thao tác cho key không tồn tại, kết quả là false và Laravel không tự tạo item rỗng.

Điểm này quan trọng với session phụ, giỏ hàng tạm, trạng thái import hoặc token nội bộ: gia hạn được không đồng nghĩa item chắc chắn tồn tại. Nếu key có thể hết hạn giữa hai thao tác, code phải xử lý nhánh false thay vì giả định mọi thứ ổn.
Một ví dụ gia hạn giỏ hàng đúng chỗ
public function keepCartAlive(int $userId): void
{
$key = "cart:{$userId}";
if (! Cache::touch($key, now()->addMinutes(30))) {
report("Không thể gia hạn {$key}");
}
}
Không nên gọi đoạn này ở mọi request. Chỉ gia hạn khi có tín hiệu hoạt động liên quan, chẳng hạn thêm sản phẩm hoặc mở lại trang checkout. Nếu không, một bot hay tab bị bỏ quên có thể giữ cache lâu hơn chính sách sản phẩm mong muốn.
Những điểm cần kiểm tra trước khi dùng
| Kiểm tra | Lý do |
|---|---|
| Cache store đang dùng | Đảm bảo driver hỗ trợ thao tác và hành vi TTL mong đợi |
Nhánh trả về false | Key có thể đã hết hạn hoặc không tồn tại |
| Đơn vị TTL | Số nguyên là giây; ngày giờ giúp ý định rõ hơn |
| Giới hạn thời gian tối đa | Tránh gia hạn vô hạn dữ liệu đáng lẽ phải hết hạn |
Nếu dự án vẫn ở Laravel 12, xem trước đường nâng cấp Laravel 13 rồi mới dùng API mới. Với công cụ Composer chạy một lần, bài CPX 2.0 cũng có ví dụ cách cô lập thử nghiệm khỏi project chính.
Race condition vẫn cần được tính tới
touch() bỏ được vòng đọc rồi ghi lại value, nhờ đó giảm một kiểu race condition phổ biến. Tuy nhiên key vẫn có thể hết hạn ngay trước lúc store xử lý lệnh. Boolean false chính là tín hiệu để ứng dụng chọn tạo lại dữ liệu, trả người dùng về bước trước hoặc ghi log tùy mức độ quan trọng.
Không nên biến nhánh thất bại thành một lệnh put() tự động nếu không có value đáng tin cậy. Ví dụ cache giỏ hàng chứa nhiều dòng sản phẩm mà request hiện tại chỉ biết user ID; tạo một mảng rỗng để “sửa” lỗi có thể xóa dấu vết cần phục hồi. Với dữ liệu có nguồn gốc rõ ràng, có thể dùng remember() để dựng lại; với dữ liệu phiên tạm, nên báo hết hạn.
Nên test những store nào?
Fixture của bài dùng file cache để chứng minh contract cơ bản, nhưng production có thể chạy Redis, Memcached hoặc database. Test tích hợp nên chạy đúng driver đang deploy và bao phủ ít nhất bốn trường hợp: key tồn tại, key thiếu, TTL dạng số giây và thời điểm Carbon. Nếu hệ thống có nhiều worker, thêm một test hai tiến trình cùng cập nhật value và TTL để chắc rằng logic nghiệp vụ không phụ thuộc vào thứ tự mong manh.
Cũng cần quan sát TTL trực tiếp bằng công cụ của store. Với Redis, xem TTL key trước và sau; với database cache, kiểm tra cột expiration. Assertion chỉ đọc lại value chưa đủ để chứng minh thời gian sống đã thay đổi, vì value giống nhau là điều touch() cố tình giữ.
Kết luận
Cache::touch() hợp khi value còn đúng và chỉ TTL cần thay đổi. API ngắn hơn vòng get()/put(), nhưng boolean trả về vẫn phải được xử lý. Fixture Laravel và toàn bộ dependency tải để test đã được xóa sau khi hoàn tất.