PhxMediaLibrary.PathGenerator behaviour (PhxMediaLibrary v0.6.1)

Copy Markdown View Source

Behaviour for generating storage paths for media files, plus a set of delegating functions that route calls to the configured implementation.

Built-in generators

Configuring a custom generator

config :phx_media_library,
  path_generator: PhxMediaLibrary.PathGenerator.DateBased

Writing a custom generator

Implement the PhxMediaLibrary.PathGenerator behaviour and configure it:

defmodule MyApp.TenantPathGenerator do
  @behaviour PhxMediaLibrary.PathGenerator

  @impl true
  def relative_path(media, conversion) do
    relative_path(media, conversion, %{})
  end

  @impl true
  def relative_path(media, conversion, path_context) do
    tenant_id = Map.get(path_context, :tenant_id, "shared")
    base = Path.join([to_string(tenant_id), media.mediable_type, media.mediable_id, media.uuid])
    ext = Path.extname(media.file_name)
    name = Path.rootname(media.file_name)
    filename = if conversion, do: "#{name}_#{conversion}#{ext}", else: media.file_name
    Path.join(base, filename)
  end

  @impl true
  def for_new_media(attrs), do: for_new_media(attrs, %{})

  @impl true
  def for_new_media(attrs, path_context) do
    tenant_id = Map.get(path_context, :tenant_id, "shared")
    Path.join([to_string(tenant_id), attrs.mediable_type, attrs.mediable_id, attrs.uuid, attrs.file_name])
  end
end

Then configure:

config :phx_media_library,
  path_generator: MyApp.TenantPathGenerator

And pass context when generating paths:

PhxMediaLibrary.PathGenerator.relative_path(media, :thumb, %{tenant_id: "acme"})

The path_context escape hatch

The optional path_context map lets you thread arbitrary data — such as tenant_id, user_id, or space_id — into a custom path generator without coupling to custom_properties or the media schema itself.

The built-in generators ignore path_context; custom generators may use it however they like.

The 3-arity forms of relative_path/3 and for_new_media/2 are @optional_callbacks. If your generator does not implement them, calls that supply a path_context fall back gracefully to the 2-arity / 1-arity forms.

Summary

Callbacks

Generate a storage path for new media (before the record has been persisted).

Generate a storage path for new media with an optional path context map.

Generate the relative storage path for a media item.

Generate the relative storage path for a media item with an optional path context map.

Functions

Generate a storage path for new media (before it has been persisted).

Generate a storage path for new media with extra path context.

Get the full filesystem path for a media item (local storage only).

Get the full filesystem path for a media item with extra path context (local storage only).

Generate the relative storage path for a media item.

Generate the relative storage path for a media item with extra path context.

Callbacks

for_new_media(attrs)

@callback for_new_media(attrs :: map()) :: String.t()

Generate a storage path for new media (before the record has been persisted).

for_new_media(attrs, path_context)

(optional)
@callback for_new_media(attrs :: map(), path_context :: map()) :: String.t()

Generate a storage path for new media with an optional path context map.

This is an optional callback. When not implemented, callers that supply a path_context will automatically fall back to for_new_media/1.

relative_path(media, conversion)

@callback relative_path(
  media :: PhxMediaLibrary.Media.t(),
  conversion :: atom() | String.t() | nil
) ::
  String.t()

Generate the relative storage path for a media item.

relative_path(media, conversion, path_context)

(optional)
@callback relative_path(
  media :: PhxMediaLibrary.Media.t(),
  conversion :: atom() | String.t() | nil,
  path_context :: map()
) :: String.t()

Generate the relative storage path for a media item with an optional path context map.

This is an optional callback. When not implemented, callers that supply a path_context will automatically fall back to relative_path/2.

Functions

for_new_media(attrs)

@spec for_new_media(map()) :: String.t()

Generate a storage path for new media (before it has been persisted).

Delegates to the configured path generator.

for_new_media(attrs, path_context)

@spec for_new_media(map(), map()) :: String.t()

Generate a storage path for new media with extra path context.

Falls back to for_new_media/1 if the configured generator does not implement the optional 2-arity callback.

full_path(media, conversion)

@spec full_path(PhxMediaLibrary.Media.t(), atom() | nil) :: String.t() | nil

Get the full filesystem path for a media item (local storage only).

Returns nil if the configured storage adapter for media.disk does not expose a local path (e.g. S3).

Example

iex> PhxMediaLibrary.PathGenerator.full_path(media, nil)
"/app/priv/static/uploads/posts/abc-123/uuid/photo.jpg"

iex> PhxMediaLibrary.PathGenerator.full_path(media, :thumb)
"/app/priv/static/uploads/posts/abc-123/uuid/photo_thumb.jpg"

full_path(media, conversion, path_context)

@spec full_path(PhxMediaLibrary.Media.t(), atom() | nil, map()) :: String.t() | nil

Get the full filesystem path for a media item with extra path context (local storage only).

Returns nil if the configured storage adapter does not expose a local path (e.g. S3).

relative_path(media, conversion \\ nil)

@spec relative_path(PhxMediaLibrary.Media.t(), atom() | String.t() | nil) ::
  String.t()

Generate the relative storage path for a media item.

Delegates to the configured path generator (see PhxMediaLibrary.Config.path_generator/0).

Example

iex> PhxMediaLibrary.PathGenerator.relative_path(media, nil)
"posts/abc-123/uuid/photo.jpg"

iex> PhxMediaLibrary.PathGenerator.relative_path(media, :thumb)
"posts/abc-123/uuid/photo_thumb.jpg"

relative_path(media, conversion, path_context)

@spec relative_path(PhxMediaLibrary.Media.t(), atom() | String.t() | nil, map()) ::
  String.t()

Generate the relative storage path for a media item with extra path context.

Falls back to relative_path/2 if the configured generator does not implement the optional 3-arity callback.

Example

iex> PhxMediaLibrary.PathGenerator.relative_path(media, :thumb, %{tenant_id: "acme"})
"acme/posts/abc-123/uuid/photo_thumb.jpg"