كيفية إنشاء موارد متداخلة لتطبيق Ruby on Rails

مقدمة

روبي على ريلز هو إطار تطبيق ويب مكتوب بلغة روبي يقدم للمطورين نهجًا محدد الرأي لتطوير التطبيقات. يوفر العمل مع ريلز:

  • توجيهات لمعالجة الأمور مثل التوجيه، والبيانات القابلة للتخزين، وإدارة الموارد.
  • A firm grounding in the model-view-controller (MCV) architectural pattern, which separates an application’s logic, located in models, from the presentation and routing of application information.

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

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

المتطلبات المسبقة

لمتابعة هذا البرنامج التعليمي، ستحتاج إلى:

  • A local machine or development server running Ubuntu 18.04. Your development machine should have a non-root user with administrative privileges and a firewall configured with ufw. For instructions on how to set this up, see our Initial Server Setup with Ubuntu 18.04 tutorial.
  • تم تثبيت Node.js و npm على جهازك المحلي أو الخادم التطويري. يستخدم هذا البرنامج التعليمي إصدار 10.16.3 من Node.js وإصدار 6.9.0 من npm. للإرشادات حول تثبيت Node.js وnpm على Ubuntu 18.04، اتبع التعليمات في قسم “تثبيت باستخدام PPA” من كيفية تثبيت Node.js على Ubuntu 18.04.
  • يجب أن يكون لديك Ruby و rbenv و Rails مثبتة على جهازك المحلي أو الخادم التطويري، وفقًا للخطوات 1-4 في كيفية تثبيت Ruby on Rails باستخدام rbenv على Ubuntu 18.04. يستخدم هذا البرنامج التعليمي Ruby 2.5.1، و rbenv 1.1.2، و Rails 5.2.3.
  • يجب أن يكون SQLite مثبتًا، وتم إنشاء تطبيق معلومات أسماك أساسي، وفقًا للتوجيهات في كيفية بناء تطبيق Ruby on Rails.

الخطوة 1 — بناء النموذج المتداخل

سيستفيد تطبيقنا من تجميع Active Record الارتباطات لبناء علاقة بين نماذج Shark و Post: ستنتمي المشاركات إلى أسماك معينة، ويمكن لكل سمكة أن تمتلك عدة مشاركات. بالتالي، ستكون نماذجنا Shark و Post مرتبطة بواسطة الارتباطات belongs_to و has_many.

الخطوة الأولى في بناء التطبيق بهذه الطريقة ستكون إنشاء نموذج Post والموارد ذات الصلة. للقيام بذلك، يمكننا استخدام أمر rails generate scaffold، الذي سيمنحنا نموذجًا، وتحول جدول قاعدة البيانات لتغيير بنية القاعدة، ومتحكم، ومجموعة كاملة من العروض لإدارة العمليات القياسية إنشاء، قراءة، تحديث، وحذف (CRUD)، وقوالب للأجزاء الجزئية، والمساعدين، والاختبارات. سنحتاج إلى تعديل هذه الموارد، ولكن باستخدام أمر scaffold سيوفر لنا بعض الوقت والطاقة حيث يولد هيكلًا يمكننا استخدامه كنقطة انطلاق.

أولاً، تأكد من أنك في دليل sharkapp لمشروع Rails الذي أنشأته في المتطلبات الأساسية:

  1. cd sharkapp

قم بإنشاء موارد Post الخاصة بك باستخدام الأمر التالي:

rails generate scaffold Post body:text shark:references

با `body:text`، نخبر Rails بتضمين حقل `body` في جدول قاعدة البيانات `posts` — الجدول الذي يتطابق مع نموذج `Post`. كما نضمن أيضًا استخدام الكلمة الرئيسية `:references`، التي تُعد الجمع بين نماذج `Shark` و `Post`. تحديداً، سيضمن هذا وجود مفتاح أجنبي يمثل كل إدخال لسمكة في قاعدة البيانات `sharks` في قاعدة البيانات `posts`.

