NTM Solutions

Thứ Ba, 15 tháng 9, 2026

📘 LARAVEL 13 — BÀI 39 — STORAGE

Storage là hệ thống quản lý file của Laravel.

Thay vì tự xử lý đường dẫn file bằng PHP thuần, Laravel cung cấp Storage để làm việc với upload, lưu trữ, đọc, xóa, di chuyển và tạo URL cho file một cách thống nhất.

Trong Blog CMS của chúng ta, Storage đặc biệt quan trọng vì hệ thống có:

  • Upload ảnh bài viết.

  • Hiển thị ảnh bài viết.

  • Thay thế ảnh cũ.

  • Xóa ảnh khi xóa Post.

  • Lưu đường dẫn ảnh vào Database.

  • Quản lý file public/private.

  • Sau này có thể chuyển từ Local Storage sang S3 hoặc dịch vụ tương thích S3.

Laravel 13 sử dụng filesystem abstraction dựa trên Flysystem, cho phép ứng dụng sử dụng cùng một API khi làm việc với Local, SFTP, Amazon S3 và các filesystem tương thích S3.

📌 Ghi chú:

🚀 Nếu đã upload mã nguồn lên host thật, nên chọn Amazon S3 ngay từ đầu (vì số lượng 📷 ảnh và 🎬 videos chèn trong posts sẽ rất lớn trong tương lai) 👉 xem mục 35.

🛠️ Mã nguồn hoàn chỉnh CRUD post có kèm upload ảnh đã có sẵn trong Bài 20 — Upload File 📤 và Bài 27 — CRUD Posts 📝.


1. Storage là gì?

Trong PHP thuần, nếu muốn upload file, chúng ta thường phải làm việc trực tiếp với:

move_uploaded_file()

và tự xử lý:

đường dẫn
tên file
thư mục
quyền truy cập
URL
xóa file

Laravel đơn giản hóa toàn bộ quá trình này thông qua:

use Illuminate\Support\Facades\Storage;

Ví dụ:

Storage::put(
    'example.txt',
    'Hello Laravel 13'
);

Laravel sẽ xử lý việc lưu file thông qua filesystem đang được cấu hình.


2. Filesystem trong Laravel

File cấu hình chính:

config/filesystems.php

Có thể hình dung:

Laravel Application
        │
        ▼
   Storage API
        │
 ┌──────┼───────────┐
 ▼      ▼           ▼
Local  SFTP        S3

Điểm quan trọng:

Code Laravel có thể gần như không thay đổi khi chuyển nơi lưu trữ file.

Ví dụ:

Storage::put('photos/image.jpg', $content);

Sau này có thể chuyển từ Local sang S3 bằng cấu hình disk thay vì viết lại toàn bộ logic upload.


3. Storage Disk

Laravel gọi mỗi nơi lưu trữ là một Disk.

Ví dụ:

local
public
s3

Có thể hình dung:

Storage
   │
   ├── local
   │
   ├── public
   │
   └── s3

Khi gọi:

Storage::put(...)

Laravel sử dụng default disk.

Nếu muốn chỉ rõ disk:

Storage::disk('public')->put(...);

4. Local Disk

Trong Laravel 13, local disk mặc định lưu file tương đối với:

storage/app/private

Ví dụ:

Storage::disk('local')->put(
    'example.txt',
    'Hello Laravel'
);

File sẽ nằm trong vùng private của ứng dụng.

Theo cấu hình filesystem mặc định của Laravel 13, local driver sử dụng storage/app/private.


5. Public Disk

Nếu file cần được trình duyệt truy cập trực tiếp, Laravel cung cấp:

public

disk.

Mặc định:

storage/app/public

Ví dụ:

Storage::disk('public')->put(
    'images/example.jpg',
    $content
);

File sẽ được lưu vào:

storage/app/public/images/example.jpg

Nhưng chỉ lưu file ở đây chưa đủ.


6. storage:link

Để file trong:

storage/app/public

có thể truy cập thông qua web, Laravel sử dụng symbolic link:

public/storage
        ↓
storage/app/public

Tạo link:

php artisan storage:link

Sau đó:

public/storage

sẽ trỏ tới:

storage/app/public

Laravel khuyến nghị sử dụng symbolic link này để những file public trong storage/app/public có thể được truy cập từ web.


7. Cấu trúc thư mục

Sau khi chạy:

php artisan storage:link

có thể hình dung:

project/
│
├── app/
├── public/
│   ├── index.php
│   └── storage/
│
├── storage/
│   └── app/
│       ├── private/
│       └── public/
│           ├── images/
│           └── posts/
│
└── config/
    └── filesystems.php

Quan hệ:

public/storage
       │
       ▼
storage/app/public

8. Storage Facade

Import:

use Illuminate\Support\Facades\Storage;

Ví dụ:

Storage::put(
    'hello.txt',
    'Laravel 13'
);

Đọc file:

$content = Storage::get('hello.txt');

Kiểm tra file:

if (Storage::exists('hello.txt')) {
    // File tồn tại
}

9. Lưu file bằng put()

Ví dụ:

Storage::put(
    'documents/example.txt',
    'Hello Laravel 13'
);

Nếu sử dụng:

Storage::disk('public')->put(
    'documents/example.txt',
    'Hello Laravel 13'
);

file sẽ nằm trong public disk.


10. Lưu UploadedFile bằng store(---)

Đây là phần quan trọng nhất đối với Blog CMS.

Giả sử Form:

<form
    method="POST"
    enctype="multipart/form-data"
>
    @csrf

    <input
        type="file"
        name="image"
    >

    <button type="submit">
        Upload
    </button>
</form>

Trong Controller:

$image = $request->file('image');

$path = $image->store('posts', 'public');

Laravel sẽ lưu file vào:

storage/app/public/posts/

và trả về path, ví dụ:

posts/abc123.jpg

Laravel sử dụng tên file duy nhất khi store() được gọi, đồng thời xác định extension dựa trên MIME type của file.


11. Lưu ảnh Blog Post

Ví dụ Controller:

if ($request->hasFile('image')) {

    $path = $request->file('image')
        ->store('posts', 'public');

    $post->image = $path;
}

Database chỉ cần lưu:

posts/abc123.jpg

Không nên lưu toàn bộ:

D:\www\...

hoặc:

http://localhost/...

Database nên lưu relative path.

Ví dụ:

posts/abc123.jpg

12. Vì sao Database chỉ lưu Path?

Giả sử:

Database

image
----------------------
posts/abc123.jpg

Local:

storage/app/public/posts/abc123.jpg

Sau này chuyển sang S3:

S3
└── posts/abc123.jpg

Database vẫn có thể giữ:

posts/abc123.jpg

Code chỉ cần thay đổi disk hoặc cấu hình Storage.

Đây là một trong những lợi ích lớn của filesystem abstraction.


13. Hiển thị ảnh

Sau khi chạy:

php artisan storage:link

có thể sử dụng:

<img
    src="{{ Storage::url($post->image) }}"
    alt="{{ $post->title }}"
>

Import trong Blade:

@php
    use Illuminate\Support\Facades\Storage;
@endphp

Hoặc trong nhiều trường hợp có thể dùng:

<img
    src="{{ asset('storage/' . $post->image) }}"
    alt="{{ $post->title }}"
>

Tuy nhiên:

Storage::url()

là cách phù hợp hơn khi muốn code phụ thuộc vào filesystem disk thay vì hard-code đường dẫn /storage. Laravel cung cấp url() để tạo URL tương ứng với disk đang sử dụng.


14. Storage::url()

Ví dụ:

$url = Storage::url(
    'posts/abc123.jpg'
);

Với local public disk, URL thường có dạng:

/storage/posts/abc123.jpg

Với S3:

https://bucket.example.com/posts/abc123.jpg

Như vậy Controller/Blade không nhất thiết phải biết file đang nằm ở Local hay Cloud.


15. storeAs()

store() tự tạo tên file.

Nếu muốn tự đặt tên:

$path = $request->file('image')
    ->storeAs(
        'posts',
        'my-post.jpg',
        'public'
    );

Kết quả:

storage/app/public/posts/my-post.jpg

Cấu trúc:

storeAs(
    $directory,
    $filename,
    $disk
);

16. hashName()

Laravel cũng cung cấp:

$file->hashName();

Ví dụ:

$name = $request->file('image')->hashName();

Kết quả có thể giống:

X7k8p9Lm2.jpg

Đây là một cách tránh các vấn đề khi người dùng upload nhiều file có cùng tên.


