عمليات البحث المدعومة بالنموذج في Rails. مثال على الانفجار المفاجئ:
## app/searches/post_search.rb
class PostSearch < TalentScout :: ModelSearch
criteria :title_includes do | string |
where ( "title LIKE ?" , "% #{ string } %" )
end
criteria :within , choices : {
"Last 24 hours" => 24 . hours ,
"Past Week" => 1 . week ,
"Past Month" => 1 . month ,
"Past Year" => 1 . year ,
} do | duration |
where ( "created_at >= ?" , duration . ago )
end
criteria :only_published , :boolean , default : true do | only |
where ( "published" ) if only
end
order :created_at , default : :desc
order :title
end
## app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@search = model_search
@posts = @search . results
end
end
<!-- app/views/posts/index.html.erb -->
<%= form_with model : @search , local : true , method : :get do | form | %>
<%= form . label :title_includes %>
<%= form . text_field :title_includes %>
<%= form . label :within %>
<%= form . select :within , @search . each_choice ( :within ) , include_blank : true %>
<%= form . label :only_published %>
<%= form . check_box :only_published %>
<%= form . submit %>
<% end %>
< table >
< thead >
< tr >
< th >
<%= link_to_search "Title" , @search . toggle_order ( :title ) %>
<%= img_tag " #{ @search . order_directions [ :title ] || "unsorted" } _icon.png" %>
</ th >
< th >
<%= link_to_search "Time" , @search . toggle_order ( :created_at ) %>
<%= img_tag " #{ @search . order_directions [ :created_at ] || "unsorted" } _icon.png" %>
</ th >
</ tr >
</ thead >
< tbody >
<% @posts . each do | post | %>
< tr >
< td > <%= link_to post . title , post %> </ td >
< td > <%= post . created_at %> </ td >
</ tr >
<% end %>
</ tbody >
</ table >
في المثال أعلاه:
PostSearch
مسؤولية البحث عن نماذج Post
. يمكنه تطبيق أي مجموعة من المعايير المحددة، وتجاهل قيم الإدخال المفقودة أو الفارغة أو غير الصالحة تلقائيًا. ويمكنه أيضًا ترتيب النتائج بأحد الترتيبات المحددة له، إما في الاتجاه الصعودي أو التنازلي.PostsController#index
المساعد model_search
لإنشاء مثيل PostSearch
، وتعيينه لمتغير @search
لاستخدامه لاحقًا في العرض. يتم أيضًا تعيين نتائج البحث إلى متغير لاستخدامه في العرض.@search
. يتم استخدام المساعد link_to_search
لإنشاء روابط في رأس الجدول والتي تقوم بفرز النتائج. لاحظ أن طريقة toggle_order
المستخدمة هنا تقوم بإرجاع كائن بحث جديد، مع ترك @search
بدون تعديل.للحصول على شرح تفصيلي للطرق المستخدمة في هذا المثال، راجع وثائق API.
يمكنك استخدام منشئ talent_scout:search
لإنشاء تعريف فئة بحث نموذجي. على سبيل المثال،
$ rails generate talent_scout:search post
سيتم إنشاء ملف "app/searches/post_search.rb" يحتوي على:
class PostSearch < TalentScout :: ModelSearch
end
ترث فئات البحث من TalentScout::ModelSearch
. يتم استنتاج فئة النموذج المستهدف من اسم فئة البحث. على سبيل المثال، سيبحث PostSearch
عن نماذج Post
بشكل افتراضي. لتجاوز هذا الاستدلال، استخدم ModelSearch::model_class=
:
class EmployeeSearch < TalentScout :: ModelSearch
self . model_class = Person # search for Person models instead of `Employee`
end
يتم تحديد معايير البحث باستخدام الأسلوب ModelSearch::criteria
. يمكن تحديد تعريفات المعايير بإحدى الطرق الثلاث: باستخدام عبارة ضمنية، أو باستخدام كتلة استعلام صريحة، أو باستخدام مرجع نطاق النموذج. للتوضيح، معايير :title
الثلاثة التالية جميعها متكافئة:
class Post < ActiveRecord :: Base
scope :title_equals , -> ( string ) { where ( title : string ) }
end
class PostSearch < TalentScout :: ModelSearch
criteria :title
criteria :title do | string |
where ( title : string )
end
criteria :title , & :title_equals
end
لاحظ أنه يتم تقييم كتل الاستعلام الصريحة في سياق ActiveRecord::Relation
الخاص بالنموذج، تمامًا مثل كتل scope
Active Record.
يمكن أن يحدد تعريف المعايير نوع البيانات، مما يؤدي إلى طباعة قيمة الإدخال الخاصة به قبل تمريرها إلى كتلة الاستعلام أو النطاق. كمثال:
class PostSearch < TalentScout :: ModelSearch
criteria :created_on , :date do | date |
where ( created_at : date . beginning_of_day .. date . end_of_day )
end
end
PostSearch . new ( created_on : "Dec 31, 1999" )
هنا، يتم كتابة السلسلة "Dec 31, 1999"
التي تم تمريرها إلى مُنشئ PostSearch
إلى Date
قبل تمريرها إلى كتلة الاستعلام.
نوع المعايير الافتراضي هو :string
، مما يعني أنه سيتم تحويل جميع قيم الإدخال إلى سلاسل افتراضيًا. يضمن هذا الإعداد الافتراضي (على عكس الإعداد الافتراضي لعدم الكتابة) سلوكًا متسقًا بغض النظر عن كيفية إنشاء كائن البحث، سواء من قيم مكتوبة بقوة أو من معلمات طلب نموذج البحث.
أنواع المعايير المتوفرة هي نفسها بالنسبة لسمات Active Model: :big_integer
, :boolean
, :date
, :datetime
, :decimal
, :float
, :integer
, :string
, :time
، بالإضافة إلى أي أنواع مخصصة تحددها.
يتوفر أيضًا نوع راحة إضافي: :void
. النوع :void
يطبع مثل :boolean
، ولكنه يمنع تطبيق المعايير عندما تكون القيمة المكتوبة خاطئة. على سبيل المثال:
class PostSearch < TalentScout :: ModelSearch
criteria :only_edited , :void do
where ( "modified_at > created_at" )
end
end
# The following will apply `only_edited`:
PostSearch . new ( only_edited : true )
PostSearch . new ( only_edited : "1" )
# The following will skip `only_edited`:
PostSearch . new ( only_edited : false )
PostSearch . new ( only_edited : "0" )
PostSearch . new ( only_edited : "" )
بدلاً من تحديد النوع، قد يحدد تعريف المعايير الاختيارات. تحدد الاختيارات مجموعة من القيم التي يمكن تمريرها إلى كتلة الاستعلام.
يمكن تحديد الاختيارات كمجموعة من القيم الصديقة للإنسان:
class PostSearch < TalentScout :: ModelSearch
criteria :category , choices : %w[ Science Tech Engineering Math ] do | name |
where ( category : name . downcase )
end
end
...أو كتجزئة بمفاتيح صديقة للإنسان:
class PostSearch < TalentScout :: ModelSearch
criteria :within , choices : {
"Last 24 hours" => 24 . hours ,
"Past Week" => 1 . week ,
"Past Month" => 1 . month ,
"Past Year" => 1 . year ,
} do | duration |
where ( "created_at >= ?" , duration . ago )
end
end
ستكون القيمة التي تم تمريرها إلى كتلة الاستعلام إحدى القيم الموجودة في المصفوفة أو إحدى القيم الموجودة في التجزئة. يمكن إنشاء كائن البحث باستخدام أي من قيم المصفوفة أو مفاتيح التجزئة أو قيم التجزئة:
PostSearch . new ( category : "Math" )
PostSearch . new ( within : "Last 24 hours" )
PostSearch . new ( within : 24 . hours )
ولكن إذا تم تحديد خيار غير صالح، فلن يتم تطبيق المعايير المقابلة:
# The following will skip the criteria, but will not raise an error:
PostSearch . new ( category : "Marketing" )
PostSearch . new ( within : 12 . hours )
يمكن أن يحدد تعريف المعايير قيمة افتراضية، والتي سيتم تمريرها إلى كتلة الاستعلام عندما تكون قيمة الإدخال مفقودة. ستظهر القيم الافتراضية أيضًا في نماذج البحث.
class PostSearch < TalentScout :: ModelSearch
criteria :within_days , :integer , default : 7 do | num |
where ( "created_at >= ?" , num . days . ago )
end
end
# The following are equivalent:
PostSearch . new ( )
PostSearch . new ( within_days : 7 )
لن يتم تطبيق المعايير إذا كان أي مما يلي صحيحًا:
قيمة إدخال المعايير مفقودة، ولم يتم تحديد أي قيمة افتراضية.
تم إنشاء كائن البحث باستخدام ActionController::Parameters
(بدلاً من Hash)، وقيمة إدخال المعايير blank?
ولم يتم تحديد أي قيمة افتراضية. (يمنع هذا السلوك حقول نموذج البحث الفارغة من التأثير على نتائج البحث.)
فشل تلبيس قيمة إدخال المعايير. على سبيل المثال:
class PostSearch < TalentScout :: ModelSearch
criteria :created_on , :date do | date |
where ( created_at : date . beginning_of_day .. date . end_of_day )
end
end
# The following will skip `created_on`, but will not raise an error:
PostSearch . new ( created_on : "BAD" )
يحدد تعريف المعايير النوع :void
، وقيمة الإدخال المطبوعة خاطئة.
يحدد تعريف المعايير الاختيارات، وقيمة الإدخال ليست اختيارًا صالحًا.
تُرجع كتلة استعلام المعايير nil
. على سبيل المثال:
class PostSearch < TalentScout :: ModelSearch
criteria :minimum_upvotes , :integer do | minimum |
where ( "upvotes >= ?" , minimum ) unless minimum <= 0
end
end
# The following will skip the `minimum_upvotes` where clause:
PostSearch . new ( minimum_upvotes : 0 )
يتم تحديد أوامر نتائج البحث باستخدام طريقة ModelSearch::order
:
class PostSearch < TalentScout :: ModelSearch
order :created_at
order :title
order :category
end
PostSearch . new ( order : :created_at )
PostSearch . new ( order : :title )
PostSearch . new ( order : :category )
يمكن تطبيق أمر واحد فقط في المرة الواحدة، ولكن يمكن أن يشتمل الأمر على عدة أعمدة:
class PostSearch < TalentScout :: ModelSearch
order :category , [ :category , :title ]
end
# The following will order by "category, title":
PostSearch . new ( order : :category )
تم اختيار هذا التصميم المقيد لأنه يسمح بعمليات فرز منسقة متعددة الأعمدة باستخدام واجهات مستخدم أبسط لفرز عمود واحد، ولأنه يمنع عمليات الفرز المخصصة متعددة الأعمدة التي قد لا تكون مدعومة بواسطة فهرس قاعدة بيانات.
يمكن تطبيق الأمر في الاتجاه الصعودي أو التنازلي. ستطبق طريقة ModelSearch#toggle_order
طلبًا في الاتجاه التصاعدي، أو ستغير اتجاه الطلب المطبق من تصاعدي إلى تنازلي:
class PostSearch < TalentScout :: ModelSearch
order :created_at
order :title
end
# The following will order by "title":
PostSearch . new ( ) . toggle_order ( :title )
PostSearch . new ( order : :created_at ) . toggle_order ( :title )
# The following will order by "title DESC":
PostSearch . new ( order : :title ) . toggle_order ( :title )
لاحظ أن طريقة toggle_order
لا تقوم بتعديل كائن البحث الموجود. وبدلاً من ذلك، يقوم بإنشاء كائن بحث جديد بالترتيب الجديد وقيم المعايير لكائن البحث السابق.
عند تطبيق ترتيب متعدد الأعمدة في الاتجاه التنازلي، تتأثر جميع الأعمدة:
class PostSearch < TalentScout :: ModelSearch
order :category , [ :category , :title ]
end
# The following will order by "category DESC, title DESC":
PostSearch . new ( order : :category ) . toggle_order ( :category )
للتحايل على هذا السلوك وبدلاً من ذلك إصلاح عمود في اتجاه ثابت، قم بإلحاق " ASC"
أو " DESC"
باسم العمود:
class PostSearch < TalentScout :: ModelSearch
order :category , [ :category , "created_at ASC" ]
end
# The following will order by "category, created_at ASC":
PostSearch . new ( order : :category )
# The following will order by "category DESC, created_at ASC":
PostSearch . new ( order : :category ) . toggle_order ( :category )
يمكن تطبيق الأمر في الاتجاه التصاعدي أو التنازلي مباشرةً، دون استخدام toggle_order
، وذلك عن طريق إلحاق اللاحقة المناسبة:
class PostSearch < TalentScout :: ModelSearch
order :title
end
# The following will order by "title":
PostSearch . new ( order : :title )
PostSearch . new ( order : "title.asc" )
# The following will order by "title DESC":
PostSearch . new ( order : "title.desc" )
اللواحق الافتراضية، كما هو موضح في المثال أعلاه، هي ".asc"
و ".desc"
. تم اختيارها لملاءمتها لـ I18n. يمكن تجاوزها كجزء من تعريف الطلب:
class PostSearch < TalentScout :: ModelSearch
order "Title" , [ :title ] , asc_suffix : " (A-Z)" , desc_suffix : " (Z-A)"
end
# The following will order by "title":
PostSearch . new ( order : "Title" )
PostSearch . new ( order : "Title (A-Z)" )
# The following will order by "title DESC":
PostSearch . new ( order : "Title (Z-A)" )
يمكن تعيين أمر كأمر افتراضي، مما سيؤدي إلى تطبيق هذا الأمر عندما لا يتم تحديد أي أمر بخلاف ذلك:
class PostSearch < TalentScout :: ModelSearch
order :created_at , default : :desc
order :title
end
# The following will order by "created_at DESC":
PostSearch . new ( )
# The following will order by "created_at":
PostSearch . new ( order : :created_at )
# The following will order by "title":
PostSearch . new ( order : :title )
لاحظ أن اتجاه الترتيب الافتراضي يمكن أن يكون تصاعديًا أو تنازليًا عن طريق تحديد default: :asc
أو default: :desc
، على التوالي. وأيضًا، تمامًا كما يمكن تطبيق أمر واحد فقط في كل مرة، يمكن تعيين أمر واحد فقط كأمر افتراضي.
يمكن تعريف نطاق البحث الافتراضي باستخدام ModelSearch::default_scope
:
class PostSearch < TalentScout :: ModelSearch
default_scope { where ( published : true ) }
end
سيتم تطبيق النطاق الافتراضي بغض النظر عن المعايير أو قيم إدخال الطلب.
يمكن لوحدات التحكم استخدام الأسلوب المساعد model_search
لإنشاء كائن بحث باستخدام معلمات استعلام الطلب الحالي:
class PostsController < ApplicationController
def index
@search = model_search
@posts = @search . results
end
end
في المثال أعلاه، يقوم model_search
بإنشاء كائن PostSearch
. يتم اشتقاق فئة البحث عن النموذج تلقائيًا من اسم فئة وحدة التحكم. لتجاوز فئة بحث النموذج، استخدم ::model_search_class=
:
class EmployeesController < ApplicationController
self . model_search_class = PersonSearch
def index
@search = model_search # will construct PersonSearch instead of `EmployeeSearch`
@employees = @search . results
end
end
في هذه الأمثلة، يتم تخزين كائن البحث في @search
لاستخدامه في طريقة العرض. لاحظ أن @search.results
يُرجع ActiveRecord::Relation
، لذلك يمكن تطبيق أي نطاق إضافي، مثل ترقيم الصفحات.
يمكن تقديم نماذج البحث باستخدام منشئ نماذج ريلز وكائن البحث:
<%= form_with model: @search, local: true, method: :get do |form| %>
<%= form.label :title_includes %>
<%= form.text_field :title_includes %>
<%= form.label :created_on %>
<%= form.date_field :created_on %>
<%= form.label :only_published %>
<%= form.check_box :only_published %>
<%= form.submit %>
<% end %>
لاحظ method: :get
الوسيطة إلى form_with
. هذا مطلوب.
سيتم ملء حقول النموذج بقيم إدخال المعايير (أو الافتراضية) التي تحمل نفس الاسم من @search
. يمكن استخدام حقول النموذج المناسبة للنوع، على سبيل المثال date_field
للنوع :date
، و check_box
للأنواع :boolean
و :void
، وما إلى ذلك.
افتراضيًا، سيتم إرسال النموذج إلى إجراء الفهرس الخاص بوحدة التحكم التي تتوافق مع model_class
لكائن البحث. على سبيل المثال، PostSearch.model_class
هو Post
، لذلك سيتم إرسال النموذج الذي يحتوي على مثيل PostSearch
إلى PostsController#index
. لتغيير المكان الذي يتم إرسال النموذج إليه، استخدم خيار :url
الخاص بـ form_with
.
يمكن عرض روابط البحث باستخدام طريقة عرض link_to_search
المساعدة:
<%= link_to_search "Sort by title", @search.toggle_order(:title, :asc) %>
سيشير الارتباط تلقائيًا إلى وحدة التحكم الحالية والإجراء الحالي، مع معلمات الاستعلام من كائن البحث المحدد. للارتباط بوحدة تحكم أو إجراء مختلف، قم بتمرير تجزئة الخيارات بدلاً من كائن البحث:
<%= link_to_search "Sort by title", { controller: "posts", action: "index",
search: @search.toggle_order(:title, :asc) } %>
يقبل المساعد link_to_search
أيضًا نفس خيارات HTML التي يقبلها مساعد Rails link_to
:
<%= link_to_search "Sort by title", @search.toggle_order(:title, :asc),
id: "title-sort-link", class: "sort-link" %>
...بالإضافة إلى كتلة المحتوى:
<%= link_to_search @search.toggle_order(:title, :asc) do %>
Sort by title <%= img_tag "sort_icon.png" %>
<% end %>
ModelSearch
توفر فئة ModelSearch
عدة طرق مفيدة عند عرض العرض.
إحدى هذه الطرق هي ModelSearch#toggle_order
، والتي تم عرضها في الأمثلة السابقة. تذكر أن toggle_order
هو أسلوب منشئ لا يعدل كائن البحث. وبدلاً من ذلك، يقوم بتكرار كائن البحث، ويقوم بتعيين الترتيب على الكائن الجديد. يعد هذا السلوك مناسبًا لإنشاء روابط لمتغيرات متعددة للبحث، مثل فرز الروابط في رؤوس أعمدة الجدول.
ModelSearch#with
و ModelSearch#without
طريقتان إضافيتان لأسلوب الإنشاء هما ModelSearch#with
و ModelSearch#without
. مثل toggle_order
، تقوم كلتا الطريقتين بإرجاع كائن بحث جديد، مع ترك كائن البحث الأصلي بدون تعديل. يقبل التابع with
تجزئة قيم إدخال المعايير لدمجها فوق المجموعة الأصلية من قيم إدخال المعايير:
class PostSearch < TalentScout :: ModelSearch
criteria :title
criteria :published , :boolean
end
# The following are equivalent:
PostSearch . new ( title : "Maaaaath!" , published : true )
PostSearch . new ( title : "Maaaaath!" ) . with ( published : true )
PostSearch . new ( title : "Math?" ) . with ( title : "Maaaaath!" , published : true )
يقبل التابع without
قائمة من قيم إدخال المعايير لاستبعادها (لا تزال قيم المعايير الافتراضية مطبقة):
class PostSearch < TalentScout :: ModelSearch
criteria :title
criteria :published , :boolean , default : true
end
# The following are equivalent:
PostSearch . new ( title : "Maaaaath!" )
PostSearch . new ( title : "Maaaaath!" , published : false ) . without ( :published )
ModelSearch#each_choice
هناك طريقة أخرى مفيدة وهي ModelSearch#each_choice
، والتي ستتكرر على الاختيارات المحددة لمعايير معينة. يمكن استخدام هذا لإنشاء روابط لمتغيرات البحث:
class PostSearch < TalentScout :: ModelSearch
criteria :category , choices : %w[ Science Tech Engineering Math ]
end
<% @search.each_choice(:category) do |choice, chosen| %>
<%= link_to_search "Category: #{choice}", @search.with(category: choice),
class: ("active" if chosen) %>
<% end %>
لاحظ أنه إذا كانت الكتلة التي تم تمريرها إلى each_choice
تقبل وسيطتين، فستشير الوسيطة الثانية إلى ما إذا كان الاختيار قد تم اختياره حاليًا.
إذا لم يتم تمرير أي كتلة إلى each_choice
، فسيتم إرجاع Enumerator
. يمكن استخدام هذا لإنشاء خيارات لمربع التحديد:
<%= form_with model: @search, local: true, method: :get do |form| %>
<%= form.select :category, @search.each_choice(:category) %>
<%= form.submit %>
<% end %>
يمكن أيضًا استدعاء التابع each_choice
باستخدام :order
. سيؤدي القيام بذلك إلى التكرار على كل اتجاه لكل ترتيب محدد، مما يؤدي إلى الحصول على التسميات المناسبة بما في ذلك لاحقة الاتجاه:
class PostSearch < TalentScout :: ModelSearch
order "Title" , [ :title ] , asc_suffix : " (A-Z)" , desc_suffix : " (Z-A)"
order "Time" , [ :created_at ] , asc_suffix : " (oldest first)" , desc_suffix : " (newest first)"
end
<%= form_with model: @search, local: true, method: :get do |form| %>
<%= form.select :order, @search.each_choice(:order) %>
<%= form.submit %>
<% end %>
سيدرج مربع التحديد في النموذج أعلاه أربعة خيارات: "العنوان (من الألف إلى الياء)"، "العنوان (ZA)"، "الوقت (الأقدم أولاً)"، "الوقت (الأحدث أولاً)".
ModelSearch#order_directions
أخيرًا، يقوم الأسلوب المساعد ModelSearch#order_directions
بإرجاع HashWithIndifferentAccess
الذي يعكس الاتجاه المطبق حاليًا لكل طلب محدد. يحتوي على مفتاح لكل ترتيب محدد، ويربط كل مفتاح إما بـ :asc
أو :desc
أو nil
.
class PostSearch < TalentScout :: ModelSearch
order "Title" , [ :title ]
order "Time" , [ :created_at ]
end
< thead >
< tr >
<% @search . order_directions . each do | order , direction | %>
< th >
<%= link_to_search order , @search . toggle_order ( order ) %>
<%= img_tag " #{ direction || "unsorted" } _icon.png" %>
</ th >
<% end %>
</ tr >
</ thead >
تذكر أنه يمكن تطبيق أمر واحد فقط في المرة الواحدة، وبالتالي فإن قيمة واحدة فقط في التجزئة، على الأكثر، لن تكون nil
.
أضف الجوهرة إلى ملف Gemfile الخاص بك:
$ bundle add talent_scout
وقم بتشغيل مولد التثبيت:
$ rails generate talent_scout:install
قم بتشغيل bin/test
لتشغيل الاختبارات.
رخصة معهد ماساتشوستس للتكنولوجيا