الفرق بين المراجعتين لصفحة: «Rails/active record validations»
جميل-بيلوني (نقاش | مساهمات) أنشأ الصفحة ب'يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال مي...' |
جميل-بيلوني (نقاش | مساهمات) طلا ملخص تعديل |
||
(4 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة) | |||
سطر 1: | سطر 1: | ||
يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال ميزة التحققات من الصحة (validations) التي يوفرها | <noinclude>{{DISPLAYTITLE:عمليات التحقق من Active Record في ريلز}}</noinclude> | ||
* كيفية استعمال مساعدي التحقق من الصحة (validation helpers) | [[تصنيف:Rails]] | ||
[[تصنيف:Rails Models]] | |||
يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال ميزة التحققات من الصحة (validations) التي يوفرها [[Rails/active record|Active Record]]. بعد قراءة هذا الدليل، ستتعرَّف على: | |||
* كيفية استعمال مساعدي التحقق من الصحة (validation helpers) لـ [[Rails/active record|Active Record]] المدمجين. | |||
* كيفية إنشاء توابع مخصصة للتحقق من الصحة. | * كيفية إنشاء توابع مخصصة للتحقق من الصحة. | ||
* كيفية العمل مع رسالة الخطأ المولدة عبر عملية التحقق. | * كيفية العمل مع رسالة الخطأ المولدة عبر عملية التحقق. | ||
سطر 25: | سطر 28: | ||
يمكن استخدام هذه الطرق في حالات مخصصة. برأي فريق عمل ريلز، طريقة التحقُّقات في مستوى النماذج (model-level validations) هي الطريقة المثلى في معظم الحالات. | يمكن استخدام هذه الطرق في حالات مخصصة. برأي فريق عمل ريلز، طريقة التحقُّقات في مستوى النماذج (model-level validations) هي الطريقة المثلى في معظم الحالات. | ||
=== متى يحدث | === متى يحدث التحقق؟ === | ||
هناك نوعان من كائنات [[Rails/active record basics| | هناك نوعان من كائنات [[Rails/active record basics|Active Record]]: الكائنات التي تتفاعل مع سجل من قاعدة بياناتك، والكائنات التي لا تتفاعل. عند إنشائك لكائن جديد، مثلًا باستخدام التابع <code>new</code>، لن يتم حفظ هذا الكائن في قاعدة البيانات بعد. بعد استدعائك للتابع <code>save</code> على هذا الكائن، سيتم حفظه في جدول قاعدة البيانات. يستخدم [[Rails/active record|Active Record]] تابع النسخة <code>?new_record</code> للتأكّد من وجود السجل في قاعدة البيانات أم لا. لنطلع على نموذج [[Rails/active record|Active Record]] البسيط التالي:<syntaxhighlight lang="rails"> | ||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
end | end | ||
سطر 50: | سطر 53: | ||
* <code>update</code> | * <code>update</code> | ||
* <code>update!</code> | * <code>update!</code> | ||
ترمي التوابع ذات الإصدار | ترمي التوابع ذات الإصدار المُغيِّر (bang version مثل التابع <code>!save</code>) استثناءات عند عدم صحة الكائنات. بينما لا تفعل ذلك التوابع ذات الإصدار غير المُغيِّر (non-bang versions) مثل التابعان <code>save</code> و <code>update</code> اللذان يعيدان القيمة <code>false</code>، والتابع <code>create</code> يعيد الكائن نفسه. | ||
=== تجاوز التحقُّقات === | === تجاوز التحقُّقات === | ||
سطر 69: | سطر 72: | ||
=== التابعان <code>?valid</code> و <code>?invalid</code> === | === التابعان <code>?valid</code> و <code>?invalid</code> === | ||
قبل حفظ كائن | قبل حفظ كائن [[Rails/active record|Active Record]]، يشغّل ريلز التحقُّقات الخاصة بك. في حال أثمرت هذه التحقُّقات أخطاءً، فلن يحفظ ريزل الكائن. | ||
يمكنك أيضًا تشغيل التحقُّقات يدويًا. إذ يقوم التابع <code>?valid</code> بتشغيل التحقق ويعيد القيمة <code>true</code> في حال نجاحه، والقيمة <code>false</code> عدا عن ذلك. كما رأيت أعلاه:<syntaxhighlight lang="rails"> | يمكنك أيضًا تشغيل التحقُّقات يدويًا. إذ يقوم التابع <code>?valid</code> بتشغيل التحقق ويعيد القيمة <code>true</code> في حال نجاحه، والقيمة <code>false</code> عدا عن ذلك. كما رأيت أعلاه:<syntaxhighlight lang="rails"> | ||
سطر 78: | سطر 81: | ||
Person.create(name: "John Doe").valid? # => true | Person.create(name: "John Doe").valid? # => true | ||
Person.create(name: nil).valid? # => false | Person.create(name: nil).valid? # => false | ||
</syntaxhighlight>بعد قيام كائن | </syntaxhighlight>بعد قيام كائن [[Rails/active record|Active Record]] بالتحقُّقات، يمكن الوصول لرسائل الخطأ عن طريق تابع النسخة <code>errors.messages</code>، الذي يعيد مجموعة من الأخطاء. من التعريف، يكون الكائن صحيحًا في حال كانت هذه المجموعة خالية بعد تنفيذ التحقُّق. | ||
تجدر الملاحظة أنَّ الكائن الذي يتم تهيئته باستخدام <code>new</code> لن يعطي أي خطأ حتّى لو كان غير صالح، لأنَّ التحقُّقات تنفّذ تلقائيًا عند حفظ الكائن، كما هو الحال في التابعين <code>create</code> و <code>save</code>.<syntaxhighlight lang="rails"> | تجدر الملاحظة أنَّ الكائن الذي يتم تهيئته باستخدام <code>new</code> لن يعطي أي خطأ حتّى لو كان غير صالح، لأنَّ التحقُّقات تنفّذ تلقائيًا عند حفظ الكائن، كما هو الحال في التابعين <code>create</code> و <code>save</code>.<syntaxhighlight lang="rails"> | ||
سطر 134: | سطر 137: | ||
</syntaxhighlight>إن استخدام <code>details</code> مع التحقُّقات اليدوية مغطّى بشكل كامل في قسم العمل مع أخطاء التحقُّقات. | </syntaxhighlight>إن استخدام <code>details</code> مع التحقُّقات اليدوية مغطّى بشكل كامل في قسم العمل مع أخطاء التحقُّقات. | ||
== | == مساعدو التحقق == | ||
يزوّد | يزوّد [[Rails/active record|Active Record]] مجموعةً معرفةً مسبقًا من المساعدين الذين يمكنك استخدامهم بشكل مباشر في تعريفات الأصناف الخاصة بك. تزوّد هؤلاء المساعدين بقواعد متعارف عليها لإجراء التحقُّقات. في كل مرة يفشل فيها تحقُّق ما، سيتم إضافة رسالة خطأ إلى المجموعة <code>errors</code> الخاصة بالكائن، وتضم هذه الرسالة اسم الحقل الذي فشل التحقُّق منه. | ||
يقبل كل مساعد مجموعة من أسماء الحقول، أي في سطر تعريف وحيد | يقبل كل مساعد مجموعة من أسماء الحقول، أي يمكنك في سطر تعريف وحيد إضافة نفس التحقُّق لمجموعة من الحقول. | ||
جميعهم يقبلون الخيارين <code>on:</code> و <code>message:</code>، اللذَين يحددان متى يتم تشغيل التحقُّقات وما الرسالة الواجب إضافتها في حال فشل التحقُّق. يقبل الخيار <code>on:</code> إحدى القيمتين <code>create:</code> أو <code>update:</code>. هناك رسائل افتراضية لكل نوع من أنواع التحقُّقات، والتي يتم استخدامها عند عدم تحديد رسالة بشكل يدوي في الخيار <code>message:</code>. لنطلّع على كل مساعد من هؤلاء المساعدين. | |||
=== المساعد <code>acceptance</code> === | |||
يأكّد هذا المساعد أن صندوق الاختيار (checkbox) تم تحديده على واجهة المستخدم عند إرسال النموذج. يستخدم هذا المساعد بشكل تقليدي عند حاجة المستخدم للموافقة على شروط استخدام التطبيق مثلَا، أو للتأكد من قراءة نص معين، أو أي شيء مماثل.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :terms_of_service, acceptance: true | |||
end | end | ||
</syntaxhighlight>يُنفّذ هذا التحديد فقط عندما يكون <code>terms_of_service</code> ليس <code>nil</code>. إن رسالة الخطأ الافتراضية لهذا المساعد هي "must be accepted". يمكنك تمرير رسالة يدوية عن طريق الخيار <code>message:</code>.<syntaxhighlight lang="rails"> | |||
يُنفّذ هذا التحديد فقط عندما يكون terms_of_service ليس nil. إن رسالة الخطأ الافتراضية لهذا المساعد هي "must be accepted". يمكنك تمرير رسالة يدوية عن طريق الخيار message:. | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :terms_of_service, acceptance: { message: 'must be abided' } | |||
end | end | ||
</syntaxhighlight>يمكن أيضًا تمرير الخيار <code>accept:</code> الذي يحدد القيم التي يجب اعتبارها خيارًا مقبولًا. القيمة الافتراضية لهذا الخيار هي <code>[1,true]</code> ويمكن تغييرها بسهولة:<syntaxhighlight lang="rails"> | |||
يمكن أيضًا تمرير الخيار accept: الذي يحدد القيم التي يجب اعتبارها | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :terms_of_service, acceptance: { accept: 'yes' } | |||
validates :eula, acceptance: { accept: ['TRUE', 'accepted'] } | |||
end | end | ||
</syntaxhighlight>هذا التحقُّق مخصَّص لتطبيقات الويب وليس من الضروري تخزين هذا القبول في أي مكان في قاعدة البيانات. في حال لم تكن تملك هذا الحقل في قاعدة البيانات، يقوم المساعد بإنشائه لك بشكل افتراضي. في حال عدم وجود الحقل في قاعدة البيانات، يجب ضبط الخيار <code>accept</code> أو أن يحوي القيمة <code>true</code>، وإلا لن يتم تشغيل التحقُّق. | |||
=== المساعد <code>validates_associated</code> === | |||
يمكنك استخدام هذا المساعد في حال كان نموذجك يملك ارتباطات مع نماذج أخرى تحتاج للتحقُّق أيضًا. عند محاولتك حفظ السجل، سيتم استدعاء التابع <code>?valid</code> على كل كائن من الكائنات المرتبطة أيضًا.<syntaxhighlight lang="rails"> | |||
=== | |||
يمكنك استخدام هذا المساعد في حال كان | |||
class Library < ApplicationRecord | class Library < ApplicationRecord | ||
has_many :books | |||
validates_associated :books | |||
end | end | ||
</syntaxhighlight>سيعمل هذا التحقُّق على جميع الأنواع المرتبطة. | |||
'''تحذير''': لا تستخدم المساعد <code>validates_associated</code> على طرفي الارتباط، إذ سيؤدي ذلك إلى استدعاء أحدهما للآخر مما يؤدي إلى الدخول في حلقة لا نهائية. | |||
إن رسالة الخطأ الافتراضية لهذا التحقُّق هي "is invalid". تجب الملاحظة أن لكل كائن من الكائنات المرتبطة مجموعة الأخطاء الخاصة به، ولن يتم حفظ مجموعة الأخطاء للكائنات الأبناء في الكائن المستدعي. | إن رسالة الخطأ الافتراضية لهذا التحقُّق هي "is invalid". تجب الملاحظة أن لكل كائن من الكائنات المرتبطة مجموعة الأخطاء الخاصة به، ولن يتم حفظ مجموعة الأخطاء للكائنات الأبناء في الكائن المستدعي. | ||
=== | === المساعد <code>confirmation</code> === | ||
يجب أن تستخدم هذا | يجب أن تستخدم هذا المساعد عند وجود حقلين نصيَّين يجب على كل منهما أن يرسلا نفس القيمة تمامًا. مثلًا، قد تحتاج إلى تحقُّق بريد الكتروني أو كلمة مرور. ينشئ هذا التحقُّق حقلًا افتراضيًا ويسمّيه بنفس اسم الحقل الأساسي، يليه العبارة "confirmation_".<syntaxhighlight lang="rails"> | ||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :email, confirmation: true | |||
end | end | ||
</syntaxhighlight>ويجب أن يبدو نموذج العرض الخاص بك كالتالي:<syntaxhighlight lang="html"> | |||
ويجب أن يبدو نموذج العرض الخاص بك كالتالي: | |||
<%= text_field :person, :email %> | <%= text_field :person, :email %> | ||
<%= text_field :person, :email_confirmation %> | <%= text_field :person, :email_confirmation %> | ||
ينفّذ هذا التحقُّق فقط عند كون email_confirmation ليس nil. لطلب التحقُّق بشكل قسري، لا | </syntaxhighlight>ينفّذ هذا التحقُّق فقط عند كون <code>email_confirmation</code> ليس <code>nil</code>. لطلب التحقُّق بشكل قسري، لا تنسَ إضافة التحقُّق <code>presence</code> للخاصية <code>confirmation</code> (سنطلع على التحقُّق <code>presence</code> لاحقًا في هذا التوثيق):<syntaxhighlight lang="rails"> | ||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :email, confirmation: true | |||
validates :email_confirmation, presence: true | |||
end | end | ||
</syntaxhighlight>يمكنك أيضًا تمرير الخيار <code>case_sensitive:</code> لتحديد فيما إذا كان القيد <code>confirmation</code> حساسًا لحالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي <code>true</code>.<syntaxhighlight lang="rails"> | |||
يمكنك أيضًا تمرير الخيار case_sensitive: لتحديد فيما إذا كان | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :email, confirmation: { case_sensitive: false } | |||
end | end | ||
</syntaxhighlight>رسالة الخطأ الافتراضية لهذا المساعد هي "doesn't match confirmation". | |||
=== المساعد <code>exclusion</code> === | |||
يتأكّد هذا المساعد من أن قيم الحقل المعطى ليست موجودة ضمن المجموعة المحددة. يمكن أن تكون هذه المجموعة أيًّا من الكائنات القابلة للتعداد (enumerable object).<syntaxhighlight lang="rails"> | |||
=== | |||
يتأكّد هذا المساعد من أن قيم الحقل المعطى ليست موجودة ضمن المجموعة المحددة. يمكن أن تكون هذه المجموعة | |||
class Account < ApplicationRecord | class Account < ApplicationRecord | ||
validates :subdomain, exclusion: { in: %w(www us ca jp), | |||
message: "%{value} is reserved." } | |||
end | end | ||
</syntaxhighlight>يملك المساعد <code>exclusion</code> الخيار <code>in:</code> الذي يقبل مجموعة من القيم التي لن يتم قبولها لخاصيات المتحقق منها. يمكن كتابة <code>in:</code> أيضًا بالشكل <code>within:</code>. يستخدم هذا المثال الخيار <code>message:</code> ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية يمكنك الاطلاع على قسم الرسائل في الأسفل. | |||
يملك | |||
رسالة الخطأ الافتراضية لهذا المساعد هي "is reserved". | رسالة الخطأ الافتراضية لهذا المساعد هي "is reserved". | ||
=== | === المساعد <code>format</code> === | ||
يؤكد هذا المساعد أن قيمة الحقل المعطى توافق التعبير | يؤكد هذا المساعد أن قيمة الحقل المعطى توافق التعبير النمطي المحدد بالخيار <code>with:</code> كالتالي:<syntaxhighlight lang="rails"> | ||
class Product < ApplicationRecord | class Product < ApplicationRecord | ||
validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, | |||
message: "only allows letters" } | |||
end | end | ||
</syntaxhighlight>بالمثل، يمكنك تحديد أن قيمة الحقل المعطى لا توافق التعبير النمطي المحدد، وذلك بالخيار <code>without:</code>. | |||
بالمثل، يمكنك تحديد أن قيمة الحقل المعطى لا توافق التعبير | |||
رسالة الخطأ الافتراضية لهذا المساعد هي "is invalid". | رسالة الخطأ الافتراضية لهذا المساعد هي "is invalid". | ||
=== | === المساعد <code>inclusion</code> === | ||
يتأكد هذا المساعد من أن قيم الحقل المعطى موجودة ضمن المجموعة | يتأكد هذا المساعد من أن قيم الحقل المعطى موجودة ضمن المجموعة المحددة:<syntaxhighlight lang="rails"> | ||
class Coffee < ApplicationRecord | class Coffee < ApplicationRecord | ||
validates :size, inclusion: { in: %w(small medium large), | |||
message: "%{value} is not a valid size" } | |||
end | end | ||
</syntaxhighlight>يملك المساعد <code>inclusion</code> الخيار <code>in:</code> الذي يستقبل مجموعة من القيم المراد قبولها فقط. يمكن كتابة <code>in:</code> أيضًا بالشكل <code>within:</code>. يستخدم هذا المثال الخيار <code>message:</code> ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية، يمكنك الاطلاع على قسم الرسائل في الأسفل. | |||
يملك | |||
رسالة الخطأ الافتراضية لهذا المساعد هي "is not included in the list". | رسالة الخطأ الافتراضية لهذا المساعد هي "is not included in the list". | ||
=== | === المساعد <code>length</code> === | ||
يتأكد هذا المساعد من طول قيم الحقل المعطى. يزوّد بمجموعة من الخيارات التي تمكّنك من تحديد قيود الطول بأشكال مختلفة: | يتأكد هذا المساعد من طول قيم الحقل المعطى. يزوّد بمجموعة من الخيارات التي تمكّنك من تحديد قيود الطول بأشكال مختلفة:<syntaxhighlight lang="rails"> | ||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :name, length: { minimum: 2 } | |||
validates :bio, length: { maximum: 500 } | |||
validates :password, length: { in: 6..20 } | |||
validates :registration_number, length: { is: 6 } | |||
end | end | ||
</syntaxhighlight>الخيارات الممكنة لقيود الطول هي: | |||
الخيارات الممكنة لقيود الطول هي: | * <code>minimum:</code>: لا يمكن للحقل أن يحوي عدد محارف أقل من الطول المعطى. | ||
* minimum: لا يمكن للحقل أن يحوي عدد محارف أقل من الطول المعطى. | * <code>maximum:</code>: لا يمكن للحقل أن يحوي عدد محارف أكثر من الطول المعطى. | ||
* maximum: لا يمكن للحقل أن يحوي عدد محارف أكثر من الطول المعطى. | * <code>in:</code> (أو <code>within:</code>): يمكن للحقل أن يحوي عدد محارف بطول موجود ضمن المجال المعطى. | ||
* in: (أو within:): يمكن للحقل أن يحوي عدد محارف بطول موجود ضمن المجال المعطى. | * <code>is:</code>: يجب على الحقل أن يحوي عدد محارف يساوي الطول المعطى. | ||
* | تعتمد رسالة الخطأ الافتراضية على نوع تحقُّق الطول المستخدم. يمكنك تعديل هذه الرسائل عن طريق الخيارات <code>wrong_length:</code>، و <code>too_long:</code>، و <code>too_short:</code>، واستخدام <code>{count}%</code> لاستيفاء طول المحارف المستخدم. يمكنك استخدام الخيار <code>message:</code> لتحديد رسالة خطأ.<syntaxhighlight lang="rails"> | ||
تعتمد رسالة الخطأ الافتراضية على نوع تحقُّق الطول المستخدم. يمكنك تعديل هذه الرسائل عن طريق الخيارات wrong_length:، و too_long:، و too_short:، واستخدام {count}% لاستيفاء طول المحارف المستخدم. يمكنك استخدام الخيار message: لتحديد رسالة خطأ. | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :bio, length: { maximum: 1000, | |||
too_long: "%{count} characters is the maximum allowed" } | |||
end | end | ||
</syntaxhighlight>تجب الملاحظة أن رسائل الخطأ الافتراضية هي في صيغة الجمع مثل:<syntaxhighlight lang="text"> | |||
"is too short (minimum is %{count} characters)" | |||
</syntaxhighlight>لهذا السبب، عند كون <code>minimum:</code> تساوي 1، يجب تزويد رسالة معدلة أو استخدام الخيار <code>presence: true</code> بدلًا من ذلك. عندما يملك الخيار <code>in:</code> أو <code>within:</code> حدًا أصغريًا مساويًا للواحد، يجب عليك تزويد رسالة معدلة أو استخدام الخيار <code>presence</code> قبل <code>length</code>. | |||
=== المساعد <code>numericality</code> === | |||
يتأكد هذا المساعد من أن الحقل يحتوي فقط على قيم رقمية. افتراضيًا، يطابق هذا المساعد إشارة الرقم الاختيارية، متبوعةً بعدد صحيح أو عشري. لتحديد قبول الأعداد الصحيحة فقط، يمكن تعيين الخيار <code>only_integer:</code> إلى <code>true</code>. | |||
=== | |||
يتأكد هذا المساعد من أن الحقل يحتوي فقط على قيم رقمية. افتراضيًا، يطابق هذا المساعد إشارة الرقم الاختيارية، | |||
وفي حال ضبط <code>only_integer:</code> إلى القيمة <code>true</code>، فسيتم استخدام التعبير النمطي التالي:<syntaxhighlight lang="text"> | |||
/\A[+-]?\d+\z/ | /\A[+-]?\d+\z/ | ||
</syntaxhighlight>وذلك للتحقُّق من قيمة الخاصية. فيما عدا ذلك، سيحاول تحويل قيمة الحقل إلى عدد باستخدام <code>[[Ruby/Float|Float]]</code>.<syntaxhighlight lang="rails"> | |||
وذلك | |||
class Player < ApplicationRecord | class Player < ApplicationRecord | ||
validates :points, numericality: true | |||
validates :games_played, numericality: { only_integer: true } | |||
end | end | ||
</syntaxhighlight>إلى جانب <code>only_integer:</code>، يقبل هذا المساعد مجموعة من الخيارات لإضافة قيود على القيم المقبولة: | |||
إلى جانب only_integer:، يقبل هذا المساعد مجموعة من الخيارات لإضافة | * الخيار <code>greater_than:</code>: يحدد أن القيمة يجب أن تكون أكبر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than %{count}". | ||
* الخيار greater_than: يحدد أن القيمة يجب أن تكون أكبر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than %{count}". | * الخيار <code>greater_than_or_equal_to:</code>: يحدد أن القيمة يجب أن تكون أكبر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than or equal to %{count}". | ||
* الخيار greater_than_or_equal_to: يحدد أن القيمة يجب أن تكون أكبر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than or equal to %{count}". | * الخيار <code>equal_to:</code>: يحدد أن القيمة يجب أن تكون تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be equal to %{count}" | ||
* الخيار equal_to: يحدد أن القيمة يجب أن تكون تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be equal to %{count}" | * الخيار <code>less_than:</code>: يحدد أن القيمة يجب أن تكون أصغر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than %{count}". | ||
* الخيار less_than: يحدد أن القيمة يجب أن تكون أصغر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than %{count}". | * الخيار <code>less_than_or_equal_to:</code>: يحدد أن القيمة يجب أن تكون أصغر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than or equal to %{count}". | ||
* الخيار less_than_or_equal_to: يحدد أن القيمة يجب أن تكون أصغر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than or equal to %{count}". | * الخيار <code>other_than:</code>: يحدد أن القيمة يجب أن تكون غير مساوية للقيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be other than %{count}" | ||
* الخيار other_than: يحدد أن القيمة يجب أن تكون | * الخيار <code>odd:</code>: يحدد أن القيمة يجب أن تكون عددًا فرديًّا. رسالة الخطأ الافتراضية لهذا الخيار هي "must be odd". | ||
* الخيار odd: يحدد أن القيمة يجب أن تكون | * الخيار <code>even:</code>: يحدد أن القيمة يجب أن تكون عددًا زوجيًّا. رسالة الخطأ الافتراضية لهذا الخيار هي "must be even". | ||
* الخيار even: يحدد أن القيمة يجب أن تكون | '''ملاحظة''': افتراضيًا، لا يسمح المساعد <code>numericality</code> بالقيم nil. يمكنك استخدام الخيار <code>allow_nil:</code> للسماح بها. | ||
ملاحظة: افتراضيًا، لا يسمح | |||
الرسالة الافتراضية لهذا المساعد هي "is not a number". | الرسالة الافتراضية لهذا المساعد هي "is not a number". | ||
=== | === المساعد <code>presence</code> === | ||
يتحقق هذا المساعد من أن عدم خلو خاصيات محدَّدة من أية قيمة. يستخدم هذا المساعد التابع <code>?blank</code> للتأكد من أن القيمة هي <code>nil</code>، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :name, :login, :email, presence: true | |||
end | end | ||
</syntaxhighlight>إذا أردت أن تكون متأكدًا من وجود ارتباط ما، يجب أن تتحقّق من وجود الكائن المرتبط بنفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط.<syntaxhighlight lang="rails"> | |||
إذا أردت أن تكون متأكدًا من وجود ارتباط ما، يجب أن تتحقّق من وجود الكائن المرتبط بنفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط. | |||
class LineItem < ApplicationRecord | class LineItem < ApplicationRecord | ||
belongs_to :order | |||
validates :order, presence: true | |||
end | end | ||
</syntaxhighlight>من أجل تحقُّق السجلات المرتبطة الضروري وجودها، يجب تحديد الخيار <code>inverse_of:</code> من أجل الارتباط:<syntaxhighlight lang="rails"> | |||
من أجل تحقُّق السجلات المرتبطة الضروري وجودها، يجب تحديد | |||
class Order < ApplicationRecord | class Order < ApplicationRecord | ||
has_many :line_items, inverse_of: :order | |||
end | end | ||
</syntaxhighlight>في حال قمت بالتحقُّق من وجود كائن مرتبط عن طريق العلاقة has_one أو has_many، سيتفقّد من كون الكائن غير محقق للتابع <code>?blank</code> ولا التابع <code>?marked_for_destruction</code>. | |||
لمَّا كان <code>?false.blank</code> يعيد القيمة <code>true</code>، فإذا أردت التحقُّق من وجود حقل من النوع <code>Boolean</code>، فيجب عليك القيام بأحد التحققين التاليين:<syntaxhighlight lang="rails"> | |||
validates :boolean_field_name, inclusion: { in: [true, false] } | |||
validates :boolean_field_name, exclusion: { in: [nil] } | |||
</syntaxhighlight>باستخدام أحد هذين التحقُّقين، يمكنك التأكد من عدم استقبال الحقل المنطقي القيمة <code>nil</code>. | |||
< | |||
باستخدام أحد | |||
=== المساعد <code>absence</code> === | |||
يؤكد هذا المساعد غياب وعدم وجود قيم خاصية محددة. يستخدم هذا المساعد التابع <code>?present</code> للتأكد من أن القيمة هي ليست <code>nil</code>، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :name, :login, :email, absence: true | |||
end | end | ||
</syntaxhighlight>إذا أردت أن تكون متأكدًا من غياب ارتباط ما، يجب أن تتحقّق من غياب الكائن المرتبط نفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط.<syntaxhighlight lang="rails"> | |||
إذا أردت أن تكون متأكدًا من غياب ارتباط ما، يجب أن تتحقّق من غياب الكائن المرتبط | |||
class LineItem < ApplicationRecord | class LineItem < ApplicationRecord | ||
belongs_to :order | |||
validates :order, absence: true | |||
end | end | ||
</syntaxhighlight>من أجل تحقُّق السجلات المرتبطة الضروري غيابها، يجب تحديد الخيار <code>inverse_of:</code> من أجل الارتباط:<syntaxhighlight lang="rails"> | |||
من أجل تحقُّق السجلات المرتبطة الضروري غيابها، يجب تحديد | |||
class Order < ApplicationRecord | class Order < ApplicationRecord | ||
has_many :line_items, inverse_of: :order | |||
end | end | ||
</syntaxhighlight>في حال قمت بالتحقُّق من غياب كائن مرتبط عن طريق علاقة has_one أو has_many، سيتفقّد من أنَّ الكائن غير محقق للتابع <code>?present</code> ولا التابع <code>?marked_for_destruction</code>. | |||
لما كان <code>?false.present</code> يعيد القيمة <code>false</code>، فإذا أردت التحقُّق من غياب حقل من النوع المنطقي، فيجب عليك استعمال:<syntaxhighlight lang="rails"> | |||
validates :field_name, exclusion: { in: [true, false] } | |||
</syntaxhighlight>رسالة الخطأ الافتراضية لهذا المساعد هي "must be blank". | |||
< | |||
رسالة الخطأ الافتراضية لهذا المساعد هي "must be blank" | |||
=== المساعد <code>uniqueness</code> === | |||
يتحقق هذا المساعد من أنَّ قيمة الحقل فريدة وغير مكررة قبل حفظ الكائن. لا ينشئ هذا المساعد قيد التميز في قاعدة البيانات، لذلك قد تحدث حالة إضافة سجلّين بقيمتين متساويتين في حقل واحد تريده أن يكون فريدًا، وذلك من قبل اتصالين منفصلين مع قاعدة البيانات. لتفادي هذا، يجب عليك إنشاء فهرس فريد لهذا الحقل في قاعدة البيانات.<syntaxhighlight lang="rails"> | |||
class Account < ApplicationRecord | class Account < ApplicationRecord | ||
validates :email, uniqueness: true | |||
end | end | ||
</syntaxhighlight>يجري تنفيذ التحقُّق عن طريق تنفيذ استعلام [[SQL]] على جدول النموذج، بحثًا عن سجل موجود يملك ذات القيمة في الحقل المعطى. | |||
يمكن استخدام الخيار <code>scope:</code> لتحديد خاصية أو أكثر مستخدمة للتحقق <code>uniqueness</code>:<syntaxhighlight lang="rails"> | |||
يمكن استخدام الخيار scope: لتحديد | |||
class Holiday < ApplicationRecord | class Holiday < ApplicationRecord | ||
validates :name, uniqueness: { scope: :year, | |||
message: "should happen once per year" } | |||
end | end | ||
</syntaxhighlight>إذا أردت إنشاء قيدٍ على قاعدة البيانات لتفادي تجاوز التحقُّقات <code>uniqueness</code> المستخدمة للخيار <code>scope:</code>، يجب أن تنشئ فهرسًا فريدًا في كلا الحقلين في قاعدة البيانات. اقرأ [http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html دليل MySQL] للمزيد من المعلومات حول إنشاء فهرس لحقول متعددة أو [https://www.postgresql.org/docs/current/static/ddl-constraints.html دليل PostgreSQL] لأمثلة حول قيود التميز المستخدمة على نطاق مجموعة من الحقول في آن معًا. | |||
هناك أيضًا الخيار <code>case_sensitive:</code> لتحديد فيما إذا كان القيد <code>uniqueness</code> يراعي حالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي <code>true</code>.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | |||
هناك أيضًا | validates :name, uniqueness: { case_sensitive: false } | ||
end | end | ||
</syntaxhighlight>'''ملاحظة''': بعض قواعد البيانات مهيّئة لتنفيذ عمليات البحث غير الحساسة لحالة الأحرف في كلتا الحالتين. | |||
ملاحظة: بعض قواعد | |||
رسالة الخطأ الافتراضية لهذا المساعد هي "has already been taken". | رسالة الخطأ الافتراضية لهذا المساعد هي "has already been taken". | ||
=== المساعد <code>validates_with</code> === | |||
يمرر هذا المساعد السجل الواجب التحقق منه إلى صنف آخر منفصل للقيام بعملية التحقُّق.<syntaxhighlight lang="rails"> | |||
يمرر هذا المساعد السجل الواجب | |||
class GoodnessValidator < ActiveModel::Validator | class GoodnessValidator < ActiveModel::Validator | ||
def validate(record) | |||
if record.first_name == "Evil" | |||
record.errors[:base] << "This person is evil" | |||
end | |||
end | |||
end | end | ||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates_with GoodnessValidator | |||
end | end | ||
</syntaxhighlight>'''ملاحظة''': الأخطاء المضافة إلى <code>[record.errors[:base</code> تتعلق بحالة السجل ككل، وليس بحقل معين. | |||
يقبل المساعد <code>validates_with</code> صنفًا، أو مجموعة من الأصناف المستخدمة للتحقُّق. لا يوجد رسالة خطأ افتراضية لهذا المساعد. يمكنك تحديد رسائل الخطأ يدويًا عن طريق الصنف <code>validator</code>. | |||
يقبل المساعد validates_with صنفًا، أو مجموعة من الأصناف المستخدمة للتحقُّق. لا يوجد رسالة خطأ افتراضية لهذا المساعد. يمكنك تحديد رسائل الخطأ يدويًا عن طريق | |||
لكتابة تابع التحقُّق، يجب أن تستقبل وسيطًا يمثل السجل المطلوب تحقُّقه. | لكتابة تابع التحقُّق، يجب أن تستقبل وسيطًا يمثل السجل المطلوب تحقُّقه. | ||
كما هو الحال في كل عمليات التحقُّق، يقبل هذا المساعد الخيارات if: و unless: و on:. إذا مرّرت خيارات إضافية، | كما هو الحال في كل عمليات التحقُّق، يقبل هذا المساعد الخيارات <code>if:</code> و <code>unless:</code> و <code>on:</code>. إذا مرّرت خيارات إضافية، فسيتم تمريرها إلى الصنف <code>validator</code> ضمن الوسيط <code>options</code>:<syntaxhighlight lang="rails"> | ||
class GoodnessValidator < ActiveModel::Validator | class GoodnessValidator < ActiveModel::Validator | ||
def validate(record) | |||
if options[:fields].any?{|field| record.send(field) == "Evil" } | |||
record.errors[:base] << "This person is evil" | |||
end | |||
end | |||
end | end | ||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates_with GoodnessValidator, fields: [:first_name, :last_name] | |||
end | end | ||
</syntaxhighlight>من الجدير بالملاحظة أنّه يتم تهيئة الصنف <code>validator</code> '''مرّة واحدة''' فقط ضمن دورة حياة التطبيق ككل، وليس في كل مرة يتم تنفيذ تحقُّق معيّن؛ لذلك كن حذرًا أثناء التعامل مع متغيرات النسخة داخله. | |||
إذا كان صنف التحقُّق <code>validator</code> الخاص بك معقدًا لدرجة حاجته لمتغيّرات نسخة، فيمكنك استخدام كائنات [[Ruby|روبي]] البسيطة بدلًا من ذلك:<syntaxhighlight lang="rails"> | |||
إذا كان صنف التحقُّق الخاص بك معقدًا لدرجة حاجته لمتغيّرات | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validate do |person| | |||
GoodnessValidator.new(person).validate | |||
end | |||
end | end | ||
class GoodnessValidator | class GoodnessValidator | ||
def initialize(person) | |||
@person = person | |||
end | |||
def validate | |||
if some_complex_condition_involving_ivars_and_private_methods? | |||
@person.errors[:base] << "This person is evil" | |||
end | |||
end | |||
# ... | |||
end | end | ||
</syntaxhighlight> | |||
=== المساعد <code>validates_each</code> === | |||
يتحقَّق هذا المساعد من مجموعة من الخاصيات مقابل كتلة من التعليمات البرمجية، إذ لا يملك تابع تحقُّق معرف مسبقًا. يجب عليك إنشاء هذا التابع باستخدام كتلة، وكل خاصية ممرّرة للمساعد <code>validates_each</code> سيتم اختبارها في هذه الكتلة. في المثال التالي، لا نريد من الأسماء الأولى والكنيات أن تبدأ بأحرف صغيرة.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates_each :name, :surname do |record, attr, value| | |||
record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/ | |||
end | |||
end | end | ||
</syntaxhighlight>تستقبل الكتلة السجل، واسم الخاصية وقيمتها. يمكنك القيام بأي عملية داخل الكتلة للتأكد من صحة البيانات المستقبلة. في حال فشل التحقُّق، يجب عليك إضافة رسالة خطأ إلى النموذج، موضّحًا سبب فشل عملية التحقق من صحّة البيانات. | |||
== خيارات عملية التحقق الشائعة == | |||
إليك مجموعة من خيارات التحقُّق الشائعة المستخدمة في [[Rails/active record|Active Record]]: | |||
إليك مجموعة من خيارات التحقُّق الشائعة المستخدمة في | |||
=== الخيار <code>allow_nil:</code> === | |||
يتجاوز الخيار <code>allow_nil:</code> التحقُّق عندما تكون القيمة الواجب التحقق منها هي <code>nil</code>.<syntaxhighlight lang="rails"> | |||
class Coffee < ApplicationRecord | class Coffee < ApplicationRecord | ||
validates :size, inclusion: { in: %w(small medium large), | |||
message: "%{value} is not a valid size" }, allow_nil: true | |||
end | end | ||
</syntaxhighlight>لقراءة كل الخيارات حول الوسيط <code>message</code>، اطّلع على [https://guides.rubyonrails.org/active_record_validations.html#message التوثيق الخاص به]. | |||
=== الخيار <code>allow_blank:</code> === | |||
يشابه هذا الخيار الخيار <code>allow_nil:</code>، إذ يتجاوز التحقُّق في حال كانت قيمة الخاصية هي <code>?blank</code>، مثل القيمة <code>nil</code> أو سلسلة نصية فارغة.<syntaxhighlight lang="rails"> | |||
الخيار allow_blank: | |||
يشابه هذا الخيار الخيار allow_nil:، إذ يتجاوز التحقُّق في حال كانت قيمة | |||
class Topic < ApplicationRecord | class Topic < ApplicationRecord | ||
validates :title, length: { is: 5 }, allow_blank: true | |||
end | end | ||
Topic.create(title: "").valid? | Topic.create(title: "").valid? # => true | ||
Topic.create(title: nil).valid? # => true | Topic.create(title: nil).valid? # => true | ||
</syntaxhighlight> | |||
=== الخيار <code>message:</code> === | |||
كما رأيت مسبقًا، يمكّنك الخيار <code>message:</code> من تحديد رسالة خطأ يراد إضافتها لمجموعة الأخطاء <code>errors</code> عند فشل عملية التحقُّق. عند عدم تحديد هذا الخيار، يرسل [[Rails/active record|Active Record]] رسالة الخطأ الافتراضية من أجل كل مساعد تحقُّق. يقبل الخيار <code>message:</code> إما [[Ruby/String|سلسلة نصية]] أو كائنًا من النوع <code>[[Ruby/Proc|Proc]]</code>. | |||
يمكن للسلسلة النصية الممررة للخيار <code>message:</code> أن تحوي كل من <code>{value}%</code> أو <code>{attribute}%</code> أو <code>{model}%</code> التي يتم استبدالها دينياميكيًا عند فشل التحقُّق. يتم تنفيذ هذا الاستبدال باستخدام الجوهرة [[Rails/i18n|I18n]]. | |||
يمكن | |||
أمَّا في حال استعمال الكائن <code>[[Ruby/Proc|Proc]]</code> مع الخيار <code>message:</code>، يتم تمرير وسيطين هما: الكائن المراد التحقق منه، ومجموعة بأزواج المفاتيح والقيم التالية: <code>model:</code> و <code>attribute:</code> و <code>value:</code>.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
# رسالة ثابتة | |||
# رسالة ثابتة | validates :name, presence: { message: "must be given please" } | ||
# %{value} رسالة مع قيمة ديناميكية. يتم استبدال | |||
# متاحتان أيضًا %{model} و %{attribute} بالقيمة الحقيقية للحقل. إن | |||
# رسالة مع قيمة ديناميكية. | validates :age, numericality: { message: "%{value} seems wrong" } | ||
# | # Proc | ||
validates :username, | |||
uniqueness: { | |||
# object = الكائن المراد التحقق منه | |||
# data = { model: "Person", attribute: "Username", value: <username> } | |||
message: ->(object, data) do | |||
"Hey #{object.name}!, #{data[:value]} is taken already! Try again #{Time.zone.tomorrow}" | |||
end | |||
} | |||
end | end | ||
</syntaxhighlight> | |||
الخيار on: | === الخيار <code>on:</code> === | ||
يمكّنك هذا الخيار من تحديد مكان التحقُّق. السلوك الافتراضي لجميع مساعدات التحقُّق المضمنة هو تشغيل التحقُّق عند الحفظ (أي عند إنشاء أو تحديث السجلات). إذا أردت تغيير هذا السلوك، فيمكنك تحديد الخيار <code>on:</code> على <code>create:</code> لتشغيل التحقُّق فقط عند إنشاء كائن جديد، أو على <code>update:</code> لتشغيل التحقُّق فقط عند تعديل كائن موجود مسبقًا.<syntaxhighlight lang="rails"> | |||
يمكّنك هذا الخيار من تحديد مكان التحقُّق. السلوك الافتراضي لجميع مساعدات التحقُّق المضمنة هو تشغيل التحقُّق عند الحفظ (أي عند إنشاء أو | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
# سيكون بالإمكان تحديث عنوان البريد الإلكتروني مع قيمة مكررة | |||
validates :email, uniqueness: true, on: :create | |||
# سيكون بالإمكان إنشاء سجل مع قيمة غير عددية للعمر | |||
validates :age, numericality: true, on: :update | |||
# القيمة الافتراضية هي التحقق عند الإنشاء والتحديث | |||
validates :name, presence: true | |||
end | end | ||
</syntaxhighlight>يمكنك أيضًا استخدام الخيار <code>on:</code> لتعريف سياق مخصّص. يجب تشغيل السياق المخصص بشكل ظاهري عن طريق تمرير اسم السياق إلى التوابع <code>?valid</code> أو <code>?invalid</code> أو <code>save</code>.<syntaxhighlight lang="rails"> | |||
يمكنك أيضًا استخدام الخيار on: لتعريف سياق مخصّص. يجب تشغيل السياق المخصص بشكل ظاهري عن طريق تمرير اسم السياق إلى التوابع ?valid أو ?invalid أو save. | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :email, uniqueness: true, on: :account_setup | |||
validates :age, numericality: true, on: :account_setup | |||
end | end | ||
person = Person.new | person = Person.new | ||
</syntaxhighlight>يتم تنفيذ <code>(person.valid?(:account_setup</code> عند التحقُّقين دون حفظ السجل. ويقوم التابع <code>(person.sav(context: :account_setup</code> بالتحقُّق من الكائن <code>person</code> في السياق <code>account_setup</code> قبل الحفظ. عند التشغيل بشكل ظاهري، يتم التحقُّق من السجلات عن طريق عمليات التحقق المحددة فقط لسجلات هذا السياق أو التي لا تملك سياقًا مطلقًا. | |||
== التحقُّقات الصارمة == | |||
يمكنك تحديد فيما إذا كان تحقُّق ما صارم (strict) أم لا ورفع الاستثناء <code>ActiveModel::StrictValidationFailed</code> عند فشل عملية التحقق.<syntaxhighlight lang="rails"> | |||
التحقُّقات | |||
يمكنك تحديد فيما إذا كان تحقُّق ما | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :name, presence: { strict: true } | |||
end | end | ||
Person.new.valid? | Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank | ||
</syntaxhighlight>يمكنك أيضًا تمرير استثناءات مخصّصة للخيار <code>strict:</code>.<syntaxhighlight lang="rails"> | |||
يمكنك أيضًا تمرير استثناءات مخصّصة للخيار strict:. | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :token, presence: true, uniqueness: true, strict: TokenGenerationException | |||
end | end | ||
Person.new.valid? # => TokenGenerationException: Token can't be blank | |||
</syntaxhighlight> | |||
== التحققات الشرطية == | |||
في بعض الأحيان، قد يكون من المنطقي التحقُّق من سجل ما فقط عند تحقق شرط معيّن. يمكنك إنجاز ذلك عن طريق الخيارين <code>if:</code> و <code>unless:</code>، اللذَين يستقبلان [[Ruby/Symbol|رمزًا]]، أو كائنًا من النوع <code>[[Ruby/Proc|Proc]]</code>، أو [[Ruby/Array|مصفوفة]]. يمكنك استخدام الخيار <code>if:</code> لتحديد متى يجب تنفيذ عملية التحقُّق. وفي حال أردت تحديد متى لا يجب تنفيذ التحقُّق، يمكنك استخدام الخيار <code>unless:</code>. | |||
في بعض الأحيان، قد يكون من المنطقي | |||
=== استخدام رمز مع الخيارين <code>if:</code> و <code>unless:</code> === | |||
يمكنك ربط الخيارين <code>if:</code> و <code>unless:</code> مع رمز موافق لاسم التابع المستدعى قبل عملية التحقُّق. هذا الخيار هو الخيار الأكثر استخدامًا.<syntaxhighlight lang="rails"> | |||
class Order < ApplicationRecord | class Order < ApplicationRecord | ||
validates :card_number, presence: true, if: :paid_with_card? | |||
def paid_with_card? | |||
payment_type == "card" | |||
end | |||
end | end | ||
</syntaxhighlight> | |||
استخدام Proc مع | === استخدام الكائن <code>Proc</code> مع الخيارين <code>if:</code> و <code>unless:</code> === | ||
أخيرًا، يمكنك ربط الخيارين <code>if:</code> و <code>unless:</code> مع كائن من النوع <code>[[Ruby/Proc|Proc]]</code> يتم مناداته. باستخدام الكائن <code>[[Ruby/Proc|Proc]]</code>، يمكنك التحقُّق شرطيًا ضمن سطر من التعليمات بدلًأ من تابع منفصل. هذا الخيار هو مفضّل لمحبّي الأسطر الواحدة.<syntaxhighlight lang="rails"> | |||
class Account < ApplicationRecord | class Account < ApplicationRecord | ||
validates :password, confirmation: true, | |||
unless: Proc.new { |a| a.password.blank? } | |||
end | end | ||
</syntaxhighlight> | |||
تجميع التحقُّقات الشرطية | === تجميع التحقُّقات الشرطية === | ||
قد يكون من المفيد أحيانًا أن تستخدم مجموعة من التحقُّقات شرطًا واحدًا. يمكن تنفيذ ذلك عن باستخدام <code>with_options</code>.<syntaxhighlight lang="rails"> | |||
قد يكون من المفيد أحيانًا أن تستخدم مجموعة من التحقُّقات شرطًا واحدًا. يمكن تنفيذ ذلك عن باستخدام with_options. | |||
class User < ApplicationRecord | class User < ApplicationRecord | ||
with_options if: :is_admin? do |admin| | |||
admin.validates :password, length: { minimum: 10 } | |||
admin.validates :email, presence: true | |||
end | |||
end | end | ||
</syntaxhighlight>جميع التحقُّقات الواقعة داخل الكتلة التي تخص <code>with_options</code> سيمرّر لها الشرط <code>if: :is_admin?</code> تلقائيًّا. | |||
=== دمج شروط التحقُّق === | |||
وفي الطرف الآخر، عندما تحدد مجموعة من الشروط متى يجب تنفيذ تحقُّق معيّن، يمكن استخدام مصفوفة. أي يمكنك استخدام الخيارين <code>if:</code> و <code>unless:</code> على التحقُّق نفسه.<syntaxhighlight lang="rails"> | |||
وفي الطرف الآخر، عندما تحدد مجموعة من الشروط متى يجب تنفيذ تحقُّق معيّن، يمكن استخدام مصفوفة. أي يمكنك استخدام الخيارين if: و unless: على التحقُّق نفسه. | |||
class Computer < ApplicationRecord | class Computer < ApplicationRecord | ||
validates :mouse, presence: true, | |||
if: [Proc.new { |c| c.market.retail? }, :desktop?], | |||
unless: Proc.new { |c| c.trackpad.present? } | |||
end | end | ||
</syntaxhighlight>يتم تنفيذ هذا التحقُّق عندما تتحقق جميع الشروط في <code>if:</code>، ولا تتحقق أي من الشروط في <code>unless:</code>. | |||
== تنفيذ تحققات مخصصة == | |||
عند عدم كفاية مساعدات التحقُّق المضمنة، يمكنك كتابة تحقُّقات أو توابع تحقُّق خاصة بك بناءً على حاجتك. | |||
== تنفيذ | |||
عند عدم كفاية مساعدات التحقُّق المضمنة، يمكنك كتابة | |||
=== عمليات التحقق المخصصة === | |||
عمليات التحقق المخصصة هي أصناف ترث من الصنف الأب <code>ActiveModel::Validator</code>. يجب أن تعرّف هذه الأصناف التابع <code>validate</code> الذي يقبل تمرير السجل المطلوب التحقُّق منه وسيطًا له ثم يجري عملية التحقق عليه. تستدعى علمية التحقق المخصصة باستخدام التابع <code>validates_with</code>.<syntaxhighlight lang="rails"> | |||
class MyValidator < ActiveModel::Validator | class MyValidator < ActiveModel::Validator | ||
def validate(record) | |||
unless record.name.starts_with? 'X' | |||
record.errors[:name] << 'Need a name starting with X please!' | |||
end | |||
end | |||
end | end | ||
class Person | class Person | ||
include ActiveModel::Validations | |||
validates_with MyValidator | |||
end | end | ||
</syntaxhighlight>أسهل طريقة لإضافة تحقُّق مخصص للتحقُّق من خاصيات فردية هو عن طريق الصنف المريح <code>ActiveModel::EachValidator</code>. في هذه الحالة، يجب أن يعرِّف صنف التحقُّق التابع <code>validate_each</code> الذي يقبل ثلاث وسائط هي: سجل، وخاصية، وقيمة. تكون هذه الوسائط مرتبطة بالكائن، والخاصية المراد التحقُّق منها، وقيمة الخاصية المُمرَّرة للكائن.<syntaxhighlight lang="rails"> | |||
أسهل طريقة لإضافة | |||
class EmailValidator < ActiveModel::EachValidator | class EmailValidator < ActiveModel::EachValidator | ||
def validate_each(record, attribute, value) | |||
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i | |||
record.errors[attribute] << (options[:message] || "is not an email") | |||
end | |||
end | |||
end | end | ||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :email, presence: true, email: true | |||
end | end | ||
</syntaxhighlight>كما هو ظاهر في المثال، يمكنك أيضًا دمج التحقُّقات القياسية المُعرَّفة مسبقًا مع تحقُّقات مخصصة. | |||
كما هو ظاهر في المثال، يمكنك أيضًا دمج التحقُّقات | |||
=== التوابع المخصصة === | === التوابع المخصصة === | ||
يمكنك أيضًا | يمكنك أيضًا تعريف توابع تتحقق من حالة السجلات وتضيف رسائل خطأ إلى المجموعة <code>errors</code> عند فشل عملية التحقق. يجب عليك بعد ذلك تسجيل هذه التوابع باستخدام تابع الصنف <code>[http://api.rubyonrails.org/v5.2.2/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate validate]</code>، ممرّرًا [[Ruby/Symbol|رموزًا]] لأسماء هذه التوابع. | ||
يمكنك تمرير أكثر من رمز لكل تابع صنف، مسبوقًا بالتحقُّق المستخدم بنفس ترتيب تسجيله. | |||
يتحقق التابع <code>?valid</code> من خلو مجموعة الأخطاء (errors collection) من الأخطاء، لتتمكّن توابع التحقُّق المخصصة من إضافة الأخطاء إلى المجموعة عند فشل التحقُّق:<syntaxhighlight lang="rails"> | |||
class Invoice < ApplicationRecord | class Invoice < ApplicationRecord | ||
validate :expiration_date_cannot_be_in_the_past, | |||
:discount_cannot_be_greater_than_total_value | |||
def expiration_date_cannot_be_in_the_past | |||
if expiration_date.present? && expiration_date < Date.today | |||
errors.add(:expiration_date, "can't be in the past") | |||
end | |||
end | |||
def discount_cannot_be_greater_than_total_value | |||
if discount > total_value | |||
errors.add(:discount, "can't be greater than total value") | |||
end | |||
end | |||
end | end | ||
</syntaxhighlight>افتراضيًا، يتم تشغيل التحقُّقات المشابهة عند كل استدعاء للتابع <code>?valid</code> أو عند حفظ الكائن. لكن من الممكن أيضًا التحكم بوقت تنفيذ هذه التحقُّقات المخصصة عن طريق تحديد الخيار <code>on:</code> مع التابع <code>validate</code>، مستخدمًا إما <code>create:</code> أو <code>update:</code>.<syntaxhighlight lang="rails"> | |||
افتراضيًا، يتم تشغيل التحقُّقات المشابهة عند كل | |||
class Invoice < ApplicationRecord | class Invoice < ApplicationRecord | ||
validate :active_customer, on: :create | |||
def active_customer | |||
errors.add(:customer_id, "is not active") unless customer.active? | |||
end | |||
end | end | ||
</syntaxhighlight> | |||
== العمل مع أخطاء | == العمل مع أخطاء التحققات == | ||
إضافةً إلى | إضافةً إلى التابعين <code>?valid</code> و <code>?invalid</code> الموضّحين أعلاه، يزوّد ريلز بمجموعة من التوابع المستخدمة للعمل مع مجموعة الأخطاء <code>errors</code> والحصول على معلومات حول صحة الكائنات. | ||
إليك قائمة بأكثر التوابع استخدامًا. الرجاء الاطلاع على توثيق <code>ActiveModel::Errors</code> لقائمة بالتوابع المتاحة. | |||
=== <code>Errors</code> === | |||
يعيد كائنًا من النوع <code>ActiveModel::Errors</code> يحوي جميع الأخطاء. كل مفتاح هو اسم الخاصية، وكل قيمة هي مصفوفة برسائل الخطأ المرتبطة بالخاصية.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :name, presence: true, length: { minimum: 3 } | |||
end | end | ||
person = Person.new | person = Person.new | ||
person.valid? # => false | person.valid? # => false | ||
person.errors.messages | person.errors.messages | ||
# => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]} | |||
person = Person.new(name: "John Doe") | person = Person.new(name: "John Doe") | ||
person.valid? # => true | person.valid? # => true | ||
person.errors.messages # => {} | person.errors.messages # => {} | ||
</syntaxhighlight> | |||
=== []errors === | === <code>[]errors</code> === | ||
يُستخدَم <code>[]errors</code> عندما تريد التحقق من رسائل الخطأ لخاصية محددة. يعيد هذا التابع مصفوفة من رسائل الخطأ الموافقة للخاصية المعطاة. في حال لم يكن هنالك أخطاء للحقل المعطى، فيعيد التابع مصفوفةً فارغةً.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :name, presence: true, length: { minimum: 3 } | |||
end | end | ||
person = Person.new(name: "John Doe") | person = Person.new(name: "John Doe") | ||
person.valid? # => true | person.valid? # => true | ||
person.errors[:name] # => [] | person.errors[:name] # => [] | ||
person = Person.new(name: "JD") | person = Person.new(name: "JD") | ||
person.valid? # => false | person.valid? # => false | ||
person.errors[:name] # => ["is too short (minimum is 3 characters)"] | person.errors[:name] # => ["is too short (minimum is 3 characters)"] | ||
person = Person.new | person = Person.new | ||
person.valid? # => false | person.valid? # => false | ||
person.errors[:name] | person.errors[:name] | ||
# => ["can't be blank", "is too short (minimum is 3 characters)"] | |||
</syntaxhighlight> | |||
=== <code>errors.add</code> === | |||
يمكّنك التابع <code>add</code> من إضافة رسائل الخطأ إلى خاصية محددة. يستقبل هذا التابع في وسائطه اسم الخاصية الواجب إضافة رسالة الخطأ عليها، ورسالة الخطأ المطلوبة. | |||
=== errors.add === | |||
يمكّنك التابع add من إضافة رسائل الخطأ إلى | |||
يعيد التابع <code>errors.full_messages</code> (أو مثيله <code>errors.to_a</code>) رسائل الخطأ بنمط سهل القراءة للمستخدم، مع اسم الخاصية السابق لرسالة الخطأ، كما هو ظاهر في المثال التالي.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
def a_method_used_for_validation_purposes | |||
errors.add(:name, "cannot contain the characters !@#%*()_-+=") | |||
end | |||
end | end | ||
person = Person.create(name: "!@#") | person = Person.create(name: "!@#") | ||
person.errors[:name] | person.errors[:name] | ||
# => ["cannot contain the characters !@#%*()_-+="] | |||
person.errors.full_messages | person.errors.full_messages | ||
# => ["Name cannot contain the characters !@#%*()_-+="] | |||
</syntaxhighlight>مثيل عن التابع <code>errors.add</code> هو استخدام <code>>></code> لإضافة رسالة إلى المصفوفة <code>errors.messages</code> لخاصية:<syntaxhighlight lang="rails"> | |||
مثيل عن التابع errors | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
def a_method_used_for_validation_purposes | |||
errors.messages[:name] << "cannot contain the characters !@#%*()_-+=" | |||
end | |||
end | end | ||
person = Person.create(name: "!@#") | person = Person.create(name: "!@#") | ||
person.errors[:name] | person.errors[:name] | ||
# => ["cannot contain the characters !@#%*()_-+="] | |||
person.errors.to_a | person.errors.to_a | ||
# => ["Name cannot contain the characters !@#%*()_-+="] | |||
</syntaxhighlight> | |||
=== <code>errors.details</code> === | |||
يمكنك تحديد نوع التحقُّق لكائن معلومات الخطأ المعاد عن طريق التابع <code>errors.add</code>.<syntaxhighlight lang="rails"> | |||
=== errors.details === | |||
يمكنك تحديد نوع | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
def a_method_used_for_validation_purposes | |||
errors.add(:name, :invalid_characters) | |||
end | |||
end | end | ||
person = Person.create(name: "!@#") | person = Person.create(name: "!@#") | ||
person.errors.details[:name] | person.errors.details[:name] | ||
# => [{error: :invalid_characters}] | |||
</syntaxhighlight>لتحسين كائن معلومات الخطأ بحيث يحوي المحارف غير المسموحة على سبيل المثال، يمكنك تمرير مفاتيح إضافية للتابع <code>errors.add</code>.<syntaxhighlight lang="rails"> | |||
لتحسين كائن معلومات الخطأ بحيث يحوي المحارف غير المسموحة على سبيل المثال، يمكنك تمرير مفاتيح إضافية للتابع errors.add. | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
def a_method_used_for_validation_purposes | |||
errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=") | |||
end | |||
end | end | ||
person = Person.create(name: "!@#") | person = Person.create(name: "!@#") | ||
person.errors.details[:name] | person.errors.details[:name] | ||
# => [{error: :invalid_characters, not_allowed: "!@#%*()_-+="}] | |||
</syntaxhighlight>جميع التحقُّقات المدمجة في ريلز تملأ كائنات معلومات الخطأ بمجموعات متوافقة مع نوع التحقُّق المستخدم. | |||
=== <code>[errors[:base</code> === | |||
يمكنك إضافة رسائل خطأ متعلقة بحالة الكائن ككل، بدلًا من أن تكون متعلقة بخاصية وحيدة. يمكنك استخدام هذا التابع عند الحاجة للقول أن الكائن ككل هو غير صحيح، بغض النظر عن قيم خاصياته. بما أن <code>[errors[:base</code> هي مصفوفة، يمكنك إضافة سلسلة نصية لها وسيتم استخدامها كرسالة خطأ.<syntaxhighlight lang="rails"> | |||
=== [errors[:base === | |||
يمكنك إضافة رسائل خطأ متعلقة بحالة الكائن ككل، بدلًا من أن تكون متعلقة | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
def a_method_used_for_validation_purposes | |||
errors[:base] << "This person is invalid because ..." | |||
end | |||
end | end | ||
</syntaxhighlight> | |||
=== errors.clear === | === <code>errors.clear</code> === | ||
يُستخدَم التابع <code>clear</code> عندما تود بإرادتك أن تزيل جميع رسائل الخطأ من المجموعة <code>errors</code>. بالطبع، استدعاء التابع <code>clear</code> مع كائن غير صحيح لن يجعله صحيحًا: أي ستكون المجموعة <code>errors</code> فارغة، لكن عند استدعاء التابع <code>?valid</code> في المرة القادمة أو أي تابع يحاول حفظ الكائن في قاعدة البيانات، فسيتم تنفيذ التحقُّق من جديد. وفي حال فشله، ستمتلئ المجموعة <code>errors</code> من جديد.<syntaxhighlight lang="rails"> | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :name, presence: true, length: { minimum: 3 } | |||
end | end | ||
person = Person.new | person = Person.new | ||
person.valid? # => false | person.valid? # => false | ||
person.errors[:name] | person.errors[:name] | ||
# => ["can't be blank", "is too short (minimum is 3 characters)"] | |||
person.errors.clear | person.errors.clear | ||
person.errors.empty? # => true | person.errors.empty? # => true | ||
person.save # => false | person.save # => false | ||
person.errors[:name] | person.errors[:name] | ||
# => ["can't be blank", "is too short (minimum is 3 characters)"] | |||
</syntaxhighlight> | |||
=== <code>errors.size</code> === | |||
يعيد التابع <code>size</code> عدد رسائل الخطأ للكائن.<syntaxhighlight lang="rails"> | |||
=== errors.size === | |||
يعيد التابع size عدد رسائل الخطأ للكائن. | |||
class Person < ApplicationRecord | class Person < ApplicationRecord | ||
validates :name, presence: true, length: { minimum: 3 } | |||
end | end | ||
person = Person.new | person = Person.new | ||
person.valid? # => false | person.valid? # => false | ||
person.errors.size # => 2 | person.errors.size # => 2 | ||
person = Person.new(name: "Andrea", email: "andrea@example.com") | person = Person.new(name: "Andrea", email: "andrea@example.com") | ||
person.valid? # => true | person.valid? # => true | ||
person.errors.size # => 0 | person.errors.size # => 0 | ||
</syntaxhighlight> | |||
== عرض أخطاء | == عرض أخطاء التحقق في الواجهات == | ||
متى ما أنشأت نموذجًا وأضفت تحقُّقات له، ففي حال تم إنشاء هذا النموذج (model) عن طريق نموذج ويب (web form)، فقد يكون من الضروري إظهار رسالة الخطأ عند فشل التحقُّق. | |||
لأن كل تطبيق يراعي هذه الحالة بطريقة مختلفة، لا يزود ريلز بأي مساعد واجهة للمساعدة بتوليد هذه الرسائل مباشرةً. لكن نظرًا للعدد الكبير من التوابع المزودة من ريلز للتفاعل مع التحقُّقات بشكل عام، من السهل بناء هذه التوابع يدويًّا. علاوةً على ذلك، عند توليد scaffold (هو جزء من النمط البرمجي MVC حيثُ يُمكنك إختيار جزء مُعين من قاعدة البيانات للعمل عليه)، يقوم ريلز بوضع تعليمات ERB في الملف form.html.erb_ المولّد، والذي يظهر القائمة الكاملة بأخطاء هذا النموذج. | |||
بفرض أنه لدينا نموذجًا تم حفظه في متغير نسخة تسمى <code>article@</code>، يبدو الأمر كذلك:<syntaxhighlight lang="rails"> | |||
<% if @article.errors.any? %> | <% if @article.errors.any? %> | ||
<div id="error_explanation"> | |||
<h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2> | |||
<ul> | |||
<% @article.errors.full_messages.each do |msg| %> | |||
<li><%= msg %></li> | |||
<% end %> | |||
</ul> | |||
</div> | |||
<% end %> | <% end %> | ||
</syntaxhighlight>من ناحية أخرى، في حال كنت تستخدم مساعدات ريلز لإنشاء نماذج الويب، عند فشل تحقُّق على حقل معين، ستولّد هذه المساعدات عنصر <code>[[HTML/div|<nowiki><div></nowiki>]]</code> إضافي حول الحقل.<syntaxhighlight lang="rails"> | |||
من ناحية أخرى، في حال كنت تستخدم مساعدات | <div class="field_with_errors"> | ||
<input id="article_title" name="article[title]" size="30" type="text" value=""> | |||
</div> | |||
</syntaxhighlight>يمكنك بناءً على ذلك تعديل شكل هذا العنصر كما تشاء. الشكل الافتراضي المضاف من vdg. مثلًا، يقوم بإنشاء قواعد CSS التالية:<syntaxhighlight lang="rails"> | |||
<input id="article_title" name="article[title]" size="30" type="text" value=""> | |||
يمكنك بناءً على ذلك تعديل شكل هذا العنصر كما تشاء. الشكل الافتراضي المضاف من | |||
.field_with_errors { | .field_with_errors { | ||
padding: 2px; | |||
background-color: red; | |||
display: table; | |||
} | } | ||
</syntaxhighlight>ممّا يعني أن أي حقل يملك أخطاءً سيتم تلوين حدوده باللون الأحمر بسماكة 2 بكسل. | |||
== مصادر == | |||
* [https://guides.rubyonrails.org/active_record_validations.html#custom-validators صفحة Active Record Validations في توثيق Ruby On Rails الرسمي.] |
المراجعة الحالية بتاريخ 09:23، 24 مارس 2019
يعلِّمك هذا الدليل كيفية التحقق من حالة الكائنات قبل إرسالها إلى قاعدة البيانات باستعمال ميزة التحققات من الصحة (validations) التي يوفرها Active Record. بعد قراءة هذا الدليل، ستتعرَّف على:
- كيفية استعمال مساعدي التحقق من الصحة (validation helpers) لـ Active Record المدمجين.
- كيفية إنشاء توابع مخصصة للتحقق من الصحة.
- كيفية العمل مع رسالة الخطأ المولدة عبر عملية التحقق.
نظرة عامة على عمليات التحقق
الشيفرة التالية تظهر مثالًا عن تحقق بسيط جدًا:
class Person < ApplicationRecord
validates :name, presence: true
end
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false
كما يمكنك أن ترى، تمكنّنا التحقُّقات من معرفة أن الكائن Person
ليس صحيحًا بدون اسم؛ مما يعني أن الكائن Person
الثاني لن يتم حفظه في قاعدة البيانات.
قبل أن ندخل في التفاصيل، لنتحدث عن أهمية التحقُّقات في تطبيقك.
لمَ نستخدم التحقُّقات؟
تستخدم التحقُّقات للتأكّد من أنّ البيانات التي يتم حفظها في قاعدة البيانات هي صحيحة حتمًا. على سبيل المثال، قد يكون من الضروري على تطبيقك التأكّد من أنّ كل مستخدم يزوّدك ببريده الالكتروني وعنوانه الخاص. التحقُّقات على مستوى النماذج هي الطريقة الأمثل للتأكد من أنّ البيانات الصحيحة تمامًا يتم حفظها في قاعدة البيانات. تعتبر التحقُّقات مستقلة عن قاعدة البيانات، ولا يمكن تجاوزها من المستخدم النهائي، وتتمتّع بأريحية بالصيانة والاختبار. يجعل ريلز التحقُّقات سهلة الاستخدام، ويزوّد بمساعدات مدمجة من أجل الحاجات المختلفة، ويمكّنك من كتابة توابع التحقُّق الخاصة بك أيضًا.
هناك مجموعة من الطرق للتأكّد من البيانات قبل حفظها في قاعدة البيانات، بما فيها القيود الأصلية لقاعدة البيانات، وتحقُّقات من طرف المستخدم وتحقُّقات من طرف المتحكّم. إليك شرح قصير عن إيجابيات وسلبيّات كلّ منها:
- إن قيود قاعدة البيانات أو الإجراءات المضمَّنة (Stored Procedures) تجعل هيكلية التحقُّق معتمدة تمامًا على قاعدة البيانات، وتصعّب من عمليّة الاختبار والصيانة. لكن في حال كانت قاعدة البيانات الخاصة بك مستخدمة من قبل تطبيقات أخرى، فقد يكون من الضروري استخدام قيود قاعدة البيانات. علاوةً على ذلك، تستطيع قيود قاعدة البيانات معالجة بعض الحالات بشكل آمن وسلس (كالحقول الفريدة في الجداول كثيرة البيانات)، والتي من الصعب تنفيذها بطرق أخرى.
- يمكن للتحقُّقات من طرف المستخدم (Client-side validations) أن تكون مفيدة، لكن لا يمكن الاعتماد عليها في حال تم تطبيقها بمفردها فقط. في حال تم تطبيقها باستخدام JavaScript، يستطيع المستخدم تجاوزها في حال قام بإلغاء JavaScript في المستعرض الخاص به. لكن في حال تم دمجها مع تقنيات أخرى، يمكن للتحقُّقات من طرف المستخدم أن تكون طريقة مريحة وسهلة لتزويد المستخدم بتغذية راجعة آنية أثناء استخدامه لموقعك.
- يمكن للتحقُّقات التي على مستوى المتحكّم (Controller-level validations) أن تكون مشوّقة للاستخدام، لكن تصبح بعض الأوقات غير عملية وصعبة للاختبار والصيانة. عند الإمكانية، يجب على متحكّماتك أن تبقى مجرّدة ومختصرة، ممّا يجعل العمل على تطبيقك أمرًا سهلًا مع مرور الوقت.
يمكن استخدام هذه الطرق في حالات مخصصة. برأي فريق عمل ريلز، طريقة التحقُّقات في مستوى النماذج (model-level validations) هي الطريقة المثلى في معظم الحالات.
متى يحدث التحقق؟
هناك نوعان من كائنات Active Record: الكائنات التي تتفاعل مع سجل من قاعدة بياناتك، والكائنات التي لا تتفاعل. عند إنشائك لكائن جديد، مثلًا باستخدام التابع new
، لن يتم حفظ هذا الكائن في قاعدة البيانات بعد. بعد استدعائك للتابع save
على هذا الكائن، سيتم حفظه في جدول قاعدة البيانات. يستخدم Active Record تابع النسخة ?new_record
للتأكّد من وجود السجل في قاعدة البيانات أم لا. لنطلع على نموذج Active Record البسيط التالي:
class Person < ApplicationRecord
end
يمكننا أن نرى كيف يعمل عن طريق الاطلاع على خرج الأمر rails console
:
$ bin/rails console
>> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
>> p.new_record?
=> true
>> p.save
=> true
>> p.new_record?
=> false
إنّ إنشاء وحفظ سجل جديد سيرسل العملية SQL INSERT
لقاعدة البيانات. وإن تعديل سجل موجود مسبقًا سيرسل العملية SQL UPDATE
بدلًا من ذلك. تُشغَّل التحقُّقات عادةً قبل إرسال هذه الأوامر إلى قاعدة البيانات. في حال فشل أحد التحقُّقات، سيتم تعيين الكائن ككائن غير صحيح ولن يتم إرسال الاستعلام INSERT
أو UPDATE
. يحدّ هذا من حفظ كائنات غير صحيحة في قاعدة البيانات. يمكنك تحديد فيما إذا أردت تنفيذ تحقُّقات معينة عند إنشاء كائن أو تعديله أو حفظه.
تحذير: هناك طرائق عدَّة لتغيير حالة كائن ما في قاعدة البيانات. تشغّل بعض التوابع عمليات التحقق، والبعض الآخر لا يفعل ذلك. ممّا يعني أنّه يمكنك حفظ كائن ما في قاعدة البيانات في حالة غير صحيحة إن لم تكن على دراية.
تشغّل التوابع التالية عمليات التحقق، وتحفظ الكائن في قاعدة البيانات فقط عند كونه صحيحًا:
create
create!
save
save!
update
update!
ترمي التوابع ذات الإصدار المُغيِّر (bang version مثل التابع !save
) استثناءات عند عدم صحة الكائنات. بينما لا تفعل ذلك التوابع ذات الإصدار غير المُغيِّر (non-bang versions) مثل التابعان save
و update
اللذان يعيدان القيمة false
، والتابع create
يعيد الكائن نفسه.
تجاوز التحقُّقات
تتجاوز التوابع التالية عمليات التحقق، وتحفظ الكائن في قاعدة البيانات بغض النظر عن صحته. بناءً على ذلك، يجب استخدام هذه التوابع بحذر:
decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
update_all
update_attribute
update_column
update_columns
update_counters
تجب الملاحظة أنّ للتابع save
إمكانية تجاوز التحقُّقات أيضًا، عن طريق تمرير القيمة false
للوسيط validate
. بالمثل، يجب استخدام هذه الطريقة بحذر:
save(validate: false)
التابعان ?valid
و ?invalid
قبل حفظ كائن Active Record، يشغّل ريلز التحقُّقات الخاصة بك. في حال أثمرت هذه التحقُّقات أخطاءً، فلن يحفظ ريزل الكائن.
يمكنك أيضًا تشغيل التحقُّقات يدويًا. إذ يقوم التابع ?valid
بتشغيل التحقق ويعيد القيمة true
في حال نجاحه، والقيمة false
عدا عن ذلك. كما رأيت أعلاه:
class Person < ApplicationRecord
validates :name, presence: true
end
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false
بعد قيام كائن Active Record بالتحقُّقات، يمكن الوصول لرسائل الخطأ عن طريق تابع النسخة errors.messages
، الذي يعيد مجموعة من الأخطاء. من التعريف، يكون الكائن صحيحًا في حال كانت هذه المجموعة خالية بعد تنفيذ التحقُّق.
تجدر الملاحظة أنَّ الكائن الذي يتم تهيئته باستخدام new
لن يعطي أي خطأ حتّى لو كان غير صالح، لأنَّ التحقُّقات تنفّذ تلقائيًا عند حفظ الكائن، كما هو الحال في التابعين create
و save
.
class Person < ApplicationRecord
validates :name, presence: true
end
>> p = Person.new
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {}
>> p.valid?
# => false
>> p.errors.messages
# => {name:["can't be blank"]}
>> p = Person.create
# => #<Person id: nil, name: nil>
>> p.errors.messages
# => {name:["can't be blank"]}
>> p.save
# => false
>> p.save!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
>> Person.create!
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
التابع ?invalid
هو ببساطة عكس التابع ?valid
، إذ ينفّذ التحقُّق، ويعيد القيمة true
في حال وجود أخطاء في الكائن، و false
فيما عدا ذلك.
التابع []errors
للتأكد من صحة حقل ما في كائن، يمكنك استخدام التابع [errors[:attribute
، حيث attribute:
هو الحقل المطلوب التحقق منه. يعيد هذا التابع مصفوفةً من الأخطاء من أجل الحقل المطلوب، وفي حال عدم وجود أخطاء فيتم إعادة مصفوفة فارغة.
يفيد التابع هذا فقط بعد تنفيذ التحقُّقات، لأنّه فقط يقوم بقراءة مصفوفة الأخطاء ولا ينفّذ التحقُّقات بحد ذاتها. ويعتبر مختلفًا عن التابع ActiveRecord::Base.invalid
الموضح أعلاه لأنّه لا يعبّر عن صحّة كائن كامل، بل يحتوي فقط على أخطاء الحقل المطلوب من الكائن.
class Person < ApplicationRecord
validates :name, presence: true
end
>> Person.new.errors[:name].any? # => false
>> Person.create.errors[:name].any? # => true
سنقوم بتغطية أخطاء التحقُّقات بشكل كامل في قسم العمل مع أخطاء التحقُّقات.
الخاصية errors.details
للتأكد من التحقُّقات التي فشلت على حقل ما، يمكنك استخدام [errors.details[:attribute
. يعيد هذا التابع مصفوفة من القيم hash مع المفتاح error:
لجلب رمز التحقُّق:
class Person < ApplicationRecord
validates :name, presence: true
end
>> person = Person.new
>> person.valid?
>> person.errors.details[:name] # => [{error: :blank}]
إن استخدام details
مع التحقُّقات اليدوية مغطّى بشكل كامل في قسم العمل مع أخطاء التحقُّقات.
مساعدو التحقق
يزوّد Active Record مجموعةً معرفةً مسبقًا من المساعدين الذين يمكنك استخدامهم بشكل مباشر في تعريفات الأصناف الخاصة بك. تزوّد هؤلاء المساعدين بقواعد متعارف عليها لإجراء التحقُّقات. في كل مرة يفشل فيها تحقُّق ما، سيتم إضافة رسالة خطأ إلى المجموعة errors
الخاصة بالكائن، وتضم هذه الرسالة اسم الحقل الذي فشل التحقُّق منه.
يقبل كل مساعد مجموعة من أسماء الحقول، أي يمكنك في سطر تعريف وحيد إضافة نفس التحقُّق لمجموعة من الحقول.
جميعهم يقبلون الخيارين on:
و message:
، اللذَين يحددان متى يتم تشغيل التحقُّقات وما الرسالة الواجب إضافتها في حال فشل التحقُّق. يقبل الخيار on:
إحدى القيمتين create:
أو update:
. هناك رسائل افتراضية لكل نوع من أنواع التحقُّقات، والتي يتم استخدامها عند عدم تحديد رسالة بشكل يدوي في الخيار message:
. لنطلّع على كل مساعد من هؤلاء المساعدين.
المساعد acceptance
يأكّد هذا المساعد أن صندوق الاختيار (checkbox) تم تحديده على واجهة المستخدم عند إرسال النموذج. يستخدم هذا المساعد بشكل تقليدي عند حاجة المستخدم للموافقة على شروط استخدام التطبيق مثلَا، أو للتأكد من قراءة نص معين، أو أي شيء مماثل.
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true
end
يُنفّذ هذا التحديد فقط عندما يكون terms_of_service
ليس nil
. إن رسالة الخطأ الافتراضية لهذا المساعد هي "must be accepted". يمكنك تمرير رسالة يدوية عن طريق الخيار message:
.
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { message: 'must be abided' }
end
يمكن أيضًا تمرير الخيار accept:
الذي يحدد القيم التي يجب اعتبارها خيارًا مقبولًا. القيمة الافتراضية لهذا الخيار هي [1,true]
ويمكن تغييرها بسهولة:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { accept: 'yes' }
validates :eula, acceptance: { accept: ['TRUE', 'accepted'] }
end
هذا التحقُّق مخصَّص لتطبيقات الويب وليس من الضروري تخزين هذا القبول في أي مكان في قاعدة البيانات. في حال لم تكن تملك هذا الحقل في قاعدة البيانات، يقوم المساعد بإنشائه لك بشكل افتراضي. في حال عدم وجود الحقل في قاعدة البيانات، يجب ضبط الخيار accept
أو أن يحوي القيمة true
، وإلا لن يتم تشغيل التحقُّق.
المساعد validates_associated
يمكنك استخدام هذا المساعد في حال كان نموذجك يملك ارتباطات مع نماذج أخرى تحتاج للتحقُّق أيضًا. عند محاولتك حفظ السجل، سيتم استدعاء التابع ?valid
على كل كائن من الكائنات المرتبطة أيضًا.
class Library < ApplicationRecord
has_many :books
validates_associated :books
end
سيعمل هذا التحقُّق على جميع الأنواع المرتبطة.
تحذير: لا تستخدم المساعد validates_associated
على طرفي الارتباط، إذ سيؤدي ذلك إلى استدعاء أحدهما للآخر مما يؤدي إلى الدخول في حلقة لا نهائية.
إن رسالة الخطأ الافتراضية لهذا التحقُّق هي "is invalid". تجب الملاحظة أن لكل كائن من الكائنات المرتبطة مجموعة الأخطاء الخاصة به، ولن يتم حفظ مجموعة الأخطاء للكائنات الأبناء في الكائن المستدعي.
المساعد confirmation
يجب أن تستخدم هذا المساعد عند وجود حقلين نصيَّين يجب على كل منهما أن يرسلا نفس القيمة تمامًا. مثلًا، قد تحتاج إلى تحقُّق بريد الكتروني أو كلمة مرور. ينشئ هذا التحقُّق حقلًا افتراضيًا ويسمّيه بنفس اسم الحقل الأساسي، يليه العبارة "confirmation_".
class Person < ApplicationRecord
validates :email, confirmation: true
end
ويجب أن يبدو نموذج العرض الخاص بك كالتالي:
<%= text_field :person, :email %>
<%= text_field :person, :email_confirmation %>
ينفّذ هذا التحقُّق فقط عند كون email_confirmation
ليس nil
. لطلب التحقُّق بشكل قسري، لا تنسَ إضافة التحقُّق presence
للخاصية confirmation
(سنطلع على التحقُّق presence
لاحقًا في هذا التوثيق):
class Person < ApplicationRecord
validates :email, confirmation: true
validates :email_confirmation, presence: true
end
يمكنك أيضًا تمرير الخيار case_sensitive:
لتحديد فيما إذا كان القيد confirmation
حساسًا لحالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي true
.
class Person < ApplicationRecord
validates :email, confirmation: { case_sensitive: false }
end
رسالة الخطأ الافتراضية لهذا المساعد هي "doesn't match confirmation".
المساعد exclusion
يتأكّد هذا المساعد من أن قيم الحقل المعطى ليست موجودة ضمن المجموعة المحددة. يمكن أن تكون هذه المجموعة أيًّا من الكائنات القابلة للتعداد (enumerable object).
class Account < ApplicationRecord
validates :subdomain, exclusion: { in: %w(www us ca jp),
message: "%{value} is reserved." }
end
يملك المساعد exclusion
الخيار in:
الذي يقبل مجموعة من القيم التي لن يتم قبولها لخاصيات المتحقق منها. يمكن كتابة in:
أيضًا بالشكل within:
. يستخدم هذا المثال الخيار message:
ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية يمكنك الاطلاع على قسم الرسائل في الأسفل.
رسالة الخطأ الافتراضية لهذا المساعد هي "is reserved".
المساعد format
يؤكد هذا المساعد أن قيمة الحقل المعطى توافق التعبير النمطي المحدد بالخيار with:
كالتالي:
class Product < ApplicationRecord
validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
message: "only allows letters" }
end
بالمثل، يمكنك تحديد أن قيمة الحقل المعطى لا توافق التعبير النمطي المحدد، وذلك بالخيار without:
.
رسالة الخطأ الافتراضية لهذا المساعد هي "is invalid".
المساعد inclusion
يتأكد هذا المساعد من أن قيم الحقل المعطى موجودة ضمن المجموعة المحددة:
class Coffee < ApplicationRecord
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size" }
end
يملك المساعد inclusion
الخيار in:
الذي يستقبل مجموعة من القيم المراد قبولها فقط. يمكن كتابة in:
أيضًا بالشكل within:
. يستخدم هذا المثال الخيار message:
ليظهر كيف يمكنك دمج قيمة الحقل في رسالة التحقُّق. لعرض كافة خيارات الرسائل اليدوية، يمكنك الاطلاع على قسم الرسائل في الأسفل.
رسالة الخطأ الافتراضية لهذا المساعد هي "is not included in the list".
المساعد length
يتأكد هذا المساعد من طول قيم الحقل المعطى. يزوّد بمجموعة من الخيارات التي تمكّنك من تحديد قيود الطول بأشكال مختلفة:
class Person < ApplicationRecord
validates :name, length: { minimum: 2 }
validates :bio, length: { maximum: 500 }
validates :password, length: { in: 6..20 }
validates :registration_number, length: { is: 6 }
end
الخيارات الممكنة لقيود الطول هي:
minimum:
: لا يمكن للحقل أن يحوي عدد محارف أقل من الطول المعطى.maximum:
: لا يمكن للحقل أن يحوي عدد محارف أكثر من الطول المعطى.in:
(أوwithin:
): يمكن للحقل أن يحوي عدد محارف بطول موجود ضمن المجال المعطى.is:
: يجب على الحقل أن يحوي عدد محارف يساوي الطول المعطى.
تعتمد رسالة الخطأ الافتراضية على نوع تحقُّق الطول المستخدم. يمكنك تعديل هذه الرسائل عن طريق الخيارات wrong_length:
، و too_long:
، و too_short:
، واستخدام {count}%
لاستيفاء طول المحارف المستخدم. يمكنك استخدام الخيار message:
لتحديد رسالة خطأ.
class Person < ApplicationRecord
validates :bio, length: { maximum: 1000,
too_long: "%{count} characters is the maximum allowed" }
end
تجب الملاحظة أن رسائل الخطأ الافتراضية هي في صيغة الجمع مثل:
"is too short (minimum is %{count} characters)"
لهذا السبب، عند كون minimum:
تساوي 1، يجب تزويد رسالة معدلة أو استخدام الخيار presence: true
بدلًا من ذلك. عندما يملك الخيار in:
أو within:
حدًا أصغريًا مساويًا للواحد، يجب عليك تزويد رسالة معدلة أو استخدام الخيار presence
قبل length
.
المساعد numericality
يتأكد هذا المساعد من أن الحقل يحتوي فقط على قيم رقمية. افتراضيًا، يطابق هذا المساعد إشارة الرقم الاختيارية، متبوعةً بعدد صحيح أو عشري. لتحديد قبول الأعداد الصحيحة فقط، يمكن تعيين الخيار only_integer:
إلى true
.
وفي حال ضبط only_integer:
إلى القيمة true
، فسيتم استخدام التعبير النمطي التالي:
/\A[+-]?\d+\z/
وذلك للتحقُّق من قيمة الخاصية. فيما عدا ذلك، سيحاول تحويل قيمة الحقل إلى عدد باستخدام Float
.
class Player < ApplicationRecord
validates :points, numericality: true
validates :games_played, numericality: { only_integer: true }
end
إلى جانب only_integer:
، يقبل هذا المساعد مجموعة من الخيارات لإضافة قيود على القيم المقبولة:
- الخيار
greater_than:
: يحدد أن القيمة يجب أن تكون أكبر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than %{count}". - الخيار
greater_than_or_equal_to:
: يحدد أن القيمة يجب أن تكون أكبر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be greater than or equal to %{count}". - الخيار
equal_to:
: يحدد أن القيمة يجب أن تكون تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be equal to %{count}" - الخيار
less_than:
: يحدد أن القيمة يجب أن تكون أصغر تمامًا من القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than %{count}". - الخيار
less_than_or_equal_to:
: يحدد أن القيمة يجب أن تكون أصغر أو تساوي القيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be less than or equal to %{count}". - الخيار
other_than:
: يحدد أن القيمة يجب أن تكون غير مساوية للقيمة المعطاة. رسالة الخطأ الافتراضية لهذا الخيار هي "must be other than %{count}" - الخيار
odd:
: يحدد أن القيمة يجب أن تكون عددًا فرديًّا. رسالة الخطأ الافتراضية لهذا الخيار هي "must be odd". - الخيار
even:
: يحدد أن القيمة يجب أن تكون عددًا زوجيًّا. رسالة الخطأ الافتراضية لهذا الخيار هي "must be even".
ملاحظة: افتراضيًا، لا يسمح المساعد numericality
بالقيم nil. يمكنك استخدام الخيار allow_nil:
للسماح بها.
الرسالة الافتراضية لهذا المساعد هي "is not a number".
المساعد presence
يتحقق هذا المساعد من أن عدم خلو خاصيات محدَّدة من أية قيمة. يستخدم هذا المساعد التابع ?blank
للتأكد من أن القيمة هي nil
، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.
class Person < ApplicationRecord
validates :name, :login, :email, presence: true
end
إذا أردت أن تكون متأكدًا من وجود ارتباط ما، يجب أن تتحقّق من وجود الكائن المرتبط بنفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط.
class LineItem < ApplicationRecord
belongs_to :order
validates :order, presence: true
end
من أجل تحقُّق السجلات المرتبطة الضروري وجودها، يجب تحديد الخيار inverse_of:
من أجل الارتباط:
class Order < ApplicationRecord
has_many :line_items, inverse_of: :order
end
في حال قمت بالتحقُّق من وجود كائن مرتبط عن طريق العلاقة has_one أو has_many، سيتفقّد من كون الكائن غير محقق للتابع ?blank
ولا التابع ?marked_for_destruction
.
لمَّا كان ?false.blank
يعيد القيمة true
، فإذا أردت التحقُّق من وجود حقل من النوع Boolean
، فيجب عليك القيام بأحد التحققين التاليين:
validates :boolean_field_name, inclusion: { in: [true, false] }
validates :boolean_field_name, exclusion: { in: [nil] }
باستخدام أحد هذين التحقُّقين، يمكنك التأكد من عدم استقبال الحقل المنطقي القيمة nil
.
المساعد absence
يؤكد هذا المساعد غياب وعدم وجود قيم خاصية محددة. يستخدم هذا المساعد التابع ?present
للتأكد من أن القيمة هي ليست nil
، أو سلسلة محرفية فارغة، التي هي سلسلة محرفية تحوي فراغًا واحدًا أو أكثر، أو لا تحوي محارف على الإطلاق.
class Person < ApplicationRecord
validates :name, :login, :email, absence: true
end
إذا أردت أن تكون متأكدًا من غياب ارتباط ما، يجب أن تتحقّق من غياب الكائن المرتبط نفسه، وليس المفتاح الأجنبي المستخدم للدلالة على الارتباط.
class LineItem < ApplicationRecord
belongs_to :order
validates :order, absence: true
end
من أجل تحقُّق السجلات المرتبطة الضروري غيابها، يجب تحديد الخيار inverse_of:
من أجل الارتباط:
class Order < ApplicationRecord
has_many :line_items, inverse_of: :order
end
في حال قمت بالتحقُّق من غياب كائن مرتبط عن طريق علاقة has_one أو has_many، سيتفقّد من أنَّ الكائن غير محقق للتابع ?present
ولا التابع ?marked_for_destruction
.
لما كان ?false.present
يعيد القيمة false
، فإذا أردت التحقُّق من غياب حقل من النوع المنطقي، فيجب عليك استعمال:
validates :field_name, exclusion: { in: [true, false] }
رسالة الخطأ الافتراضية لهذا المساعد هي "must be blank".
المساعد uniqueness
يتحقق هذا المساعد من أنَّ قيمة الحقل فريدة وغير مكررة قبل حفظ الكائن. لا ينشئ هذا المساعد قيد التميز في قاعدة البيانات، لذلك قد تحدث حالة إضافة سجلّين بقيمتين متساويتين في حقل واحد تريده أن يكون فريدًا، وذلك من قبل اتصالين منفصلين مع قاعدة البيانات. لتفادي هذا، يجب عليك إنشاء فهرس فريد لهذا الحقل في قاعدة البيانات.
class Account < ApplicationRecord
validates :email, uniqueness: true
end
يجري تنفيذ التحقُّق عن طريق تنفيذ استعلام SQL على جدول النموذج، بحثًا عن سجل موجود يملك ذات القيمة في الحقل المعطى.
يمكن استخدام الخيار scope:
لتحديد خاصية أو أكثر مستخدمة للتحقق uniqueness
:
class Holiday < ApplicationRecord
validates :name, uniqueness: { scope: :year,
message: "should happen once per year" }
end
إذا أردت إنشاء قيدٍ على قاعدة البيانات لتفادي تجاوز التحقُّقات uniqueness
المستخدمة للخيار scope:
، يجب أن تنشئ فهرسًا فريدًا في كلا الحقلين في قاعدة البيانات. اقرأ دليل MySQL للمزيد من المعلومات حول إنشاء فهرس لحقول متعددة أو دليل PostgreSQL لأمثلة حول قيود التميز المستخدمة على نطاق مجموعة من الحقول في آن معًا.
هناك أيضًا الخيار case_sensitive:
لتحديد فيما إذا كان القيد uniqueness
يراعي حالة الأحرف أم لا. القيمة الافتراضية لهذا الخيار هي true
.
class Person < ApplicationRecord
validates :name, uniqueness: { case_sensitive: false }
end
ملاحظة: بعض قواعد البيانات مهيّئة لتنفيذ عمليات البحث غير الحساسة لحالة الأحرف في كلتا الحالتين.
رسالة الخطأ الافتراضية لهذا المساعد هي "has already been taken".
المساعد validates_with
يمرر هذا المساعد السجل الواجب التحقق منه إلى صنف آخر منفصل للقيام بعملية التحقُّق.
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if record.first_name == "Evil"
record.errors[:base] << "This person is evil"
end
end
end
class Person < ApplicationRecord
validates_with GoodnessValidator
end
ملاحظة: الأخطاء المضافة إلى [record.errors[:base
تتعلق بحالة السجل ككل، وليس بحقل معين.
يقبل المساعد validates_with
صنفًا، أو مجموعة من الأصناف المستخدمة للتحقُّق. لا يوجد رسالة خطأ افتراضية لهذا المساعد. يمكنك تحديد رسائل الخطأ يدويًا عن طريق الصنف validator
.
لكتابة تابع التحقُّق، يجب أن تستقبل وسيطًا يمثل السجل المطلوب تحقُّقه.
كما هو الحال في كل عمليات التحقُّق، يقبل هذا المساعد الخيارات if:
و unless:
و on:
. إذا مرّرت خيارات إضافية، فسيتم تمريرها إلى الصنف validator
ضمن الوسيط options
:
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if options[:fields].any?{|field| record.send(field) == "Evil" }
record.errors[:base] << "This person is evil"
end
end
end
class Person < ApplicationRecord
validates_with GoodnessValidator, fields: [:first_name, :last_name]
end
من الجدير بالملاحظة أنّه يتم تهيئة الصنف validator
مرّة واحدة فقط ضمن دورة حياة التطبيق ككل، وليس في كل مرة يتم تنفيذ تحقُّق معيّن؛ لذلك كن حذرًا أثناء التعامل مع متغيرات النسخة داخله.
إذا كان صنف التحقُّق validator
الخاص بك معقدًا لدرجة حاجته لمتغيّرات نسخة، فيمكنك استخدام كائنات روبي البسيطة بدلًا من ذلك:
class Person < ApplicationRecord
validate do |person|
GoodnessValidator.new(person).validate
end
end
class GoodnessValidator
def initialize(person)
@person = person
end
def validate
if some_complex_condition_involving_ivars_and_private_methods?
@person.errors[:base] << "This person is evil"
end
end
# ...
end
المساعد validates_each
يتحقَّق هذا المساعد من مجموعة من الخاصيات مقابل كتلة من التعليمات البرمجية، إذ لا يملك تابع تحقُّق معرف مسبقًا. يجب عليك إنشاء هذا التابع باستخدام كتلة، وكل خاصية ممرّرة للمساعد validates_each
سيتم اختبارها في هذه الكتلة. في المثال التالي، لا نريد من الأسماء الأولى والكنيات أن تبدأ بأحرف صغيرة.
class Person < ApplicationRecord
validates_each :name, :surname do |record, attr, value|
record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/
end
end
تستقبل الكتلة السجل، واسم الخاصية وقيمتها. يمكنك القيام بأي عملية داخل الكتلة للتأكد من صحة البيانات المستقبلة. في حال فشل التحقُّق، يجب عليك إضافة رسالة خطأ إلى النموذج، موضّحًا سبب فشل عملية التحقق من صحّة البيانات.
خيارات عملية التحقق الشائعة
إليك مجموعة من خيارات التحقُّق الشائعة المستخدمة في Active Record:
الخيار allow_nil:
يتجاوز الخيار allow_nil:
التحقُّق عندما تكون القيمة الواجب التحقق منها هي nil
.
class Coffee < ApplicationRecord
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size" }, allow_nil: true
end
لقراءة كل الخيارات حول الوسيط message
، اطّلع على التوثيق الخاص به.
الخيار allow_blank:
يشابه هذا الخيار الخيار allow_nil:
، إذ يتجاوز التحقُّق في حال كانت قيمة الخاصية هي ?blank
، مثل القيمة nil
أو سلسلة نصية فارغة.
class Topic < ApplicationRecord
validates :title, length: { is: 5 }, allow_blank: true
end
Topic.create(title: "").valid? # => true
Topic.create(title: nil).valid? # => true
الخيار message:
كما رأيت مسبقًا، يمكّنك الخيار message:
من تحديد رسالة خطأ يراد إضافتها لمجموعة الأخطاء errors
عند فشل عملية التحقُّق. عند عدم تحديد هذا الخيار، يرسل Active Record رسالة الخطأ الافتراضية من أجل كل مساعد تحقُّق. يقبل الخيار message:
إما سلسلة نصية أو كائنًا من النوع Proc
.
يمكن للسلسلة النصية الممررة للخيار message:
أن تحوي كل من {value}%
أو {attribute}%
أو {model}%
التي يتم استبدالها دينياميكيًا عند فشل التحقُّق. يتم تنفيذ هذا الاستبدال باستخدام الجوهرة I18n.
أمَّا في حال استعمال الكائن Proc
مع الخيار message:
، يتم تمرير وسيطين هما: الكائن المراد التحقق منه، ومجموعة بأزواج المفاتيح والقيم التالية: model:
و attribute:
و value:
.
class Person < ApplicationRecord
# رسالة ثابتة
validates :name, presence: { message: "must be given please" }
# %{value} رسالة مع قيمة ديناميكية. يتم استبدال
# متاحتان أيضًا %{model} و %{attribute} بالقيمة الحقيقية للحقل. إن
validates :age, numericality: { message: "%{value} seems wrong" }
# Proc
validates :username,
uniqueness: {
# object = الكائن المراد التحقق منه
# data = { model: "Person", attribute: "Username", value: <username> }
message: ->(object, data) do
"Hey #{object.name}!, #{data[:value]} is taken already! Try again #{Time.zone.tomorrow}"
end
}
end
الخيار on:
يمكّنك هذا الخيار من تحديد مكان التحقُّق. السلوك الافتراضي لجميع مساعدات التحقُّق المضمنة هو تشغيل التحقُّق عند الحفظ (أي عند إنشاء أو تحديث السجلات). إذا أردت تغيير هذا السلوك، فيمكنك تحديد الخيار on:
على create:
لتشغيل التحقُّق فقط عند إنشاء كائن جديد، أو على update:
لتشغيل التحقُّق فقط عند تعديل كائن موجود مسبقًا.
class Person < ApplicationRecord
# سيكون بالإمكان تحديث عنوان البريد الإلكتروني مع قيمة مكررة
validates :email, uniqueness: true, on: :create
# سيكون بالإمكان إنشاء سجل مع قيمة غير عددية للعمر
validates :age, numericality: true, on: :update
# القيمة الافتراضية هي التحقق عند الإنشاء والتحديث
validates :name, presence: true
end
يمكنك أيضًا استخدام الخيار on:
لتعريف سياق مخصّص. يجب تشغيل السياق المخصص بشكل ظاهري عن طريق تمرير اسم السياق إلى التوابع ?valid
أو ?invalid
أو save
.
class Person < ApplicationRecord
validates :email, uniqueness: true, on: :account_setup
validates :age, numericality: true, on: :account_setup
end
person = Person.new
يتم تنفيذ (person.valid?(:account_setup
عند التحقُّقين دون حفظ السجل. ويقوم التابع (person.sav(context: :account_setup
بالتحقُّق من الكائن person
في السياق account_setup
قبل الحفظ. عند التشغيل بشكل ظاهري، يتم التحقُّق من السجلات عن طريق عمليات التحقق المحددة فقط لسجلات هذا السياق أو التي لا تملك سياقًا مطلقًا.
التحقُّقات الصارمة
يمكنك تحديد فيما إذا كان تحقُّق ما صارم (strict) أم لا ورفع الاستثناء ActiveModel::StrictValidationFailed
عند فشل عملية التحقق.
class Person < ApplicationRecord
validates :name, presence: { strict: true }
end
Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
يمكنك أيضًا تمرير استثناءات مخصّصة للخيار strict:
.
class Person < ApplicationRecord
validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
Person.new.valid? # => TokenGenerationException: Token can't be blank
التحققات الشرطية
في بعض الأحيان، قد يكون من المنطقي التحقُّق من سجل ما فقط عند تحقق شرط معيّن. يمكنك إنجاز ذلك عن طريق الخيارين if:
و unless:
، اللذَين يستقبلان رمزًا، أو كائنًا من النوع Proc
، أو مصفوفة. يمكنك استخدام الخيار if:
لتحديد متى يجب تنفيذ عملية التحقُّق. وفي حال أردت تحديد متى لا يجب تنفيذ التحقُّق، يمكنك استخدام الخيار unless:
.
استخدام رمز مع الخيارين if:
و unless:
يمكنك ربط الخيارين if:
و unless:
مع رمز موافق لاسم التابع المستدعى قبل عملية التحقُّق. هذا الخيار هو الخيار الأكثر استخدامًا.
class Order < ApplicationRecord
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
end
استخدام الكائن Proc
مع الخيارين if:
و unless:
أخيرًا، يمكنك ربط الخيارين if:
و unless:
مع كائن من النوع Proc
يتم مناداته. باستخدام الكائن Proc
، يمكنك التحقُّق شرطيًا ضمن سطر من التعليمات بدلًأ من تابع منفصل. هذا الخيار هو مفضّل لمحبّي الأسطر الواحدة.
class Account < ApplicationRecord
validates :password, confirmation: true,
unless: Proc.new { |a| a.password.blank? }
end
تجميع التحقُّقات الشرطية
قد يكون من المفيد أحيانًا أن تستخدم مجموعة من التحقُّقات شرطًا واحدًا. يمكن تنفيذ ذلك عن باستخدام with_options
.
class User < ApplicationRecord
with_options if: :is_admin? do |admin|
admin.validates :password, length: { minimum: 10 }
admin.validates :email, presence: true
end
end
جميع التحقُّقات الواقعة داخل الكتلة التي تخص with_options
سيمرّر لها الشرط if: :is_admin?
تلقائيًّا.
دمج شروط التحقُّق
وفي الطرف الآخر، عندما تحدد مجموعة من الشروط متى يجب تنفيذ تحقُّق معيّن، يمكن استخدام مصفوفة. أي يمكنك استخدام الخيارين if:
و unless:
على التحقُّق نفسه.
class Computer < ApplicationRecord
validates :mouse, presence: true,
if: [Proc.new { |c| c.market.retail? }, :desktop?],
unless: Proc.new { |c| c.trackpad.present? }
end
يتم تنفيذ هذا التحقُّق عندما تتحقق جميع الشروط في if:
، ولا تتحقق أي من الشروط في unless:
.
تنفيذ تحققات مخصصة
عند عدم كفاية مساعدات التحقُّق المضمنة، يمكنك كتابة تحقُّقات أو توابع تحقُّق خاصة بك بناءً على حاجتك.
عمليات التحقق المخصصة
عمليات التحقق المخصصة هي أصناف ترث من الصنف الأب ActiveModel::Validator
. يجب أن تعرّف هذه الأصناف التابع validate
الذي يقبل تمرير السجل المطلوب التحقُّق منه وسيطًا له ثم يجري عملية التحقق عليه. تستدعى علمية التحقق المخصصة باستخدام التابع validates_with
.
class MyValidator < ActiveModel::Validator
def validate(record)
unless record.name.starts_with? 'X'
record.errors[:name] << 'Need a name starting with X please!'
end
end
end
class Person
include ActiveModel::Validations
validates_with MyValidator
end
أسهل طريقة لإضافة تحقُّق مخصص للتحقُّق من خاصيات فردية هو عن طريق الصنف المريح ActiveModel::EachValidator
. في هذه الحالة، يجب أن يعرِّف صنف التحقُّق التابع validate_each
الذي يقبل ثلاث وسائط هي: سجل، وخاصية، وقيمة. تكون هذه الوسائط مرتبطة بالكائن، والخاصية المراد التحقُّق منها، وقيمة الخاصية المُمرَّرة للكائن.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
class Person < ApplicationRecord
validates :email, presence: true, email: true
end
كما هو ظاهر في المثال، يمكنك أيضًا دمج التحقُّقات القياسية المُعرَّفة مسبقًا مع تحقُّقات مخصصة.
التوابع المخصصة
يمكنك أيضًا تعريف توابع تتحقق من حالة السجلات وتضيف رسائل خطأ إلى المجموعة errors
عند فشل عملية التحقق. يجب عليك بعد ذلك تسجيل هذه التوابع باستخدام تابع الصنف validate
، ممرّرًا رموزًا لأسماء هذه التوابع.
يمكنك تمرير أكثر من رمز لكل تابع صنف، مسبوقًا بالتحقُّق المستخدم بنفس ترتيب تسجيله.
يتحقق التابع ?valid
من خلو مجموعة الأخطاء (errors collection) من الأخطاء، لتتمكّن توابع التحقُّق المخصصة من إضافة الأخطاء إلى المجموعة عند فشل التحقُّق:
class Invoice < ApplicationRecord
validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value
def expiration_date_cannot_be_in_the_past
if expiration_date.present? && expiration_date < Date.today
errors.add(:expiration_date, "can't be in the past")
end
end
def discount_cannot_be_greater_than_total_value
if discount > total_value
errors.add(:discount, "can't be greater than total value")
end
end
end
افتراضيًا، يتم تشغيل التحقُّقات المشابهة عند كل استدعاء للتابع ?valid
أو عند حفظ الكائن. لكن من الممكن أيضًا التحكم بوقت تنفيذ هذه التحقُّقات المخصصة عن طريق تحديد الخيار on:
مع التابع validate
، مستخدمًا إما create:
أو update:
.
class Invoice < ApplicationRecord
validate :active_customer, on: :create
def active_customer
errors.add(:customer_id, "is not active") unless customer.active?
end
end
العمل مع أخطاء التحققات
إضافةً إلى التابعين ?valid
و ?invalid
الموضّحين أعلاه، يزوّد ريلز بمجموعة من التوابع المستخدمة للعمل مع مجموعة الأخطاء errors
والحصول على معلومات حول صحة الكائنات.
إليك قائمة بأكثر التوابع استخدامًا. الرجاء الاطلاع على توثيق ActiveModel::Errors
لقائمة بالتوابع المتاحة.
Errors
يعيد كائنًا من النوع ActiveModel::Errors
يحوي جميع الأخطاء. كل مفتاح هو اسم الخاصية، وكل قيمة هي مصفوفة برسائل الخطأ المرتبطة بالخاصية.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
person = Person.new
person.valid? # => false
person.errors.messages
# => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]}
person = Person.new(name: "John Doe")
person.valid? # => true
person.errors.messages # => {}
[]errors
يُستخدَم []errors
عندما تريد التحقق من رسائل الخطأ لخاصية محددة. يعيد هذا التابع مصفوفة من رسائل الخطأ الموافقة للخاصية المعطاة. في حال لم يكن هنالك أخطاء للحقل المعطى، فيعيد التابع مصفوفةً فارغةً.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
person = Person.new(name: "John Doe")
person.valid? # => true
person.errors[:name] # => []
person = Person.new(name: "JD")
person.valid? # => false
person.errors[:name] # => ["is too short (minimum is 3 characters)"]
person = Person.new
person.valid? # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]
errors.add
يمكّنك التابع add
من إضافة رسائل الخطأ إلى خاصية محددة. يستقبل هذا التابع في وسائطه اسم الخاصية الواجب إضافة رسالة الخطأ عليها، ورسالة الخطأ المطلوبة.
يعيد التابع errors.full_messages
(أو مثيله errors.to_a
) رسائل الخطأ بنمط سهل القراءة للمستخدم، مع اسم الخاصية السابق لرسالة الخطأ، كما هو ظاهر في المثال التالي.
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.add(:name, "cannot contain the characters !@#%*()_-+=")
end
end
person = Person.create(name: "!@#")
person.errors[:name]
# => ["cannot contain the characters !@#%*()_-+="]
person.errors.full_messages
# => ["Name cannot contain the characters !@#%*()_-+="]
مثيل عن التابع errors.add
هو استخدام >>
لإضافة رسالة إلى المصفوفة errors.messages
لخاصية:
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.messages[:name] << "cannot contain the characters !@#%*()_-+="
end
end
person = Person.create(name: "!@#")
person.errors[:name]
# => ["cannot contain the characters !@#%*()_-+="]
person.errors.to_a
# => ["Name cannot contain the characters !@#%*()_-+="]
errors.details
يمكنك تحديد نوع التحقُّق لكائن معلومات الخطأ المعاد عن طريق التابع errors.add
.
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.add(:name, :invalid_characters)
end
end
person = Person.create(name: "!@#")
person.errors.details[:name]
# => [{error: :invalid_characters}]
لتحسين كائن معلومات الخطأ بحيث يحوي المحارف غير المسموحة على سبيل المثال، يمكنك تمرير مفاتيح إضافية للتابع errors.add
.
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=")
end
end
person = Person.create(name: "!@#")
person.errors.details[:name]
# => [{error: :invalid_characters, not_allowed: "!@#%*()_-+="}]
جميع التحقُّقات المدمجة في ريلز تملأ كائنات معلومات الخطأ بمجموعات متوافقة مع نوع التحقُّق المستخدم.
[errors[:base
يمكنك إضافة رسائل خطأ متعلقة بحالة الكائن ككل، بدلًا من أن تكون متعلقة بخاصية وحيدة. يمكنك استخدام هذا التابع عند الحاجة للقول أن الكائن ككل هو غير صحيح، بغض النظر عن قيم خاصياته. بما أن [errors[:base
هي مصفوفة، يمكنك إضافة سلسلة نصية لها وسيتم استخدامها كرسالة خطأ.
class Person < ApplicationRecord
def a_method_used_for_validation_purposes
errors[:base] << "This person is invalid because ..."
end
end
errors.clear
يُستخدَم التابع clear
عندما تود بإرادتك أن تزيل جميع رسائل الخطأ من المجموعة errors
. بالطبع، استدعاء التابع clear
مع كائن غير صحيح لن يجعله صحيحًا: أي ستكون المجموعة errors
فارغة، لكن عند استدعاء التابع ?valid
في المرة القادمة أو أي تابع يحاول حفظ الكائن في قاعدة البيانات، فسيتم تنفيذ التحقُّق من جديد. وفي حال فشله، ستمتلئ المجموعة errors
من جديد.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
person = Person.new
person.valid? # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]
person.errors.clear
person.errors.empty? # => true
person.save # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]
errors.size
يعيد التابع size
عدد رسائل الخطأ للكائن.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
person = Person.new
person.valid? # => false
person.errors.size # => 2
person = Person.new(name: "Andrea", email: "andrea@example.com")
person.valid? # => true
person.errors.size # => 0
عرض أخطاء التحقق في الواجهات
متى ما أنشأت نموذجًا وأضفت تحقُّقات له، ففي حال تم إنشاء هذا النموذج (model) عن طريق نموذج ويب (web form)، فقد يكون من الضروري إظهار رسالة الخطأ عند فشل التحقُّق.
لأن كل تطبيق يراعي هذه الحالة بطريقة مختلفة، لا يزود ريلز بأي مساعد واجهة للمساعدة بتوليد هذه الرسائل مباشرةً. لكن نظرًا للعدد الكبير من التوابع المزودة من ريلز للتفاعل مع التحقُّقات بشكل عام، من السهل بناء هذه التوابع يدويًّا. علاوةً على ذلك، عند توليد scaffold (هو جزء من النمط البرمجي MVC حيثُ يُمكنك إختيار جزء مُعين من قاعدة البيانات للعمل عليه)، يقوم ريلز بوضع تعليمات ERB في الملف form.html.erb_ المولّد، والذي يظهر القائمة الكاملة بأخطاء هذا النموذج.
بفرض أنه لدينا نموذجًا تم حفظه في متغير نسخة تسمى article@
، يبدو الأمر كذلك:
<% if @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
من ناحية أخرى، في حال كنت تستخدم مساعدات ريلز لإنشاء نماذج الويب، عند فشل تحقُّق على حقل معين، ستولّد هذه المساعدات عنصر <div>
إضافي حول الحقل.
<div class="field_with_errors">
<input id="article_title" name="article[title]" size="30" type="text" value="">
</div>
يمكنك بناءً على ذلك تعديل شكل هذا العنصر كما تشاء. الشكل الافتراضي المضاف من vdg. مثلًا، يقوم بإنشاء قواعد CSS التالية:
.field_with_errors {
padding: 2px;
background-color: red;
display: table;
}
ممّا يعني أن أي حقل يملك أخطاءً سيتم تلوين حدوده باللون الأحمر بسماكة 2 بكسل.