معرفی فریمورک Phoenix

ترجمه و تالیف : ارسطو عباسی
تاریخ انتشار : 23 مهر 98
خواندن در 6 دقیقه
دسته بندی ها : آموزشی

Phoenix در مقایسه با فریمورک‌هایی مانند جنگو و Ruby on Rails فریمورک جدیدی به شمار می‌رود که با استفاده از زبان برنامه‌نویسی Elixir ساخته شده است. می‌توان در بین فریمورک‌های مختلف این مورد را به عنوان یکی از برترین موارد از نظر کارایی دانست.

در این مطلب از وبسایت راکت قصد داریم تا کامپوننت‌های پایه‌ای این فریمورک را بررسی کرده و در نهایت یک وب اپلیکیشن بسیار ساده را با آن ایجاد کنیم.

سورس کدهای مربوط به این مطلب را نیز می‌توانید در این مخزن گیت‌هاب مشاهده نمایید. 

معرفی Phoenix

Phoenix یک فریمورک مدرن برای توسعه سمت سرور است که با استفاده از زبان برنامه‌نویسی Elixir نوشته شده و براساس معماری MVC یا Model-View-Controller کار می‌کند. اگر با فریمورک‌های دیگری مانند جنگو و لاراول کار کرده باشید مطمئناً می‌دانید که این معماری به چه صورتی کار می‌کند. با این حال شاید بپرسید که چرا Phoenix به وجود آمده؟ آیا به یک فریمورک MVC دیگر نیاز داریم؟ Phoenix از نظر کارایی، مقیاس‌پذیری و بهره‌وری در سطح بالایی قرار دارد. البته دلیل اصلی بالا بودن سطح این موارد زبانی است که Phoenix از آن استفاده می‌کند. به لطف Elixir و ويژگی مانند concurrency شما قابلیت آن را دارید که اپلیکیشن بهینه‌ای را تحویل دهید.

البته Phoenix مشکلاتی نیز دارد. یکی از مشکلات اصلی نبود جامعه کاربری بزرگ است. این مشکل باعث می‌شود تا سؤالات بی جواب بسیار زیادی وجود داشته باشد و از طرفی جدای از مستندات اصلی خود Phoenix منابع یادگیری زیادی در دسترس نباشد. یکی دیگر از معایب آن نبود یک اکوسیستم بزرگ برای پکیج‌های Phoenix است. این موارد مشکلاتی هستند که شما در زمان کار با فریمورکی مانند جنگو یا Ruby on Rails نخواهید داشت. 

البته مشکلات مربوط به Phoenix می‌تواند با گذشت زمان حل شود. اما یکی از مشکلات دیگر Phoenix که مربوط به زبان Elixir می‌شود سینتکس آن است. یادگیری سینتکس این زبان برنامه‌نویسی کار نسبتاً دشواری است چرا که سینتکس منحصر به فردی داشته و شباهتی با زبان‌هایی مانند پایتون، روبی و… ندارد. Elixir یک زبان برنامه‌نویسی شئ‌گرا نیست، از این رو نیاز خواهید داشت تا پارادایم برنامه‌نویسی تابعی را یاد بگیرید که یادگیری آن نسبت به شئ‌گرایی کمی دشوار است. البته اگر به صورت خوش‌بینانه این مسئله را نگاه کنید باید بگوییم که یادگیری یک پارادایم جدید می‌تواند توانایی‌های شما را در توسعه نرم‌افزار به چالش کشیده و از شما برنامه‌نویس بسیار بهتری بسازد.

اجزای فریمورک Phoenix

به صورت معمولی هر اپلیکیشن مبتنی بر Phoenix از قسمت‌های زیر تشکیل شده است:

  • Endpoint – در‌واقع این بخشی از ساختار هر request است. با استفاده از این بخش می‌توانید قابلیت‌های مختلفی را به هر request اضافه کنید.
  • Router – با استفاده از Router شما می‌توانید هر request را به controller مربوطه ارسال کنید. 
  • Controller – این بخش ترکیبی از کنش‌های مختلف ست که در نهایت requestها را مدیریت کرده و داده‌های مرتبطی را به view ارسال می‌کند. 
  • Views – در این معماری views مانند یک لایه نمایشی کار می‌کند. این لایه templateهای مربوط به یک درخواست را رندر می‌کند. 
  • Template – این بخش شامل فایل‌هایی می‌شود که در نهایت در جواب درخواست‌ها ارسال می‌شود. 
  • Channels – بخشی اساسی برای مدیریت کردن سوکت‌ها. در‌واقع این بخش به ما کمک می‌کند تا بتوانیم یک ارتباط بلادرنگ بین کلاینت و سرور ایجاد کنیم.
  • PubSub – یکی از المان‌های مهم برای آنکه Channelها به خوبی کار بکنند. 