بمجرد تشغيل الأمر، سترى إخراجًا يؤكد الموارد التي أنشأها Rails للتطبيق. قبل المضي قدمًا، يمكنك التحقق من ملف الترحيل الخاص بقاعدة البيانات للاطلاع على العلاقة التي توجد الآن بين نماذجك وجداول قاعدة البيانات. استخدم الأمر التالي لعرض محتويات الملف، متأكدًا من استبدال الطابع الزمني على ملف الترحيل الخاص بك بما هو معروض هنا:

  1. cat db/migrate/20190805132506_create_posts.rb

سترى الناتج التالي:

Output
class CreatePosts < ActiveRecord::Migration[5.2] def change create_table :posts do |t| t.text :body t.references :shark, foreign_key: true t.timestamps end end end

كما ترى، يتضمن الجدول عمودًا لمفتاح أجنبي لسمكة. سيأخذ هذا المفتاح الشكل `model_name_id` — في حالتنا، `shark_id`.

قامت Rails بإنشاء العلاقة بين النماذج في أماكن أخرى أيضًا. انظر إلى نموذج `Post` الجديد الذي تم إنشاؤه مع الأمر التالي:

cat app/models/post.rb
Output
class Post < ApplicationRecord belongs_to :shark end

تُعيد الجمعية `belongs_to` إعداد علاقة بين النماذج حيث ينتمي مثيل واحد من النموذج المعلن إلى مثيل واحد من النموذج المسمى. في حالة تطبيقنا، يعني هذا أن مشاركة واحدة تنتمي إلى سمكة واحدة.

بالإضافة إلى تعيين هذا العلاقة، أنشأت أيضًا الأوامر rails generate scaffold طرق وعروض للمنشورات، كما فعلت لموارد القرش في الخطوة 3 من كيفية بناء تطبيق Ruby on Rails.

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

الخطوة 2 — تحديد التوجيهات المتداخلة والعلاقات للنموذج الأم

لقد قامت Rails بتعيين العلاقة belongs_to بالفعل في نموذج Post لدينا، بفضل الكلمة الرئيسية :references في أمر rails generate scaffold، ولكن من أجل أن تعمل هذه العلاقة بشكل صحيح، سنحتاج أيضًا إلى تحديد علاقة has_many في نموذج Shark لدينا أيضًا. سنحتاج أيضًا إلى إجراء تغييرات في التوجيه الافتراضي الذي قدمته Rails لنا من أجل جعل موارد المنشورات أطفال موارد القرش.

لإضافة الجمعية has_many إلى نموذج Shark، افتح app/models/shark.rb باستخدام nano أو محررك المفضل:

  1. nano app/models/shark.rb

أضف السطر التالي إلى الملف لتأسيس العلاقة بين الأسماك القرشية والمشاركات:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

شيء واحد يستحق التفكير هنا هو ما يحدث للمشاركات بمجرد حذف قرش معين. من المحتمل أننا لا نرغب في استمرار المشاركات المرتبطة بقرش محذوف في قاعدة البيانات. لضمان حذف أي مشاركات مرتبطة بقرش معين عند حذف ذلك القرش، يمكننا تضمين الخيار dependent مع الجمعية.

أضف الكود التالي إلى الملف لضمان أن الإجراء destroy على قرش معين يحذف أي مشاركات مرتبطة:

~/sharkapp/app/models/shark.rb
class Shark < ApplicationRecord
  has_many :posts , dependent: :destroy
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

بمجرد الانتهاء من إجراء هذه التغييرات، احفظ وأغلق الملف. إذا كنت تستخدم nano، يمكنك القيام بذلك عن طريق الضغط على CTRL+X، ثم Y، ثم ENTER.

بعد ذلك، افتح ملف config/routes.rb لتعديل العلاقة بين مسارات الموارد الخاصة بك:

  1. nano config/routes.rb

