البرمجة

دوال استدعاء Active Record في Rails

دوال استدعاء Active Record في إطار عمل Ruby on Rails: المفاهيم، الآليات، والتطبيقات المتقدمة

يُعد Active Record أحد أهم مكونات إطار العمل Ruby on Rails، حيث يُمثل طبقة الوصول إلى البيانات (ORM – Object Relational Mapping) التي تربط بين الكائنات البرمجية في تطبيقات Ruby وقواعد البيانات العلائقية مثل PostgreSQL، MySQL، وSQLite. تعتمد فلسفة Active Record على الجمع بين منطق البيانات ومنطق الأعمال في نفس الكائن، مما يُبسط بشكل كبير عملية بناء التطبيقات المعتمدة على قواعد البيانات.

الفكرة الأساسية وراء Active Record

في جوهره، يقوم Active Record بربط كل كائن برمجي في التطبيق بجدول معين في قاعدة البيانات. على سبيل المثال، إن كان هناك جدول باسم users، فإن Active Record ينشئ نموذجًا برمجيًا يسمى User يمكّن المطور من تنفيذ العمليات الأساسية (CRUD) مباشرة من خلال هذا الكائن دون الحاجة إلى كتابة استعلامات SQL يدوية.

مفهوم دوال الاستدعاء (Scopes أو Active Record Query Interface)

تُعرف دوال الاستدعاء في Active Record بمجموعة الدوال التي تُمكن المطور من استدعاء سجلات من قاعدة البيانات باستخدام بنية شبيهة باللغات الطبيعية. وهي توفر واجهة برمجية غنية تُمكن من تنفيذ استعلامات معقدة بطريقة سلسلة وقابلة للتركيب والاختبار.

البنية الأساسية لدوال الاستدعاء

يتم استدعاء هذه الدوال عادة باستخدام بنية تعتمد على الـ method chaining (سلسلة من الاستدعاءات المتتابعة)، مما يسمح ببناء استعلامات دقيقة وفعالة. مثال على ذلك:

ruby
User.where(active: true).order(created_at: :desc).limit(10)

يُترجم هذا الاستدعاء إلى استعلام SQL يبحث عن المستخدمين النشطين (active = true) ويرتبهم حسب تاريخ الإنشاء تنازليًا، ويعيد أول 10 نتائج فقط.

أنواع دوال الاستدعاء في Active Record

1. دوال التحديد (Finder Methods)

تُستخدم هذه الدوال لاسترجاع بيانات من قاعدة البيانات وفق معايير معينة.

where

ruby
User.where(age: 25)

يُولد الاستعلام:

sql
SELECT * FROM users WHERE age = 25

find

ruby
User.find(1)

يسترجع السجل الذي يملك المفتاح الأساسي (Primary Key) بالقيمة 1.

find_by

ruby
User.find_by(email: "[email protected]")

يُرجع أول سجل يُطابق الشرط المحدد.

first و last

ruby
User.first User.last

تُستخدم لاسترجاع أول أو آخر سجل في الجدول وفق ترتيب المفتاح الأساسي.

2. دوال الترتيب والتصفية

order

ruby
User.order(:name)

limit و offset

ruby
User.limit(10).offset(20)

تستخدم لتحديد عدد السجلات المسترجعة وتجاوز عدد معين من السجلات.

group و having

ruby
Order.group(:status).having("count(id) > 5")

تُستخدم في حالات التجميع وتحليل البيانات.

3. دوال الشرط المنطقي

or

ruby
User.where(name: "Ali").or(User.where(age: 25))

تُولد استعلامًا يُطابق أحد الشرطين.

not

ruby
User.where.not(name: "Ali")

يُرجع جميع المستخدمين الذين لا يُطابق اسمهم “Ali”.

4. دوال الترتيب الزمني

created_at و updated_at

ruby
Post.where("created_at >= ?", 1.week.ago)

تُستخدم في تصفية السجلات بناءً على التاريخ أو الوقت.

5. دوال الانضمام (Join)

تُستخدم للربط بين الجداول المختلفة.

ruby
User.joins(:posts).where(posts: { published: true })

6. دوال التحميل المسبق (Eager Loading)

includes و preload

ruby
User.includes(:posts).where(posts: { published: true })

يُساعد في تقليل عدد الاستعلامات من خلال تحميل العلاقات في استعلام واحد.

7. دوال النطاق (Scopes)