17. Lấy Extension

Có thể lấy extension:

$extension = $file->extension();

Ví dụ:

jpg

hoặc:

png

Laravel xác định extension dựa trên MIME type thay vì chỉ tin vào tên file do client gửi lên.


18. Lấy MIME Type

Có thể sử dụng:

$mime = Storage::mimeType(
    $post->image
);

Ví dụ:

image/jpeg
image/png

19. Kiểm tra file tồn tại

if (Storage::disk('public')->exists(
    $post->image
)) {
    // File tồn tại
}

Điều này rất hữu ích trước khi:

  • Xóa file.

  • Đọc file.

  • Hiển thị file.

  • Kiểm tra file cũ trước khi thay thế.


20. Xóa File

Để xóa:

Storage::disk('public')->delete(
    $post->image
);

Ví dụ:

if ($post->image) {
    Storage::disk('public')->delete(
        $post->image
    );
}

Đây chính là logic phù hợp với Blog CMS của chúng ta.


21. Xóa ảnh cũ khi Update Post

Đây là trường hợp rất thực tế.

Admin đang có:

posts/old-image.jpg

Sau đó upload:

posts/new-image.jpg

Nếu chỉ update Database:

Database
    ↓
new-image.jpg

thì:

old-image.jpg

vẫn nằm trong Storage.

Kết quả:

Database
   ↓
new-image.jpg

Storage
   ├── old-image.jpg   ← rác
   └── new-image.jpg

Do đó phải xóa ảnh cũ.


22. Ví dụ Update Post hoàn chỉnh

use Illuminate\Support\Facades\Storage;

public function update(Request $request, Post $post)
{
    $validated = $request->validate([
        'title' => ['required', 'string', 'max:255'],
        'slug' => ['required', 'string', 'max:255'],
        'excerpt' => ['nullable', 'string'],
        'content' => ['required', 'string'],
        'image' => [
            'nullable',
            'image',
            'max:2048',
        ],
    ]);

    if ($request->hasFile('image')) {

        if ($post->image) {
            Storage::disk('public')
                ->delete($post->image);
        }

        $validated['image'] = $request
            ->file('image')
            ->store('posts', 'public');
    }

    $post->update($validated);

    return redirect()
        ->route('posts.index')
        ->with(
            'success',
            'Cập nhật bài viết thành công.'
        );
}

Ở đây:

'max:2048'

tương đương:

2048 KB ≈ 2 MB

23. Xóa Post và ảnh

Khi xóa Post:

public function destroy(Post $post)
{
    if ($post->image) {
        Storage::disk('public')
            ->delete($post->image);
    }

    $post->delete();

    return redirect()
        ->route('posts.index')
        ->with(
            'success',
            'Xóa bài viết thành công.'
        );
}

Điểm quan trọng:

if ($post->image)

để tránh cố gắng xóa một path rỗng hoặc null.


24. Copy File

Có thể copy:

Storage::copy(
    'posts/old.jpg',
    'posts/new.jpg'
);

Hoặc chỉ rõ disk:

Storage::disk('public')->copy(
    'posts/old.jpg',
    'posts/new.jpg'
);

25. Move File

Di chuyển:

Storage::move(
    'posts/old.jpg',
    'posts/new.jpg'
);

Ví dụ:

posts/draft/image.jpg

sang:

posts/published/image.jpg

26. Liệt kê Files

Lấy danh sách file trong một thư mục:

$files = Storage::files('posts');

Nếu muốn lấy cả file trong thư mục con:

$files = Storage::allFiles('posts');

Laravel cung cấp cả files()allFiles() cho việc liệt kê file.


27. Làm việc với Directory

Tạo thư mục:

Storage::makeDirectory('posts');

Xóa thư mục:

Storage::deleteDirectory('posts');

Lấy danh sách directory:

$directories = Storage::directories('posts');

Hoặc toàn bộ directory con:

$directories = Storage::allDirectories('posts');

28. File Size

Có thể lấy kích thước file:

$size = Storage::size(
    'posts/example.jpg'
);

Kết quả tính bằng:

bytes

Ví dụ:

204800

Có thể chuyển thành:

200 KB

29. Last Modified

Lấy thời điểm file được chỉnh sửa:

$time = Storage::lastModified(
    'posts/example.jpg'
);

Kết quả là UNIX timestamp.


