تعرفنا في الدرس السابق على الارتباطات وأنواعها وسنتابع في هذا الدرس أهم النصائح والخدع والتحذيرات.
3 نصائح، خدع وتحذيرات
يجب عليك معرفة هذه الأشياء لتستخدم ارتباطات Active Record بشكل أفضل وأكفأ في تطبيقات Rails:
- التحكم في التخزين المؤقت.
- تجنب تضارب الأسماء.
- تحديث المخطط.
- التحكم في نطاق (scope) الارتباط.
- الارتباطات ثنائية الاتجاه.
3.1 التحكم في التخزين المؤقت
تبنى جميع الارتباطات حول التخزين المؤقت، والذي يحافظ على نتيجة الاستعلام الأخيرة متاحة للعمليات القادمة، يتم تقاسم ذاكرة التخزين المؤقت عبر الأساليب، على سبيل المثال:
author.books # retrieves books from the database author.books.size # uses the cached copy of books author.books.empty? # uses the cached copy of books
لكن ماذا لو أردنا إعادة تحميل التخزين المؤقت، لأن البيانات قد تتغير عن طريق أجزاء اخرى من التطبيق؟ فقط أدعو إعادة التحميل في الارتباط:
author.books # retrieves books from the database
author.books.size # uses the cached copy of books
author.books.reload.empty? # discards the cached copy of books
# and goes back to the database
3.2 تجنب تضارب الأسماء
أنت لا تملك حرية كاملة في اختيار الاسم الذي تريده للارتباطات، لإن إنشاء الارتباط سيضيف أسلوب بهذا الاسم إلى النموذج، ومن السيئ إعطاء اسم ارتباط مستخدم بالفعل لمثيل أسلوب ActiveRecord::Base
، فأسلوب الارتباط سيتجاوز أسلوب الأساس وسيكسر الأشياء، من الأسماء السيئة: attributes و connection.
3.3 تحديث المخطط
الارتباطات مفيدة للغاية لكنها ليست سحرية، فأنت مسؤول عن المحافظة على مخطط قاعدة البيانات ليطابق الارتباطات، وهذا يعني شيئين في الممارسة العملية حسب نوع الارتباطات التي تصنعها، فارتباطات belongs_to
تحتاج إلى إنشاء مفاتيح خارجية، ولارتباطات has_and_belongs_to_many
ستحتاج إلى إنشاء جدول الضم المناسب.
3.3.1 إنشاء مفاتيح خارجية لارتباطات belongs_to
عندما تعلن عن ارتباط belongs_to
، ستحتاج إلى إنشاء مفاتيح خارجية حسب الحاجة، فعلى سبيل المثال، فكر في هذا النموذج:
class Book < ApplicationRecord
belongs_to :author
end
يحتاج هذا الإعلان إلى أن يُدعم عن طريق مفتاح خارجي مناسب مُعلن في جدول books:
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
t.datetime :published_at
t.string :book_number
t.integer :author_id
end
end
end
إذا أنشئت ارتباط بعد وقت من إنشاء النموذج الأساسي، ستحتاج إلى تذكر إنشاء تهجير add_column
لتوفير مفتاح الخارجي الضروري.
من الممارسات الجيدة إضافة فهرس (index
) إلى المفتاح الخارجي لتحسين أداء الطلبات وإضافة قيد على المفتاح الخارجي لضمان سلامة البيانات المرجعية:
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
t.datetime :published_at
t.string :book_number
t.integer :author_id
end
add_index :books, :author_id
add_foreign_key :books, :authors
end
end
3.3.2 إنشاء جداول الضم لارتباطات has_and_belongs_to_many
إذا أنشئت ارتباط has_and_belongs_to_many
، فستحتاج إلى إنشاء جدول الضم (joining table)، إذا لم يحدد اسم جدول الضم بشكل صريح عن طريق خيار join_table:
، فسينشئ Active Record الاسم عن طريق استخدام الكتاب المعجمي لأسماء الصنف، فالضم بين نماذج المؤلف والكتاب سيُكوّن اسم جدول الضم بشكل افتراضي هو “authors_books
”، لأن A تأتي قبل B في الترتيب المعجمي.
Quoteتنبيه:
تُحسب الأسبقية بين أسماء النماذج باستخدام العامل<=>
للسلاسل النصية، وهذا يعني إذا كانت السلاسل النصية ذات أطوال مختلفة، وكانت كلا السلسلتين متساويتين مقارنة بالسلسلة الأقصر، فإن السلسلة النصية الأطول ذا أسبقية أعلى من السلسلة الأقصر، فعلى سبيل المثال، قد تملك جدول “paper_boxes
” وجدول “papers
” لإنشاء اسم جدول الضم “papers_paper_boxes
” بسبب طول اسم “paper_boxes
”، لكن في الحقيقة سيكون اسم جدول الضم هو “paper_boxes_papers
” (لأن ‘_’ هي أقل من ‘s’ في أغلب الترميزات الشائعة).
مهما كان الاسم، يجب عليك إنشاء جدول الضم يدويا مع التهجير المناسب، فعلى سبيل المثال:
class Assembly < ApplicationRecord
has_and_belongs_to_many :parts
end
class Part < ApplicationRecord
has_and_belongs_to_many :assemblies
end
يجب أن تدعم هذه بالتهجير لإنشاء جدول assemblies_parts
، والذي يجب أن يُنشئ دون مفتاح رئيسي:
class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0]
def change
create_table :assemblies_parts, id: false do |t|
t.integer :assembly_id
t.integer :part_id
end
add_index :assemblies_parts, :assembly_id
add_index :assemblies_parts, :part_id
end
end
مررنا id: false
إلى create_table
لأن الجدول لا يُمثل نموذج، وهذا ضروري للارتباط ليعمل بشكل صحيح.
قد تلاحظ تصرفات غريبة في ارتباط has_and_belongs_to_many
مثل مُعرّفات نموذج سيئة أو استثناءات حول تضارب المعرّفات، لكنها نادرا ما تحدث.
يمكنك استخدام أسلوب create_join_table
أيضًا.
class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0]
def change
create_join_table :assemblies, :parts do |t|
t.index :assembly_id
t.index :part_id
end
end
end
3.4 التحكم في نطاق scope الارتباط
تبحث الارتباطات افتراضيا عن الكائنات ضمن نطاق الوحدة (module) الحالية، وهذا جيد عندما تعلن عن نماذج Active Record ضمن الوحدة، فعلى سبيل المثال:
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account
end
class Account < ApplicationRecord
belongs_to :supplier
end
end
end
وهذا سيعمل بشكل صحيح، لأن كل من صنف المُورّد والحساب تم تعريفهما داخل نفس النطاق، لكن الشيفرة البرمجية التالية لن تعمل، لأنه عُرّف المُورّد والحساب في نطاقات مختلفة:
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier
end
end
end
لربط نموذج مع نموذج في مساحة اسم (namespace
) مختلفة يجب عليك تحديد اسم الصنف كاملا في إعلان الارتباط:
module MyApplication
module Business
class Supplier < ApplicationRecord
has_one :account,
class_name: "MyApplication::Billing::Account"
end
end
module Billing
class Account < ApplicationRecord
belongs_to :supplier,
class_name: "MyApplication::Business::Supplier"
end
end
end
3.5 الارتباطات ثنائية الاتجاه
من الطبيعي أن تعمل الارتباطات في كلا الاتجاهين، وهذا الأمر يتطلب إعلان في نموذجين مختلفين:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
end
سيحاول Active Record تلقائيا معرفة أن هذيّن النموذجين يشتركان في ارتباط ثنائي الاتجاه بناءا على اسم الارتباط، وبهذه الطريقة سيحمّل Active Record نسخة واحدة من كائن Author وبذلك سيصبح تطبيقك أكثر كفاءة ويمنع البيانات غير المتناسقة:
a = Author.first
b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = 'David'
a.first_name == b.author.first_name # => true
يدعم Active Record التعرف التلقائي لأغلب الارتباطات مع الأسماء القياسيّة، ومع ذلك لن يُعرّف Active Record الارتباطات ثنائية الاتجاه تلقائيا إذا احتوت على أي من الخيارات التالية:
-
conditions:
-
through:
-
polymorphic:
-
class_name:
-
foreign_key
على سبيل المثال، فكر في إعلان النماذج التالية:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end
لن يتعرف Active Record تلقائيا على الارتباط ثنائي الاتجاه:
a = Author.first
b = a.books.first
a.first_name == b.writer.first_name # => true
a.first_name = 'David'
a.first_name == b.writer.first_name # => false
يوفر Active Record خيار :inverse_of
حتى تتمكن من الإعلان الارتباطات ثنائية الاتجاه بشكل صريح:
class Author < ApplicationRecord
has_many :books, inverse_of: 'writer'
end
class Book < ApplicationRecord
belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end
من خلال تضمين خيار :inverse_of
في إعلان ارتباط has_many
، سيتعرّف Active Record على ارتباط ثنائي الاتجاه:
a = Author.first
b = a.books.first
a.first_name == b.writer.first_name # => true
a.first_name = 'David'
a.first_name == b.writer.first_name # => true
توجد بعض القيود على دعم inverse_of:
:
-
لا تعمل مع ارتباطات
through:
-
لا تعمل مع ارتباطات
polymorphic:
-
لا تعمل مع ارتباطات
as:
وسنتابع في الدرس القادم التعرّف على مرجع الارتباط المفصّل.
المصدر:
توثيقات Ruby on Rails
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.