أساسيات Active Record في ريلز

من موسوعة حسوب

هذا الدليل هو مدخل إلى المكتبة Active Record. بعد قراءة هذا الدليل، ستتعرف على:

  • ما هي تقنية ORM (اختصار للعبارة Object Relational Mapping أي "ربط الكائنات العِلاقيَّة") والسجل الفعَّال وكيفية استعمالهما في ريلز.
  • كيف ينسجم السجل الفعَّال مع النموذج MVC (اختصار للعبارة Model-View-Controller).
  • كيفية استعمال نماذج السجل الفعَّال (Active Record models) لمعالجة وتعديل البيانات المخزَّنة في قاعدة بيانات عِلاقيِّة (relational database).
  • اصطلاحات تسمية مخطَّط السجل الفعَّال.
  • مفهوم تهجيرات، وتحققات، وردود نداء قاعدة البيانات.

ما هو Active Record؟

إن Active Record هو الحرف M في MVC - أي "النموذج" (Model)، الذي يعبر عن الطبقة المسؤولة عن تمثيل منطق العمل وبياناته في النظام. يسهّل Active Record عمليّة إنشاء واستخدام الكائنات التي يجب المحافظة على بياناتها في قاعدة بيانات. Active Record هو تنفيذ "لنمط Active Record"، والذي يعبر بذاته عن نظام ربط الكائنات بالعلاقات (Object Relational Mapping).

نمط Active Record

شُرح نمط عمل Active Record من قبل مارتن فولر (Martin Fowler) في كتابه "أنماط معمارية التطبيقات المؤسساتية" (Patterns of Enterprise Application Architecture). في Active Record، تحمل الكائنات البيانات المحفوظة والسلوك المنفذ على هذه البيانات. يأخذ Active Record بالحسبان الرأي الذي ينص على أن ضمان الوصول للبيانات عبر جزء من الكائن سيجعل المستخدمين على دراية بهذا الكائن وكيفية قراءة وكتابة البيانات من وعلى قاعدة البيانات.

ربط الكائنات بالعلاقات

إن ربط الكائنات بالعلاقات، وكما يُرمز له اختصارًا ORM، هو عبارة عن طريقة لربط الكائنات الخاصة بتطبيقٍ ما بجداول في نظام إدارة قواعد بيانات علائقية. باستخدام هذا النظام، يمكن تخزين وقراءة الخاصيات والعلاقات من قاعدة البيانات بسهولة دون الحاجة لكتابة تعليمات SQL مباشرةً.

ملاحظة: إذا لم تكن ملمًا بأنظمة إدارة قواعد البيانات العلائقية، أو بلغة الاستعلام البنيوية SQL، يرجى الاطلاع على توثيق لغة SQL أو الاطلاع هذا المقال (والمقالات الأخرى حول SQL في أكاديمية حسوب). إن فهم طريقة عمل قواعد البيانات العلائقية مهم جدًا لفهم السجلات الفعالة في ريلز بشكل عام.

Active Record كإطار عمل ORM

يزوّد Active Record بمجموعة من المهام، أهمها القابلية على:

  • تمثيل النماذج (models) وبياناتها.
  • تمثيل العلاقات بين هذه النماذج.
  • تمثيل هيكلية الوراثة الهرمية (inheritance hierarchies) بين النماذج المتعددة.
  • التحقق من النماذج قبل حفظها في قاعدة البيانات.
  • تنفيذ عمليات قواعد البيانات بشكل غرضي التوجه.

العرف فوق الضبط في Active Record

عند كتابة التطبيقات باستخدام لغات برمجة أو واجهات عمل أخرى، قد يكون من الضروري كتابة الكثير من تعليمات التهيئة والضبط الضرورية لعمل التطبيق. يعد ذلك صحيحًا من أجل واجهات عمل ORM بشكل عام. لكن إذا اتبعت العرف المتبنى من قبل ريلز، فستحتاج إلى كتابة القليل من تعليمات التهيئة (وبعض الأوقات قد لا تحتاج إلى كتابة تهيئة مطلقًا) عند إنشاء نماذج Active Record. تصب الفكرة في حال هيأت تطبيقك بالطريقة ذاتها معظم الوقت، فتلك الطريقة يجب أن تكون الطريقة الافتراضية. لذلك، ستحتاج إلى التهيئات الصريحة فقط في تلك الحالات التي لا تستطيع فيها التقيّد بالأعراف المعيارية (standard convention).

أعراف التسمية

بشكل افتراضي، يستخدم Active Record بعض أعراف (اصطلاحات) التسمية للحاجة إلى الربط بين النماذج وجداول قاعدة البيانات. يجمع ريلز أسماء الأصناف لمعرفة أسماء الجداول. لذا، من أجل الصنف Book، يجب أن تنشئ جدولًا في قاعدة البيانات يسمّى books. تعد طريقة الجمع الخاصة بريلز طريقةً قويةً جدًا، والتي تمكّنك من جمع الكلمات النظامية والشاذة. عند استخدام أسماء الأصناف المكوّنة من كلمتين أو أكثر، يجب أن يراعي اسم النموذج أعراف لغة روبي، وذلك باستخدام نمط سنام الجمل (CamelCase)، بينما يجب على أسماء الجداول أن تحوي الكلمات مفصولة بواسطة علامة الترقيم "_". أمثلة:

  • جدول قاعدة بيانات: جمع مع علامة ترقيم "_" تفصل الكلمات (مثال: book_clubs).
  • صنف النموذج Model: مفرد مع أول حرف كبير من كل كلمة (مثال: BookClub).
النموذج/الصنف الجدول/المخطط
Article articles
LineItem line_items
Deer deers
Mouse mice
Person people

أعراف المخطط

يستخدم Active Record بعض أعراف التسمية من أجل الحقول في قواعد البيانات، وذلك بناءً على الهدف من هذه الحقول.

  • المفاتيح الأجنبية (Foreign keys): يجب على هذه الحقول أن تراعي النمط التالي "مُعرِّف_اسم_الجدول_المفرد_id" (مثال: item_id، order_id). هذه هي الحقول التي يجب على Active Record النظر إليها عند إنشاء العلاقات بين النماذج.
  • المفاتيح الرئيسية (Primary keys): بشكل افتراضي، يستخدم Active Record حقل العدد الصحيح المسمى id كالمفتاح الرئيسي للجدول. عند استخدام ترحيلات Active Record لإنشاء جداولك، سيتم إنشاء هذا الحقل تلقائيًا.

هناك مجموعة من الحقول الاختيارية التي تضيف ميزات إضافية إلى كائنات Active Record:

  • الحقل created_at: يعيّن تلقائيًا تاريخ إنشاء السجل.
  • الحقل updated_at: يعيّن تلقائيًا تاريخ تعديل السجل.
  • الحقل lock_version: يضيف القفل الذكي إلى نموذج.
  • الحقل type: يحدّد أن النموذج يستخدم وراثة الجدول الوحيد.
  • الحقل ‎(association_name)_type: يخزن النوع من أجل العلاقات متعددة الأشكال.
  • الحقل ‎(table_name)_count: يستخدم للتخزين المؤقت لعدد الكائنات المرتبطة في العلاقات. مثلًا، إن الحقل comments_count في الصنف Article الذي يملك عدة نُسخ من Comment سيخزّن عدد التعليقات المرتبطة بكل مقالة.

ملاحظة: في حين أن هذه الحقول اختيارية، فهي حقيقةً محجوزةً من قبل Active Record. تجنب تسمية الحقول بأسماء محجوزة إلى في حال أردت المزيد من الفعالية. مثلًا، الكلمة type هي كلمة محجوزة تستخدم لوراثة الجدول الوحيد. في حال لم تكن مستخدمًا لهذه الوراثة، حاول المجيء بأسماء مختلفة مثل "context"، والتي تعبر بشكل دقيق عن البيانات التي تُنمذَج (modeling).

إنشاء نماذج Active Record

من السهل جدًا إنشاء نماذج Active Record. كل ما يتوجب عليك فعله هو إنشاء صنف فرعي يرث من الصنف ApplicationRecord:

class Product < ApplicationRecord
end

سينشئ هذا النموذج Product، الذي يربط بالجدول products في قاعدة البيانات. عن طريق هذا الصنف، يمكنك أيضًا ربط الحقول في كل سطر من أسطر الجدول بخاصّيات ضمن النموذج. بفرض أن الجدول products تم إنشاؤه عن طريق تنفيذ تعليمة SQL التالية:

