+3

Tìm hiểu về File Upload Laravel.

Introduction

Trong bài viết này chúng ta sẽ chúng ta sẽ tìm hiểu về các sử lý file trong Laravel. Hầu hết các ứng dụng đều liên quan đến việc tải file thuộc một số loại như: ảnh, pdf... Trong phạm vi bài viết này chúng ta sẽ cùng xây dựng một ứng dụng đơn giản trong đó người dùng cần nhập thông tin của mình và ảnh. Trong ứng dụng này chúng ta cần xử lý quá trình tải tệp lên, xử lý xác thực để tải lên và biết phải làm gì khi tệp tải lên không thành công.

Setting Up

Để bắt đầu chúng ta cần cài đặt laravel nếu của bạn đã được cài đặt trước đó hãy bỏ qua bước này.

composer global require laravel/installer

Sau khi command trên hoàn tất chúng ta bắt đầu tạo ứng dụng laravel.

laravel new profile-form

Chạy lệnh trên sẽ tạo ra một ứng dụng laravel là profile-form và lưu trong thư mục hiện tại với tên profile-form. Khởi chaỵ serve ứng dụng:

cd profile-form
php artisan serve

Hãy mở link : http://localhost:8000/ bạn sẽ thấy hiển thị landing mặc định của laravel.

Writing the Database Migration

Chúng ta cần tạo một bảng users để lưu thông tin chi tiết về người dùng. Laravel cung cấp sẵn bản users được tìm thấy tại: database\migrations\2014_10_12_000000_create_users_table.php hãy chỉnh sửa nó.

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->string('photo');
            $table->rememberToken();
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

Sửa đổi file .env để có được một kết nối đến cơ sở dữ liệu & chạy cmd sau:

php artisan migrate

Cập nhật User model:

// App\User.php

protected $fillable = [
    'name', 'email', 'password', 'photo'
];

Sau khi hoàn tất chúng ta sẽ tiến hành xây dựng giao diện biểu mẫu.

Building the Frontend

Tạo file resources/views/register.blade.php với nội dung sau:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Register | Profile Form</title>
    <!-- Fonts -->
    <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <!-- Styles -->
    <style>
      .form-box{
        display: flex;
        justify-content: center;
        flex-direction: column;
        align-items: center;
        margin-bottom: 10px
      }
    </style>
  </head>
  <body>
    <nav class="navbar navbar-expand-sm navbar-light bg-light">
      <a class="navbar-brand" href="#">Profile Form</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
          <li class="nav-item active">
            <a class="nav-link" href="#">Register <span class="sr-only">(current)</span></a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#">Login</a>
          </li>
        </ul>
        <form class="form-inline my-2 my-lg-0">
          <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
          <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
        </form>
      </div>
    </nav>
    <div class="container">
      <h1>Register</h1>
      <section class="form-box">
        <form action="#" class="col-md-5">
          <div class="form-group">
            <label for="name">Full Name</label>
            <input type="text" id="name" name="name"  class="form-control" required>
          </div>
          <div class="form-group">
            <label for="email">Email Address</label>
            <input type="email" name="email" id="email" class="form-control" required>
          </div>
          <div class="form-group">
            <label for="password">
              Password
            </label>
            <input type="password" name="password" id="password" class="form-control" required>
          </div>
          <div class="form-group">
            <label for="password_confirmation">Confirm Password</label>
            <input type="password" name="password_confirmation" id="password_confirmation" class="form-control" required>
          </div>
          <div class="form-group">
            <label for="photo">Attach a photograph</label>
            <input type="file" name="photo" id="photo" class="form-control-file" required>
          </div>
          <div class="form-group">
            <button type="submit" class="btn btn-outline-primary">Submit</button>
          </div>
        </form>
      </section>
    </div>
  </body>
</html>

Chúng ta sẽ sử dụng bootstrap cho việc css. Chúng ta đã tạo tất cả các trường đều là bắt buộc và người dùng.

Trường đặc biệt quan trọng đối với chúng tôi ở đây là <input type="file". Cập nhật web.php để có route đến trang đăng ký.

// web.php
...
Route::get('/register', function () {
    return view('register');
});

Đến trang http://localhost:8000/register sẽ hiển thị trang đăng ký.

Chúng ta cần chỉ định loại tệp được hỗ trợ, các tệp hình ảnh, chúng ta cần chỉ định trong phần tử đầu vào rằng chúng tôi chỉ muốn các tệp hình ảnh. Phần file sẽ như sau:

<div class="form-group">
  <label for="photo">Attach a photograph</label>
  <input type="file" name="photo" id="photo" accept="image/*" class="form-control-file">
</div>

Chúng ta cần thêm thuộc tính enctype="multipart/form-data" để có thế upload file.

<form action="#" class="col-md-5" enctype="multipart/form-data">

Tiếp theo chúng ta sẽ viết Controller để xử lý dữ liệu tải lên.

Writing the Controller

Phần backend của chúng ta có thể xử lý việc xác thực, lưu trữ và chuyển hướng, xác thực người dùng... Laravel đi kèm với RegisterControllervà có thể tìm thấy tại: app\Http\Controllers\Auth\RegisterController.php.

Validation

Phương thức validator trong RegisterController có chức năng là xác thực dữ liệu đầu vào. Chúng ta sẽ update lại để phản ánh những gì chúng ta mong muốn. Sửa đổi phương thức validator trong RegisterController.php

