21
Mar

ทำระบบค้นหาสำหรับเว็บแบบฟิลเตอร์ข้อมูลได้ด้วย Search and Filter

Category: Blog / 648 views

สวัสดีครับผม วันนี้ผมอยากจะเล่าถึงเรื่องราวผ่านการทำงานของผมสักหน่อย เพราะสืบเนื่องมาจากว่าช่วงนี้ผมมีโปรเจ็คชิ้นหนึ่งครับเป็นเว็บที่เป็น E-commerce ขายอุปกรณ์ไฟฟ้า โดยถือว่าเป็นเว็บที่มีโครงสร้างซับซ้อนอยู่พอตัวครับ โดยเงื่อนไขในการทำเว็บนี้มีดังนี้ครับ

  • มีระบบสั่งซื้อสินค้าผ่านหน้าเว็บออนไลน์
  • มีระบบจัดการสต็อคสินค้า
  • เป็นเว็บไซต์สองภาษา
  • มีระบบฟิลเตอร์ข้อมูลตามคุณลักษณะเฉพาะของสินค้านั้นๆ (เรียกว่า Ordering Number)
  • แสดงผล Ordering Number ที่เกิดจากการฟิลเตอร์ ในหน้าลิสต์สินค้า หน้ารายละเอียดสินค้า หน้า cart และส่งไปยังอีเมล
  • สินค้าบางตัวเป็นแบบส่ังทำพิเศษ ต้องให้ระบุความต้องการลงไปในใบสั่งซื้อสินค้านั้นด้วย
หลังจากได้เห็นสโคปงานแล้ว ก็เช่นเคยครับ ผมเป็นคนทำเว็บที่ใช้ WordPress + Woocommerce ในการพัฒนาอยู่แล้ว เครื่องมือทำเว็บอันดับหนึ่งในวงการ แต่เราต้องจับมาพัฒนาต่ออีกสักหน่อยครับ ก็มีการใช้งานดังนี้ครับ
  • ระบบสั่งซื้อสินค้าผ่านหน้าเว็บออนไลน์ ใช้ Woocommerce
  • ระบบจัดการสต็อคสินค้า อันนี้ Woocommerce มีให้อยู่แล้ว แต่เดี๋ยวจะมีแอดวานซ์ไปอีก เพราะต้องตัดสตอคข้ามเว็บ! ไว้ผมจะมาเล่าให้ฟัง
  • เว็บไซต์สองภาษา ตอนแรกจะใช้ WPML แต่หลังจากคิดเรื่องของ Performance และกลุ่มลูกค้าแล้ว ผมแนะนำไปว่าสร้างอีก url หนึ่งไปเลย (sub domain) เพราะยังไงลูกค้าที่อ่านไทย ก็ไม่น่าจะไปอ่านอังกฤษ หรือคนที่อ่านไทยไม่ออก ก็ไม่ซื้อของผ่านหน้าไทยอยู่แล้ว
  • ระบบฟิลเตอร์ข้อมูล ด้วยความที่สินค้าแบ่งออกเป็นกลุ่มต่างๆได้ประมาณ 42 รูปแบบ ผมใช้ปลักอิน Search and Filter Pro มาทำระบบฟิลเตอร์ครับ ดีงามพระรามแปดมาก แนะนำสุดๆ (ไม่ฟรี แต่โคตรคุ้ม ซื้อเหอะ)
  • คุณลักษณะจำเพาะของสินค้าแต่ละตัว ผมทำเป็น custom field ครับ โดยใช้ปลักอิน Advanced Custom fields ซึ่งทุกวันนี้แทบจะเป็นปลักอินพื้นฐานที่ผมมีในเว็บที่ทำให้ลูกค้า
  • แสดงผลข้อมูล Ordering Number ของสินค้า อันนี้ต้องเขียนคำสั่งเพิ่มขึ้นมาเอง
  • สินค้าบางตัวที่ต้องระบุความต้องการเข้าไปพิเศษนั้น ผมใช้ WooCommerce TM Extra Product Options (เสียตังค์) ทำงานคู่กับ YITH WooCommerce Wishlist