CREATE TABLE products (
   id int(11) NOT NULL auto_increment,
   name varchar(255),
   PRIMARY KEY  (id)
);

تنشئ هذه التعليمة جدولًا بعمودين، id و name. كل سطر من هذا العامود يمثل منتجًا بهذين الوسيطين. وبالتالي، عليك أن تكتب التعليمات التالية لإنشاء سطر جديد:

p = Product.new
p.name = "Some Book"
puts p.name # "Some Book"

تجاوز أعراف التسمية

ماذا لو أردت اتباع أعراف تسمية مختلفة أو أردت استخدام تطبيقك المكتوب بريلز مع قاعدة بيانات قديمة؟ ليست مشكلة، يمكنك بسهولة تجاوز أعراف التسمية.

يرث الصنف ApplicationRecord من الصنف ActiveRecord::Base، الذي يعرّف مجموعة من الطرق المساعدة. يمكنك استخدام التابع ActiveRecord::Base.table_name=‎ لتحديد اسم الجدول الذي يجب استخدامه.

class Product < ApplicationRecord
  self.table_name = "my_products"
end

إذا فعلت ذلك، عليك أن تحدد بشكل يدوي اسم الصنف الذي يحوي التجهيزات (my_products.yml)، باستخدام التابع set_fixture_class في تعريف الاختبار:

class ProductTest < ActiveSupport::TestCase
  set_fixture_class my_products: Product
  fixtures :my_products
  ...
end

من الممكن تجاوز العمود الذي يستخدم مفتاحًا رئيسيًا للجدول باستخدام التابع ActiveRecord::Base.primary_key:

class Product < ApplicationRecord
  self.primary_key = "product_id"
end

العمليات CRUD: قراءة وكتابة البيانات

المصطلح CRUD هو اختصار لأربع عمليات مستخدمة على البيانات: إنشاء (Create)، قراءة (Read)، تحديث (Update)، وحذف (Delete). تلقائيًا، ينشئ Active Record توابعًا لإتاحة قراء وكتابة البيانات المخزنة في قواعد البيانات.

الإنشاء

يمكن إنشاء كائنات Active Record من جدول Hash أو كتلة أو من خلال تعيين الخاصيات بشكل يدوي بعد الإنشاء. يعيد التابع new كائنًا جديدًا، بينما يعيد التابع create الكائن ويحفظه مباشرةً في قاعدة البيانات.

على سبيل المثال، بفرض النموذج User الذي يحوي على الخاصيات name و occupation، يمكن استخدام التابع create لإنشاء وحفظ سجل جديد في قاعدة البيانات.

user = User.create(name: "David", occupation: "Code Artist")

أما باستخدام التابع new، يمكن إنشاء الكائن دون حفظه في قاعدة البيانات.

user = User.new
user.name = "David"
user.occupation = "Code Artist"

بعد ذلك، يمكن حفظ السجل في قاعدة البيانات باستخدام التابع user.save. أخيرًا وليس آخرًا، إذا تم إعطاء كتلةٍ، فالكائن الجديد الناتج عن التابعين create و new يمرَّر إلى تلك كتلة من أجل عملية التهيئة:

user = User.new do |u|
  u.name = "David"
  u.occupation = "Code Artist"
end

القراءة

يزوّد Active Record بواجهة برمجية غنية من أجل قراءة البيانات من قواعد البيانات. فيما يلي مجموعة من الأمثلة المختلفة حول وصول البيانات المزودة من قبل Active Record:

# تعيد مجموعة تحوي كل المستخدمين
users = User.all

# تعيد أول مستخدم
user = User.first

# David تعيد أول مستخدم اسمه
david = User.find_by(name: 'David')

# Code Artists والأعمال David البحث عن جميع المستخدمين ذوي الأسماء   
# تنازليًا created_at والترتيب بواسطة الحقل  
users = User.where(name: 'David', occupation: 'Code Artist').order(created_at: :desc)

يمكنك قراءة المزيد حول الاستعلامات باستخدام Active Record في توثيق واجهة استعلامات Active Record.

التحديث

