Unlimited multi level menu in mvc

สวัสดีค่ะวันนี้เรามาว่ากันเรื่องเมนูกันดีกว่านะคะ คำว่าเมนู ผู้เขียนคิดว่าทุกคนต้องรู้จักแน่นอน เพราะในการพัฒนาแต่ละระบบนั้นส่วนใหญ่จะมีส่วนของงานหลายๆส่วน ทำให้มีการออกแบบหน้าจอการใช้งานหลายหน้าจอเพื่อรองรับการทำงานของระบบนั้น เมื่อส่งมอบระบบ แน่นอน!! ค่ะ ความต้องการของลูกค้าไม่หยุดแค่นั้นแน่นอน เมื่อความต้องการเพิ่ม การทำงานของหน้าจอก็เพิ่ม เมนูก็ต้องเพิ่มตามมาเช่นกัน ทำให้ผู้พัฒนาต้องไปแก้โค้ดในส่วนของเมนูทุกครั้งที่มีการเพิ่มเมนู การจัดหมวดหมูของเมนู หรือต้องการปรับเปลี่ยน path ที่ไปเรียกหน้าจอนั้นๆ แค่คิดก็ดูยุ่งยากต่อการจัดการแล้ว วันนี้ผู้เขียนจึงนำวิธีการออกแบบและพัฒนาในส่วนของเมนูที่ผู้เขียนได้ใช้พัฒนาใน MVC มาเป็นตัวอย่างให้ดูกันนะคะ สำหรับผู้อ่านที่ไม่ได้ใช้ MVC ก็สามารถนำไปปรับเปลี่ยนได้ค่ะ หลังจากพัฒนาแล้วระบบก็จะสามารถ เพิ่มเมนู หรือเปลี่ยน path ของเมนู หรือ จัดการกลุ่มของเมนู ได้โดยไม่ต้อง publish ระบบ ทุกครั้งที่มีการแก้ไข ทำให้ง่ายและไม่เสียเวลาในการจัดการเลยค่ะ เรามาเริ่มกันเลยค่ะ ผู้เขียนขอแบ่งการพัฒนาเป็นสองส่วนนะคะ คือ 1.การพัฒนาในฐานข้อมูล : ในส่วนของการออกแบบฐานข้อมูลผู้อ่านต้องออกแบบให้มีการเก็บ id ของ parent เพื่อระบุให้รู้ว่าเมนูตัวนี้เป็นลูกของเมนูตัวไหน ตัวอย่างการออกแบบฐานข้อมูลของเมนู คอลัมภ์ คำอธิบาย  ParentID Id ของ parent MenuID Id ของเมนู MenuName ชื่อของเมนู 2.การพัฒนาระบบ : เป็นการพัฒนาระบบผู้เขียนจะใช้วิธีการพัฒนาแบบใช้ Recursive เข้ามาจัดการในส่วนของการแสดงเมนู โดยข้อดีของ Recursive คือเราสามารถจัดการได้ทั้งส่วนที่มีเมนูย่อย และไม่มีเมนูย่อย ตัวอย่างในส่วนของ Recursive ที่ใช้ใน View เพื่อการแสดงเมนู ( MVC) @helper GetSubMenus(IEnumerable<menutable> siteMenu, Nullable<int> parentID) {     foreach (var i in Model.Where(a => a.ParentID.Equals(parentID)))     {         var submenu = Model.Where(a => a.ParentID.Equals(i.MenuID)).Count();           <li class=”@(submenu > 0 ? “dropdown-submenu” : “dropdown”)”>             <a href=”@(!string.IsNullOrEmpty(i.MenuLink) ? Url.Content(i.MenuLink) : “~/default)” style=”font-size:16px;”>@i.MenuName</a>             @if (submenu > 0)             {                 <ul class=”dropdown-menu”>                     @GetSubMenus(siteMenu, i.MenuID)                     @* Recursive  Call for Populate Sub items here*@                 </ul>             }         </li>     } }   @{     var mymenu = @Model;     var menuParentID = mymenu.First().ParentID; }  @if (mymenu != null && mymenu.Count() > 0) {     <nav class=”navbar navbar-default”>         <div class=”container-fluid”>             <div class=”collapse navbar-collapse” id=”bs-example-navbar-collapse-1″>                 <ul class=” nav navbar-nav”>                     @GetSubMenus(mymenu, menuParentID)                 </ul>             </div>         </div>     </nav> } ตัวอย่างหน้าจอของเมนูที่เรียกใช้ Recursive เพื่อจัดการเมนู

