تعرف على Phoenix: إطار يشبه القضبان لتطبيقات الويب الحديثة على Elixir
نشرت: 2022-03-11لقد نما إطار عمل Phoenix بشعبية كبيرة بوتيرة سريعة ، حيث يقدم إنتاجية أطر مثل Ruby on Rails ، بينما يعد أيضًا أحد أسرع الأطر المتاحة. إنه يكسر الأسطورة القائلة بأن عليك التضحية بالأداء من أجل زيادة الإنتاجية.
إذن ما هو فينيكس بالضبط؟
Phoenix هو إطار عمل ويب تم إنشاؤه باستخدام لغة برمجة Elixir. يتم استخدام Elixir ، المبني على Erlang VM ، لبناء أنظمة موزعة بزمن انتقال منخفض ومتحمل للأخطاء ، وهي صفات ضرورية بشكل متزايد لتطبيقات الويب الحديثة. يمكنك معرفة المزيد عن الإكسير من خلال منشور المدونة هذا أو من دليلهم الرسمي.
إذا كنت مطور Ruby on Rails ، فيجب عليك بالتأكيد الاهتمام بفينيكس بسبب مكاسب الأداء التي تعد بها. يمكن لمطوري الأطر الأخرى أيضًا المتابعة لمعرفة كيفية تعامل Phoenix مع تطوير الويب.
في هذه المقالة سوف نتعلم بعض الأشياء في Phoenix التي يجب أن تضعها في اعتبارك إذا كنت قادمًا من عالم Ruby on Rails.
على عكس Ruby ، تعد Elixir لغة برمجة وظيفية ، وربما يكون هذا هو الاختلاف الأكبر الذي قد تضطر إلى التعامل معه. ومع ذلك ، هناك بعض الاختلافات الرئيسية بين هذين النظامين الأساسيين التي يجب على أي شخص يتعلم Phoenix أن يكون على دراية بها من أجل زيادة إنتاجيته إلى الحد الأقصى.
اصطلاحات التسمية أبسط.
هذا صغير ، لكن من السهل إفساده إذا كنت قادمًا من Ruby on Rails.
في فينكس ، تقضي الاتفاقية بكتابة كل شيء بصيغة المفرد. لذلك سيكون لديك "UserController" بدلاً من "UsersController" كما تفعل في Ruby on Rails. ينطبق هذا في كل مكان باستثناء عند تسمية جداول قاعدة البيانات ، حيث يكون الاصطلاح هو تسمية الجدول بصيغة الجمع.
قد لا يبدو هذا مشكلة كبيرة بعد تعلم متى وأين تستخدم صيغة الجمع في Ruby on Rails ، لكن الأمر كان محيرًا بعض الشيء في البداية ، عندما كنت أتعلم استخدام Ruby on Rails ، وأنا متأكد من أنه قد أربك الكثيرين آخرين كذلك. تمنحك Phoenix شيئًا أقل تقلق بشأنه.
التوجيه أسهل في الإدارة.
إن Phoenix و Ruby on Rails متشابهان جدًا عندما يتعلق الأمر بالتوجيه. يكمن الاختلاف الرئيسي في كيفية التحكم في طريقة معالجة الطلب.
في Ruby on Rails (وتطبيقات Rack الأخرى) يتم ذلك من خلال البرامج الوسيطة ، بينما في Phoenix ، يتم ذلك باستخدام ما يشار إليه باسم "المقابس".
المقابس هي ما تستخدمه لمعالجة الاتصال.
على سبيل المثال ، يقوم البرنامج الوسيط Rails::Rack::Logger
بتسجيل طلب في ريلز ، والذي سيتم تنفيذه في فينيكس باستخدام Plug.Logger
plug. في الأساس ، يمكن عمل أي شيء يمكنك القيام به في البرامج الوسيطة Rack باستخدام المقابس.
فيما يلي مثال على جهاز توجيه في Phoenix:
defmodule HelloWorld.Router do use HelloWorld.Web, :router pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", HelloWorld do pipe_through :browser get "/", HelloController, :index end scope "/api", HelloWorld do pipe_through :api end end
أولاً ، دعونا نلقي نظرة على خطوط الأنابيب.
هذه هي مجموعات المقابس التي سينتقل الطلب من خلالها. فكر في الأمر على أنه مكدس برمجيات وسيطة. يمكن استخدامها ، على سبيل المثال ، للتحقق من أن الطلب يتوقع HTML ، وجلب الجلسة ، والتأكد من أن الطلب آمن. كل هذا يحدث قبل الوصول إلى وحدة التحكم.
نظرًا لأنه يمكنك تحديد خطوط أنابيب متعددة ، يمكنك انتقاء واختيار المقابس الضرورية لمسارات معينة. في جهاز التوجيه الخاص بنا ، على سبيل المثال ، لدينا مسار مختلف لطلبات الصفحات (HTML) وطلبات واجهة برمجة التطبيقات (JSON).
ليس من المنطقي دائمًا استخدام نفس خط الأنابيب بالضبط لأنواع مختلفة من الطلبات. فينيكس يعطينا تلك المرونة.
المشاهدات والقوالب شيئان مختلفان.
تختلف طرق العرض في Phoenix عن طرق العرض في Ruby on Rails.
تعتبر طرق العرض في Phoenix مسؤولة عن عرض القوالب وتوفير الوظائف التي تجعل استخدام البيانات الأولية أسهل للقوالب. يشبه العرض في Phoenix المساعد في Ruby on Rails ، مع إضافة عرض القالب.
لنكتب مثالاً يعرض "Hello، World!" في المتصفح.
روبي على القضبان:
التطبيق / وحدات التحكم / hello_controller.rb:
class HelloController < ApplicationController def index end end
app / views / hello / index.html.erb:
<h1>Hello, World!</h1>
فينيكس:
الويب / وحدات التحكم / hello_controller.ex:
defmodule HelloWorld.HelloController do use HelloWorld.Web, :controller def index(conn, _params) do render conn, "index.html" end end
الويب / المشاهدات / hello_view.ex:
defmodule HelloWorld.HelloView do use HelloWorld.Web, :view end
الويب / القوالب / hello / index.html.eex:
<h1>Hello, World!</h1>
يعرض هذان المثالان "Hello، World!" في المتصفح ولكن هناك بعض الاختلافات الرئيسية.
أولاً ، عليك أن تحدد صراحة القالب الذي ترغب في عرضه في Phoenix على عكس Ruby on Rails.
بعد ذلك ، كان علينا تضمين طريقة عرض في Phoenix والتي تقع بين وحدة التحكم والقالب.
الآن ، قد تتساءل لماذا نحتاج إلى عرض إذا كان فارغًا؟ المفتاح هنا هو أنه ، تحت الغطاء ، يجمع Phoenix القالب في وظيفة مساوية تقريبًا للرمز أدناه:
defmodule HelloWorld.HelloView do use HelloWorld.Web, :view def render("index.html", _assigns) do raw("<h1>Hello, World!</h1>") end end
يمكنك حذف القالب ، واستخدام وظيفة التقديم الجديدة ، وستحصل على نفس النتيجة. هذا مفيد عندما تريد فقط إرجاع نص أو حتى JSON.
النماذج هي فقط: نماذج البيانات.
في Phoenix ، تتعامل النماذج بشكل أساسي مع التحقق من صحة البيانات ومخططها وعلاقاتها مع النماذج الأخرى وكيفية تقديمها.
قد يبدو تحديد المخطط في النموذج غريبًا في البداية ، لكنه يسمح لك بإنشاء حقول "افتراضية" بسهولة ، وهي حقول غير موجودة في قاعدة البيانات. دعنا نلقي نظرة على مثال:
defmodule HelloPhoenix.User do use HelloPhoenix.Web, :model schema "users" do field :name, :string field :email, :string field :password, :string, virtual: true field :password_hash, :string end end
نحدد هنا أربعة حقول في جدول "المستخدمين": الاسم والبريد الإلكتروني وكلمة المرور و password_hash.
ليس هناك الكثير من الأشياء المثيرة للاهتمام هنا ، باستثناء حقل "كلمة المرور" الذي تم تعيينه على أنه "افتراضي".
يسمح لنا هذا بتعيين هذا الحقل والحصول عليه دون حفظ التغييرات في قاعدة البيانات. إنه مفيد لأنه سيكون لدينا منطق لتحويل كلمة المرور هذه إلى تجزئة ، والتي سنحفظها في حقل "password_hash" ثم نحفظها في قاعدة البيانات.
ما زلت بحاجة إلى إنشاء هجرة ؛ المخطط في النموذج مطلوب لأنه لا يتم تحميل الحقول تلقائيًا في النموذج كما في Ruby on Rails.
يتمثل الاختلاف بين Phoenix و Ruby on Rails في أن النموذج لا يعالج الاستمرارية في قاعدة البيانات. يتم التعامل مع هذا من خلال وحدة تسمى "الريبو" والتي تم تكوينها باستخدام معلومات قاعدة البيانات ، كما هو موضح في المثال أدناه:

config :my_app, Repo, adapter: Ecto.Adapters.Postgres, database: "ecto_simple", username: "postgres", password: "postgres", hostname: "localhost"
يتم تضمين هذا الرمز في ملفات التكوين الخاصة بالبيئة ، مثل config/dev.exs
أو config/test.exs
. يتيح لنا ذلك بعد ذلك استخدام الريبو لإجراء عمليات قاعدة البيانات مثل الإنشاء والتحديث.
Repo.insert(%User{name: "John Smith", example: "[email protected]"}) do {:ok, user} -> # Insertion was successful {:error, changeset} -> # Insertion failed end
هذا مثال شائع في وحدات التحكم في Phoenix.
نوفر للمستخدم اسمًا وبريدًا إلكترونيًا ومحاولات الريبو لإنشاء سجل جديد في قاعدة البيانات. يمكننا بعد ذلك ، اختياريًا ، التعامل مع المحاولة الناجحة أو الفاشلة كما هو موضح في المثال.
لفهم هذا الرمز بشكل أفضل ، تحتاج إلى فهم مطابقة الأنماط في الإكسير. القيمة التي تُرجعها الوظيفة هي مجموعة. ترجع الدالة مجموعة من قيمتين ، حالة ، ثم النموذج أو مجموعة التغييرات. مجموعة التغييرات هي طريقة لتتبع التغييرات والتحقق من صحة النموذج (ستتم مناقشة مجموعات التغييرات في القسم التالي).
المجموعة الأولى ، من أعلى إلى أسفل ، والتي تطابق نمط المجموعة التي تم إرجاعها بواسطة الوظيفة التي حاولت إدراج المستخدم في قاعدة البيانات ، ستقوم بتشغيل وظيفتها المحددة.
كان بإمكاننا تعيين متغير للحالة بدلاً من الذرة (وهو أساسًا ما هو رمز في Ruby) ، ولكن بعد ذلك سنطابق كل من المحاولات الناجحة والفاشلة وسنستخدم نفس الوظيفة لكلتا الحالتين. لتحديد الذرة التي نريد مطابقتها ، نحدد وظيفة خاصة لتلك الحالة.
تتمتع Ruby on Rails بوظيفة الثبات المضمنة في النموذج من خلال ActiveRecord. يضيف هذا مزيدًا من المسؤولية إلى النموذج وقد يؤدي في بعض الأحيان إلى جعل اختبار النموذج أكثر تعقيدًا نتيجة لذلك. في فينيكس ، تم فصل هذا بطريقة منطقية وتمنع انتفاخ كل نموذج بمنطق الثبات.
تتيح التغييرات قواعد التحقق والتحويل الواضحة.
في Ruby on Rails ، يمكن أن يكون التحقق من صحة البيانات وتحويلها مصدر أخطاء يصعب العثور عليها. هذا لأنه ليس واضحًا على الفور عندما يتم تحويل البيانات عن طريق عمليات الاسترجاعات مثل "before_create" وعمليات التحقق من الصحة.
في Phoenix ، تقوم صراحة بإجراء عمليات التحقق والتحويل هذه باستخدام مجموعات التغييرات. هذه إحدى ميزاتي المفضلة في Phoenix.
دعنا نلقي نظرة على مجموعة التغييرات بإضافة واحدة إلى النموذج السابق:
defmodule HelloPhoenix.User do use HelloPhoenix.Web, :model schema "users" do field :name, :string field :email, :string field :password, :string, virtual: true field :password_hash, :string end def changeset(struct, params \\ %{}) do struct |> cast(params, [:name, :email, :password]) |> validate_required([:email, :password]) end end
هنا تقوم المجموعة بأمرين.
أولاً ، تستدعي وظيفة "cast" وهي قائمة بيضاء للحقول المسموح بها ، على غرار "strong_parameters" في Ruby on Rails ، ثم تتحقق من تضمين حقلي "البريد الإلكتروني" و "كلمة المرور" ، مما يجعل حقل "الاسم" اختياري. بهذه الطريقة ، يمكن فقط تعديل الحقول التي تسمح بها بواسطة المستخدمين.
الشيء الجميل في هذا النهج هو أننا لسنا مقيدين بمجموعة واحدة من التغييرات. يمكننا إنشاء عدة تغييرات. تعتبر مجموعة التغييرات للتسجيل وواحدة لتحديث المستخدم أمرًا شائعًا. ربما نريد فقط طلب حقل كلمة المرور عند التسجيل ولكن ليس عند تحديث المستخدم.
قارن هذا الأسلوب بما يتم إجراؤه بشكل شائع في Ruby on Rails ، سيكون عليك تحديد أن التحقق يجب أن يتم فقط على "إنشاء". هذا يجعل من الصعب أحيانًا في ريلز اكتشاف ما تفعله التعليمات البرمجية بمجرد أن يكون لديك نموذج معقد.
وظيفة الاستيراد سهلة ومرنة.
يتم استيراد الكثير من وظائف مكتبة تسمى "Ecto" إلى النماذج. عادةً ما تحتوي النماذج على هذا الخط بالقرب من الجزء العلوي:
use HelloPhoenix.Web, :model
توجد الوحدة النمطية "HelloPhoenix.Web" في "web / web.ex". في الوحدة ، يجب أن تكون هناك وظيفة تسمى "النموذج" على النحو التالي:
def model do quote do use Ecto.Schema import Ecto import Ecto.Changeset import Ecto.Query end end
هنا يمكنك معرفة الوحدات النمطية التي نستوردها من Ecto. يمكنك إزالة أو إضافة أي وحدات نمطية أخرى تريدها هنا وسيتم استيرادها إلى جميع الطرز.
هناك أيضًا وظائف مماثلة مثل "العرض" و "وحدة التحكم" والتي تخدم نفس الغرض للعروض ووحدات التحكم ، على التوالي.
قد يبدو quote
use
الكلمات الرئيسية محيرا. في هذا المثال ، يمكنك التفكير في الاقتباس على أنه يقوم بتشغيل هذا الرمز مباشرةً في سياق الوحدة النمطية التي تستدعي هذه الوظيفة. لذلك سيكون مكافئًا لكتابة الرمز داخل الاقتباس في الوحدة النمطية.
تتيح لك الكلمة الأساسية use أيضًا تشغيل التعليمات البرمجية في سياق مكان تسميتها. إنها تتطلب بشكل أساسي الوحدة المحددة ، ثم تستدعي الماكرو __using__
على الوحدة النمطية التي تقوم بتشغيلها في سياق المكان الذي تم استدعاؤه فيه. يمكنك قراءة المزيد حول الاقتباس واستخدامها في الدليل الرسمي.
يساعدك هذا حقًا في فهم مكان وجود وظائف معينة في إطار العمل ويساعد في تقليل الشعور بأن إطار العمل يقوم بالكثير من "السحر".
التزامن هو جوهر.
يأتي التزامن مجانًا في Phoenix لأنه سمة رئيسية من سمات Elixir. يمكنك الحصول على تطبيق يمكنه إنتاج عمليات متعددة وتشغيله على نوى متعددة دون أي مخاوف بشأن أمان وموثوقية الخيط.
يمكنك إنشاء عملية جديدة في الإكسير ببساطة:
spawn fn -> 1 + 2 end
كل شيء بعد spawn
وقبل end
سيعمل في عملية جديدة.
في الواقع ، يتم التعامل مع كل طلب في Phoenix في العملية الخاصة به. يستخدم Elixir قوة Erlang VM لتحقيق التزامن موثوق وفعال "مجانًا".
هذا أيضًا يجعل Phoenix خيارًا رائعًا لتشغيل الخدمات التي تستخدم WebSockets ، نظرًا لأن WebSockets بحاجة إلى الحفاظ على اتصال مفتوح بين العميل والخادم (مما يعني أنك بحاجة إلى إنشاء تطبيقك بحيث يمكنه التعامل مع الآلاف من الاتصالات المتزامنة).
ستضيف هذه المتطلبات الكثير من التعقيد إلى مشروع مبني على Ruby on Rails ، لكن يمكن لـ Phoenix تلبية هذه المتطلبات مجانًا من خلال Elixir.
إذا كنت تريد استخدام WebSockets في تطبيق Phoenix الخاص بك ، فستحتاج إلى استخدام Channels. إنه مكافئ لـ ActionCable
في Ruby on Rails ، ولكنه أقل تعقيدًا في الإعداد لأنك لست بحاجة إلى تشغيل خادم منفصل.
يجعل Phoenix إنشاء تطبيقات الويب الحديثة أمرًا غير مؤلم.
بينما كنا نركز إلى حد كبير على الاختلافات ، فإن لدى Phoenix بعض الأشياء المشتركة مع Ruby on Rails.
تتبع Phoenix تقريبًا نفس نمط MVC مثل Ruby on Rails ، لذا فإن معرفة الكود الذي يذهب حيث لا ينبغي أن يكون صعبًا الآن بعد أن عرفت الاختلافات الرئيسية. تمتلك Phoenix أيضًا مولدات مماثلة مثل Ruby on Rails لإنشاء النماذج ووحدات التحكم والترحيلات والمزيد.
بعد تعلم Elixir ، ستقترب ببطء من مستويات إنتاجية Ruby on Rails بمجرد أن تشعر براحة أكبر مع Phoenix.
المرات القليلة التي أشعر فيها بعدم الإنتاجية هي عندما أواجه مشكلة تم حلها بواسطة جوهرة في Ruby ، ولا يمكنني العثور على مكتبة مشابهة لـ Elixir. لحسن الحظ ، يتم سد هذه الفجوات ببطء من قبل مجتمع الإكسير المتنامي.
تساعد الاختلافات في Phoenix في حل الكثير من المعاناة التي جاءت مع إدارة مشاريع Ruby on Rails المعقدة. على الرغم من أنهم لا يحلون جميع المشكلات ، إلا أنهم يساعدون في دفعك في الاتجاه الصحيح. اختيار إطار عمل بطيء لأنك تريد الإنتاجية لم يعد عذراً صالحاً ، يتيح لك Phoenix الحصول على كليهما.