النطاقات (Scopes) هي طرق معرفة مسبقًا لتطبيق شروط متكررة على الاستعلامات.

ruby
class User < ApplicationRecord scope :active, -> { where(active: true) } scope :recent, -> { order(created_at: :desc) } end User.active.recent

8. دوال التجميع (Aggregate Functions)

count، sum، average، minimum، maximum

ruby
User.count Order.sum(:total_price) User.average(:age)

مقارنة بين دوال Active Record وكتابة SQL يدوية

المعيار Active Record SQL التقليدي
قابلية القراءة عالية متوسطة إلى منخفضة حسب تعقيد الاستعلام
القابلية للاختبار مرتفعة بسبب القابلية للتركيب أقل مرونة
الأمان من الحقن آمنة افتراضيًا تتطلب معالجة يدوية لمنع SQL Injection
القابلية للصيانة أفضلية كبيرة عبر فصل منطق الاستعلام تتطلب تحديثات يدوية في الاستعلامات النصية

كيفية بناء استعلامات مركبة باستخدام Active Record

واحدة من أبرز نقاط القوة في Active Record هي القدرة على تجميع سلسلة من الاستدعاءات لبناء استعلام معقد دون فقدان القدرة على القراءة أو التعقيد البرمجي.

ruby
Product.joins(:category) .where(categories: { active: true }) .where("price > ?", 100) .order(price: :desc) .limit(5)

هذا المثال يستخرج 5 منتجات تنتمي لفئات نشطة وسعرها أكبر من 100، مرتبة تنازليًا حسب السعر.

إنشاء دوال استدعاء مخصصة

يمكن إنشاء دوال استدعاء مخصصة في نماذج Active Record لتمثيل منطق أعمال معين.

ruby
class Article < ApplicationRecord def self.published where(published: true) end end Article.published

كما يمكن استخدام lambda لزيادة المرونة:

ruby
scope :by_author, ->(author_id) { where(author_id: author_id) }

تحسين الأداء باستخدام Active Record

تُتيح Active Record عدة آليات لتحسين الأداء، مثل:

  • استخدام select لتحديد الحقول المطلوبة فقط:

ruby
User.select(:id, :name)
  • التحميل المسبق للعلاقات باستخدام includes:

ruby
Post.includes(:comments).where(comments: { approved: true })
  • استخدام pluck لاسترجاع أعمدة معينة مباشرة في شكل مصفوفة دون تحميل الكائنات:

ruby
User.pluck(:email)
  • استخدام find_each لتقسيم السجلات على دفعات عند التعامل مع كميات كبيرة:

ruby
User.find_each(batch_size: 1000) do |user| # معالجة المستخدم end

التعامل مع الاستثناءات في استدعاءات Active Record

يتضمن Active Record آلية متكاملة للتعامل مع الاستثناءات:

  • ActiveRecord::RecordNotFound: عند استخدام find لسجل غير موجود.

  • ActiveRecord::RecordInvalid: عند فشل التحقق من صحة السجل.

  • ActiveRecord::StatementInvalid: في حال وجود خطأ في الاستعلام SQL.

ruby
begin User.find(999) rescue ActiveRecord::RecordNotFound => e Rails.logger.error("لم يتم العثور على المستخدم: #{e.message}") end

الحالات المتقدمة: استعلامات JSON و HStore

يدعم Active Record قواعد البيانات التي تحتوي على أعمدة من نوع JSON و HStore مثل PostgreSQL:

ruby
Event.where("metadata ->> 'source' = ?", 'web')

أو:

ruby
Product.where("specs @> ?", { color: 'red' }.to_json)

الجدول التالي يوضح أشهر دوال الاستدعاء في Active Record:

الدالة الوظيفة
where تحديد شرط لاختيار السجلات
find استرجاع سجل عبر المفتاح الأساسي
order ترتيب السجلات
limit / offset تحديد عدد السجلات أو تجاوز عدد معين
group / having تجميع السجلات وتحليلها
includes / joins تحميل العلاقات أو الانضمام إلى جداول أخرى
scope إنشاء نطاقات مخصصة
pluck استرجاع أعمدة معينة كمصفوفة
select استرجاع كائنات Active Record ببيانات جزئية فقط
find_each استرجاع السجلات على دفعات
merge دمج استعلامين في استعلام واحد
readonly تحميل السجلات بدون إمكانية التعديل عليها

المراجع