Read More »

Oracle: retrieve top n records for each group

วิธีการเขียน Query เพื่อดึงข้อมูลข้อมูลสูงสุดหรือต่ำสุด N ลำดับแรกของแต่ละกลุ่มออกมาจากตาราง สมมติว่าเรามีข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่ซึ่งประกอบด้วย 5 ฟิลด์ข้อมูลดังตัวอย่างข้างล่าง ข้อมูล: ตาราง TEST_NEW_STUDENT เป็นตัวอย่างข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่จำนวน 773 รายการ โจทย์: ต้องการดึงข้อมูลนักศึกษาที่ได้คะแนนภาษาอังกฤษสูงสุดแยกตามคณะจากข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่จำนวน 773 รายการนี้ เริ่มต้นด้วย query ดังนี้ SELECT a.*, ROW_NUMBER () OVER (PARTITION BY fac_id ORDER BY eng_score DESC) AS val_row_number FROM test_new_student a จุดสำคัญของ query ข้างต้นก็คือฟังก์ชัน ROW_NUMBER ซึ่งเป็นฟังก์ชันที่จะให้เลขบรรทัดของผลลัพธ์ออกมาตามการจัดกลุ่มข้อมูลหรือการเรียงลำดับที่เรากำหนดไว้ด้วยคำสั่ง OVER, PARTITION BY และ ORDER BY ที่ตามมา จากตัวอย่างข้างต้นหมายความว่า เรากำลัง Select * จากตาราง TEST_NEW_STUDENT พร้อมกับดึงเลข ROW_NUMBER ออกมา โดยเป็นเลขบรรทัดที่ให้แบ่งกลุ่มด้วยคณะ และให้เรียงลำดับด้วยคะแนนภาษาอังกฤษจากมากไปน้อย ซึ่งผลที่ได้จะเป็นดังนี้ สังเกตที่ฟิลด์ VAL_ROW_NUMBER จะเห็นว่ามันแสดงตามอันดับของคะแนนภาษาอังกฤษ และถูกแบ่งตามคณะอย่างเรียบร้อย แค่นี้เราก็สามารถที่จะ select เอา Top ที่เท่าไหร่ของแต่ละกลุ่มได้แล้ว โดยเลือกเอา VAL_ROW_NUMBER ที่ต้องการ ซึ่งสามารถทำได้โดยเขียน select…where ซ้อน query ข้างต้นเข้าไปอีกทีดังนี้ SELECT * FROM (SELECT a.*, ROW_NUMBER () OVER (PARTITION BY fac_id ORDER BY eng_score DESC) AS val_row_number FROM test_new_student a) WHERE val_row_number <= 1; ผลลัพธ์: สรุปจากความต้องการที่ตั้งไว้ เราสามารถแก้ได้โดยใช้ Window Function ซึ่งมันสามารถหาเลขลำดับบางอย่างภายในกลุ่มข้อมูลออกมาให้ได้รูปแบบทั่วไปของ query คือ 1 SELECT *, 2 WFUNCTION OVER (PARTITION BY GROUP_FIELDS ORDER BY ORDER_FIELDS) n 3 FROM SOURCE โดยที่ SOURCE คือตัวข้อมูล WFUNCTION  หมายถึง Window Function ตัวอย่างที่เราเลือกใช้คือ ROW_NUMBER() GROUP_FIELDS คือรายการฟิลด์ที่จะแบ่งกลุ่ม เขียนเหมือนตอนที่เราจะ group by สามารถแบ่งด้วยหลายฟิลด์ก็ได้ ORDER_FIELDS คือรายการฟิลด์ที่ใช้เรียงลำดับ, สามารถเรียงด้วยหลายฟิลด์ก็ได้, ใส่ ASC หรือ DESC ได้เหมือนคำสั่ง order by ปกติเราไม่จำเป็นต้องใส่ทั้ง partition by และ order by อาจจะใส่แค่อันใดอันหนึ่ง แล้วแต่ว่าต้องการแบ่งกลุ่มหรือเรียงลำดับหรือไม่ n เป็นชื่อ alias ของผลลัพธ์ จะตั้งชื่ออย่างไรก็ได้ เมื่อเราได้เลขลำดับ n ของแต่ละกลุ่มออกมาแล้ว ทีนี้จะเอาไปหา Top N หรือพลิกแพลงยังไง ก็แล้วแต่จะ query ออกมา

Read More »

Oracle: retrieve top n records from a query

Top-N queries เป็นวิธีการดึงข้อมูลสูงสุดหรือต่ำสุด N ลำดับแรกออกมาจากตาราง โดยวิธีการดึงข้อมูลแบบ Top-N นั้นมีได้หลายวิธี แต่ในบทความนี้จะนำเสนอวิธีการดึงข้อมูลแบบ Top-N records เพียง 3 วิธีการดังนี้ 1. Inline View and ROWNUM 2. WITH Clause and ROWNUM 3. ROW_NUMBER   สมมติว่าเรามีข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่ซึ่งประกอบด้วย 5 ฟิลด์ข้อมูลดังตัวอย่างข้างล่าง ข้อมูล: ตาราง TEST_NEW_STUDENT เป็นตัวอย่างข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่จำนวน 773 รายการ โจทย์: ต้องการดึงข้อมูลนักศึกษาที่ได้คะแนนภาษาอังกฤษสูงสุด 5 อันดันแรกจากข้อมูลคะแนนภาษาอังกฤษของนักศึกษาใหม่จำนวน 773 รายการนี้   เริ่มต้น Top-N query ตามลำดับเพื่อแก้โจทย์กันค่ะ Inline View and ROWNUM Classic Top-N style query SELECT a.*,rownum FROM (SELECT * FROM test_new_student ORDER BY eng_score desc) a WHERE ROWNUM <= 5; ผลลัพธ์: • จากผลลัพธ์ที่ได้ข้อมูลจะถูกจัดเรียงจากคะแนนจากมากไปน้อยก่อนด้วย ORDER BY clause และหลังจากนั้นก็จะจำกัดจำนวนข้อมูลที่ต้องการด้วย ROWNUM • Pseudocolumn ROWNUM เป็นค่าตัวเลขแสดงลำดับที่ของการดึงข้อมูลจากตาราง • กรณีที่ต้องการข้อมูลคะแนนภาษาอังกฤษต่ำสุด ใส่ ASC แทน DESC ตรง ORDER BY clause   WITH Clause and ROWNUM จากตัวอย่างข้างต้นเรายังสามารถเขียน query ด้วย WITH clause แทนที่ inline view ได้ดังนี้ WITH ordered_query AS (SELECT * FROM test_new_student ORDER BY eng_score desc) SELECT ordered_query.*,rownum FROM ordered_query WHERE rownum <= 5;   ROW_NUMBER ฟังก์ชัน ROW_NUMBER เป็นฟังก์ชันที่กำหนดค่าลำดับของข้อมูลที่จัดเรียงตามข้อมูลที่กำหนดไว้ใน order_by_clause โดยจะมีค่าเริ่มต้นเท่ากับ 1 โดยเราจะมีวิธีการเขียน query ได้ดังนี้ SELECT * FROM (SELECT a.*, row_number() OVER (ORDER BY eng_score DESC) AS val_row_number FROM test_new_student a) WHERE val_row_number <= 5; ผลลัพธ์: สังเกตที่ฟิลด์ VAL_ROW_NUMBER จะเห็นว่ามันแสดงตามอันดับของคะแนนภาษาอังกฤษแล้ว แค่นี้เราก็สามารถที่จะ select เอา Top ที่เท่าไหร่ได้แล้ว โดยเลือกเอา VAL_ROW_NUMBER ที่ต้องการ

Read More »

เปลี่ยน ubuntu sources.list ก่อนสร้าง image ด้วย dockerfile

การใช้งาน docker นั้นเราสามารถใช้ image จาก docker hub หรือเราจะสร้าง image ของเราเอง ซึ่งมีหลายวิธีในการสร้าง image แบบของเราเอง (custom) วิธีหนึ่งคือการใช้ dockerfile อย่างคร่าว ๆ คือ mkdir ~/mydocker cd ~/mydocker touch dockerfile docker built -t test_app:20170713 . docker images ในไฟล์ชื่อ dockerfile นี้จะมีไวยกรณ์ประมาณนี้ # Image tag: test_app:20170713 <– บรรทัดนี้คือ comment FROM ubuntu:16.04 <– บรรทัดนี้คือ ไปเอา image ชื่อ ubuntu:16.04 จาก docker hub RUN apt-get update <– บรรทัดนี้คือ คำสั่งบอกว่าจะติดตั้ง หลังคำว่า RUN นั่นเอง RUN apt-get dist-upgrade -y RUN apt-get install -y apache2 libapache2-mod-php7.0 php7.0 COPY … ADD … EXPOSE … CMD … และยังมี command อื่น ๆ อีก ทีนี้จากการที่ต้องลองผิดลองถูกบ่อย ๆ จึงพบว่า หากเราเพิ่มคำสั่ง 2 บรรทัดนี้เข้าไปก่อนบรรทัด RUN apt-get update ก็จะทำให้เราได้ใช้ ubuntu repository ที่ต้องการแทนค่า default ที่ archive.ubuntu.com เช่นต้องการให้มาใช้ th.archive.ubuntu.com ก็เขียนคำสั่งดังนี้ RUN sed -i ‘s/\/us.archive/\/th.archive/g’ /etc/apt/sources.list RUN sed -i ‘s/\/archive/\/th.archive/g’ /etc/apt/sources.list ผลลัพธ์คือ หลังจากทำคำสั่ง docker built -t test_app:20170713 . จะเห็นว่าในขั้นตอนการ build นั้นจะดาวน์โหลดไฟล์ได้รวดเร็วกว่าเดิมมาก จึงนำความรู้มาบอกกันครับ อ้อลืมบอกว่าบทความที่เขียนนี้ ผมทดสอบกับ docker version 17.06.0-ce ครับ อยากแนะนำความรู้เกี่ยวกับ docker ที่อ่านมา พบว่าน่าสนใจ ลองอ่านดูครับ อ่านง่าย   บทความในต่างประเทศ How To Install and Use Docker on Ubuntu 16.04 (https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04) How to Build an Image with the Dockerfile (https://www.sitepoint.com/how-to-build-an-image-with-the-dockerfile/) Dockerfile reference (https://docs.docker.com/engine/reference/builder/) Best practices for writing Dockerfiles (https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/) How to update Docker image to maintain your containers secure  (https://bobcares.com/blog/update-docker-image/2/) How to upgrade docker container after its image changed (https://stackoverflow.com/questions/26734402/how-to-upgrade-docker-container-after-its-image-changed) Manage data in containers (https://docs.docker.com/engine/tutorials/dockervolumes/)  

Read More »

รู้จักฟังก์ชัน Excel ตอนที่ 2 เรื่อง ตระกูลท่าน Count (COUNTIF, COUNTIFS)

