| defmodule Plausible.Funnel do |
| @min_steps 2 |
| @max_steps 8 |
|
|
| @moduledoc """ |
| A funnel is a marketing term used to capture and describe the journey |
| that users go through, from initial step to conversion. |
| A funnel consists of several steps (here: #{@min_steps}..#{@max_steps}). |
| |
| This module defines the database schema for storing funnels |
| and changeset helpers for enumerating the steps within. |
| |
| Each step references a goal (either a Custom Event or Visit) |
| - see: `Plausible.Goal`. |
| """ |
|
|
| use Ecto.Schema |
| import Ecto.Changeset |
|
|
| alias Plausible.Funnel.Step |
|
|
| defmacro min_steps() do |
| quote do |
| unquote(@min_steps) |
| end |
| end |
|
|
| defmacro max_steps() do |
| quote do |
| unquote(@max_steps) |
| end |
| end |
|
|
| defmacro __using__(_opts \\ []) do |
| quote do |
| require Plausible.Funnel |
| alias Plausible.Funnel |
| end |
| end |
|
|
| @type t() :: %__MODULE__{} |
| schema "funnels" do |
| field :name, :string |
| field :strict_order, :boolean, default: false |
| belongs_to :site, Plausible.Site |
|
|
| has_many :steps, Step, |
| preload_order: [ |
| asc: :step_order |
| ], |
| on_replace: :delete |
|
|
| has_many :goals, through: [:steps, :goal] |
| timestamps() |
| end |
|
|
| def changeset(funnel \\ %__MODULE__{}, attrs \\ %{}) do |
| funnel |
| |> cast(attrs, [:name, :strict_order]) |
| |> validate_required([:name]) |
| |> put_steps(attrs[:steps] || attrs["steps"]) |
| |> validate_length(:steps, min: @min_steps, max: @max_steps) |
| |> unique_constraint(:name, |
| name: :funnels_name_site_id_index |
| ) |
| end |
|
|
| def put_steps(changeset, steps) do |
| steps |
| |> Enum.map(&Step.changeset(%Step{}, &1)) |
| |> Enum.with_index(fn step, step_order -> |
| Ecto.Changeset.put_change(step, :step_order, step_order + 1) |
| end) |
| |> then(&Ecto.Changeset.put_assoc(changeset, :steps, &1)) |
| end |
| end |
|
|