จริงๆเว็บนี้แค่นี้ก็มีอะไรอยากจะเล่าเยอะมากเลยครับ ฮ่าๆ แต่ผมขอเอาเรื่องที่ใช้เวลาเยอะสุดก่อนละกันครับ ซึ่งก็คือการทำระบบ Search and Filter นั่นเอง  แต่ด้วยความที่ปลักอินนี้มีการออกแบบมาดีอยู่แล้ว เวลาใช้งานเราแค่มาปรับให้ถูกต้องกับความต้องการของเราครับ

สำหรับการใช้งานพื้นฐานของปลักอินนี้ไม่ยากเลยครับ ติดตั้ง อ่านคู่มือก็เข้าใจแล้ว ในบทความนี้ผมจะไม่ขอเล่าถึงพื้นฐานตรงนั้น แต่จะมาแบ่งปันประสบการณ์ที่ผมว่ามันเฉพาะเพิ่มขึ้นมาครับ

จากภาพด้านบน ซ้ายมือก็คือฟิลเตอร์ของสินค้าที่ว่าครับ ซึ่งตัวเลือกที่มีนี้จะแตกต่างกันไป แต่ละหมวดหมู่มีจำนวนตัวเลือกไม่เหมือนกัน โดยตัวเลือกก็มาจาก Custom Post Type ที่เราสร้างครับ และด้วยความที่ฟอร์มนี้จะให้แสดงผลเฉพาะข้อมูลที่เป็น Product เท่านั้น การตั้งค่าก็จะเป็นแบบดังภาพคับ

ก็คือตรง General ให้เลือกเฉพาะ Products ครับ จะได้ไม่ไปยุ่งกับ Post Type อื่นๆ และอย่างไรก็แล้วแต่ด้วยความที่แต่ละหมวดหมู่ จะมีฟอร์มแบบนี้เป็นของตัวเอง ดังนั้นเราต้องตั้งค่าให้ฟอร์มตัวนี้เล่นกับเฉพาะหมวดหมู่ของมันเท่านั้น ถ้าเราไม่ตั้งค่า กรณีที่ตัวเลือกในฟอร์ม ถูกรีเซต (ตัวเลือกใน Drop down เป็น All items)  มันจะเอาสินค้าในหมวดหมู่อื่นๆ มาแสดงด้วยครับ ซึ่งในความจริง ถ้าจะรีเซตมันก็ควรจะแสดงแค่สินค้าเฉพาะหมวดหมู่นั้นๆเท่านั้น ให้เราตั้งค่า Choose which kinds of pages S&F will try to do this on: ให้เลือก Tag, Category & Taxonomy Archives

และตั้งค่าที่นี่ด้วยครับในแท๊บ Tags, Categories & Taxonomy ตรง Product Categories ให้ใส่ ID ของหมวดหมู่สินค้าไปครับ ถ้าไม่รู้ ก็ให้คลิกที่เครื่องหมายแว่นขยาย เราจะสามารถคลิกหมวดหมู่ที่ว่าได้  ในโปรเจ็คของผมหนึ่งฟอร์ม คือหนึ่งหมวดหมู่อยู่แล้ว ผมก็เลยใส่แค่ไอดีของหมวดหมู่นั้นๆเท่านั้นๆ ถ้าใครเอาไปใช้แล้วใช้หลายหมวดหมู่ก็แค่ใส่ไอดี ตามด้วย comma ( , ) ครับ

ต่อมาในส่วนของการแสดงผลของการฟิลเตอร์และค้นหานะครับ ด้วยความที่แต่ละหมวดหมู่สินค้ามีฟอร์มไม่เหมือนกัน ดังนั้นการใช้ display results method จึงเลือกเป็นแบบ Using a Shortcode แล้วเราจะได้ตัวเลข shortcode มา ก็ให้เราไปสร้างหน้า Page สำหรับแสดงผลการค้นหาสำหรับหมวดหมู่นั้นๆ แล้วก็วาง shortcode ที่ได้ลงไปครับ (แต่ถ้าใครมีฟอร์มแค่ฟอร์มเดียว ใช้กับ Woocommerce ร่วมกัน ก็เลือก method ตรงนี้เป็น Woocommerce Shop ก็ได้คับ แต่ถ้าเมื่อไหร่ที่เอาไปใช้กับ Woocommerce แล้วแต่ละหมวดหมู่ดันมีฟอร์มไม่เหมือนกัน แนะนำให้ใช้แบบผมครับ เพราะถ้าใช้ Woocommerce Shop มันรองรับแค่ฟอร์มเดียวเท่านั้น)

เมื่อเราสร้างหน้า Page ขึ้นมาแล้ววาง Shortcode ไปแล้วก็ให้เรา Publish หน้าที่ว่า แล้วเอา URL ของหน้านั้นมาใส่ในช่อง Result URL: ครับ จำเป็นต้องใส่นะครับ

อย่างในเคสของผมมีสี่สิบฟอร์มก็มีสี่สิบหน้าครับ ฮ่าๆ ทำกันหัวบานไปเลย (แต่ไม่แน่นะครับ ในอนาคต เวอร์ชั่นใหม่ เขาอาจจะออกแบบมาให้หน้า Shop รองรับหลายๆฟอร์มก็ได้  ผมได้มีโอกาสคุยกับนักพัฒนา เขาบอกว่าเป็นไอเดียที่น่าสนใจเหมือนกัน ก็คงต้องลุ้นกันอีกที)

มาดูวิธีการแสดงตัวเลือกในฟอร์มของเรากันบ้าง ซึ่งตัวปลักอินนี้เนี่ย มันเจ๋งสุดๆเลยครับ ดึงโน่นดึงนี้มาเป็นตัวฟิลเตอร์ได้หมด อย่างในเคสของผม ผมต้องการดึงมาจาก Custom Fields ผมก็เลือกแบบ Post Meta ครับ วิธีการก็ง่ายดายมาก จับลากๆ วางๆ สลับตำแหน่งได้ด้วย

แต่ก่อนอื่นใดนั้นให้เราเลือกตัวฟิลเตอร์บนสุดเป็น Taxonomy นะครับ แล้วตั้งค่าเป็น Product Category เพื่อให้ฟอร์มนี้เล่นกับหมวดหมู่ที่เราตั้งค่าไว้ก่อนหน้านั่นเองเองครับ ห้ามลืมนะครับ วางเอาไว้บนสุดเลย ซึ่งแน่นอนว่าเมื่อเราแสดงฟอร์มในหน้าเว็บ ตัวเลือกนี้ก็จะโผล่ไปด้วย แต่เราก็ซ่อนมันง่ายๆด้วยคำสั่ง CSS ครับ disable:none ไปได้เลย

จากนั้นในตัวเลือกอื่นๆก็เป็นค่าที่ดึงมาอัตโนมัติจาก Custom Fields ตัวเลือกก็เลยใช้แบบ Post Meta ครับผมแล้วก็ตั้งค่าให้ตรงว่าจะเลือกมาจากฟิลด์ตัวไหน

ฟอร์ม Search Form UI ด้านบนเราสามารถลากย้ายคำแหน่งได้นะครับ สะดวกสบายมากๆ ก็นั่งทำไปครับให้ครบตามจำนวนตัวเลือกที่ต้องการ (ข้อสังเกต ในกรณีที่ Custom Fields นั้นสร้างร่วมกับสินค้า เมื่อสร้างแล้วให้เราไปสร้างสินค้าสักตัวขึ้นมา แล้วก็ระบุค่าสำหรับฟิลด์แต่ละอันไปนะครับ เพื่อให้ระบบค้นหารู้ตรวจเจอ ถ้าไม่อย่างนั้น มันจะไม่แสดงตัวเลือกในหน้าเว็บคับ … ผมก็แก้ไขด้วยการเพิ่มสินค้าหมวดหมู่ละหนึ่งตัวคับผม)

ส่วนการแสดงแบบฟอร์ม ก็ให้เราเอา Shortcode ไปแปะครับ อย่างของผมก็เอาไปแปะใน Widgets ครับ สร้าง text widget ขึ้นมา แต่ละอันก็ใส่แต่ละโค้ดครับ

ทีนี้ครับ ด้วยความที่ Widgets มันก็อยู่ใน Sidebar เดียวกัน มีสี่สิบฟอร์ม มันก็ต้องโผล่มาสี่สิบตัวอะดิ! ใช่ครับ ตามหลักมันก็ต้องเป็นแบบนั้น แต่ปัญหานี้แก้ได้ง่ายนิดเดียวครับ ด้วยการใช้เงื่อนไขในการแสดงผลว่า เห้ย ฟอร์มนี้เนี่ย ให้แสดงผลเฉพาะหน้านี้นะหรือเฉพาะหมวดหมู่สินค้านี้เท่านั้นนะโว้ย ถ้าไม่ใช่หมวดหมู่ที่ว่าก็ไม่ต้องแสดง

พระเอกแก้ไขปัญหาเรื่องนี้ก็คือการใช้ Widgets Visibility ของปลักอิน Jetpacks ครับ อย่างการตั้งค่าของผมก็คือให้โชว์เฉพาะตามเงื่อนไขนี้เท่านั้นนะ

เท่านี้ฟอร์มของเราก็จะโผล่มาตามความต้องการของเราแล้วครับผม

เอาหล่ะ นี้มันยังแค่การต้ังค่าใช้งาน มันยังมีอะไรที่ผมต้องเล่นกันมันมากกว่านี้ครับ โปรเจ็คบักสนทำมันก็มีอะไรท้าทายเพิ่มมาแบบนี้แหละครับ ฮ่าๆ

กรณีที่หนึ่ง Selection นั้น มี Options ให้เลือกแค่เพียงตัวเดียว

ด้วยความที่ค่า default ของฟอร์มที่เราสร้างจากปลักอิน Search and filter มันจะแสดงเป็น All items ครับ แต่บางทีมันก็ดูไม่เมคเซนส์ เพราะถ้ามันมีตัวเลือกช่องนั้นแค่อันเดียว ทำไมไม่ให้มันแสดงค่าตัวนั้นไปเลยหละ? วิธีการก็คือ

ตรง Change All items Label ให้เราใส่ค่า Value ของ Options นั้นไปได้เลยครับ อย่างเช่นในเคสของผม ฟอร์มนี้ มันมี Option แค่ Ex9A ผมก็ใส่คำว่า Ex9A ไปเลย ซึ่งจริงๆมันก็คือแค่เปลี่ยนคำว่า All items ให้เป็นคำที่เราต้องการแค่นั้นแหละครับ มันก็ดูหลอกตาได้ใช่มะ ฮ่าๆๆ

แต่ในแง่ของ User Experience มันก็ยังดูแปลกๆครับ เพราะเมื่อ User คลิก Drop down ของตัวเลือกนี้ ก็จะเจอ Ex9A เรียงกันสองบรรทัดติดกัน ซึ่งมันแปลกๆที่มีตัวเลือกแบบเดียวกันโผล่มาสองตัว (ก็แหงละ อันบนมันคือตัวหลอกนี่นา)

จากประสบการณ์การออกแบบ UX อะไรที่เขาไม่ต้องไปเล่นกับมัน ก็ไม่ต้องให้เขาไปยุ่งครับ ผมก็เลยต้องเขียนโค้ดเพิ่มเติมขึ้นมาเพื่อ

  • ตัวเลือกไหนก็ตามที่มีตัว Options แค่ตัวเดียว ห้ามให้เขาคลิกได้
  • เปลี่ยนสีตัวเลือกให้เป็นสีเทา จะได้เป็นที่เข้าใจกันว่ามันคลิกไม่ได้นะหนู

ทีนี้ User ก็จะได้ไปเล่นแค่กับตัวเลือกที่มันมีออพชั่นเยอะๆ ไม่ต้องเสียเวลามาสนใจพวกที่ค่ามันฟิกอยู่แล้ว มาดูวิธีทำตัวแรกก่อนคับ

  • ตัวเลือกไหนก็ตามที่มี Options ตัวเดียว สั่งไปเลยว่าห้ามคลิก!!

การแก้ปัญหานี้แก้ได้ด้วยการใช้ Javascript เข้ามาเอี่ยวครับ หลักการก็ง่ายมากครับ ให้เราทำการตรวจสอบฟอร์มครับ ตาม Flow Chart นี้เลย (ผมเขียนคร่าวๆ อาจจะไม่ถูกหลักการเขียน flow chart แบบมาตรฐานนะครับ)

ก็คืออย่างแรก ให้ตรวจสอบ Selection Form ตัวนั้นก่อนว่ามีตัว Options ข้างใน <=2 หรือไม่ (ตัวแรกเป็น all items ที่ถูกเราเปลี่ยนชื่อ อันที่สองเป็นค่า Value ของ options นั้นจริงๆที่ถูกดึงมาจาก Custom Fields) ถ้าไม่ใช่ก็ข้ามไปไม่ต้องทำอะไร แต่ถ้าใช่ ให้เพิ่ม disabled (คำสั่งปิดการใช้งาน) ลงใน selection form ที่ว่าครับ

เอาหละครับ และนี่คือโค้ด javascript ที่ว่าครับ ให้เอาไปไว้ก่อน </body> นะครับ


jQuery(function() {
jQuery("select.sf-input-select").prop('disabled', function() {
return jQuery('option', this).length <= 2;
});

ต่อมาครับ เราก็ไปเขียน CSS เพื่อให้ Selection Form ตัวนี้เป็นสีเทา เพื่อบอกว่าคลิกไม่ได้นะหนู


select:disabled {
color: #b9b9b9;
}

เนี่ยครับ ฟอร์ม ก็จะเป็นสีเทาไปแล้ว และคลิกไม่ได้ด้วย เย่!

แสดงผล Options แค่บางตัว โดยเช็คจากเงื่อนไขจากตัวเลือก Selection ก่อนหน้า

ตัวนี้จะซับซ้อนหน่อยครับ ยกตัวอย่างเช่น จากภาพด้านบน สมมติว่า Breaking Capacity มีตัวเลือกสองตัวคือ 2A กับ 4A ส่วนตัวเลือกที่สอง Rated Current นั้นจะแสดงผลตามตัวเลือกของ breaking คับ

ผมลองวาดภาพประกอบให้ดูนะครับ เช่น อันแรกถ้าเป็น all items อันที่สองก็แสดงลิสต์ตัวเลือกทั้งหมด แต่ถ้าเขาเลือก 2A อันที่สองก็จะแสดงแค่ 40A กับ 63A เท่านั้น (เฉพาะตัวที่ทำสีเหลือง)

พระเอกของงานนี้ก็ใช้ Javascripts เข้ามาช่วยอีกเช่นเคยครับ แต่สืบเนื่องมาจากว่าฟอร์มนี้เป็นฟอร์มที่ถูก Generate มาจากปลักอิน ซึ่งมันก็ Output ออกมาแบบหาความสัมพันธ์ที่จะทำเงื่อนไขได้ยากมากครับ

อย่างเคสตัวอย่างนะคับ ฟอร์ม Selection จะเป็นแบบนี้ครับ


<select name="_sfm_rc_up_63ka_poles[]" class="sf-input-select">
<option class="sf-level-0 sf-item-0" selected="selected" data-sf-depth="0" value="">All Items</option>
<option class="sf-level-0 sf-option-active" data-sf-count="-1" data-sf-depth="0" value="2 A">2 A</option>
<option class="sf-level-0" data-sf-count="-1" data-sf-depth="0" value="4 A">4 B</option>
</select>

<select name="_sfm_rc_up_63ka_rated_current[]" class="sf-input-select">

<option class="sf-level-0 sf-item-0 sf-option-active" selected="selected" data-sf-depth="0" value="">All Items</option>
<option class="sf-level-0 " data-sf-count="-1" data-sf-depth="0" value="25 A">25 A</option>
<option class="sf-level-0 " data-sf-count="-1" data-sf-depth="0" value="40 A">40 A</option>
<option class="sf-level-0 " data-sf-count="-1" data-sf-depth="0" value="63 A">63 A</option>
<option class="sf-level-0 " data-sf-count="-1" data-sf-depth="0" value="80 A">80 A</option>
<option class="sf-level-0 " data-sf-count="-1" data-sf-depth="0" value="100 A">100 A</option>
</select>

ด้วยความที่มันเป็นแบบนี้ ขั้นตอนนี้เลยใช้ความถึกหน่อยครับ คือเช็คเป็นเคสๆไป (ซึ่งถ้าใครมีวิธีการที่สะดวกกว่านี้ ก็แสดงความคิดเห็นกันได้นะครับ)

โดยการแก้ปัญหาของผมก็คือ

  • ผมสร้าง Array มาเก็บข้อมูลก่อนครับ โดยเป็น Arrays สำหรับ All items, 2 A, 4 A
  • เช็คเงื่อนไขว่า Selection แรกเลือก Options ชื่อว่าอะไร แล้วก็ให้ตัว Selection ตัวที่สองเอาค่าจาก array นั้นมาแสดง

ลองดูโค้ดกันดีกว่าครับ เขียนในก่อน </body> เช่นเคยคับ


var allOptions = ["1A", "2A", "3A", "4A", "5A", "6A", "8A", "10A", "12A", "15A" , "20A"];
var twoAOptions = ["20A"];
var fourBOptions = ["1A", "2A", "3A", "4A", "5A", "6A", "8A", "10A", "12A", "15A"];

jQuery('[name="_sfm_fuse_holder_breaking_capacity[]"]').on("change", function() {
if(jQuery(this).val() === "2 A") {
jQuery('[name="_sfm_fuse_holder_rated_current[]"]').find("option").remove().end().append('<option class="sf-level-0 sf-item-0 sf-option-active" selected="selected" data-sf-depth="0" value="">All Items</option>');
for(i=0; i <= twoAOptions.length -1; i++){
var option = '<option class="sf-level-0 " data-sf-count="-1" data-sf-depth="0" value="'+twoAOptions[i]+'">'+twoAOptions[i]+'</option>';
jQuery('[name="_sfm_fuse_holder_rated_current[]"]').append(option);
}
} else if(jQuery(this).val() === "4 A") {
jQuery('[name="_sfm_fuse_holder_rated_current[]"]').find("option").remove().end().append('<option class="sf-level-0 sf-item-0 sf-option-active" selected="selected" data-sf-depth="0" value="">All Items</option>');
for(i=0; i <= fourBOptions.length -1; i++){
var option = '<option class="sf-level-0 " data-sf-count="-1" data-sf-depth="0" value="'+fourBOptions[i]+'">'+fourBOptions[i]+'</option>';
jQuery('[name="_sfm_fuse_holder_rated_current[]"]').append(option);
}
} else {
jQuery('[name="_sfm_fuse_holder_rated_current[]"]').find("option").remove().end().append('<option class="sf-level-0 sf-item-0 sf-option-active" selected="selected" data-sf-depth="0" value="">All Items</option>');
for(i=0; i <= allOptions.length -1; i++){
var option = '<option class="sf-level-0 " data-sf-count="-1" data-sf-depth="0" value="'+allOptions[i]+'">'+allOptions[i]+'</option>';
jQuery('[name="_sfm_fuse_holder_rated_current[]"]').append(option);
}
}
})

ซึ่งการแก้ปัญหาตัวนี้ ต้องขอบคุณเพื่อนฝูงในสังคมโปรแกรมเมอร์ Stackoverflow ครับที่ร่วมด้วยช่วยกันคิดวิธีแก้ปัญหา ผมว่าเป็นสังคมที่ดีมาก ไม่หวงวิชากันครับ

และนี่แหละครับคือสิ่งที่ผมอยากจะนำมาแบ่งปันในบทความการทำเว็บตอนนี้ มันยาวมากๆ ตอนแรกคิดว่าจะแบ่งเป็นตอนๆ แต่คิดไปคิดมา ให้มันอยู่ด้วยกันนี่แหละ จะไม่ได้ต้องไปคลิกอ่านหลายลิงค์ ซึ่งผมก็หวังว่าบทความชุดนี้จะช่วยให้เพื่อนๆสามารถลองเอาไปปรับใช้กับการพัฒนาเว็บของตัวเองได้นะครับ มีอะไรก็แลกเปลี่ยนคุยกันได้ครับ แล้วเจอกันใหม่คร้าบ


Phraisohn Siripool is a Graphic Designer and Website Developer. Contact Buksohn for Your Business Endeavours