بعد إعادة كائن Active Record، يمكن تعديل خاصياته ومن ثم حفظه في قاعدة البيانات:

user = User.find_by(name: 'David')
user.name = 'Dave'
user.save

اختصارًا لهذه التعليمات، يمكن استخدام الطريقة التالية:

user = User.find_by(name: 'David')
user.update(name: 'Dave')

يكون هذا مفيدًا عند تحديث مجموعة من الخاصيات بآن واحد. في حال أردت تحديث مجموعة من السجلات بدفعة واحدة، يمكنك استخدام التابع update_all.

User.update_all "max_login_attempts = 3, must_change_password = 'true'"

الحذف

بالمثل، عند استرجاع كائن Active Record، يمكن حذفه من قاعدة البيانات كالتالي:

user = User.find_by(name: 'David')
user.destroy

في حال أردت حذف مجموعة من السجلات دفعة واحدة، يمكنك استخدام التابع destroy_all.

# وحذفه David البحث عن المستخدم ذي الاسم
User.where(name: 'David').destroy_all

# حذف جميع المستخدمين
User.destroy_all

التحقق

يمكنك Active Record من التحقق من حالة السجل قبل حفظه في قاعدة البيانات. هناك مجموعة من الطرق التي يمكنك استخدامها للتحقق من سجلك والتأكد من أن الخاصيات ليست فارغة، أو ليست مكررة، أو تتبع نسق معين، والمزيد من عمليات التحقق.

إن عملية التحقق من صحة السجلات هي عملية مهمة جدًا عند الحفظ في قاعدة البيانات، وبالتالي يأخذ التابع save و update هذه العمليات بالحسبان عند العمل: إذ يعيد هذين التابعين القيمة false عند فشل التحقق، ولا تنفذ أية عمليات على قاعدة البيانات. يمكن تجاوز هذا التصرف عن طريق التابعين save!‎ و update!‎، التي تعتبر أشد وأكثر صرامة، إذ ترمي استثناءً من النوع ActiveRecord::RecordInvalid في حال فشل عملية التحقق. على سبيل المثال:


class User < ApplicationRecord
  validates :name, presence: true
end
 
user = User.new
user.save  # => false
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

يمكنك قراءة المزيد حول هذا الموضوع في دليل التحقق من صحة السجل الفعَّال.

توابع رد النداء (Callbacks)

تتيح توابع رد النداء الخاصة بـ Active Record إمكانية ربط تعليمات معينة بأحداث مخصصة في دورة حياة النموذج. يمكّنك ذلك من إضافة السلوك لنماذجك عن طريق تنفيذ التعليمات بشكل خفي عند إطلاق هذه الأحداث، مثلًا عند إنشاء سجل جديد، أو تحديثه أو حذفه. يمكنك تعلم المزيد عن توابع رد النداء في دليل ردود نداء Active Record.

التهجيرات

يزوّد ريلز بلغة ذات نطاق لإدارة مخططات قواعد البيانات التي تدعى "بالتهجيرات" (migrations). تُخزّن التهجيرات في ملفات، وتنفّذ على قواعد البيانات التي يدعمها Active Record بواسطة rake. في الملف التالي تهجير ينشئ جدولًا:

class CreatePublications < ActiveRecord::Migration[5.0]
  def change
    create_table :publications do |t|
      t.string :title
      t.text :description
      t.references :publication_type
      t.integer :publisher_id
      t.string :publisher_type
      t.boolean :single_issue
 
      t.timestamps
    end
    add_index :publications, :publication_type_id
  end
end

يستمر ريلز في تعقُّب حالة الملفات المحفوظة في قاعدة البيانات، ويزوّد بطريقة لإعادة حالة التهجير. لتنفيذ التهجير، يمكنك استخدام الأمر rails db:migrate، ولإعادته، يمكنك استخدام الأمر rails db:rollback.

الجدير بالملاحظة أنَّ التعليمات السابقة هي حيادية لقواعد البيانات، إذ يمكن تنفيذها على قواعد البيانات من نوع MySQL أو PostgreSQL أو Oracle أو العديد من قواعد البيانات الأخرى. يمكنك تعلّم المزيد عن التهجيرات في دليل التهجيرات الخاصة بActive Record.

مصادر