Laravel custom messages for array validation
Bài đăng này đã không được cập nhật trong 7 năm
Indexs
- Như ở bài trước mình có nói thì bài này mình tiếp tục chia sẻ bài viết dạng Vấn Đề - Giải pháp về các issue trong quá trình code để bạn nào có gặp vấn đề tương tự thì có thế tham khảo giải pháp của mình hoặc có giải pháp nào hay hơn thì comment chia sẻ cho mọi người. Bài này viết về validation trong Laravel, cụ thể là custom lại message khi validate array, có chút liên quan để bài trước đấy nhé, các bạn có thể tham khảo ở link bên trên.
1. Vấn đề
Input:
- Thực hiện validate tag trong lúc create hoặc edit bài viết, phần hiển thị tag sử dụng thư viện Select2 :
- Độ dài mỗi tag tối đa 15 ký tự
- Một post có tối đa 5 tag
- Nếu chọn tag đã bị block thì hiển thị ra thông báo lỗi. (bên Admin sẽ cho chức năng block tag)
Output:
- Validate được tag và hiển thị ra thông báo lỗi tương ứng.
2. Giải pháp
- Database:
- Bảng Tag có id, tag_name, activated
- Ý tưởng: Trong bài trước ta đã thực hiện validate độ dài mỗi tag và số lượng tag tối đa ở phía client, nhưng để đảm bảo tính an toàn ta vẫn nên thực hiện validate ở phía server, sau đây là một vài cách:
-
Cách 1: Trước khi submit form đến method
create
hoặcupdate
, ta sẽ gửi request ajax đến methodcheckValidate()
để kiểm tra xem nếu data hợp lệ thì ta sẽ tiếp tục điều hướng request đến method create hoặc update tương ứng, còn không sẽ báo lỗi. -
File js sẽ như sau:
$(document).on('click', '.btnSubmit', function() { var tags = $('#tag').val(); $.ajax({ url: baseUrl() + '/posts/checkValidate/', type: 'POST', data: { tags: tags, }, success: function (response) { if (response.success) { $.ajax({ url: baseUrl() + '/posts/update/', type: 'POST', data: { tags: tags, }, success: function (response) { if (response.success) { alert(response.msgSuccess); } else { alert(response.msgError); } } }); } else { $('.tagError').text(response.msgError); } } }); });
-
Phương thức
checkValidate()
:public function checkValidate(Request $request) { $tags = $request->input('tags'); if (count($tags) > 0) { if (count($tags) > 5) { return response()->json([ 'success' => false, 'msgError' => "The tag may not have more than 5 items.", ]); } $tmp = ''; foreach ($tags as $tag) { if (strlen($tag) > 15) { $tmp = $tmp . $tag . ", "; } } if ($tmp != '') { return response()->json([ 'success' => false, 'msgError' => substr($tmp, 0, -2) . " may not be greater than 15 characters.", ]); } $tagNotActivate = DB::table('tag')->whereIn('id', $tags)->where('activated', 0)->pluck('tag_name')->toArray(); if (count($tagNotActivate)) { return response()->json([ 'success' => false, 'msgError' => implode(', ', $tagNotActivate) . " have been blocked.", ]); } } return response()->json([ 'success' => true ]); }
-
Trên view ta cần thêm thẻ chứa class
.tagError
để hiển thị lỗi<select multiple="true" name="tags[]" id="tag" class="form-control select2"> @foreach(old('tags', $topic->tags) as $tag) <option value="{{ $tag }}" selected="selected">{{ $tag }}</option> @endforeach </select> <span class="tagError"></span>
-
So với view ở bài trước sử dụng if để check xem có tồn tại
old("tag")
thì lần này ta truyền luôn đối thứ 2 vào phương thứcold('tags', $topic->tags)
của helper có vẻ hay hơn. -
Nhưng cách này khá thủ công, phải gửi request 2 lần đến server và với những người dùng biết chút ít kỹ thuật thì vẫn có thể pass qua được validate bằng cách sửa file js => không an toàn.
-
- Cách 2: sử dụng luôn validate array của laravel
- Tạo file
PostRequest.php
bằng câu lệnh make:request bên controller ta chỉ cần truyền đối sốPostRequest
vào methodstore
hoặcupdate
là data sẽ được valid qua filePostRequest.php
(Tất nhiên ta cần use PostRequest ở đầu file nhé).public function update(PostRequest $request, $id) { // }
- Phương thức
rules
public function rules(Request $request) { $tagsError = ""; $input = $request->all(); if (isset($input['tags'])) { $tagsBlockName = DB::table('tag')->whereIn('tag_name', $input['tags']) ->where('activated', 1) ->pluck('tag_name') ->toArray(); $tagsError = implode(',', $tagsBlockName); } $rules = [ 'tags.*' => "max:15|size:5|not_in:$tagsError", ]; return $rules; }
- Phương thức
message
public function messages() { return [ 'tags.*.max' => "The tag may not be greater than 15 characters.", 'tags.*.size' => "The tag may not have more than 5 items.", 'tags.*.not_in' => "The tag has been blocked.", ]; }
- Ở trường hợp valid đội dài mỗi tag và số lượng tag tối đa thì message hiển thị lỗi ổn, nhưng ở phần valid tag block nếu ta không chỉ rõ tag nào bị block thì người dùng sẽ không biết được tag nào trong 5 tag đã bị block để gỡ bỏ. Ngoài ra khi thực hiện test bạn sẽ thấy message lỗi bị hiển thi nhiều lần do đây là validate mảng, rất củ chuối đúng không.
- Đến đây ban đầu mình đã định viết một validation mới cho phần này theo hướng dẫn documment. Nhưng ngó lại thấy phần Customizing The Error Format có thể giải quyết được nên mình triển luôn.
- Ta sẽ thêm phương thức
formatErrors
như sau:protected function formatErrors(Validator $validator) { $results = []; $tagFlag = false; $messages = $validator->errors()->messages(); foreach ($messages as $key => $value) { if (!str_contains($key, 'tags') || !$tagFlag) { $results[] = $value[0]; } if (str_contains($key, 'tags') && !$tagFlag) { $tagFlag = true; } } return $results; }
- Phương thức
formatErrors
sẽ loại bỏ các message trùng lặp chỉ giữ lại một message duy nhất, hàmstr_contains
check xem có tồn tại text trong chuỗi hay không, thấy helper của laravel có nên sài luôn - Lưu ý: ta cũng cần sửa lại message lỗi phần valid tag block để hiển thị được tag nào người dùng nhập vào đã bị block rồi:
tags.*.not_in' => ":values have been blocked.",
- Cách 2 này khá ngon lành rồi, nếu các bạn có cách nào hay hơn nữa thì share ở dưới nhé
- Tạo file
Tài liệu tham khảo Laravel Validation
All rights reserved