30. File Visibility

Laravel có khái niệm:

public
private

Public

File có thể được người khác truy cập.

Ví dụ:

Ảnh bài viết
Ảnh sản phẩm
Avatar

Private

File không nên được truy cập trực tiếp.

Ví dụ:

Tài liệu cá nhân
File nội bộ
File download cần kiểm tra quyền

Laravel filesystem abstraction sử dụng visibility để biểu diễn quyền truy cập file trên các backend khác nhau.


31. Lưu File Public

Ví dụ:

Storage::put(
    'posts/example.jpg',
    $content,
    'public'
);

Hoặc:

Storage::disk('public')->put(
    'posts/example.jpg',
    $content
);

32. Private File

Không phải tất cả file đều nên nằm trong:

storage/app/public

Nếu file nhạy cảm hoặc cần kiểm tra quyền trước khi download, có thể sử dụng private storage.

Ví dụ:

storage/app/private/documents/

Controller kiểm tra quyền:

if (! $user->can('download', $document)) {
    abort(403);
}

Sau đó mới trả file cho người dùng.


33. Download File

Laravel có thể trả file download thông qua:

return Storage::download(
    'documents/example.pdf'
);

Có thể chỉ định tên file download:

return Storage::download(
    'documents/example.pdf',
    'tai-lieu.pdf'
);

Đây là một mô hình phù hợp cho:

Private files
      ↓
Authorization
      ↓
Download

thay vì để file public trực tiếp.


34. Temporary URL

Laravel hỗ trợ URL có thời hạn:

$url = Storage::temporaryUrl(
    'file.jpg',
    now()->addMinutes(5)
);

Ví dụ:

URL
 ↓
có hiệu lực 5 phút
 ↓
hết hạn
 ↓
không truy cập được nữa

Điều này đặc biệt hữu ích với file private hoặc cloud storage. Laravel 13 hỗ trợ temporary URLs cho local và S3 trong các cấu hình phù hợp.


35. Amazon S3

Khi Website lớn hơn, có thể chuyển file sang:

Amazon S3

Laravel hỗ trợ S3 thông qua Flysystem.

Cài package:

composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies

Sau đó cấu hình:

AWS_ACCESS_KEY_ID=your-key
AWS_SECRET_ACCESS_KEY=your-secret
AWS_DEFAULT_REGION=your-region
AWS_BUCKET=your-bucket
AWS_USE_PATH_STYLE_ENDPOINT=false

Sau đó:

FILESYSTEM_DISK=s3

Code upload có thể vẫn rất giống:

$path = $request->file('image')
    ->store('posts', 's3');

Đây chính là lợi ích của filesystem abstraction.


36. S3-Compatible Storage

Không chỉ Amazon S3.

Laravel 13 có thể làm việc với các storage tương thích S3 như:

Cloudflare R2
DigitalOcean Spaces
Vultr Object Storage
Hetzner Cloud Storage
RustFS

Thông thường chỉ cần cấu hình credentials và endpoint phù hợp.


37. Upload File trong Blog CMS

Quy trình của Blog CMS:

User
 │
 ▼
Form Upload
 │
 ▼
Validation
 │
 ▼
UploadedFile
 │
 ▼
Storage
 │
 ▼
storage/app/public/posts
 │
 ▼
Database
 │
 └── posts/abc123.jpg

Database:

posts.image
       ↓
posts/abc123.jpg

Storage:

storage/app/public/posts/abc123.jpg

URL:

/storage/posts/abc123.jpg

38. Không nên lưu file trực tiếp vào Database

Không nên thiết kế:

posts
----------------
image = BINARY DATA

cho ảnh Blog thông thường.

Thay vào đó:

posts
----------------
image = posts/abc123.jpg

File:

Storage
   ↓
posts/abc123.jpg

Database chỉ quản lý:

Path

Storage quản lý:

File

39. Storage và Upload Validation

Storage không thay thế Validation.

Trước khi lưu file:

$request->validate([
    'image' => [
        'nullable',
        'image',
        'max:2048',
    ],
]);

Sau đó:

$request->file('image')
    ->store('posts', 'public');

Quy trình:

Upload
  ↓
Validation
  ↓
Storage

Không nên:

Upload
  ↓
Storage ngay

mà không kiểm tra file.


40. Storage và Blog CMS của chúng ta