این موارد را می‌توان کامپوننت‌هایی دانست که در بیشتر اپلیکیشن‌های ساخته شده با Phoenix وجود دارند.

نصب Phoenix

نصب کردن Phoenix کار سختی نیست. برای انجام این کار باید به صورت ترتیبی موارد زیر را انجام دهید:

  • نصب Erlang 18 – زبان برنامه‌نویسی Elixir براساس ماشین مجازی Erlang کار می‌کند به همین دلیل نیاز داریم تا آن را ابتدا نصب کنیم. Erlang را می‌توانید در تمام سیستم عامل‌های امروزی نصب کنید. 
  • نصب Elixir 1.4 یا بالاتر – می‌توانید این مورد را نیز روی اغلب سیستم عامل‌ها نصب نمایید.
  • نصب Phoenix از طریق دستور زیر:

mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez

تقریباً تمام مواردی که نیاز به نصب آن‌ها را دارید در این مراحل توضیح داده شد. اما مواردی نیز وجود دارد که ممکن است نیاز به نصب آن‌ها نیز داشته باشید. برای مثال اگر قصد ایجاد یک وب اپلیکیشن واقعی را دارید باید از یک سیستم مدیریت بانک اطلاعاتی استفاده کنید. می‌توانید PostgreSQL، MySQL و یا MongoDB را نصب کنید. البته Phoenix با SQLite نیز کار می‌کند اما این دیتابیس تنها برای محیط‌های توسعه مناسب است.

اپلیکیشن ما

بعد از آنکه Phoenix را نصب کردید حال نیاز است تا از آن استفاده نمایید. برای اینکار می‌توانید از دستور زیر استفاده کنید:

mix phx.new phoenix_sample

با این کار یک اپلیکیشن جدید با نام phoenix_sample ایجاد می‌شود. این پروژه یک ساختار اصلی را نیز در خود دارد. این موضوع را در نظر داشته باشید که نام پروژه حتماً باید با کلمات کوچک نوشته شود. سازنده کد نیز در روند ایجاد پروژه از شما در ارتباط با نصب مستقلات سؤال می‌پرسد که در جواب آن کلید Y را وارد کنید.

پروژه ما در این مطلب قرار است از PostgreSQL استفاده کند اما اگر شما قصد استفاده از سیستم دیگری را دارید تنها کافی‌ست تا نام آن را در فایل کانفیگ پروژه config/dev.exs در قسمت مربوطه وارد کنید. برای انجام چنین کاری قطعه کد زیر را در فایل گفته شده پیدا کنید:

config :phoenix_sample, PhoenixSample.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "user",
  password: "secret",
  database: "phoenix_sample_dev",
  hostname: "localhost",
  pool_size: 10

بعد از انجام موارد لازم برای پیکربندی از طریق زیر یک دیتابیس جدید را ایجاد کنید:

mix ecto.create

در نهایت برای اجرا کردن سرور نیاز است تا از دستور زیر استفاده کنید:

mix phx.server

حال وارد آدرس http://localhost:4000 شده و مطمئن شوید که با صفحه خوش آمدگویی روبرو خواهید شد.

ایجاد یک صفحه ساده

حال که ما بدنه اصلی پروژه را ایجاد کردیم بیایید یکسری کامپوننت به آن اضافه کنیم. ابتدا نیاز است تا یک کنترلر جدید با یک action با نام index ایجاد کنیم. برای ایجاد کنترلر جدید نیاز است تا وارد پوشه lib/phoenix_sample_web/controllers شده و در آنجا فایل albums_controller.ex را ایجاد کنید.

defmodule PhoenixSampleWeb.AlbumsController do
  use PhoenixSampleWeb, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end
end

در ابتدای این قطعه کد ما ماژول‌هایی را از Elixir وارد پروژه کرده‌ایم. بعد از آن یک تابع جدید برای رندر تمپلیت index.html را ایجاد کرده‌ایم. این تابع دو ورودی conn و _params را دریافت می‌کند که شامل اطلاعاتی در ارتباط با درخواست می‌شود. 

بعد از این نیاز است تا یک view را ایجاد کنیم. برای اینکار وارد پوشه lib/phoenix_sample_web/views شده و یک فایل با نام albums_view.ex را ایجاد کنید.

defmodule PhoenixSampleWeb.AlbumsView do
  use PhoenixSampleWeb, :view
end

به عنوان یک نکته مهم این موضوع را در نظر بگیرید که اولین قسمت از نام view و controller باید یکسان باشند. در نهایت یک فایل index.html.eex را در داخل پوشه lib/phoenix_sample_web/templates/albumsfolder ایجاد کرده و محتوای زیر را در آن قرار دهید:

<h1>Albums</h1>

محتوای این تمپلیت به یک generic layout تبدیل خواهد شد که می‌توانید آن را در پوشه lib/phoenix_sample_web/templates/layout پیدا کنید.

Routes

آخرین کاری که برای مشاهده اولین صفحه ساخته باید انجام داد ایجاد یک route جدید است. تمام routeها را می‌توانید در lib/phoenix_sample_web/router.ex مشاهده کنید. 

defmodule PhoenixSampleWeb.Router do
  use PhoenixSampleWeb, :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 "/", PhoenixSampleWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
  end
end

گزینه‌های بسیار زیادی در این قطعه کد وجود دارد. تشریح خط به خط این کدها در حوصله این مطلب نیست اما می‌توانید موارد مهم را در زیر مشاهده کنید:

  • pipeline :browser یکسری رفتار و تبدیلات را تعریف می‌کند که باید به یک درخواست اعمال شود. در داخل این قسمت می‌توانید یک سری پلاگین را مشاهده کنید که بسته به نوع درخواست اجرا خواهند شد. 
  • scope “/”, PhoenixSampleWeb مانند یک فضای نامی رفتار می‌کند. برای مثال اگر قصد ایجاد یک زیرشاخه /api را داشته باشید باید یک scope جدید را ایجاد کنید. در این حالت تمام routeهای شما در زیرشاخه فضای نامی /api قرار خواهند گرفت برای مثال: /api/users یا /api/comments/new
  • get “/”, PageController, :index مسیری است که به صورت پیشفرض ایجاد می‌شود. این بدان معناست که هر وقت که یک درخواست GET دریافت شد این درخواست باید به action مربوط به PageController :index هدایت شود.

حال بیایید یک route جدید را در بالای get ایجاد کنیم. برای اینکار کافی‌ست به صورت زیر عمل نمایید:

  scope "/", PhoenixSampleWeb do
    pipe_through :browser # Use the default browser stack
 
    get "/albums", AlbumsController, :index # <---
    get "/", PageController, :index
  end

تمام شد! حال می‌توانید به مسیر http://localhost:4000/albums مراجعه نمایید تا مطمئن شوید که همه چیز درست مانند انتظارهای‌تان کار می‌کند. در زمان اجرای آدرس بالا اگر به ترمینال نگاه کنید باید خروجی زیر را مشاهده نمایید:

[info] GET /albums
[debug] Processing with PhoenixSampleWeb.AlbumsController.index/2
  Parameters: %{}
  Pipelines: [:browser]
[info] Sent 200 in 0┬╡s

مدیریت دیتابیس

در ابتدای این مطلب ما با شیوه ساخت یک بانک اطلاعاتی آشنا شدیم. حال نیاز است تا آن را با یکسری دیتا پر کنیم. Phoenix مانند دیگر فریمورک‌ها از قابلیت migrations برخوردار است. Migration را می‌توان یکسری فایل معرفی کرد که عملیات‌های مورد نیاز برای اجرا شدن روی یک دیتابیس را تعریف می‌کنند. برای مثال شما می‌توانید به سادگی عملیات‌های ایجاد، حذف و ویرایش جداول بانک اطلاعاتی را تنها با چند دستور ساده انجام دهید.

حال بیایید یک Migration جدید را برای تعریف طرح کلی یک جدول ایجاد کنیم:

mix phx.gen.schema Album albums name:string singer:string track_count:integer

در این مثال من گزینه‌های زیر را تعریف کرده‌ام:

  • Album که نام شِما یا Schema مربوط به جدول است.
  • albums نام جدول مورد نظر
  • name:string و singer:string – دو فیلد از نوع رشته با نام‌های name و singer
  • track_count:integer – یک فیلد از نوع عدد صحیح با نام track_count

بعد از اینکه این دستور را اجرا کردید یک migration جدید در priv\repo\migrations ایجاد خواهد شد. اگر براساس این مطلب جلو رفته باشید محتوای این فایل باید مانند زیر باشد:

defmodule PhoenixSample.Repo.Migrations.CreateAlbums do
  use Ecto.Migration

  def change do
    create table(:albums) do
      add :name, :string
      add :singer, :string
      add :track_count, :integer

      timestamps()
    end

  end
end

از آنجایی که در قسمت‌های قبل به خوبی فیلدها را بررسی کردیم درک این قسمت از مطلب سخت نخواهد بود. البته یک timestamps() وجود دارد که ممکن است با آن آشنایی نداشته باشید. این دستور بدان معناست که فیلدهای insert_at و updated_at نیز به جدول ساخته شده اضافه شوند. این فیلدها در زمانی که یک رکورد جدید اضافه شود بروز می‌شوند.

حال برای آنکه migration را روی اپلیکیشن اعمال کنید نیاز است از دستور زیر استفاده کنید:

mix ecto.migrate

بعد از این دستور نیاز است خروجی زیر برای شما نمایش داده شود:

[info] == Running PhoenixSample.Repo.Migrations.CreateAlbums.change/0 forward
[info] create table albums
[info] == Migrated in 0.0s

این بدان معناست که جدول مورد نظر شما ساخته شده است.

افزودن محتوا به بانک اطلاعاتی

برای اینکه همه چیز را ساده‌تر جلو ببریم ابتدا نیاز است تا یک دستور کنسولی مربوط به Elixir را اجرا کنیم. از طریق این دستور توانایی مدیریت داده‌ها را خواهیم داشت.

iex -S mix

بعد از آن دستورات زیر را اجرا کنید:

PhoenixSample.Repo.insert(%PhoenixSample.Album{name: "Reload", singer: "Metallica", track_count: 13})

با انجام این کار عملیات INSERT روی دیتابیس مورد نظر انجام می‌شود. اگر تمام ورودی‌ها به درستی انجام شده باشد خروجی زیر برای شما به نمایش در خواهد آمد.

[debug] QUERY OK db=32.0ms
INSERT INTO "albums" ("name","singer","track_count","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" ["Reload", "Metallica", 13, {{2018, 3, 13}, {16, 13, 44, 267000}}, {{2018, 3, 13}, {16, 13, 44, 269000}}]
{:ok,
 %PhoenixSample.Album{
   __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
   id: 1,
   inserted_at: ~N[2018-03-13 16:13:44.267000],
   name: "Reload",
   singer: "Metallica",
   track_count: 13,
   updated_at: ~N[2018-03-13 16:13:44.269000]
 }}

در نظر داشته باشید که فیلدهای مربوط به inserted_at و updated_at به صورت خودکار تکمیل می‌شوند. جدای از این موارد یک فیلد جدید با نام id نیز بوجود خواهد آمد که primary_key است و به صورت خودکار روی دیتابیس نوشته می‌شود.

Rendering Albums

حال نیاز است تا براساس قطعه کد زیر controller مربوط به albums_controller.ex را ویرایش کنید:

 defmodule PhoenixSampleWeb.AlbumsController do
  alias PhoenixSample.{Repo, Album} # <--- 1
  use PhoenixSampleWeb, :controller
  import Ecto.Query # <--- 2

  def index(conn, _params) do
    render conn, "index.html",
    albums: Repo.all(from a in Album,
    select: %{:name => a.name, :singer => a.singer, :tracks => a.track_count}) # <--- 3
  end
end

سه نکته مهم در این قطعه کد وجود دارد که باید بررسی شود:

  • در قطعه کد بالا یک alias جدید را برای ساده‌تر کردن روند توسعه و فراخوانی ایجاد کرده‌ایم.
  • برای نوشتن کوئری‌های پیچیده پکیج Ecto.query را به پروژه اضافه کرده‌ایم. 
  • یک متغیر albums را نیز ایجاد کرده‌ایم که قرار است محتوای آن از طریق تمپلیت‌ قابل دسترس باشد.

حال برای دسترسی به مقادیر این متغیر از طریق تمپلیت به صورت زیر عمل می‌کنیم:

<h1>Albums</h1>

<%= for album <- @albums do %>
  <p>Name: <%= album.name %></p>
  <p>Singer: <%= album.singer %></p>
  <p>Tracks: <%= album.tracks %></p>
  <hr>
<% end %>

در نهایت پروژه را اجرا کرده و وارد آدرس localhost:4000/albums شوید.

در پایان

در این مطلب از وبسایت راکت به بررسی فریمورکی مدرن و سطح بالا پرداختیم که مطمئناً می‌تواند برای کاربردهای سطح بالا بسیار مناسب باشد. البته مطمئناً باید گفت که یادگیری این فریمورک زمان می‌برد چرا که شما نیاز دارید تا Elixir را از ابتدا یاد بگیرید. اما در نهایت مقیاس‌پذیری و کارایی بالایی را می‌توان از این فریمورک انتظار داشت.

منبع

گردآوری و تالیف ارسطو عباسی
آفلاین
user-avatar

من ارسطو‌ام :) کافی نیست؟! :)

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید