You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
➜ mix phx.new app
We are almost there! The following steps are missing:
$ cd app
Then configure your database in config/dev.exs and run:
$ mix ecto.create
Start your Phoenix app with:
$ mix phx.server
You can also run your app inside IEx (Interactive Elixir) as:
$ iex -S mix phx.server
➜ cd app
➜ mix ecto.create # Създаваме базата данни
➜ mix phx.gen.auth Accounts User users # Създаваме authentication
Структура на Phoenix проект
assets съдържа frontend assets - най-вече Javascript и CSS.
lib съдържа кода на нашето приложение.
lib/my_app съдържа бизнес логиката. Тук се случва интеракцията с базата данни, т.е. тук живее Model частта.
lib/my_app_web съдържа кода, отговорен за предоставянето на програмата като уеб приложение, т.е. тук живеят Controller и View частите.
Всичко, което сме правили на лекции и домашни досега (освен лекцията за Plug), е код, който живее в lib/my_app
priv съдържа файлове и скриптове, които не са част от приложението, но са нужни в production. Това включва статични файлове, скриптове за миграция на базата данни, файлове, свързани с i18n и др.
Предоставят връзка с външни ресурси - бази данни, API-та и т.н.
Скриват досадни детайли.
При смяна на парола трябва да се изтрие старата, да се изтрият всички съществуващи токени, да се запише новата парола, (опционално) да се изпрати мейл, че е настъпила промяна (без да пращате парола в текстов вид) и т.н.
Грижат се за несигурността/недетерминистичността на поведението.
Най-често като връщат {:ok, result} или {:error, reason}
Групират много сървиси/core модули и предоставят единно API за достъп
Accounts контекст модула използва с User, UserToken, UserNotifier и т.н., за да предостави API за работа с потребители.
defmoduleMyAppWeb.UserControllerdouseMyAppWeb,:controlleraliasMyApp.AccountsaliasMyApp.Accounts.User# ...defcreate(conn,%{"user"=>user_params})docaseAccounts.create_user(user_params)do{:ok,user}->conn|>put_flash(:info,"User created successfully.")|>redirect(to: ~p"/users/#{user}"){:error,%Ecto.Changeset{}=changeset}->render(conn,:new,changeset: changeset)endendend
Actions
Controller actions са просто функции, които приемат Plug.Conn и параметри и връщат Plug.Conn.
Можем да ги именуваме както пожелаем, стига да съвпадат с някоя дефиниция в рутера.
Phoenix ни предоставя помощни функции като render/2, render/3, json/2, text/2 и др.
Тези функции по подадените аргументи създават отговор.
Добавят нужните response headers.
Изпращат отговор чрез Plug.Conn.send_resp/2.
Plug.Conn.send_resp/2 изпраща отговора и извиква halt, с което заявката е приключена.
Action parameters
Вторият аргумент на всеки action е речник с параметри.
Тези параметри са обединение на множество параметри.
Прието е да ги именуваме params.
Съдържат всички параметри от заявката.
Получават се чрез обединение на conn.path_params, conn.body_params и conn.query_params
Параметрите от пътя: /hello/:name;
Параметрите от тялото на заявката (например при POST заявка);
Параметрите от URL-а ?param1=value1¶m2=value2¶m3=value3;
Да не се бъркат path_params и query_params.
render/{2,3}
render/рендериране е процесът на генериране на HTML, който ще бъде върнат на клиента.
Използва се, когато връщаме HTML, който ще бъде визуализиран от браузър.
Функция, която използва темплейт, за да създаде HTML отговор, да попълни параметрите в темплейта, да добави правилния HTTP хедър и да изпрати отговор на потребителя.
Приема като аргументи:
conn;
Темплейт като атом или низ;
Речник assigns със стойности, които ще бъдат интерполирани в темплейта.
В последната major версия (1.7 от 02.2023) са направени значителни промени във View частта.
Преди 1.7 се използваше :phoenix_view библиотеката, която генерира render функции, използвайки
темплейти от файлове на диска.
От 1.7 се препочита писането на Components.
Function/Stateless components - функция, която приема assigns и връща ~H структура.
Module/Live components - модул, който дефинира Component. Грижи се за своето състояние, обработва събития и т.н.
В тази лекция ще дадем примери само с Components.
View
Грижи се за визуалната репрезентация на данните.
Включва писането на templates/components/layouts чрез използването на HTML/CSS/Javascript.
От Phoenix 1.7 по подразбиране използва Tailwind CSS.
Използва се също Elixir:
Написване на помощни функции в .ex файлове
Embedded Elixir в самите темплейти - .heex файловото разширение и ~H сигил.
attr:for,:any,required: true,doc: "the data structure for the form"attr:as,:any,default: nil,doc: "the server side parameter to collect all input under"attr:rest,:global,include: ~w(autocomplete name rel action enctype method novalidate target),doc: "the arbitrary HTML attributes to apply to the form tag"slot:inner_block,required: trueslot:actions,doc: "the slot for form actions, such as a submit button"defsimple_form(assigns)do~H"""<.form:let={f}for={@for}as={@as}{@rest}><divclass="mt-10 space-y-8 bg-white"><%=render_slot(@inner_block,f) %><div:for={action<-@actions}class="mt-2 flex items-center justify-between gap-6"><%=render_slot(action,f) %></div></div></.form>"""end# More examples here: https://gist.github.com/IvanIvanoff/f2034c33e162eb6dde551d9f0c242bd5
View vs Template vs Layout vs Component
Template е HTML структурата, която има "празни" места, които се попълват от параметри.
View е частта от кода, която е отговорна за рендерирането на темплейтите. Допълнително може да се грижи и за обработката на параметрите, който се използват в темплейта.
Layout е template, който "обгръща" нашите темплейти. Обикновено тук включваме <html>, <head> и <body> таговете, като всички наши темплейти се добавят между <body> и </body>.
Използва се, за да спести повтарянето на код.
Може да се влагат Layouts.
View vs Template vs Layout vs Component
Component може да значи две неща:
Function Component - функция, която приема assigns и връща ~H структура.
Module Compontent - модул, който дефинира Component. Грижи се за своето състояние, обработва събития и т.н.
Функцията-компонент може да живее в самия controller/live модул, което опростява значително сложността на структурата - няма нужда от oтделни view и template файлове. Особено полезно и използвано е при LiveView (следващата лекция).
Sigils
Механизъм за представяне на данни чрез тяхната текстова репрезентация.
~D[2023-05-05] вместо Date.new(2023, 5, 5)
~r/foo|bar/ вместо Regex.compile!("foo|bar")
Това позволява добавянето на функционалност чрез библиотека, а не чрез разширяване на синтаксиса на езика.
Синтаксис: %{}, {}, [], etc.
Сигил: ~r/foo/, ~D[2023-05-15] и т.н.
До Elixir 1.14 името на сигилите е една буква: ~s, ~r, ~U, ~D и т.н.
От Elixir 1.15 името на сигилите може да е повече от една буква, но е задължително всички букви да са главни.
За да се избегнат неясноти: var=~opts[bar] vs var =~ opts[bar] vs var = ~opts[bar]
Очаква се да добавят ~PID, ~PORT и други подобни сигили.
defmoduleMyAppWeb.UserControllerdouseMyAppWeb,:controlleraliasMyApp.AccountsaliasMyApp.Accounts.Userdefindex(conn,_params)dousers=Accounts.list_users()render(conn,:index,users: users)enddefcreate(conn,%{"user"=>user_params})docaseAccounts.create_user(user_params)do{:ok,user}->conn|>put_flash(:info,"User created successfully.")|>redirect(to: ~p"/users/#{user}"){:error,%Ecto.Changeset{}=changeset}->render(conn,:new,changeset: changeset)endenddefshow(conn,%{"id"=>id})douser=Accounts.get_user!(id)render(conn,:show,user: user)endend
Generators
Mix Task, който генерира код за вас.
Удобни са, когато трябва бързо да създадем приложение.
Удобни са за изграждане на скелет, върху който след това да добавяме.
Предоставените генератори са добре обмислени и написани от опитни програмисти.
Добавят и тестове.
Има различни видове генератори:
Създават таблица в базата данни и модули за работа с тях.
Създават и HTML страници за интеракция с базата.
Създават логика за регистрация и автентикация.
Четете внимателно какво принтира всеки генератор, защото може да се наложи вие да довършите част от работата.
mix phx.new
Генератор, който създава нов Phoenix проект.
Създава структурата на проекта, Router, Endpoint, Telemetry, Gettext и др.
HTML/CSS компонентите в 1.7+ са написани от екипа на Tailwind CSS.
Помислили са за accessability на ключовите компоненти, предоставени с новия проект:
Компонентите в core_components.ex са написани, като се вземат предвид screen readers потребители.
Тоест хора със зрителни нарушения ще могат по-лесно да използват сайта.
Това не важи за всички уеб сайтове - при грешно имплементиран фокус може да се получи разминаване между това, което е на екрана (някой modal), и това къде се намира фокусът на screen reader-a
mix phx.gen.auth
mix phx.gen.auth Accounts User users
Създава система за автентикация на потребители, която включва:
mix phx.gen.json - Генерира таблица в базата данните, схема и контекст модули и контролер. Сервира ресурса като JSON API, а не HTML. Принтира на екрана какво трябва да добавите в Router
mix phx.gen.live - Генерира LiveView ресурс.
mix phx.gen.release --docker - Генерира нужните файлове за създаване на Mix Release, както и Dockerfile.
mix phx.gen.secret - Генерира случаен низ с достатъчна ентропия, за да може да бъде използван като secret.
И други.
Instrument/Instrumentation
Instrumentation означава измерване и мониториране на изпълнението/производителността на дадена програма.
Binary Instrumentation
Това се прави с цел събиране на данни, нужни за профилиране, откриване на грешки, проследяване на изпълнението, откриване на бавни части от програмата и т.н.
Instrument (глагол) - извършване на Instrumentation
Instrument (съществително) - Парче код/библиотека/tool, които извършват логиката по Instrumentation.
Можем да наречем инструменти profiler/logger/tracer.
Telemetry
:telemetry библиотеката за dynamic dispatching на events.
Фокусът е върху метрики и instrumentation.
Тези събития (events) могат да бъдат каквото пожелаете, например:
Започване/завършване на Phoenix заявка.
Започване/завършване на Ecto заявка.
След това тези events може да бъдат агрегирани:
95-и персентил за времето за изпълнение на цялата заявка;
Среден брой Ecto заявки в минута/час;
Други
# event emitting:telemetry.execute([:web,:request,:done],%{latency: latency},%{request_path: path,status_code: status})# event handlingdefmoduleLogResponseHandlerdorequireLoggerdefhandle_event([:web,:request,:done],measurements,metadata,_config)doLogger.info("[#{metadata.request_path}] #{metadata.status_code} sent in #{measurements.latency}")endend
i18n
Internazionalization е процесът по дизайн и имплементация на софтуер така, че да може да бъде адаптиран за различни езици.
В Elixir това се постига чрез използване на библиотеката :gettext.
Вместо директно използване на низове "Hello World!" се използва gettext("Hello world!"), където "Hello world!" е msgid
Поддържа и други функции:
ngettext - За "превод" между единствено и множествено число
dgettext - Приема като допълнителен аргумент домейн
pgettext - Съобщение с контекст, който може да се използва за разрешаване на неясноти. Например file може да е както глагол, така и съществително.
locale - Посочва езика, на който ще бъдат визуализирани текстовете.
Mailer
Phoenix генераторът добавя модули, свързани с изпращане на мейли.
Използва :swoosh библиотеката.
По подразбиране работи с локален адаптер, който вместо да праща мейли, ги записва на диска.
В config.runtime.exs в коментар има описание на нужната конфигурация, за да заработи.
Може да използвате Mailchimp/Mailjet/Mailgun/etc.
Phoenix.Channel
Абстракция над Websocket
Позволява писането на soft real-time приложения:
Чат;
Push notifications;
Интерактивни приложения;
Начин на работа:
Клиент установява връзка със сървъра, използвайки Websocket;
Клиентът се присединява към един или повече Topics (идентифицирани чрез произволен низ);
Един Topic може да е публичен (всички могат да се присединят);
Един Topic може да е частен, например позволявате на клиентът да се присъедини към Topic my_room:10 само ако ID-то на потребителя е 10.
Клиентът може да пише (push) съобщения в Topic и да получава съобщения от този Topic.