حاليًا، يبدو الملف كما يلي:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :posts 
  resources :sharks

  root 'sharks#index'
  # لمزيد من التفاصيل حول DSL المتاحة في هذا الملف، انظر إلى http://guides.rubyonrails.org/routing.html
end

الكود الحالي ينشئ علاقة مستقلة بين مساراتنا، عندما نريد التعبير عن علاقة تابعة بين القروش ومشاركاتها.

لنقم بتحديث إعلان المسار الخاص بنا لجعل :sharks الأب لـ :posts. قم بتحديث الكود في الملف ليبدو كما يلي:

~/sharkapp/config/routes.rb
Rails.application.routes.draw do
  resources :sharks do
    resources :posts
end
  root 'sharks#index'
  # للحصول على تفاصيل حول لغة التوجيه المتاحة في هذا الملف، انظر http://guides.rubyonrails.org/routing.html
end

احفظ الملف وأغلقه عند الانتهاء من التحرير.

بعد تحديث هذه التغييرات، يمكنك المضي قدما في تحديث متحكم posts الخاص بك.

الخطوة 3 — تحديث متحكم المنشورات

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

افتح ملف متحكم المنشورات:

  1. nano app/controllers/posts_controller.rb

حاليا، يبدو الملف كما يلي:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # احصل على /مقالات
  # احصل على /مقالات.json
  def index
    @posts = Post.all
  end

  # احصل على /مقالات/1
  # احصل على /مقالات/1.json
  def show
  end

  # احصل على /مقالات/new
  def new
    @post = Post.new
  end

  # احصل على /مقالات/1/edit
  def edit
  end

  # ارسل /مقالات
  # ارسل /مقالات.json
  def create
    @post = Post.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # اصلاح/ضع /مقالات/1
  # اصلاح/ضع /مقالات/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # حذف /مقالات/1
  # حذف /مقالات/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # استخدم الاستدعاءات لمشاركة الإعدادات المشتركة أو القيود بين الإجراءات.
    def set_post
      @post = Post.find(params[:id])
    end

    # لا تثق أبدًا بالمعلمات من الإنترنت المخيف، فقط السماح بالقائمة البيضاء من خلاله.
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

مثل مُتحكم القروش لدينا، تعمل طرق هذا المُتحكم مع مثيلات فئة المقالة المُرتبطة. على سبيل المثال، يُنشئ الطريقة جديد مثيلًا جديدًا من فئة المقالة، وتقوم الطريقة الفهرس بجلب جميع المثيلات من الفئة، وتستخدم الطريقة ضبط_المقالة find و params لتحديد مقالة معينة بواسطة id. إذا كنا نريد، ومع ذلك، أن تكون مثيلات المقالات لدينا مرتبطة بمثيلات أسماك معينة، سنحتاج إلى تعديل هذا الكود، حيث أن فئة المقالة تعمل حاليًا ككيان مستقل.

سيقوم تعديلاتنا باستخدام شيئين:

  • الطرق التي أصبحت متاحة لنا عندما أضفنا التجمعات belongs_to و has_many إلى نماذجنا. على وجه التحديد، لدينا الآن الوصول إلى طريقة build بفضل الارتباط has_many الذي قمنا بتعريفه في نموذجنا Shark. ستسمح لنا هذه الطريقة بإنشاء مجموعة من كائنات المنشور المرتبطة بكائن سمكة معين، باستخدام المفتاح الخارجي shark_id الذي يوجد في قاعدة البيانات الخاصة بنا posts.
  • المسارات ومساعدي التوجيه التي أصبحت متاحة عندما قمنا بإنشاء مسار posts متداخل. للحصول على قائمة كاملة من المسارات المثالية التي تصبح متاحة عند إنشاء علاقات متداخلة بين الموارد، انظر إلى توثيق ريلز. في الوقت الحالي، سيكون من الكافي بالنسبة لنا أن نعرف أنه لكل سمكة محددة — مثل sharks/1 — سيكون هناك مسار مرتبط للمنشورات المتعلقة بهذه السمكة: sharks/1/posts. كما ستكون هناك مساعدي توجيه مثل shark_posts_path(@shark) و edit_sharks_posts_path(@shark) التي تشير إلى هذه المسارات المتداخلة.

