Tìm hiểu Laravel (P11) - Loading tags with select2 and ajax

Indexs

  • Theo như kế hoạch thì mình dự tình phần này sẽ tìm hiểu về HTTP Responses. Nhưng lý thuyết hoài cũng chán nên bài này sẽ mang hơi hướng thực hành hơn chút. AE vừa đọc vừa làm thử xem sao nhé, vấn đề thực chất là không khó nhưng đến khi bắt tay vào làm thì mình khá lúng túng và mất kha khá thời gian để giải quyết.

1. Input và Output

Input:

  • Như tiêu đề đã nêu Loading tags with select2 and ajax là một phần trong task sửa bài post mình đã nhận, cụ thể như sau:
    • Sự dụng thư viện Select2 hiển thị tags của bài viết
    • Nếu user nhập tag chưa có thì create mới
    • Độ dài mỗi tag tối đa 15 ký tự
    • Một post có tối đa 5 tag

Output:

  • Update được tags cho bài viết.

2. Giải pháp

  • Database: có 3 bảng
    • Bảng Post sẽ có trường id, title, content,..
    • Bảng Tag có id, tag_name,..
    • Bảng TagPost có id, post_id, tag_id
  • Ý tưởng:
    • Ở màn hình edit, từ post ta sẽ lấy ra được list các tags của post để hiển thị ra.
    • Khi user sửa tags ta sẽ gửi danh sách các tags sau khi sửa lên server gọi đây là mảng $newTags:
    • Từ post ta sẽ lấy được list các tag của post trước khi update tạm gọi đây là mảng $oldTags.
    • So sánh 2 mảng trên để lấy ra các tag mới thêm vào (đặt là: $addTags) và các tag đã xóa đi (gọi là: $delTags).
    • Từ mảng $delTags ta sẽ xóa các record tương ứng trong bảng TagPost, còn mảng $addTags sẽ có 2 trường hợp. Nếu tag mới thêm đã có trong bảng Tag thì chỉ cẩn insert bảng ghi mới vào bảng TagPost, còn nếu chưa có thì đồng thời ta phải tạo bản ghi mới cho bảng Tag.
  • Thực hiện:
    • Trên view ta cần tạo form edit với input tags như sau:
          <select multiple="true" name="tags[]" id="tag" class="form-control select2">
             @foreach($post->tags as $tagName)
                 <option value="{{ $tagId }}" selected="selected">{{ $tagName }}</option>
             @endforeach
          </select>
      
      • Tất nhiên là để hiển thị được tag ta cần include thư viện Select2 bạn nhé:
         @section('style')
             {!! Html::style('js/plugins/select2/select2.css') !!}
         @endsection
         @section('script')
             {!! Html::script('js/plugins/select2/select2.full.min.js') !!}
             {!! Html::script('/js/post_edit.js') !!}
         @endsection
      
    • Trong file post_edit.js ta sẽ viết ajax để load các tags trong bảng Tag ra theo input user nhập vào kiểu autocomplete
          var baseUrl = window.location.protocol + '//' + window.location.host;
          $('#tag').select2({
             multiple: true,
             tags: true,
             tokenSeparators: [',', ' '],
             ajax: {
                 url: baseUrl + '/posts/getTags/',
                 dataType: 'json',
                 data: function (params) {
                     return {
                         q: $.trim(params.term)
                     };
                 },
                 processResults: function (data) {
                     return {
                         results: data
                     };
                 },
                 cache: true
             },
         });
      
    • Phía controller ta sẽ viết hàm lấy ra các tag đã có sẵn theo input nhập vào như sau:
         public function getTags(Request $request)
         {
             $input = $request->q;
             if (empty($input)) {
                 return response()->json([]);
             }
      
             $tags = DB::table('tag')->where('tag_name', 'like', $input . '%')->get();
             $formattedTags = [];
      
             foreach ($tags as $tag) {
                 $formattedTags[] = ['id' => $tag->tag_name, 'text' => $tag->tag_name];
             }
      
             return response()->json($formattedTags);
         }
      
    • Đến đây ta đã hiển thị được ra các tag của Post và suggest được các tag đã có sẵn trong database khi user nhập input. Tiếp theo trong hàm update post ta sẽ lấy ra 2 mảng $delTags$addTags như sau:
         $oldTags = $post->tags; // danh sách tag cũ trước khi edit, mảng này có dạng [$id => $tagName, ..]
         $newTags = isset($request->input['tags']) ? $request->input['tags'] : []; // danh sách các tag sau khi người dùng đã sửa, mảng có dạng [$tagName1, $tagName2,..]
      
         $delTags = array_diff($oldTags, $newTags);  // mảng các tag cần xóa
         $addTags = array_diff($newTags, $oldTags);  // mảng các tag cần thêm vào
      
    • Như vậy ta đã có được 2 mảng cần thiết, với mỗi tag trong bảng $delTags ta sẽ tìm ra id của tag ta dùng $tagId = array_search($tagName, $oldTags) và xóa record ở bảng TagTopic. Tương tự với mảng $addTags ta duyệt từng tag nếu tag đã có trong bảng Tag thì chỉ cần insert vào bảng TagTopic, còn chưa thì insert cả vào bảng Tag nữa.
    • Để giới hạn số lượng tag của bài viết và độ dài của mỗi tag, ta cần thêm đoạn js sau vào file post_edit.js
      $('#tag').change(function() {
         var value = $(this).val();
         if (value.length > 5) {
             $('#tag option:last').remove();
         }
      
         value.forEach(function(tag) {
             if (tag.length > 15) {
                 $('#tag option:last').remove();
             }
         });
      });
      
    • Hoặc là bạn chỉ cần thêm 2 parameter của Select2 sau:
          maximumInputLength: 15,
          maximumSelectionLength: 5,
      
    • Trong trường hợp form submit bị lỗi, mà bạn vẫn muốn giữ các tag vừa sửa thì bạn cần sửa lại HTML như sau:
      <select multiple="true" name="tags[]" id="tag" class="form-control select2">
         @if (old('tags'))
             @foreach(old('tags') as $tag)
                 <option value="{{ $tag }}" selected="selected">{{ $tag }}</option>
             @endforeach
         @else
             @foreach($topic->tags as $tagName)
                 <option value="{{ $tagName }}" selected="selected">{{ $tagName }}</option>
             @endforeach
         @endif
      </select>
      

Tài liệu tham khảo Laravel HTTP Responses Select2


All Rights Reserved