Plato CMS - A schema-driven content management system for Phoenix.
Configuration
In your config.exs:
config :my_app, :plato,
repo: MyApp.RepoOr configure a default:
config :plato,
default_otp_app: :my_appUsage
Query content by schema name:
# Get unique content (for singleton schemas like "homepage")
{:ok, homepage} = Plato.get_content("homepage", otp_app: :my_app)
# Get all content for a schema
{:ok, blog_posts} = Plato.list_content("blog_post", otp_app: :my_app)
# Get specific content by ID
{:ok, post} = Plato.get_content_by_id(1, otp_app: :my_app)Field Access
Content returns a map with field names as keys:
homepage.title
#=> "Welcome to My Site"
homepage.hero_image
#=> %{url: "...", alt_text: "..."} (resolved referenced content)
Summary
Functions
Create content for a schema.
Get unique content by schema name.
Get unique content by schema name, raises on error.
Get content by matching a field value.
Get content by database ID.
List all content instances for a schema.
Syncs code-defined schemas to the database.
Update content by ID.
Types
Functions
@spec create_content(String.t(), map(), opts()) :: {:ok, content_map()} | {:error, Ecto.Changeset.t() | atom()}
Create content for a schema.
Examples
Plato.create_content("blog_post", %{
title: "My Post",
body: "Content here",
author_id: 1 # ID of another content instance
}, otp_app: :my_app)
# => {:ok, %{title: "My Post", body: "Content here", author: %{...}}}
@spec get_content(String.t(), opts()) :: {:ok, content_map()} | {:error, atom()}
Get unique content by schema name.
Returns {:ok, content_map} if found, {:error, reason} otherwise.
Works only for schemas marked as unique: true.
Examples
Plato.get_content("homepage", otp_app: :my_app)
# => {:ok, %{title: "Welcome", tagline: "...", hero: %{...}}}
Plato.get_content("nonexistent", otp_app: :my_app)
# => {:error, :schema_not_found}
@spec get_content!(String.t(), opts()) :: content_map()
Get unique content by schema name, raises on error.
Examples
Plato.get_content!("homepage", otp_app: :my_app)
# => %{title: "Welcome", tagline: "..."}
@spec get_content_by_field(String.t(), String.t(), String.t(), opts()) :: {:ok, content_map()} | {:error, atom()}
Get content by matching a field value.
Finds content within a schema where a specific field matches a given value. Useful for slug-based lookups, email searches, etc.
Examples
Plato.get_content_by_field("blog-post", "slug", "my-first-post", otp_app: :my_app)
# => {:ok, %{title: "My First Post", slug: "my-first-post", body: "..."}}
Plato.get_content_by_field("author", "email", "jane@example.com", otp_app: :my_app)
# => {:ok, %{name: "Jane Doe", email: "jane@example.com", ...}}
@spec get_content_by_id(integer(), opts()) :: {:ok, content_map()} | {:error, atom()}
Get content by database ID.
Examples
Plato.get_content_by_id(1, otp_app: :my_app)
# => {:ok, %{title: "My Post", body: "..."}}
@spec list_content(String.t(), opts()) :: {:ok, [content_map()]} | {:error, atom()}
List all content instances for a schema.
Examples
Plato.list_content("blog_post", otp_app: :my_app)
# => {:ok, [
# %{title: "Post 1", body: "...", author: %{name: "John"}},
# %{title: "Post 2", body: "...", author: %{name: "Jane"}}
# ]}
Syncs code-defined schemas to the database.
Reads schema definitions from a module using Plato.SchemaBuilder and
creates or updates them in the database, marked as managed_by: "code".
Examples
# In application.ex
Plato.sync_schemas(MyApp.ContentSchemas, otp_app: :my_app)
# In a migration
Plato.sync_schemas(MyApp.ContentSchemas, repo: MyApp.Repo)Options
:repo- The Ecto repo to use (required if :otp_app not provided):otp_app- The OTP app to read repo config from (required if :repo not provided)
@spec update_content(integer(), map(), opts()) :: {:ok, content_map()} | {:error, Ecto.Changeset.t() | atom()}
Update content by ID.
Examples
Plato.update_content(1, %{title: "Updated Title"}, otp_app: :my_app)
# => {:ok, %{title: "Updated Title", ...}}