في الملف، سنبدأ بكتابة طريقة، get_shark، التي ستقوم بتشغيلها قبل كل إجراء في المتحكم. ستقوم هذه الطريقة بإنشاء متغير محلي @shark عن طريق العثور على مثيل سمكة بواسطة shark_id. بتوفر هذا المتغير لنا في الملف، سيكون من الممكن ربط المنشورات بسمكة محددة في الطرق الأخرى.

فوق باقي الطرق الخاصة الأخرى في الجزء السفلي من الملف، أضف الطريقة التالية:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
  def get_shark
@shark = Shark.find(params[:shark_id])
end
  # استخدم مراجع الاستدعاء لمشاركة الإعداد الشائع أو القيود بين الإجراءات.
. . . 

بعد ذلك، أضف الفلتر المقابل إلى الأعلى من الملف، قبل الفلتر الموجود بالفعل:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark

سيضمن هذا أن تشغل get_shark قبل كل إجراء محدد في الملف.

بعد ذلك، يمكنك استخدام هذه النسخة @shark لإعادة كتابة الطريقة index. بدلاً من جلب كافة حالات فئة Post، نريد أن تُرجع هذه الطريقة كافة حالات المشاركة المرتبطة بنسخة معينة من سمكة.

عدل الطريقة index لتبدو مثل هذا:

~/sharkapp/controllers/posts_controller.rb
. . .
  def index
    @posts = @shark.posts
  end
. . .

ستحتاج الطريقة new إلى تعديل مماثل، حيث نريد أن يتم ربط نسخة جديدة من المشاركة بسمكة محددة. لتحقيق ذلك، يمكننا استخدام الطريقة build مع متغير النسخة المحلي @shark.

قم بتغيير الطريقة new لتبدو كما يلي:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def new
    @post = @shark.posts.build
  end
. . . 

تنشئ هذه الطريقة كائن مشاركة مرتبط بالسمكة المحددة من طريقة get_shark.

بعد ذلك، سنتناول الطريقة التي ترتبط بشكل أقرب بـ new: create. تقوم طريقة create بشيئين: إنشاء كائن مشاركة جديد باستخدام المعلمات التي أدخلها المستخدمون في النموذج new، وإذا كانت هناك أخطاء، فإنها تقوم بحفظ تلك النسخة واستخدام مساعد مسار لتوجيه المستخدمين إلى حيث يمكنهم رؤية المشاركة الجديدة. في حالة الأخطاء، فإنها تقوم بعرض النموذج new مرة أخرى.

تحديث طريقة create لتبدو كالتالي:

~/sharkapp/controllers/posts_controller.rb
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

بعد ذلك، ألقِ نظرة على طريقة update. تستخدم هذه الطريقة متغير @post، الذي لم يتم تعيينه بشكل صريح في الطريقة نفسها. من أين يأتي هذا المتغير؟

ألقِ نظرة على المرشحات في أعلى الملف. المرشح الثاني، المولد تلقائيًا before_action، يوفر الإجابة:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]
  . . .

تأخذ طريقة update (مثل show، edit، و destroy) متغير @post من طريقة set_post. تبدو هذه الطريقة، المُدرجة تحت طريقة get_shark مع طرقنا الخاصة الأخرى private، حاليًا كالتالي:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = Post.find(params[:id])
  end
. . .

تماشيًا مع الطرق التي استخدمناها في أماكن أخرى في الملف، سنحتاج إلى تعديل هذه الطريقة بحيث يشير @post إلى نسخة معينة في مجموعة المنشورات المرتبطة بسمكة معينة. تذكر الطريقة build هنا — بفضل العلاقات بين نماذجنا، والطرق (مثل build) التي تتوفر لنا بفضل هذه العلاقات، كل من مثيلات منشورنا هي جزء من مجموعة من الكائنات التي ترتبط بسمكة معينة. لذلك من المنطقي أنه عند الاستعلام عن منشور معين، سنستعلم عن مجموعة المنشورات المرتبطة بسمكة معينة.

قم بتحديث set_post لتبدو كالتالي:

~/sharkapp/controllers/posts_controller.rb
. . . 
private
. . . 
  def set_post
    @post = @shark.posts.find(params[:id])
  end
. . .

بدلاً من البحث عن نسخة معينة من فئة Post بواسطة id، نقوم بالبحث عن id مطابق في مجموعة المنشورات المرتبطة بسمكة معينة.

بعد تحديث هذه الطريقة، يمكننا النظر في الأساليب update و destroy.

تستخدم الأسلوب update المتغير الحالي @post من set_post، وتستخدمه مع post_params التي قام المستخدم بإدخالها في نموذج edit. في حالة النجاح، نريد من Rails أن تعيد المستخدم إلى عرض index للمشاركات المرتبطة بسمكة معينة. في حالة الأخطاء، ستقوم Rails بإعادة عرض القالب edit مرة أخرى.

في هذه الحالة، الأمر الوحيد الذي سنحتاج إلى تغييره هو بيان redirect_to، لمعالجة التحديثات الناجحة. قم بتحديثه ليعيد توجيهه إلى shark_post_path(@shark)، الذي سيعيد التوجيه إلى عرض index لمشاركات السمكة المحددة:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end
. . .

بعد ذلك، سنقوم بإجراء تغيير مماثل على الأسلوب destroy. قم بتحديث أسلوب redirect_to ليعيد توجيه الطلبات إلى shark_posts_path(@shark) في حالة النجاح:

~/sharkapp/controllers/posts_controller.rb
. . . 
  def destroy
    @post.destroy
     respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end
. . .

هذه هي آخر تغييرات سنقوم بها. الآن لديك ملف تحكم بالمشاركات يبدو مثل هذا:

~/sharkapp/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :get_shark
  before_action :set_post, only: [:show, :edit, :update, :destroy]

  # الحصول على /منشورات
  # الحصول على /منشورات.json
  def index
    @posts = @shark.posts
  end

  # الحصول على /منشورات/1
  # الحصول على /منشورات/1.json
  def show
  end

  # الحصول على /منشورات/new
  def new
    @post = @shark.posts.build
  end

  # الحصول على /منشورات/1/edit
  def edit
  end

  # إرسال /منشورات
  # إرسال /منشورات.json
  def create
    @post = @shark.posts.build(post_params)

        respond_to do |format|
         if @post.save  
            format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
            format.json { render :show, status: :created, location: @post }
         else
            format.html { render :new }
            format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # تعديل/وضع /منشورات/1
  # تعديل/وضع /منشورات/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # حذف /منشورات/1
  # حذف /منشورات/1.json
  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

   def get_shark
     @shark = Shark.find(params[:shark_id])
   end
    # استخدام مراجع الاستدعاء لمشاركة الإعدادات المشتركة أو القيود بين الإجراءات.
    def set_post
      @post = @shark.posts.find(params[:id])
    end

    # لا تثق أبدًا في المعلمات من الإنترنت المخيفة ، دع القائمة البيضاء فقط تمر.
    def post_params
      params.require(:post).permit(:body, :shark_id)
    end
end

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

الخطوة 4 — تعديل العروض

ستشمل مراجعات قوالب العرض لدينا تغيير القوالب المتعلقة بالمنشورات ، وتعديل عرض أسماك القرش لدينا show ، حيث نريد من المستخدمين رؤية المنشورات المرتبطة بأسماك القرش المعينة.

لنبدأ مع القالب الأساسي لمنشوراتنا: الجزء الجزئي form الذي يعاد استخدامه عبر العديد من قوالب المنشورات. افتح هذا النموذج الآن:

  1. nano app/views/posts/_form.html.erb

بدلاً من تمرير نموذج الـpost فقط إلى مساعد النموذج form_with، سنمرر كل من نماذج shark و post، مع تعيين post كموروث فرعي.

قم بتغيير السطر الأول في الملف ليبدو بهذا الشكل، مع انعكاس العلاقة بين مواردنا للقرش والمنشور:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
. . . 

بعد ذلك، احذف القسم الذي يقوم بسرد shark_id للقرش المتعلق، حيث أن هذه المعلومات ليست ضرورية في العرض.

ستبدو النموذج المكتمل، مع التعديلات على السطر الأول وبدون قسم shark_id المحذوف، بهذا الشكل:

~/sharkapp/views/posts/_form.html.erb
<%= form_with(model: [@shark, post], local: true) do |form| %>
  <% if post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :body %>
    <%= form.text_area :body %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

احفظ وأغلق الملف عند الانتهاء من التحرير.

بعد ذلك، افتح عرض index، الذي سيري عناوين المنشورات المرتبطة بقرش معين:

  1. nano app/views/posts/index.html.erb

بفضل أمر rails generate scaffold، قامت Rails بتوليد الجزء الأفضل من القالب، مكتملًا مع جدول يعرض حقل body لكل منشور وقرشه المرتبط.

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

في جسم الجدول، قم بعمل التحديثات التالية:

أولاً، قم بتحديث post.shark إلى post.shark.name، بحيث يتم تضمين حقل الاسم للقرش المرتبط، بدلاً من المعلومات المعرفة حول كائن القرش نفسه:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
. . . 

ثم، قم بتغيير إعادة التوجيه Show لتوجيه المستخدمين مباشرة إلى عرض القرش المرتبط، حيث من المحتمل أن يرغبوا في وسيلة للعودة إلى القرش الأصلي. يمكننا الاستفادة من المتغير المثيل @shark الذي قمنا بتعيينه في المتحكم هنا، حيث يجعل Rails المتغيرات المثيلة التي تم إنشاؤها في المتحكم متاحة لجميع العروض. كما سنقوم بتغيير نص الرابط من Show إلى Show Shark، حتى يفهم المستخدمون وظيفته بشكل أفضل.

قم بتحديث هذا السطر إلى الشكل التالي:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>

في السطر التالي، نريد التأكد من توجيه المستخدمين إلى المسار المتداخل الصحيح عندما يقومون بتحرير مشاركة. هذا يعني أنه بدلاً من توجيههم إلى posts/post_id/edit، سيتم توجيه المستخدمين إلى sharks/shark_id/posts/post_id/edit. للقيام بذلك، سنستخدم مساعد التوجيه shark_post_path ونماذجنا، التي ستعتبرها Rails عناوين URL. كما سنقوم بتحديث نص الرابط لجعل وظيفته أكثر وضوحًا.

قم بتحديث السطر Edit ليبدو كالتالي:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>

المقبل، دعنا نضيف تغييرًا مماثلاً لرابط Destroy، بتحديث وظيفته في السلسلة، وإضافة مواردنا shark و post:

~/sharkapp/app/views/posts/index.html.erb
. . . 
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>

أخيرًا، في أسفل النموذج، سنريد تحديث مسار New Post لتوجيه المستخدمين إلى المسار المتداخل المناسب عندما يرغبون في إنشاء مشاركة جديدة. قم بتحديث السطر الأخير من الملف لاستخدام مساعد التوجيه new_shark_post_path(@shark):

~/sharkapp/app/views/posts/index.html.erb
. . . 
<%= link_to 'New Post', new_shark_post_path(@shark) %>

الملف النهائي سيبدو بهذا الشكل:

~/sharkapp/app/views/posts/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Posts</h1>

<table>
  <thead>
    <tr>
      <th>Body</th>
      <th>Shark</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td><%= post.body %></td>
        <td><%= post.shark.name %></td>
        <td><%= link_to 'Show Shark', [@shark] %></td>
        <td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
        <td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Post', new_shark_post_path(@shark) %>

احفظ وأغلق الملف عند الانتهاء من التحرير.

لن تكون التعديلات الأخرى التي سنقوم بها على عرض المشاركات متعددة بنفس القدر، حيث أن عروضنا الأخرى تستخدم الجزء الجزئي form الذي قمنا بتعديله بالفعل. ومع ذلك، سنرغب في تحديث مراجع link_to في قوالب المشاركات الأخرى ليعكس التغييرات التي قمنا بها على الجزء الجزئي form الخاص بنا.

افتح app/views/posts/new.html.erb:

  1. nano app/views/posts/new.html.erb

قم بتحديث مرجع link_to في أسفل الملف لاستخدام مساعد shark_posts_path(@shark):

~/sharkapp/app/views/posts/new.html.erb
. . . 
<%= link_to 'Back', shark_posts_path(@shark) %>

احفظ وأغلق الملف عند الانتهاء من إجراء هذا التغيير.

بعد ذلك، افتح قالب edit:

  1. nano app/views/posts/edit.html.erb

بالإضافة إلى مسار Back، سنقوم بتحديث Show ليعكس مواردنا المتداخلة. قم بتغيير السطرين الأخيرين من الملف ليبدوا هكذا:

~/sharkapp/app/views/posts/edit.html.erb
. . . 
<%= link_to 'Show', [@shark, @post] %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

احفظ وأغلق الملف.

بعد ذلك، افتح قالب show:

nano app/views/posts/show.html.erb

قم بعمل التحريرات التالية على مسارات Edit و Back في أسفل الملف:

~/sharkapp/app/views/posts/edit.html.erb
. . .
<%= link_to 'Edit', edit_shark_post_path(@shark, @post) %> |
<%= link_to 'Back', shark_posts_path(@shark) %>

احفظ وأغلق الملف عند الانتهاء.

كخطوة نهائية، سنرغب في تحديث عرض show لأسماكنا حتى تكون المشاركات مرئية للأسماك الفردية. افتح ذلك الملف الآن:

  1. nano app/views/sharks/show.html.erb

ستتضمن تحريراتنا هنا إضافة قسم Posts إلى النموذج ورابط Add Post في أسفل الملف.

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

أضف الكود التالي أسفل قسم Facts من النموذج، وفوق إعادات التوجيه في أسفل الملف:

~/sharkapp/app/views/sharks/show.html.erb
. . .
<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>

<h2>Posts</h2>
<% for post in @shark.posts %>
    <ul>
      <li><%= post.body %></li>
  </ul>
<% end %>

<%= link_to 'Edit', edit_shark_path(@shark) %> |
. . . 

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

~/sharkapp/app/views/sharks/show.html.erb
. . .
<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Add Post', shark_posts_path(@shark) %> |
<%= link_to 'Back', sharks_path %>

احفظ وأغلق الملف عند الانتهاء من التحرير.

لقد قمت الآن بإجراء تغييرات على نماذج تطبيقك ومتحكماته وعروضه لضمان أن المنشورات دائمًا مرتبطة بسمكة معينة. كخطوة نهائية، يمكننا إضافة بعض التحققات إلى نموذج Post لضمان الاتساق في البيانات التي يتم حفظها في قاعدة البيانات.

الخطوة 5 — إضافة التحققات واختبار التطبيق

في الخطوة 5 من كيفية بناء تطبيق Ruby on Rails ، قمت بإضافة التحققات إلى نموذجك Shark لضمان التوحيد والاتساق في البيانات التي يتم حفظها في قاعدة البيانات sharks. سنقوم الآن بخطوة مماثلة لضمان الضمانات لقاعدة بيانات posts أيضًا.

افتح الملف الذي يتم فيه تعريف نموذج Post الخاص بك:

  1. nano app/models/post.rb

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

~/sharkapp/app/models/post.rb
class Post < ApplicationRecord
  belongs_to :shark
  validates :body, presence: true, uniqueness: true
end

احفظ وأغلق الملف عند الانتهاء من التحرير.

بهذا التغيير الأخير المُنفَّذ، أنت الآن جاهز لتشغيل التهجيرات واختبار التطبيق.

أولاً، قم بتشغيل التهجيرات الخاصة بك:

  1. rails db:migrate

بعد ذلك، قم بتشغيل الخادم الخاص بك. إذا كنت تعمل محلياً، يمكنك القيام بذلك عن طريق تشغيل:

  1. rails s

إذا كنت تعمل على خادم تطوير، قم بتشغيل الأمر التالي بدلاً من ذلك:

  1. rails s --binding=your_server_ip

انتقل إلى جذر تطبيقك على الرابط http://localhost:3000 أو http://عنوان_خادمك_IP:3000.

دلّت درس المشروع الأساسي لـ Rails على إضافة وتحرير إدخال لسمك القرش الأبيض الكبير. إذا لم تقم بإضافة أي أسماك قرش أخرى، ستكون صفحة الهبوط للتطبيق على النحو التالي:

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

انقر على إضافة مشاركة أسفل رأس المشاركات. سيقودك ذلك إلى عرض الفهرس للمشاركات، حيث ستتاح لك الفرصة لتحديد مشاركة جديدة:

بفضل آليات المصادقة التي وضعتها في الخطوة 6 من كيفية بناء تطبيق Ruby on Rails، قد يُطلب منك المصادقة باستخدام اسم المستخدم وكلمة المرور التي أنشأتها في تلك الخطوة، اعتمادًا على ما إذا كنت قد أنشأت جلسة جديدة أم لا.

انقر على مشاركة جديدة، والتي ستوجهك إلى قالب new لمشاركتك:

في حقل الجسم، اكتب “هذه القروش مخيفة!”

انقر على إنشاء مشاركة. ستتم إعادة توجيهك إلى عرض الفهرس لجميع المشاركات التي تنتمي إلى هذا القرش:

مع عمل موارد المشاركات لدينا، يمكننا الآن اختبار تحقق البيانات الخاص بنا لضمان حفظ البيانات المطلوبة فقط في قاعدة البيانات.

من عرض index، انقر على مشاركة جديدة. في حقل الجسم للنموذج الجديد، حاول إدخال “هذه القروش مخيفة!” مرة أخرى:

انقر على إنشاء مشاركة. سترى الخطأ التالي:

انقر على العودة للعودة إلى الصفحة الرئيسية للمشاركات.

لاختبار التحقق من صحةنا الآخر، انقر على مشاركة جديدة مرة أخرى. اترك المشاركة فارغة وانقر على إنشاء مشاركة. سترى الخطأ التالي:

مع مواردك المتداخلة والتحقق من الصحة تعمل بشكل صحيح، لديك الآن تطبيق Rails يعمل يمكنك استخدامه كنقطة انطلاق للتطوير المستقبلي.

الاستنتاج

مع تطبيق Rails الخاص بك في مكانه، يمكنك الآن العمل على أشياء مثل التنسيق وتطوير مكونات واجهة المستخدم الأمامية الأخرى. إذا كنت ترغب في معرفة المزيد حول التوجيه والموارد المتداخلة، فإن توثيق Rails هو مكان رائع للبدء.

لمعرفة المزيد حول دمج الإطارات الأمامية مع تطبيقك، تفضل بالاطلاع على كيفية إعداد مشروع Ruby on Rails مع واجهة مستخدم أمامية React.

Source:
https://www.digitalocean.com/community/tutorials/how-to-create-nested-resources-for-a-ruby-on-rails-application