ruby on rails 105 Active Record Associations: مرجع الارتباط المفصّل


هشام رزق الله

تعرفنا في الدرس السابق على أهم الخدع، النصائح والتحذيرات وسنتابع في هذا الدرس مرجع الارتباط المفصّل.

4 مرجع الارتباط المفصّل

ستجد في الأقسام التالية تفاصيل حول كل نوع من أنواع الارتباط، بما في ذلك الأساليب التي تضيفها والخيارات التي يمكنك استخدامها عند إعلان الارتباط.

4.1 مرجع ارتباط belongs_to

ينشئ ارتباط belongs_to تطابق واحد لواحد (one-to-one) مع نموذج آخر، بمصطلحات قاعد البيانات، يعني هذا الارتباط أن هذا الصنف يحتوي على مفتاح خارجي، إذا كان الصنف الآخر يحتوي على مفتاح خارجي، فيجب عليك في هذه الحالة استخدام has_one بدلًا من ذلك.

4.1.1 أساليب مضافة بواسطة belongs_to

عند إعلان ارتباط belongs_to، فسيحصل الصنف المعلن على 5 أساليب مرتبطة بالارتباط:

  • association
  • association=(associate)
  • ({} = build_association(attributes
  • ({} = create_association(attributes
  • ({} = create_association!(attributes
  • reload_association

في جميع هذه النماذج، سيُستبدل الارتباط مع symbol الذي مُرر كمعامل أول إلى belongs_to، فعلى سبيل المثال، هذا الإعلان:

class Book < ApplicationRecord
  belongs_to :author
end

سيحصل كل مثيل نموذج Book على هذه الأساليب:

author
author=
build_author
create_author
create_author!
reload_author
اقتباس

ملاحظة:
يجب عليك استخدم بادئة _build عند تهيئة ارتباط has_one أو belongs_to لبناء الارتباط، بدلًا من أسلوب association.build الذي يستخدم لارتباطات has_many أو has_and_belongs_to_many، ولإنشاء واحد، استخدم بادئة _create.

4.1.1.1 association

يرجع أسلوب association الكائن المقترن إذا وجد، وإلا فسيُرجع nil.

@author = @book.author

إذا أُسترد الكائن من قاعدة البيانات لهذا الكائن، ستُرجع النسخة المخبئة، ولتجاوز هذا السلوك (وإجبار القراءة من قاعدة البيانات)، استدعي reload_association# في كائن الأب.

@author = @book.reload_author

4.1.1.2 (association=(associate

يعيّن أسلوب association= كائن مرتبط لهذا الكائن، في ما وراء الكواليس، يعني هذا استخراج المفتاح الأساسي من الكائن المرتبط وتعيين قيمة المفتاح الخارجي للكائن لنفس قيمته.

@book.author = @author

4.1.1.3 ({} = build_association(attributes 

يُرجع أسلوب build_association كائن جديد لنوع المرتبط، هذا الكائن سيُنشئ من السمات المُمرّرة وسيُعين الارتباط من خلال المفتاح الخارجي لكن لن بعد يحفظ بعد الكائن المرتبط.

@author = @book.build_author(author_number: 123,
                                  author_name: "John Doe")

4.1.1.4 ({} = create_association(attributes

سيرجع أسلوب create_association كائن جديد لنوع المرتبط، هذا الكائن سيُنشئ من السمات المُمررة وسيُعين الارتباط من خلال المفتاح الخارجي، وبمجرد تمرير جميع عمليات التحقيق validations المحددة على النموذج المرتبط، سيُحفظ الكائن المرتبط.

@author = @book.create_author(author_number: 123,
                                   author_name: "John Doe")

4.1.1.5 ({} = create_association!(attributes

يعمل كما create_association في الأعلى، لكنه يُصدر ActiveRecord::RecordInvalid إذا كان السجل record غير صالح.

4.1.2 خيارات لـ belongs_to

في حين يستخدم Rails افتراضات ذكية ستعمل بشكل صحيح في أغلب الأحيان، ستحتاج في بعض الأحيان إلى تخصيص مرجع سلوك ارتباط belongs_to، ويمكن تحقيق هذه التخصيصات بسهولة عن طريق تمرير الخيارات وكتل النطاق (scope) عند إنشاء الارتباط، فعلى سبيل المثال، هذا الارتباط يستخدم هذين الخيارين:

class Book < ApplicationRecord
  belongs_to :author, dependent: :destroy,
    counter_cache: true
end

يدعم ارتباط belongs_to هذه الخيارات:

  • autosave:
  • class_name:
  • counter_cache:
  • dependent:
  • foreign_key:
  • primary_key:
  • inverse_of:
  • polymorphic:
  • touch:
  • validate:
  • optional:

4.1.2.1 autosave:

إذا عيّنت خيار autosave: إلى true، فسيحفظ Rails جميع أعضاء المحمّلين وسيدمر الأعضاء الذين وضع عليهم علامة التدمير كلما حفظت كائن الأب.

4.1.2.2 class_name:

إذا لم يكن بالإمكان اشتقاق اسم النموذج الآخر من اسم الارتباط، يمكنك استخدام خيار class_name: لتوفير اسم النموذج، فعلى سبيل المثال، إذا كان الكتاب ينتمي إلى المؤلف، لكن اسم فعلي للنموذج الذي يحتوي على المؤلفين هو Patron:

class Book < ApplicationRecord
  belongs_to :author, class_name: "Patron"
end

4.1.2.3 counter_cache:

يمكنك استخدام خيار counter_cache: لتجعل عملية العثور على عدد الكائنات التابعة أكثر كفاءة، على سبيل المثال هذه النماذج:

class Book < ApplicationRecord
  belongs_to :author
end
class Author < ApplicationRecord
  has_many :books
end

مع هذه الإعلانات، يتطلب طلب قيمة author.books.size@ الاتصال بقاعدة البيانات لتنفيذ استعلام (*)COUNT، ولنتجنب هذا الاتصال، يمكنك إضافة ذاكرة مؤقتة لتخزين العدد إلى نموذج الانتماء:

class Book < ApplicationRecord
  belongs_to :author, counter_cache: true
end
class Author < ApplicationRecord
  has_many :books
end

مع هذا الإعلان، سيُبقي Rails قيمة الذاكرة المؤقتة مُحدّثة، وسيجيب بتلك القيمة عند الطلب من أسلوب size.
على الرغم من تحديد خيار counter_cache: في النموذج الذي يتضمن إعلان belongs_to، يجب إضافة العمود الحالي إلى النموذج المرتبط (has_many)، في الحالة أعلاه، ستحتاج إلى إضافة عمود باسم books_count إلى نموذج Author.
يمكنك تجاوز اسم الافتراضي للعمود من خلال تحديد اسم العمود المخصص في إعلان counter_cache بدلا من true، فعلى سبيل المثال، لاستخدام count_of_books بدلا من books_count:

class Book < ApplicationRecord
  belongs_to :author, counter_cache: :count_of_books
end
class Author < ApplicationRecord
  has_many :books
end
اقتباس

ملاحظة:
تحتاج إلى تحديد خيار counter_cache: في جانب belongs_to من الارتباط.
تضاف أعمدة ذاكرة التخزين المؤقت إلى قائمة النموذج التي تحتوي على سمات القراءة فقط من خلال attr_readonly.

4.1.2.4 :dependent

إذا قمت بتعيين خيار dependent: إلى:

  • destroy:، عند تدمير الكائن، ستُستدعى destroy على الكائنات المرتبطة.
  • delete:، عند تدمير الكائن، ستُحذف جميع الكائنات المرتبطة بها مباشرة من قاعدة البيانات دون استدعاء أسلوب destroy.
اقتباس

تنبيه:
يجب عليك عدم تحديد هذا الخيار على ارتباط belongs_to المتصل مع ارتباط has_many على الصنف الآخر، لأنه قد يؤدي هذا إلى سجلات يتيمة في قاعدة البيانات.

4.1.2.5 foreign_key:

بالاتفاق، يفترض Rails أن العمود المستخدم لحمل المفتاح الخارجي في هذا النموذج هو اسم الارتباط مع إضافة بادئة id_، خيار foreign_key: يُتيح لك تعيين اسم المفتاح الخارجي مباشرة:

class Book < ApplicationRecord
  belongs_to :author, class_name: "Patron",
                        foreign_key: "patron_id"
end
اقتباس

ملاحظة:
في أي حال، لن ينشئ Rails عمود المفتاح الخارجي لك، ستحتاج إلى تعريفهم بشكل صريح كجزء من عمليات التهجير.

4.1.2.6 primary_key:

بالاتفاق، يفترض Rails أن عمود id يُستخدم لاحتواء المفتاح الرئيسي للجداول، ويسمح لك خيار primary_key: بتحديد عمود مختلف.
على سبيل المثال، إذا كان لدينا جدول users مع guid كمفتاح رئيسي، أردنا جدول todos منفصل لاحتواء المفتاح الخارجي user_id في عمود guid، ثم يمكننا استخدام primary_key لتحقيق ما يشابه هذا:

class User < ApplicationRecord
  self.primary_key = 'guid' # primary key is guid and not id
end
 
class Todo < ApplicationRecord
  belongs_to :user, primary_key: 'guid'
end

عند تنفيذ user.todos.create@ فستكون قيمة user_id في سجل todo@ كقيمة guid في user@.

4.1.2.7 inverse_of:

يحدد خيار inverse_of: اسم عكس هذا الارتباط وهي has_many أو has_one، وهذه لا تعمل في التركيبة مع خيارات polymorphic:

class Author < ApplicationRecord
  has_many :books, inverse_of: :author
end
 
class Book < ApplicationRecord
  belongs_to :author, inverse_of: :books
end

4.1.2.8 polymorphic:

يشير تمرير true إلى خيار polymorphic: إلى أن الارتباط متعدد الأشكال، وسنتحدث عن الارتباطات متعدد الأشكال لاحقا في هذا الدليل.

4.1.2.9 touch:

إذا عيّنت خيار touch: إلى true، فإن timestamp لـ updated_at أو updated_on في كائن المرتبط سيُعيّن إلى الوقت الحالي كلما حُفظ الكائن أو دُمر:

class Book < ApplicationRecord
  belongs_to :author, touch: true
end
 
class Author < ApplicationRecord
  has_many :books
end

في هذه الحالة، حفظ أو تدمير كتاب سيٌحدّث timestamp في المؤلف المرتبط به، يمكنك أيضًا تحديد سمة timestamp معينة للتحديث:

class Book < ApplicationRecord
  belongs_to :author, touch: :books_updated_at
end

4.1.2.10 validate:

إذا عيّنت خيار validate: إلى true، فسيتحقق (Validate) الكائنات المرتبطة كلما حفظت هذا الكائن. وهذا الخيار يساوي false بشكل افتراضي، ولن يتحقق الكائنات المرتبطة عند حفظ الكائن.

4.1.2.11 optional:

إذا عيّنت خيار optional: إلى true، فلن يتحقق من وجود الكائن المرتبط، وهذا الخيار false بشكل افتراضي.

4.1.3 نطاقات Scopes لـ belongs_to

في بعض الأحيان قد تحتاج إلى تخصيص الاستعلام المُستخدم من قبل belongs_to، ويمكن تحقيق هذه التخصيصات عن طريق كتلة scope، فعلى سبيل المثال:

class Book < ApplicationRecord
  belongs_to :author, -> { where active: true },
                        dependent: :destroy
end

يمكنك استخدام أي من أساليب الاستعلامات القياسية داخل كتلة scope، وستجد تفاصيل التالية في الأسفل:

  • where
  • includes
  • readonly
  • select

4.1.3.1 where

يسمح لك أسلوب where بتحديد شروط كائن المرتبط.

class book < ApplicationRecord
  belongs_to :author, -> { where active: true }
end

4.1.3.2 includes

يمكنك استخدام أسلوب includes لتحديد الارتباطات من الدرجة الثانية التي تريد تحميلها (eager-loaded) عند استخدام هذا الارتباط، فعلى سبيل المثال، فكر في هذه النماذج:

class LineItem < ApplicationRecord
  belongs_to :book
end
 
class Book < ApplicationRecord
  belongs_to :author
  has_many :line_items
end
 
class Author < ApplicationRecord
  has_many :books
end

إذا كنت تسترد المؤلفين مباشرة من سطر العناصر بشكل كثير (line_item.book.author@)، فيمكنك جعل شيفرتك البرمجية أكثر كفاءة من خلال تضمين المؤلفين إلى الارتباط من سطر العناصر إلى الكتب:

class LineItem < ApplicationRecord
  belongs_to :book, -> { includes :author }
end
 
class Book < ApplicationRecord
  belongs_to :author
  has_many :line_items
end
 
class Author < ApplicationRecord
  has_many :books
end
اقتباس

ملاحظة:
لا حاجة إلى استخدام includes للارتباطات الفورية، فإذا كان لديك Book belongs_to :author فيُحمل المؤلف (eager-loaded) بشكل تلقائي عند الحاجة.

4.1.3.3 readonly

سيكون الكائن المرتبط قابل للقراءة فقط عند استرداده عن طريق الارتباط إذا استخدمت readonly.

4.1.3.4 select

يسمح لك أسلوب select بتجاوز جملة SELECT (في SQL) والتي تُستخدم لاسترداد البيانات حول الكائن المرتبط، وبشكل افتراضي، سيسترد Rails جميع الأعمدة.

اقتباس

ملاحظة:
إذا استخدمت أسلوب select على ارتباط belongs_to، يجب عليك تعيين خيار :foreign_key لضمان صحّة النتائج.

4.1.4 هل توجد أية كائنات مرتبطة؟

يمكنك معرفة هل توجد أية كائنات مرتبطة عن طريقة استخدام أسلوب ?association.nil:

if @book.author.nil?
  @msg = "No author found for this book"
end

4.1.5 متى تُحفظ الكائنات؟

إن تعيين كائن إلى ارتباط belongs_to لن يحفظ الكائن بشكل تلقائي، ولن يحفظ كائن المرتبط أيضًا.

وسنتابع في الدرس القادم من هذه السلسلة الحديث عن مرجع ارتباط has_one

المصدر:
توثيقات Ruby on Rails





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن