- vừa được xem lúc

[Filament] Xây dựng chức năng hiển thị danh sách

0 0 1

Người đăng: Vương Minh Thái

Theo Viblo Asia

Lời nói đầu

  • Ở bài viết đầu tiên trong series, chúng ta đã cùng nhau tiến hành cài đặt và demo thử chức năng hiển thị danh sách bài post trong trang admin. Tuy nhiên đó mới chỉ là một phần nhỏ, trong nội dung bài viết này, chúng ta sẽ đi chi tiết hơn về các chức năng mà Filament cung cấp để có thể xây dựng chức năng hiển thị danh sách.

Nội dung

Các loại cột ở trong filament

  • Như ở bài trước chúng ta đã biết là Filament cung cấp sẵn cho chúng ta 2 loại cột là cột tĩnh (chỉ cố chức năng hiển thị) và cột động (có thể chỉnh sửa giá trị ngay ở trang danh sách). Vậy thì hôm nay chúng ta sẽ cùng đi tìm hiểu chi tiết về cách sử dụng từng loại cột đó

Cột tĩnh

  1. TextColumn: hiển thị dữ liệu dạng văn bản (text/number/datetime/...)

    use Filament\Tables\Columns\TextColumn; TextColumn::make('title')
    
    Định dạng dữ liệu Chức năng Tên hàm
    Số thứ tự Hiển thị STT của bản ghi ->rowIndex()
    STT bắt đầu đếm từ 0 thay vì 1 ->rowIndex(isFromZero: true)
    Any Thay đổi cỡ chữ ->size(TextColumn\TextColumnSize::XXX)
    Thay đổi font chữ ->fontFamily(FontFamily::XXXX)
    Thay đổi chữ in đậm ->weight(FontWeight::XXXX)
    Có thể click để copy văn bản ->copyable()
    ->copyMessage('message'): thông báo khi copy thành công
    ->copyMessageDuration(1500): thời gian hiện thông báo
    Văn bản Hiển thị dưới dạng badge ->badge()
    Thay đổi màu sắc ->color()
    Hiển thị thêm thông tin thêm bên dưới value ->description()
    Giới hạn số ký tự hiển thị ra ->limit()
    Giới hạn số từ hiển thị ra ->words()
    Giới hạn số dòng hiển thị ra ->lineClamp()
    Tự động xuống dòng nếu nội dung dài ->wrap()
    Tiền tệ Hiển thị theo từng loại đơn vị tiền ->money('đơn vị tiền')
    Thêm tham số divideBy để chia nhỏ giá trị tiền tệ
    VD: đồng USD sẽ có đơn vị cents, khi trong database lưu theo cents thì sẽ phải chia 100 để quy đổi ra thành 1USD
    ->money('đơn vị tiền', divideBy: 100)
    Ngày tháng Hiển thị mốc thời gian ->dateTime()
    Hiển thị khoảng thời gian từ mốc thời gian tới thời điểm hiện tại ->since()
    Hiển thị thêm thông tin thời gian dưới dạng tooltips ->dateTooltip()
    ->dateTimetooltip()
    ->timeTooltip()
    Số Hiển thị dữ liệu dạng số ->numeric()
    Hiển thị số thập phân với X số sau dấu phẩy ->numeric(decimalPlaces: X)
  2. IconColumn

    use Filament\Tables\Columns\IconColumn;
    IconColumn::make('status') ->icon(fn (string $state): string => match ($state) { 'draft' => 'heroicon-o-pencil', 'reviewing' => 'heroicon-o-clock', 'published' => 'heroicon-o-check-circle', })
    
    Kiểu dữ liệu Chức năng Tên hàm
    Any Thiết lập icon cho từng giá trị ->icon(xxxx)
    Thiết lập màu sắc cho từng giá trị ->color(xxxx)
    Thay đổi kích thước icon ->icon(xxxx)
    Boolean Filament sẽ set icon/màu sắc mặc định cho giá trị true/false ->boolean()
    Tùy chỉnh thay đổi icon cho giá trị true/false ->trueIcon('heroicon-o-check-badge')
    ->falseIcon('heroicon-o-x-mark')
    Tùy chỉnh thay đổi màu sắc cho giá trị true/false ->trueColor('info')
    ->falseColor('warning')
  3. ImageColumn: hiển thị dữ liệu hình ảnh

    use Filament\Tables\Columns\ImageColumn; ImageColumn::make('thumbnail')
    
    Chức năng Tên hàm
    Mặc định Filament sẽ lấy ảnh từ thư mục public trong dự án.
    Nếu muốn thay đổi địa chỉ lưu trữ ảnh thì sẽ phải khai báo lại
    ->disk('s3')
    Tùy chỉnh kích thước ảnh ->size(40)
    ->width(200)
    ->height(50)
    Sử dụng ảnh mặc định nếu không tìm thấy ảnh ->defaultImageUrl('url')
    Hiển thị ảnh vuông ->square()
    Hiển thị ảnh tròn ->circular()
    Hiển thị ảnh tròn ->stacked()
  4. ColorColumn: sử dụng cho trường dữ liệu màu sắc (HEX, RGB, RGBA, HSL), thường có trong website bán hàng (quần áo/điện thoại) 1 sản phẩm có nhiều màu sắc khác nhau

    use Filament\Tables\Columns\ColorColumn; ColorColumn::make('color')
    
    Chức năng Tên hàm
    Cho phép click vào để copy mã màu ->copyable()
    Hiển thị thông báo hardcode khi copy ->copyMessage('Message')
    Thiết lập thời gian hiển thị thông báo ->copyMessageDuration(2000)
    Tùy chỉnh thông báo sử dụng dữ liệu trong bảng ->copyableState(fn (Product $record): string => "Color: {$record->color}")

Cột động

Với các loại cột động (có thể chỉnh sửa trực tiếp ở màn hình danh sách), có thể sử dụng Hooks để tùy chỉnh thực thi code trước/sau khi dữ liệu được lưu vào trong database

 ->beforeStateUpdated(function ($record, $state) { //thực thi trước khi lưu dữ liệu vào db. }) ->afterStateUpdated(function ($record, $state) { //thực thi sau khi lưu dữ liệu vào db. })
  1. SelectColumn

    use Filament\Tables\Columns\SelectColumn; SelectColumn::make('status') ->options([ 'draft' => 'Draft', 'reviewing' => 'Reviewing', 'published' => 'Published', ])
    
    Chức năng Tên hàm
    Validate dữ liệu ->rules(['required'])
    Không hiện placeholder ->selectPlaceHolder(false)
  2. TextInputColumn: dùng cho các trường dữ liệu kiểu văn bản ngắn và cần sửa nhanh

    use Filament\Tables\Columns\TextInputColumn; TextInputColumn::make('title')
    
    Chức năng Tên hàm
    Validate dữ liệu ->rules(['required', 'max:50'])
    Khai báo kiểu dữ liệu cho ô nhập liệu ->type('date')
  3. ToggleColumn: sử dụng cho các trường dữ liệu kiểu boolean

    use Filament\Tables\Columns\ToggleColumn; ToggleColumn::make('is_highlight')
    
  4. CheckboxColumn: tương tự với toggle, checkbox cũng được sử dụng để hiển thị dữ liệu kiểu boolean

    use Filament\Tables\Columns\CheckboxColumn; CheckboxColumn::make('is_highlight')
    

Chức năng filter

  • Thông thường phần danh sách bản ghi sẽ có rất nhiều dữ liệu, và người quản trị viên sẽ muốn phân loại theo một số thuộc tính nhất định, ví dụ như trong bảng post của mình sẽ chia theo status (draft/reviewing/public). Ở đây các bạn có thể có 2 hướng xử lý:
    • C1: chia danh sách ra thành các tab riêng biệt
    • C2: thêm chức năng lọc theo một/nhiều trường dữ liệu

C1: Chia tab phân loại bản ghi

  • Cách này chắc sẽ quen thuộc với các tín đồ shopee khi kiểm tra lịch sử đơn hàng, mỗi tab sẽ chứa những đơn hàng ở trạng thái riêng (đang lấy hàng/đang giao/đã giao/...). Cách này thường sẽ phù hợp cho việc lọc dữ liệu dựa theo 1 trường dữ liệu nhất định.

  • Để thực hiện chia tab cho trang danh sách bài post, các bạn sẽ thêm hàm getTabs() vào file ListPosts.php

    use Filament\Resources\Components\Tab;
    use Illuminate\Database\Eloquent\Builder; public function getTabs(): array
    { return [ 'all' => Tab::make() ->icon('heroicon-m-user-group') ->badge(Post::query()->count()), 'draft' => Tab::make() ->modifyQueryUsing(fn (Builder $query) => $query->where('status', 0)) ->badge(Post::query()->where('status', 0)->count()), 'reviewing' => Tab::make() ->modifyQueryUsing(fn (Builder $query) => $query->where('status', 1)) ->badge(Post::query()->where('status', 1)->count()), 'public' => Tab::make() ->modifyQueryUsing(fn (Builder $query) => $query->where('status', 2)) ->badge(Post::query()->where('status', 2)->count()), ];
    }
    
  • Filament sẽ tự động lấy tab đầu tiên trong danh sách để làm tab mặc định. Tuy nhiên bạn hoàn toàn có thể thay đổi theo ý muốn bằng cách khai báo hàm getDefaultActiveTab() và return về key của tab mà bạn muốn

    public function getDefaultActiveTab(): string | int | null
    { return 'public';
    }
    
  • Ngoài ra, các bạn có thể thêm một số tùy chọn đối với tab:

    • Tùy chỉnh title tab: mặc định thì Filament sẽ lấy key làm title của tab luôn, tuy nhiên bạn cũng có thể thay đổi nó bằng cách truyền vào trong make('tabTitle')
    • Thêm icon vào cho tab:
      • thêm ->icon() để hiển thị biểu tượng trước tên tab
      • thêm ->iconPosition(IconPosition::After) để chuyển icon hiển thị phía sau
    • Thêm badges vào tab: ta có thể sử dụng ->badges() để thêm thông tin hiển thị vào bên cạnh tên tab. Cụ thể ở đây thì mình sẽ hiển thị số lượng bản ghi tương ứng với từng tab.
      • thêm ->badgeColor(): để thay đổi màu sắc của badge
  • Và đây là thành quả mà chúng ta thu được

C2: lọc bản ghi theo một/nhiều trường dữ liệu

  • Cách này sẽ linh hoạt hơn cho người dùng vì có thể kết hợp nhiều trường dữ liệu để lọc cùng lúc. VD: bạn đang cần tìm 1 bài post:

    • của tác giả A
    • đang ở trạng thái public
    • đang ở trạng thái highlight
    • được viết từ 01/10/2024 đến 10/10/2024.
  • Để xây dựng nhanh chức năng filter với Filament, các bạn sẽ cần khai báo vào $table->filters([]) ở trong hàm table trong file Resource

    use Filament\Tables\Filters\Filter; public function table(Table $table): Table
    { return $table ->filters([ // khai báo các trường dùng để lọc ]) ->deferFilters();
    }
    
  • Một số trường hợp lọc phổ biến:

    • TernaryFilter: dùng để lọc các trường có giá trị boolean và có thể null.
      use Filament\Tables\Filters\TernaryFilter; TernaryFilter::make('is_highlight') ->nullable();
      
    • SelectFilter: dùng để lọc dựa trên những giá trị có sẵn
      use Filament\Tables\Filters\SelectFilter; //lọc theo tác giả, sử dụng relation author giữa bảng post và users
      SelectFilter::make('author') ->relationship('author', 'name') ->searchable() //có thể tìm tên tác giả ->preload(); //dữ liệu users sẽ được load ngay khi load trang thay vì khi người dùng tìm kiếm mới load //lọc theo trạng thái, danh sách options là cố định
      SelectFilter::make('status') ->multiple() //cho phép chọn nhiều giá trị ->options([ 'draft' => 'Draft', 'reviewing' => 'Reviewing', 'published' => 'Published', ])
      
    • TrashedFilter: lọc những bản ghi đã bị xóa mềm
      use Filament\Tables\Filters\TrashedFilter; TrashedFilter::make()
      
    • Nếu những trường hợp trên vẫn chưa đủ đáp ứng nhu cầu của bạn, bạn hoàn toàn có thể tùy chỉnh lại theo mong muốn cá nhân. Cụ thể trong VD ở trên là lọc ngày viết bài
      use Filament\Forms\Components\DatePicker;
      use Filament\Tables\Filters\Filter;
      use Illuminate\Database\Eloquent\Builder; Filter::make('created_at') ->form([ DatePicker::make('from'), DatePicker::make('to') ->default(now()), //mặc định lấy thời điểm hiện tại ]) ->query(function (Builder $query, array $data): Builder { return $query ->when( $data['from'], fn (Builder $query, $date): Builder => $query->whereDate('created_at', '>=', $date), ) ->when( $data['to'], fn (Builder $query, $date): Builder => $query->whereDate('created_at', '<=', $date), ); })
      
  • Mặc định, dữ liệu sẽ được lọc ngay khi người dùng tương tác với form lọc, nhưng nếu bạn muốn người dùng chọn hết các trường rồi mới thực hiện lọc thì có thể bổ sung thêm ->deferFilters(). Khi này, dữ liệu chỉ được lọc khi người dùng bấm vào nút "Apply".

Tổng kết

Như vậy là sau 2 bài viết, chúng ta đã sử dụng Filament để xây dựng được màn hình danh sách "tương đối" hoàn chỉnh. Hy vọng rằng bài viết sẽ hữu ích với các bạn.

Trong bài viết tiếp theo, chúng ta sẽ cùng nhau tìm hiểu về form trong Filament để xây dựng chức năng thêm mới, chỉnh sửa nhé.

Hẹn gặp lại các bạn trong các bài viết sau.

Tài liệu tham khảo

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tìm hiểu về Resource Controller trong Laravel

Giới thiệu. Trong laravel, việc sử dụng các route post, get, group để gọi đến 1 action của Controller đã là quá quen đối với các bạn sử dụng framework này.

0 0 455

- vừa được xem lúc

Phân quyền đơn giản với package Laravel permission

Như các bạn đã biết, phân quyền trong một ứng dụng là một phần không thể thiếu trong việc phát triển phần mềm, dù đó là ứng dụng web hay là mobile. Vậy nên, hôm nay mình sẽ giới thiệu một package có thể giúp các bạn phân quyền nhanh và đơn giản trong một website được viết bằng PHP với framework là L

0 0 540

- vừa được xem lúc

Sử dụng Swagger để xây dựng API documentation

Giới thiệu về Swagger. RESTful API là một tiêu chuẩn dùng trong việc thiết kế API cho các ứng dụng web (thiết kế Web services) để tiện cho việc quản lý các resource.

0 0 1.1k

- vừa được xem lúc

Ví dụ CRUD với Laravel và Vuejs.

1. Cài đặt Laravel. composer create-project --prefer-dist laravel/laravel vuelaravelcrud. .

0 0 169

- vừa được xem lúc

Một số tips khi dùng laravel (Part 1)

1. Show database query in raw SQL format. DB::enableQueryLog(); // Bật tính năng query logging. DB::table('users')->get(); // Chạy truy vấn bạn muốn ghi log.

0 0 103

- vừa được xem lúc

Inertiajs - Xây dựng Single Page App không cần API

Tiêu đề là mình lấy từ trang chủ của https://inertiajs.com/ chứ không phải mình tự nghĩ ra đâu nhé :v. Lâu lâu rồi chưa động tới Laravel (dự án cuối cùng mình code là ở ver 5.8), thế nên một ngày đẹp trời lượn vào đọc docs ver 8.

0 0 249