استفاده از Grails در جی کوئری و جدول داده ها
چهارشنبه 12 تیر 1398استفاده از Grails برای data tebel ها کاربرد زیادی دارد. در این مطلب قصد داریم درباره Grails و استفاده از آن در جدول های داده های بزرگ صحبت کنیم.
من یکی از طرفداران پر و پا قرص Grails هستم. من جزء آن دسته از افرادی هستم که علاقه مند به کار کردن با داده ها و آنالیز کردن آنها با استفاده از ابزارهای خط فرمان هستم، تمامی افراد گاهی اوقات نیاز دارند که بتوانند نگاهی به برخی از داده ها انداخته و آن ها را تجزیه و تحلیل کنند، داشتن یک جدول بزرگ از داده ها و تجزیه و تحلیل آن می تواند کار نسبتا سختی باشد، با استفاده از Grails در جی کوئری شما می توانید این کار را به راحتی انجام دهید. پلاگین data tables در جی کوئری کمک زیادی به شما در درک درستی از داده ها خواهد کرد.
وب سایت data tebels یک داکیومنتیشن بسیار قوی را به شما ارائه می دهد که به شما نشان می دهد چگونه می توانید از یک برنامه خوب برای تجزیه و تحلیل داده های خود استفاده کنید که این برنامه شامل جاوا اسکریپت و اچ تی ام ال می باشد. برای افرادی که علاقه مند به استفاده از Grails در قسمت بک اند خود هستند داشتن یک درک درست از نحوه ی کار کردن با داده ها ضروری می باشد، علاوه بر این داده هایی که در یک جدول یک سطری قرار دارند نیز دارای پیچیگی های خاص خود می باشند که برای کار کردن با آنها نیاز دارید که بتوانید از Grails استفاده کنید و کار کردن با آن را تمرین کنید.
مقدمه ای درباره این مقاله
در این مقاله ما قصد داریم که دو باگ بزرگ را با استفاده از ساختن یک اپلیکیشن با استفاده از Grails به شما آموزش دهیم که می تواند ساختارهای پیچیده را به راحتی برای شما تجزیه و تحلیل کند. ما برای انجام این کار قصد داریم برخی از معیارهای Grails را به شما آموزش دهیم که در واقع همان معیارهای Groovy-fied Java Hibernate می باشند، من کد مربوط به این مقاله را در گیت هاب قرار داده ام و این مقاله تنها به بیان تفاوت های ظاهری میان این کد با سایر کدها خواهد پرداخت.
پیش نیازهای مورد نیاز
به عنوان پیش نیاز برای انجام این کار شما نیاز دارید که محیط های جاوا، گرووی و Grails را بر روی سیستم خود نصب کنید، من به شخصه علاقه مند هستم که از پنجره ترمینال و Vim استفاده کنم. همانطور که مشاهده می کنید در این جا هم استفاده کرده ام. برای دریافت نسخه مدرن جاوا به شما پیشنهاد می کنم Open Java Development Kit (OpenJDK) را دانلود و نصب کنید که توسط توزیع لینوکس تولید شده و امروزه نسخه های 8، 9، 10 و 11 آن موجود است و در زمان تولید این مقاله من از نسخه 8 آن استفاده کرده ام. از نظر من بهترین راه برای این که بتوانید Groovy و Grails را به روزرسانی کنید این است که از SDKMAN برای انجام این کار استفاده کنید.
دریافت برنامه employee browser
همانطور که در بالا ذکر شد من سورس کد این برنامه employee browser را در گیت هاب قرار داده ام، برای توضیح بیشتر این برنامه با زدن کد زیر در خط فرمان لینوس تولید شده است:
cd Projects
grails create-app com.nuevaconsulting.embrow
کلاس های دامین و یونیت تست ها نیز به شکل زیر انجام می شوند:
cd embrow
grails create-domain-class com.nuevaconsulting.embrow.Position
grails create-domain-class com.nuevaconsulting.embrow.Office
grails create-domain-class com.nuevaconsulting.embrow.Employee
کلاس های دامین که با استفاده از این روش ساخته می شوند هیچ گونه ویژگی ندارند و به همین علت است که شما باید آنها را ویرایش کنید.
ویرایش کلاس های دامین
کلاس دامین پوزیشن:
package com.nuevaconsulting.embrow class Position { String name int starting static constraints = { name nullable: false, blank: false starting nullable: false } }
کلاس دامین Office:
package com.nuevaconsulting.embrow class Office { String name String address String city String country static constraints = { name nullable: false, blank: false address nullable: false, blank: false city nullable: false, blank: false country nullable: false, blank: false } }
و کلاس دامین Employee نیز به شکل زیر باید ویرایش شود:
package com.nuevaconsulting.embrow class Employee { String surname String givenNames Position position Office office int extension Date hired int salary static constraints = { surname nullable: false, blank: false givenNames nullable: false, blank: false position nullable: false office nullable: false extension nullable: false hired nullable: false salary nullable: false } }
یک نکته بسیار مهم
توجه داشته باشید که کلاس های دامین و آفیس از نوع داده ای String و int استفاده می کنند. علاوه بر این کلاس Employee نیز از فیلدهایی استفاده می کند که از نوع کلاس های پوزیشن و دامین می باشد. این موضوع باعث به وجود آمدن جدول پایگاه داده ای می شود که در آن شیء های ساخته شده از کلاس Employee برای نگهداری منابع ذخیره شده اند یا از کلیدهای خارجی برای ذخیره سازی اشیایی که از کلاس position و Office ساخته شده اند استفاده شده است.
حال شما می توانید کنترلرها، ویوها و سایر کامپوننت های تست دیگر را به شکل زیر تعریف کنید:
grails generate-all com.nuevaconsulting.embrow.Position grails generate-all com.nuevaconsulting.embrow.Office grails generate-all com.nuevaconsulting.embrow.Employee
در این مرحله شما یک CRUD (create، read، update و Delete) پایه ای دارید که آماده ی اجرا و استفاده است. من برخی از داده های پایه ای برای اجرای Grails را در Grails-app/init/com/nuevaconsulting/BootStrap.groovy گنجانده ام.
اگر شما این اپلیکیشن را با استفاده از دستورات کامند اجرا کنید:
grails run-app
حال شما می توانید اسکرین شات زیر از Grails را در آدرس http://localhost:8080/: از مرورگر خود مشاهده کنید:
با کلیک کردن بر روی لینک مربوط به OfficeController می توانید اسکرین شاتی مانند اسکرین شات زیر را در صفحه مرورگر خود مشاهده کنید:
توجه داشته باشید که این لیست توسط متد OfficeController index ساخته شده و توسط ویو مربوط به office/index.gsp. در Grails به نمایش گذاشته شده است.
به صورت مشابه شما می توانید با کلیک بر روی لینک مربوط به EmployeeController اسکرین شاتی مشابه اسکرین شات زیر را دریافت کنید:
با کلیک بر روی لینک مربوط به پوزیشن و آفیس چه اتفاقی خواهد افتاد؟
ویوها با استفاده از دستورات کلی تولید که در فایل index.gsp در Grails رخ داد به وجود می آیند و از برچسب Grails <f:table/> استفاده می کنند، که به صورت پیشفرض نام کلاس مربوط به آنها می باشد. شما می توانید این متد را به راحتی شخصی سازی کنید تا نتیجه ی بهتری را برای شما به ارمغان بیاورد که از جمله آنها می توان به مختصر و مفیدتر شدن صفحه، تولید لینک ها به صورت خودکار، صفحه بندی خودکار و تولید خودکار ستون های قابل مرتب سازی اشاره کرد.
با این وجود حتی زمانی که Grails به صورت کاملا شفاف کار می کند نیز قابلیت های محدودی را در اختیار شما قرار می دهد، به عنوان مثال فرض کنید که می خواهید تمامی کارکنانی که موقعیت آنها شامل کلمه dev می باشد را پیدا کنید، چه اتفاقی می افتد اگر شما بخواهید که ستون ها را به منظور مرتب سازی با یکدیگر ترکیب کنید و این موضوع باعث شود که مرتب سازی اولیه از بین برود؟ یا این که چه اتفاقی می افتد اگر شما یک زیر مجموعه مرتب شده را به یک صفحه پی دی اف تبدیل کنید؟
پلاگین های data table جی کوئری در Grails به شما کمک می کنند و قابلیت های زیادی را برای شما فراهم می کنند تا بتوانید یک جدول کامل از داده ها را در مرورگر خود ایجاد کنید.
ساخت ویو مرورگر employee و متدهای کنترل کننده آن
برای ساخت یک مرورگر با داده های مربوط به employee در جی کوئری و Grails شما باید دو کار را انجام دهید:
1- یک ویو مربوط به Grails ایجاد کنید که شامل اچ تی ام ال و جاوا اسکریپت برای فعال کردن Data tables باشد.
2- یک متد به کنترلر Grails اضافه کنید که ویوی جدید را مدیریت کند.
employee browser view
در داخل دایرکتور embrow/grails-app/views/employee یک فایل کپی از index.gsp را ایجاد کرده و آن را فراخوانی کنید:
cd Projects
cd embrow/grails-app/views/employee
cp index.gsp browser.gsp
در این مرحله شما می خواهید فایل browser.gsp جدید را شخصی سازی کنید و کد مربوط به Data tables در جی کوئری را به آن اضافه کنید:
به عنوان مثال من می خواهم کدهای مربوط به جاوا اسکریپت و سی اس اس را به محض امکان پذیر بودن دریافت کنم، برای انجام این کار در خط فرمان باید کدهای زیر را وارد کنم:
<title><g:message code="default.list.label" args="[entityName]" /></title>
خطوط زیر را نیز باید به این کد اضافه کنید:
script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.css"> <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.js"></script> <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/scroller/1.4.4/css/scroller.dataTables.min.css"> <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/scroller/1.4.4/js/dataTables.scroller.min.js"></script> <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/buttons/1.5.1/js/dataTables.buttons.min.js"></script> <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.flash.min.js"></script> <script type="text/javascript" charset="utf8" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script> <script type="text/javascript" charset="utf8" src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/pdfmake.min.js"></script> <script type="text/javascript" charset="utf8" src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/vfs_fonts.js"></script> <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.html5.min.js"></script> <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.print.min.js "></script> Next, remove the code that provided the data pagination in index.gsp: حال کدهایی که data pagination را در فایل index.gsp: فراهم می کنند را حذف کنید: <div id="list-employee" class="content scaffold-list" role="main"> <h1><g:message code="default.list.label" args="[entityName]" /></h1> <g:if test="${flash.message}"> <div class="message" role="status">${flash.message}</div> </g:if> <f:table collection="${employeeList}" /> <div class="pagination"> <g:paginate total="${employeeCount ?: 0}" /> </div> </div>
و کدهایی که باعث زیبا سازی جدول داده ها در جی کوئری و Grails می شوند را نیز اضافه کنید.
گام اول برای زیباسازی جدول
اولین مرحله برای انجام این کار اضافه کردن کدهای html می باشد که یک حالت جدولی پایه را برای داده های شما در مرورگر ایجاد می کند، در این اپلیکیشن زمانی که data tables با قسمت بک اند پایگاه داده ارتباط برقرار می کند، تنها حاشیه ها و ستون های جدول را ایجاد می کند و جاوا اسکریپت نیز از محتویات این جدول در Grails نگهداری می کند.
<div id="employee-browser" class="content" role="main"> <h1>Employee Browser</h1> <table id="employee_dt" class="display compact" style="width:99%;"> <thead> <tr> <th>Surname</th> <th>Given name(s)</th> <th>Position</th> <th>Office</th> <th>Extension</th> <th>Hired</th> <th>Salary</th> </tr> </thead> <tfoot> <tr> <th>Surname</th> <th>Given name(s)</th> <th>Position</th> <th>Office</th> <th>Extension</th> <th>Hired</th> <th>Salary</th> </tr> </tfoot> </table> </div>
گام دوم برای زیباسازی جدول
در مرحله بعدی شما باید کدهای مربوط به جاوا اسکریپت را اضافه کنید و سه عمل اصلی را در آن قرار دهید، ابتدا شما باید سایز باکس های مربوط به متن را وارد کنید، بعد از آن فاصله از پایین و فیلدهای هر یک از ستون ها را نیز باید وارد کنید تا بتوانید یک حالت جدولی مناسب را برای داده های خود ایجاد کنید.
<g:javascript> $('#employee_dt tfoot th').each( function() {
قطعه کد زیر سایز دادن به فیلترهایی که در انتهای جدول هستند را مدیریت می کند:
var title = $(this).text(); if (title == 'Extension' || title == 'Hired') $(this).html('<input type="text" size="5" placeholder="' + title + '?" />'); else $(this).html('<input type="text" size="15" placeholder="' + title + '?" />'); });
گام سوم از زیباسازی جدول
بعد از انجام تمامی این کارها شما باید مدل جدول خود را تعریف کنید، در این مرحله تمامی متدهای جدول شما باید تعریف شده باشند که از جمله آنها می توان به اسکرول کردن، یک رابط کاربری طبیعی، توانایی ارسال داده های مختلف در فرمت های مختلف و همین طور خاصیت اتصال به سرور و ارتباط با آن و ... توجه داشته باشید که در Grails آدرس را همان Url با استفاده از Groovy GString و متد Grails createLink() ایجاد می شوند، اطلاعات به وجود آمده به قسمت بک اند پایگاه داده ارسال می شوند و این پایگاه داده آنها را به نمایش می گذارد و نتیجه ی مناسب را باز می گرداند.
var table = $('#employee_dt').DataTable( { "scrollY": 500, "deferRender": true, "scroller": true, "dom": "Brtip", "buttons": [ 'copy', 'csv', 'excel', 'pdf', 'print' ], "processing": true, "serverSide": true, "ajax": { "url": "${createLink(controller: 'employee', action: 'browserLister')}", "type": "POST", }, "columns": [ { "data": "surname" }, { "data": "givenNames" }, { "data": "position" }, { "data": "office" }, { "data": "extension" }, { "data": "hired" }, { "data": "salary" } ] });
گام آخر برای زیباسازی جدول
در این مرحله شما باید فیلترهای ستون های خود را به نمایش بگذارید،
table.columns().every(function() { var that = this; $('input', this.footer()).on('keyup change', function(e) { if (that.search() != this.value && 8 < e.keyCode && e.keyCode < 32) that.search(this.value).draw(); });
در ادامه این کد باعث کامل شدن کدهای مربوط به زیباسازی شما می شود:
});
</g:javascript>
مشاهده ی نتایج
در این جا یک اسکرین شات از UI که توسط این ویو ساخته شده است را مشاهده می کنید:
در ادامه اسکرین شات دیگری وجود دارد که فیلتر کردن ستون و مرتب سازی چند ستونه ی آنها را نشان می دهد (مربوط به مثالی بود که به دنبال تمامی کارکنانی که آدرس فعلی آنها با dev شروع می شد بودیم):
اسکرین شات زیر نشان می دهد که اگر شما بر روی دکمه CSV کلیک کنید چه اتفاقی می افتد:
و در انتها تصویر زیر نشان دهنده ی داده های CSV در بخش LibreOffice از Grails می باشد:
بسیار خب، به نظر این بخش ساده بود، بنابراین عمل کنترلر باید تمامی محاسبات و بخش های سنگین را دارا باشد، این طور نیست؟ با ما در ادامه این مطلب همراه باشید.
عملیات employee controller browserLister
توجه داشته باشید که ما String زیر را داشتیم:
"${createLink(controller: 'employee', action: 'browserLister')}"
این String به عنوان یک URL برای برقراری ارتباط بین جدول داده ها و Data tables برچسب گذاری شده در Grails مورد استفاده قرار می گرفت که به صورت پویا یک لینک تولید می کرد و با استفاده از HTML پردازش های لازم را بر روی سرور Grails انجام می داد، با تولید یک لینک در Grails که برای EmployeeController می باشد و در آدرس زیر قرار گرفته است این پردازش به پایان می رسد:
embrow/grails-app/controllers/com/nuevaconsulting/embrow/EmployeeController.groovy
این کار به صورت اختصاصی برای متد کنترل کننده browserLister() نیز در Grails اتفاق می افتد. من برخی از دستورات پرینت را که نشان دهنده ی خروجی ها بودند در پنجره ترمینال قرار دادم تا در حین اجرا دیده شوند.
def browserLister() {
// Applies filters and sorting to return a list of desired employees
مرحله ی اول برای چاپ نتایج
در ابتدا پارامترهایی که به متد browserLister() ارسال شده بودند چاپ می شود، من معمولا کار خود برای ساخت یک متد کنترل کننده را با این کد شروع می کنم تا به صورت کامل آن را دریافت کنم:
println "employee browserLister params $params" println()
بعد از آن باید این پارامترها را پردازش کرد تا آن ها را به شکل قابل استفاده تری درآورد، ابتدا برای پارامترهای Data tables جی کوئری باید jqdtParams توسط Groovy map فراخوانی شود.
def jqdtParams = [:] params.each { key, value -> def keyFields = key.replace(']','').split(/\[/) def table = jqdtParams for (int f = 0; f < keyFields.size() - 1; f++) { def keyField = keyFields[f] if (!table.containsKey(keyField)) table[keyField] = [:] table = table[keyField] } table[keyFields[-1]] = value } println "employee dataTableParams $jqdtParams" println()
مرحله ی دوم برای چاپ نتایج
بعد از آن بر روی ستون مربوط به داده های مربوط به ستون باید این عملیات انجام شود که این کار از طریق فراخوانی columnMap توسط Groovy map شکل می گیرد:
def columnMap = jqdtParams.columns.collectEntries { k, v -> def whereTerm = null switch (v.data) { case 'extension': case 'hired': case 'salary': if (v.search.value ==~ /\d+(,\d+)*/) whereTerm = v.search.value.split(',').collect { it as Integer } break default: if (v.search.value ==~ /[A-Za-z0-9 ]+/) whereTerm = "%${v.search.value}%" as String break } [(v.data): [where: whereTerm]] } println "employee columnMap $columnMap" println()
مرحله ی سوم از چاپ نتایج
در نهایت لیستی از تمامی نام های ستون ها از columnMap و همینطور لیست متناظر با ترتیبی که ستون ها در این ویو باید باشند بازگردانده می شود، لیست های Groovy می توانند allColumnList و orderList را به ترتیب زیر فراخوانی کنند:
def allColumnList = columnMap.keySet() as List println "employee allColumnList $allColumnList" def orderList = jqdtParams.order.collect { k, v -> [allColumnList[v.column as Integer], v.dir] } println "employee orderList $orderList"
پیاده سازی هایبرنیت Grails
ما در این جا قصد داریم تا از معیار Hibernate Grails استفاده کرده و آن را پیاده سازی کنیم تا به این طریق بتوانیم انتخاب عناصر را به شکل زیباتری نشان دهیم و همینطور آنها را سفارشی سازی کنیم. استفاده کردن از این معیار در Grails نیازمند استفاده از برخی از فیلترها می باشد، ما در این جا باید فیلتر خود را به صورت مشخص تعریف کنیم که در این مثال می تواند پیچیدگی هایی را به همراه داشته باشد، استفاده از createAlias می تواند در این کار کمک زیادی به شما بکند.
def filterer = { createAlias 'position', 'p' createAlias 'office', 'o' if (columnMap.surname.where) ilike 'surname', columnMap.surname.where if (columnMap.givenNames.where) ilike 'givenNames', columnMap.givenNames.where if (columnMap.position.where) ilike 'p.name', columnMap.position.where if (columnMap.office.where) ilike 'o.name', columnMap.office.where if (columnMap.extension.where) inList 'extension', columnMap.extension.where if (columnMap.salary.where) inList 'salary', columnMap.salary.where if (columnMap.hired.where) { if (columnMap.hired.where.size() > 1) { or { columnMap.hired.where.each { between 'hired', Date.parse('yyyy/MM/dd',"${it}/01/01" as String), Date.parse('yyyy/MM/dd',"${it}/12/31" as String) } } } else { between 'hired', Date.parse('yyyy/MM/dd',"${columnMap.hired.where[0]}/01/01" as String), Date.parse('yyyy/MM/dd',"${columnMap.hired.where[0]}/12/31" as String) } }
در این مرحله ما باید تمامی کارهایی که کرده ایم را اجرا کنیم، در مرحله اول ما نیاز داریم که تمامی شیء هایی که از کلاس Employee ساخته ایم را محاسبه کنیم که این کار با استفاده از قطعه کد زیر امکان پذیر می باشد:
def recordsTotal = Employee.count()
println "employee recordsTotal $recordsTotal"
در مرحله بعدی فیلترهای مربوط به شیء های Employee را محاسبه کنید که باید کمتر از تعداد کل باشد:
def c = Employee.createCriteria() def recordsFiltered = c.count { filterer.delegate = delegate filterer() } println "employee recordsFiltered $recordsFiltered"
زمانی که شما این دو تعداد را داشته باشید می توانید شیء های ساخته شده با فیلترهای واقعی را همراه با اطلاعات خود دریافت کنید:
def orderer = Employee.withCriteria { filterer.delegate = delegate filterer() orderList.each { oi -> switch (oi[0]) { case 'surname': order 'surname', oi[1]; break case 'givenNames': order 'givenNames', oi[1]; break case 'position': order 'p.name', oi[1]; break case 'office': order 'o.name', oi[1]; break case 'extension': order 'extension', oi[1]; break case 'hired': order 'hired', oi[1]; break case 'salary': order 'salary', oi[1]; break } } maxResults (jqdtParams.length as Integer) firstResult (jqdtParams.start as Integer) }
حال شما باید نتایجی که به ترتیب به شما بازگشت داده می شود را پردازش کنید، لینک های مربوط به Employee، پوزیشن و ... را بسازید و همینطور اشیای مد نظر را نیز به عنوان سطرهای جدول در نظر بگیرید:
def dollarFormatter = new DecimalFormat('$##,###.##') def employees = orderer.collect { employee -> ['surname': "<a href='${createLink(controller: 'employee', action: 'show', id: employee.id)}'>${employee.surname}</a>", 'givenNames': employee.givenNames, 'position': "<a href='${createLink(controller: 'position', action: 'show', id: employee.position?.id)}'>${employee.position?.name}</a>", 'office': "<a href='${createLink(controller: 'office', action: 'show', id: employee.office?.id)}'>${employee.office?.name}</a>", 'extension': employee.extension, 'hired': employee.hired.format('yyyy/MM/dd'), 'salary': dollarFormatter.format(employee.salary)] }
در پایان نیز باید نتیجه ای که می خواهید به صورت JSON باز گردانید را تولید کنید که این کار نیز به شکل زیر انجام می شود:
def result = [draw: jqdtParams.draw, recordsTotal: recordsTotal, recordsFiltered: recordsFiltered, data: employees] render(result as JSON) }
- Jquery
- 2k بازدید
- 0 تشکر