พบกันอีกครั้งนะคะ กับ Excel ตอน ตระกูลท่าน Count ตอนที่ 2 ค่ะ สำหรับตอนที่ 2 นี้จะเป็นการแนะนำการใช้ Function COUNTIF และ COUNTIFS มาเริ่มกันเลยค่ะ COUNTIF เป็น Function ที่ใช้สำหรับนับข้อมูลตามเงื่อนไขที่เราต้องการ 1 เงื่อนไข เช่น ต้องการนับจำนวนคนที่ได้เกรด A : เงื่อนไขคือ “คนที่ได้เกรด A” ต้องการนับจำนวนคนที่ได้คะแนนมากกว่า 75 : เงื่อนไขคือ “คะแนนมากกว่า 75” รูปแบบ Function คือ COUNTIF(range, criteria) range คือ ช่วงข้อมูลที่ต้องการนับจำนวน criteria คือ เงื่อนไขที่ต้องการ ตัวอย่าง จากตัวอย่าง สิ่งที่ต้องการคือ หาจำนวนคนที่ได้เกรด A ดังนั้น range คือ ช่วง D3 ถึง D7 criteria คือ “A” ดังนั้นสิ่งที่จะได้เมื่อเขียน Function คือ =COUNTIF(D3:D7,”A”) ผลลัพธ์ที่ได้คือ 2 ดังภาพ   COUNTIFS เป็น Function ที่ใช้สำหรับนับข้อมูลตามเงื่อนไขที่เราต้องการโดยเงื่อนไขนั้นมีมากกว่า 1 เงื่อนไข เช่น ต้องการนับจำนวนคนที่ได้เกรด A และเป็นเพศหญิง : เงื่อนไขคือ “คนที่ได้เกรด A” และ “เพศหญิง” ต้องการนับจำนวนเด็กเข้าอบรมได้คะแนนมากกว่า 60 แต่น้อยกว่า 79 : เงื่อนไขคือ “คะแนนมากกว่า 60 แต่น้อยกว่า 79” รูปแบบ Function คือ COUNTIF(range_criteria1, criteria1, [range_criteria2, criteria2],…) range_criteria1 คือ ช่วงข้อมูลที่ต้องการนับจำนวนของเงื่อนไขที่ 1 ค่านี้จำเป็นต้องระบุ criteria1 คือ เงื่อนไขที่ต้องการ เงื่อนไขแรก ค่านี้จำเป็นต้องระบุ range_criteria2 คือ ช่วงข้อมูลที่ต้องการนับจำนวนของเงื่อนไขที่ 2 ค่านี้ไม่จำเป็นต้องระบุ criteria2 คือ เงื่อนไขที่ต้องการ เงื่อนไขที่สอง ค่านี้ไม่จำเป็นต้องระบุ สามารถระบุเงื่อนไขที่ต้องการได้เรื่อย ๆ ตัวอย่าง จากตัวอย่าง สิ่งที่ต้องการคือ หาจำนวนคนที่ได้เกรด A และได้คะแนนมากกว่า 90 ดังนั้น เงื่อนไขแรก range_criteria1 คือ ช่วง D3 ถึง D7 criteria1 คือ “A” เงื่อนไขที่สอง range_criteria2 คือ ช่วง E3 ถึง E7 criteria2 คือ “>=90” ดังนั้นสิ่งที่จะได้เมื่อเขียน Function คือ =COUNTIFS(D3:D7,”A”,E3:E7,”>=90″) ผลลัพธ์ที่ได้คือ 1 ดังภาพ สำหรับเรื่องราวของตระกูลท่าน Count ก็จบลงในตอนที่ 2 แต่เพียงเท่านี้ หวังว่าคงเป็นประโยชน์แก่ผู้อ่านไม่มากก็น้อยนะคะ แล้วพบกันใหม่ตอนหน้าค่ะ 

Read More »