protected function validator(array $data)
{
    return Validator::make($data, [
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
        'password' => ['required', 'string', 'min:8', 'confirmed'],
        'photo' => ['required', 'image']
    ]);
}

Trường name là bắt buộc và là một chuỗi mắc 255 ký tự.

Tương tụ với trường email và password.

Trường mật khẩu cần được xác nhận. Trong giao diện người dùng, chúng ta đã thêm một trường cho password_confirmation. Đây là những gì trường mật khẩu được so sánh với giá trị nhập trước đó.

Laravel cũng có quy tắc xác thực images một các tiện dụng là tệp có một trong các đuôi mở rộng sau: jpeg, png, bmp, gif, or svg.Ngoài ra còn nhiều tùy chọn khác...

Storing

Ở bước tiếp theo là lưu trữ hình ảnh. Chúng ta sẽ tạo chức năng mới để xử lý: Tệp của chúng ta có thể lưu trữ được ở nhiều nơi khác nhau ví dụ: s3, google driver, local ... Để đơn giản ở bài viết này chúng ta sẽ lưu trữ tại local.

$request->file('photo')->store('profile')

Ảnh sẽ được lưu trức tại \app\profile folder. Tệp được lưu với tên file ngẫu nhiêu và url của tệp được trả về. Chúng ta có thể custom name của file sử dụng phương thức storeAs.

$request→file(<inputName>)storeAs(<folderToBeStored>, <customName.fileExtension>)

Phần mở rộng của file có thể được lấy sử dụng clientExtension.

$request->file('photo')->extension()

Sử dụng method này chúng ta có thể lưu trữ hình ảnh của người dùng dựa trên tên người dùng.

$fileName = $request->get('name') . '.' . $request->file('photo')->extension();        
$request->file('photo')->storeAs('profile', $fileName);

Tiếp theo chúng ta sẽ sử lý lưu hình ảnh vào storage. Thêm nội dung sau bên dưới function validator

protected function storeImage(Request $request) {
  $path = $request->file('photo')->store('public/profile');
  return substr($path, strlen('public/'));
}

Function storeImage lưu trữ ảnh trên path storage/app/public/profile và trả về url đến vị trí lưu file.

Putting it all together

Vì chúng ta đang chỉnh sửa nên chúng ta cần ghi đè phương thức mặc đinh do laravel cung cấp.Laravel cung cập mặc định đăng ký users được tìm thấy tại Illuminate\Foundation\Auth\RegistersUsers.php. Cập nhật RegisterController

protected function create(array $data)
{
    return User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'password' => Hash::make($data['password']),
        'photo' => $data['photo']
    ]);
}

public function register(Request $request)
{
    $this->validator($request->all())->validate();

    $imageUrl = $this->storeImage($request);

    $data = $request->all();
    $data['photo'] = $imageUrl;
    $user = $this->create($data);

    $this->guard()->login($user);

    return $this->registered($request, $user)
                    ?: redirect($this->redirectPath());
}

Như đã thấy ví dụ trên hàm tạo được thực hiện khi người dùng gửi đến nó. Chức năng đăng ký là nơi mọi thứ xảy ra. Việc validation được thực hiện đầu tiên trước bất kỳ việc nào khác. Nếu xác nhận được thông qua, chúng ta lưu trữ file. Sau khi hoàn tất, chúng ta tạo người dùng bằng cách chuyển tất cả dữ liệu vào mảng $data.

Nếu tất cả mọi việc thành công. Chúng ta xác thực users và chuyển đến trang chủ.

Showing the Uploaded Image

Trang chủ là nơi chúng tôi sẽ hiển thị tất cả các thông tin chi tiết về người dùng. Đầu tiên, hãy tạo route and controller. Run cmd:

php artisan make:controller HomeController

Cập nhật HomeController

<?php
namespace App\Http\Controllers;

use Illuminate\Support\Facades\Auth;

class HomeController extends Controller
{
    function __construct()
    {
        $this->middleware('auth');
    }
    public function show()
    {
        return view('home')->with('user', Auth::user());
    }
}

Trong hàm __contructor chúng ta xác định rằng chúng ta sử dụng auth middleware. Có nghĩa là chỉ users đã đăng nhập mới vào được trang này.

Cập nhật route

// web.php
...
Route::get('/home', 'HomeController@show')->name('home');

Cuối cùng tạo file resources/views/home.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Home | Profile Form</title>
    <!-- Fonts -->
    <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <style>
      body {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        height: 500px;
      }
      .card {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        width: 300px;
        padding: 50px 0;
      }
      .card-img-top {
        width: 200px;
        height: 200px;
        border-radius: 50%;
      }
    </style>
  </head>
  <body>
    <div class="card">
      <img src="https://via.placeholder.com/150" class="card-img-top" alt="...">
      <div class="card-body">
        <h5 class="card-title">{{$user->name}}</h5>
        <p class="card-text">{{$user->email}}</p>
        <a class="btn btn-warning">Logout</a>
      </div>
    </div>
  </body>
</html>

Chúng ta cần tạo môi liên kết link từ public/storage đến storage/app/public bằng câu lệnh cmd sau:

php artisan storage:link

Cập nhật img tags.

<img src="{{asset('storage/'.$user->photo)}}" class="card-img-top" alt="...">

Conclusion

Qua bài viết này bạn đã biết các xử lý file với laravel chúng bạn thành công.

References

https://laravel.com/docs/8.x/filesystem

https://codesource.io/handling-file-uploads-in-laravel/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí