Bài viết sau hướng dẫn tạo HTML Form truyền dữ liệu có cấu trúc (object và array) lên server và được chuyển tự động thành format tương ứng ở PHP.
Cách tạo form gửi object/array và xử lý bằng PHP
Nếu đã từng sử dụng PHP hẳn bạn biết có thể dùng $_GET
& $_POST
để nhận dữ liệu có cấu trúc từ client bằng cách dùng dấu ngoặc vuông trong thuộc tính name
của input
để thể hiện được cấu trúc đó.
Ví dụ theo cách thông thường khi muốn truyền tham số lên server ta làm như sau:
<form action="register.php" method="POST"> <input name="username" type="text" value="User Name"> <input name="password" type="password" value="Password"> <input type="submit" value="Submit"> </form> // Result of $_POST (PHP): // array( // 'username' => 'User Name', // 'password' => 'Password' // )
Dùng ngoặc vuông trong thuộc tính name
, ta có thể gửi một Object lên server như sau:
<form action="register.php" method="POST"> <input name="user[username]" type="text" value="User Name"> <input name="user[password]" type="password" value="Password"> <input type="submit" value="Submit"> </form> // Result of $_POST (PHP): // array( // 'user' => array( // 'username' => 'User Name' // 'password' => 'Password' // ) // )
Nếu không chỉ định index trong dấu ngoặc vuông, PHP ngầm hiểu đó là một Array và sẽ tự tăng index (tính từ 0) nếu gặp phần tử trùng lặp.
<form action="register.php" method="POST"> <input name="user[]" type="text" value="User Name"> <input name="user[]" type="password" value="Password"> <input type="submit" value="Submit"> </form> // Result of $_POST (PHP): // array( // 'user' => array( // 0 => 'User Name', // 1 => 'Password' // ) // )
Và tất nhiên ta có thể chỉ định index của mảng nếu muốn.
<form action="register.php" method="POST"> <input name="user[0]" type="text" value="User Name"> <input name="user[1]" type="password" value="Password"> <input type="submit" value="Submit"> </form> // Result of $_POST (PHP): // array( // 'user' => array( // 0 => 'User Name', // 1 => 'Password' // ) // )
Tuyệt vời hơn, cách này cũng có thể áp dụng với cấu trúc dữ liệu lồng nhau (nested).
<form action="register.php" method="POST"> <input name="user[auth][username]" type="text" value="User Name"> <input name="user[auth][password]" type="password" value="Password"> <input name="user[info][][address]" type="text" value="Address 1"> <input name="user[info][][city]" type="text" value="City 1"> <input name="user[info][][address]" type="text" value="Address 2"> <input name="user[info][][city]" type="text" value="City 2"> <input type="submit" value="Submit"> </form> // Result of $_POST (PHP): // array( // 'user' => array( // 'auth' => array( // 'username' => 'User Name', // 'password' => 'Password' // ), // 'info' => array( // 0 => array( // 'address' => 'Address 1', // 'city' => 'City 1', // ), // 1 => array( // 'address' => 'Address 2', // 'city' => 'City 2', // ) // ) // ) // )
Xử lý form có object/array với JavaScript
Đôi khi phía Server lại là của bên thứ 3 và dữ liệu quy ước nhận được lại có Content Type là JSON chẳng hạn thì cách làm như trên hoàn toàn vô hiệu.
Cách làm của tôi là thay vì submit trực tiếp, tôi tách dữ liệu của Form thành một JSON object, sau đó sử dụng jQuery AJAX để gửi lên server.
<form action="register.php" method="POST"> <input name="user[auth][username]" type="text" value="User Name"> <input name="user[auth][password]" type="password" value="Password"> <input name="user[info][][address]" type="text" value="Address 1"> <input name="user[info][][city]" type="password" value="City 1"> <input name="user[info][][address]" type="text" value="Address 2"> <input name="user[info][][city]" type="password" value="City 2"> <input type="submit" value="Submit"> </form> <script src="jquery.min.js"></script> <script src="jquery.icreativ.js"></script> <script> $(document).ready(function() { $('form').submit(function(event) { let data = $(this).serializeToObject(); // convert to object $.ajax({ method: 'POST', url: $(this).attr('action'), data: JSON.stringify( data ), contentType: 'application/json; charset=UTF-8' // submit a JSON object }).done(function(response) { // Process the response here }); event.preventDefault(); // <- avoid reloading }); }); </script>
Ở đây tôi tạo một jQuery plugin là $.serializeToObject
(không phải mặc định của jQuery đâu nhé).
Nhiệm vụ của plugin này là chuyển đổi dữ liệu trong các input của một form thành một JSON object giống như những gì một server PHP nhận được như đã nói ở phần trước. Với ví dụ trên, data trả về có cấu trúc như sau:
{ 'user': { 'auth': { 'username': 'User Name', 'password': 'Password' }, 'info' : { [ { 'address': 'Address 1', 'city': 'City 1' }, { 'address': 'Address 1', 'city': 'City 1' } ] } } }
Mã nguồn của plugin $.serializeToObject
như sau:
jquery.icreativ.js
(function ($) { $.fn.serializeToObject = function() { let data = $(this).serializeArray(), ret = {}; data.forEach( obj => { var matches = obj.name.match(/^[a-zA-Z0-9_]+(\[[a-zA-Z0-9_]*])+$/); if (matches) { // array type input let lastKey = obj.name.match(/^[a-zA-Z0-9_]+/)[0], dimensions = obj.name.match(/\[[a-zA-Z0-9_]*\]/g), ref = ret; dimensions.forEach( function(key, depth) { key = key.replace(/[\[\]]/g, ''); // remove square brackets if (key === '') { // dynamic array if (lastKey == '') { let idx = ref.length - 1; if (idx == -1 || !ref[idx]) { ref[idx + 1] = []; } } else { if (!ref[lastKey]) { ref[lastKey] = []; } } } else if (key.match(/^[0-9]+$/)) { // indexed array if (lastKey == '') { let idx = ref.length - 1; if (idx == -1 || ref[idx][key] !== undefined) { ref[idx + 1] = []; } } else { if (!ref[lastKey]) { ref[lastKey] = []; } } } else { // object if (lastKey == '') { let idx = ref.length - 1; if (idx < 0 || ref[idx][key] !== undefined) { ref[idx + 1] = {}; } } else { if (!ref[lastKey]) { ref[lastKey] = {}; } } } if (lastKey === '') { let idx = ref.length - 1; ref = ref[idx]; } else { ref = ref[lastKey]; } if (depth === dimensions.length - 1) { if (key === '') { let idx = ref.length; ref[idx] = obj.value; } else { ref[key] = obj.value; } } lastKey = key; }); } else { if (!ret[obj.name]) { ret[obj.name] = obj.value; } else { if (ret[obj.name].constructor === Array) { ret[obj.name].push(obj.value); } else { let temp = ret[obj.name]; ret[obj.name] = [temp, obj.value]; } } } } ); return ret; } } )(jQuery);
Nếu bạn cần phiên bản Vanilla JS thì tôi chia sẻ mã nguồn như sau:
function serializeArray(form) { var result = []; Array.prototype.slice.call(form.elements).forEach(function (field) { if (!field.name || field.disabled || ['file', 'reset', 'submit', 'button'].indexOf(field.type) > -1) return; if (field.type === 'select-multiple') { Array.prototype.slice.call(field.options).forEach(function (option) { if (!option.selected) return; result.push({ name: field.name, value: option.value }); }); return; } if (['checkbox', 'radio'].indexOf(field.type) > -1 && !field.checked) return; result.push({ name: field.name, value: field.value }); }); return result; } function serializeObject(form) { let data = serializeArray(form); let ret = {}; data.forEach( obj => { var matches = obj.name.match(/^[a-zA-Z0-9_]+(\[[a-zA-Z0-9_]*\])+$/); if (matches) { // array type input let lastKey = obj.name.match(/^[a-zA-Z0-9_]+/)[0], dimensions = obj.name.match(/\[[a-zA-Z0-9_]*\]/g), ref = ret; dimensions.forEach( function(key, depth) { key = key.replace(/[\[\]]/g, ''); // remove square brackets if (key === '') { // dynamic array if (lastKey == '') { let idx = ref.length - 1; if (idx == -1 || !ref[idx]) { ref[idx + 1] = []; } } else { if (!ref[lastKey]) { ref[lastKey] = []; } } } else if (key.match(/^[0-9]+$/)) { // indexed array if (lastKey == '') { let idx = ref.length - 1; if (idx == -1 || ref[idx][key] !== undefined) { ref[idx + 1] = []; } } else { if (!ref[lastKey]) { ref[lastKey] = []; } } } else { // object if (lastKey == '') { let idx = ref.length - 1; if (idx < 0 || ref[idx][key] !== undefined) { ref[idx + 1] = {}; } } else { if (!ref[lastKey]) { ref[lastKey] = {}; } } } if (lastKey === '') { let idx = ref.length - 1; ref = ref[idx]; } else { ref = ref[lastKey]; } if (depth === dimensions.length - 1) { if (key === '') { let idx = ref.length; ref[idx] = obj.value; } else { if (!ref[key]) { ref[key] = obj.value; } else { if (ref[key].constructor === Array) ref[key].push(obj.value); else { let temp = ref[key]; ref[key] = [temp, obj.value]; } } } } lastKey = key; }); } else { if (!ret[obj.name]) { ret[obj.name] = obj.value; } else { if (ret[obj.name].constructor === Array) { ret[obj.name].push(obj.value); } else { let temp = ret[obj.name]; ret[obj.name] = [temp, obj.value]; } } } } ); return ret; };