Trong project hiện tại, Post có:

image

nên quy ước có thể là:

storage/app/public/posts/

Ví dụ:

posts/
├── a8d91.jpg
├── b73k2.png
└── c91mx.webp

Database:

posts.image

chỉ lưu:

posts/a8d91.jpg

Khi hiển thị:

<img
    src="{{ Storage::url($post->image) }}"
    alt="{{ $post->title }}"
>

Khi thay ảnh:

Xóa ảnh cũ
     ↓
Upload ảnh mới
     ↓
Lưu path mới

Khi xóa Post:

Xóa file
     ↓
Xóa Database record

41. Một Controller hoàn chỉnh

Ví dụ phần Store:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

public function store(Request $request)
{
    $validated = $request->validate([
        'title' => ['required', 'string', 'max:255'],
        'slug' => ['required', 'string', 'max:255'],
        'excerpt' => ['nullable', 'string'],
        'content' => ['required', 'string'],
        'category_id' => [
            'nullable',
            'exists:categories,id',
        ],
        'image' => [
            'nullable',
            'image',
            'max:2048',
        ],
    ]);

    if ($request->hasFile('image')) {
        $validated['image'] = $request
            ->file('image')
            ->store('posts', 'public');
    }

    $validated['user_id'] = $request->user()->id;

    Post::create($validated);

    return redirect()
        ->route('posts.index')
        ->with(
            'success',
            'Tạo bài viết thành công.'
        );
}

Đây là flow hoàn chỉnh:

Validate
   ↓
Check File
   ↓
Store File
   ↓
Get Path
   ↓
Add user_id
   ↓
Create Post

42. Controller Update hoàn chỉnh

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

public function update(
    Request $request,
    Post $post
) {
    $validated = $request->validate([
        'title' => ['required', 'string', 'max:255'],
        'slug' => ['required', 'string', 'max:255'],
        'excerpt' => ['nullable', 'string'],
        'content' => ['required', 'string'],
        'category_id' => [
            'nullable',
            'exists:categories,id',
        ],
        'image' => [
            'nullable',
            'image',
            'max:2048',
        ],
    ]);

    if ($request->hasFile('image')) {

        if ($post->image) {
            Storage::disk('public')
                ->delete($post->image);
        }

        $validated['image'] = $request
            ->file('image')
            ->store('posts', 'public');
    }

    $post->update($validated);

    return redirect()
        ->route('posts.index')
        ->with(
            'success',
            'Cập nhật bài viết thành công.'
        );
}

43. Controller Destroy hoàn chỉnh

use Illuminate\Support\Facades\Storage;

public function destroy(Post $post)
{
    if ($post->image) {
        Storage::disk('public')
            ->delete($post->image);
    }

    $post->delete();

    return redirect()
        ->route('posts.index')
        ->with(
            'success',
            'Xóa bài viết thành công.'
        );
}

Đây là pattern mà Blog CMS nên sử dụng:

Delete Database
+
Delete Physical File

tránh tình trạng:

Database đã xóa
nhưng file vẫn tồn tại

44. Laravel 13 và Read-Through Filesystem

Laravel 13 có thêm một filesystem driver đáng chú ý:

read-through

Mô hình:

Application
     │
     ▼
Primary Disk
     │
     │ không có file
     ▼
Fallback Disk

Nếu file chỉ tồn tại ở fallback disk, Laravel có thể đọc file từ đó và đưa bản sao sang primary disk để các request sau sử dụng primary. Đây là tính năng hữu ích khi muốn di chuyển file giữa các storage mà không phải downtime.

Đây là nội dung nâng cao.

Người mới học Storage chưa cần sử dụng ngay.


45. Storage Testing

Laravel cũng hỗ trợ Fake Storage để kiểm thử upload.

Ví dụ:

Storage::fake('photos');

Sau đó test upload:

Storage::disk('photos')
    ->assertExists('photo.jpg');

Laravel cung cấp Storage::fake() để tạo filesystem giả, giúp kiểm thử upload mà không cần ghi file thật vào storage.

Phần này sẽ được giới thiệu lại kỹ hơn trong Bonus PHPUnit của khóa học.


46. Những lỗi thường gặp

Lỗi 1 — Không chạy storage:link

Upload thành công nhưng:

ảnh không hiển thị

Kiểm tra:

php artisan storage:link

Lỗi 2 — Sai Disk

Lưu:

Storage::disk('public')

nhưng lại lấy:

Storage::disk('local')

Có thể dẫn tới không tìm thấy file.


Lỗi 3 — Database lưu sai đường dẫn

Không nên lưu:

/storage/posts/image.jpg

hoặc:

C:\xampp\...

Nên lưu:

posts/image.jpg

Lỗi 4 — Xóa Database nhưng quên xóa file

$post->delete();

nhưng không:

Storage::disk('public')
    ->delete($post->image);

Kết quả:

Database
   ↓
file đã mất record

Storage
   ↓
file rác vẫn còn

Lỗi 5 — Upload ảnh mới nhưng quên ảnh cũ

Khi Update Post:

old.jpg
new.jpg

Nếu không xóa:

old.jpg

Storage sẽ ngày càng phình to.


47. Tóm tắt các phương thức quan trọng

Phương thứcCông dụng
Storage::put()Lưu nội dung
Storage::get()Đọc file
Storage::exists()Kiểm tra tồn tại
Storage::delete()Xóa file
Storage::copy()Copy file
Storage::move()Di chuyển file
Storage::url()Tạo URL
Storage::download()Download
Storage::temporaryUrl()URL có thời hạn
Storage::files()Liệt kê file
Storage::allFiles()Liệt kê toàn bộ file
Storage::makeDirectory()Tạo thư mục
Storage::deleteDirectory()Xóa thư mục
Storage::size()Kích thước file
Storage::mimeType()MIME type
Storage::lastModified()Thời gian sửa cuối
$file->store()Upload file
$file->storeAs()Upload với tên chỉ định

48. Tổng kết

Trong bài này chúng ta đã tìm hiểu:

  • Storage là gì.

  • Filesystem của Laravel.

  • Disk.

  • Local Storage.

  • Public Storage.

  • storage:link.

  • Storage Facade.

  • put().

  • get().

  • exists().

  • store().

  • storeAs().

  • hashName().

  • Upload Image.

  • Storage URL.

  • Delete File.

  • Copy File.

  • Move File.

  • Directory.

  • File Metadata.

  • Public / Private Visibility.

  • Download.

  • Temporary URL.

  • Amazon S3.

  • S3-Compatible Storage.

  • Storage Testing.

  • Read-Through Filesystem trong Laravel 13.

  • Ứng dụng Storage vào Blog CMS.


🎯 BÀI TẬP THỰC HÀNH

Bài tập 1 — Upload ảnh Post

Tạo Form:

<input
    type="file"
    name="image"
>

Upload vào:

storage/app/public/posts

Bài tập 2 — Hiển thị ảnh

Sử dụng:

Storage::url($post->image)

để hiển thị ảnh trong Blog.


Bài tập 3 — Thay ảnh

Khi Admin upload ảnh mới:

Xóa ảnh cũ
     ↓
Upload ảnh mới
     ↓
Update Database

Bài tập 4 — Xóa Post

Khi xóa Post:

Xóa Image
     ↓
Xóa Post

Bài tập 5 — Kiểm tra Storage

Chạy:

php artisan storage:link

Sau đó kiểm tra:

storage/app/public

và:

public/storage

💡 GHI NHỚ

Database lưu thông tin về file.

Storage lưu file thật.

Public Disk dành cho những file cần truy cập công khai.

Private Storage dành cho những file cần kiểm soát quyền truy cập.

Khi thay hoặc xóa file, đừng quên xử lý file vật lý trong Storage.

Đối với Blog CMS:

                    POST
                     │
          ┌──────────┴──────────┐
          │                     │
      Database               Storage
          │                     │
          ▼                     ▼
image = posts/a.jpg    posts/a.jpg

Đây là cách Laravel tách biệt:

DATA

và:

FILES

giúp ứng dụng dễ bảo trì và sau này có thể chuyển từ Local Storage sang Cloud Storage mà không phải viết lại toàn bộ chức năng upload.

Bài tiếp theo: Bài 40 — Logging & Debug, tìm hiểu Log, Debugbar và Telescope để theo dõi, debug và quan sát ứng dụng Laravel trong quá trình phát triển.

x1
quay về MỤC LỤC

Không có nhận xét nào:

Đăng nhận xét

Facebook Youtube RSS