Packages
phoenix_kit
1.7.81
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/phoenix_kit/migrations/postgres/v17.ex
defmodule PhoenixKit.Migrations.Postgres.V17 do
@moduledoc """
PhoenixKit V17 Migration: Entities System (Dynamic Content Types)
This migration adds the dynamic entities system for creating custom content types
with flexible field schemas.
It also introduces `display_name_plural` to improve UI labelling for collections.
## Changes
### Entities Table (phoenix_kit_entities)
- Stores entity type definitions (content type blueprints)
- Includes singular and plural display names
- JSONB `fields_definition` for flexible field schemas
- JSONB `settings` for entity-specific configuration
- Supports dynamic content types like blog posts, products, team members, etc.
### Entity Data Table (phoenix_kit_entity_data)
- Stores actual records based on entity blueprints
- JSONB `data` column for field values
- JSONB `metadata` for additional information
- Generic columns for common fields (title, slug, status)
### Settings Seeds
- Inserts default entities-related settings (`entities_enabled`, etc.)
## PostgreSQL Support
- Leverages PostgreSQL's native JSONB data type
- Supports prefix for schema isolation
- Indexes on frequently queried columns
- Foreign key relationships
"""
use Ecto.Migration
@doc """
Run the V17 migration to add the entities system.
"""
def up(%{prefix: prefix} = _opts) do
create_if_not_exists table(:phoenix_kit_entities, prefix: prefix) do
add :name, :string, null: false
add :display_name, :string, null: false
add :display_name_plural, :string
add :description, :text
add :icon, :string
add :status, :string, null: false, default: "draft"
add :fields_definition, :map, null: false, default: "[]"
add :settings, :map, null: true
add :created_by, :integer, null: false
add :date_created, :utc_datetime_usec, null: false, default: fragment("NOW()")
add :date_updated, :utc_datetime_usec, null: false, default: fragment("NOW()")
end
create_if_not_exists unique_index(:phoenix_kit_entities, [:name],
name: :phoenix_kit_entities_name_uidx,
prefix: prefix
)
create_if_not_exists index(:phoenix_kit_entities, [:created_by],
name: :phoenix_kit_entities_created_by_idx,
prefix: prefix
)
create_if_not_exists index(:phoenix_kit_entities, [:status],
name: :phoenix_kit_entities_status_idx,
prefix: prefix
)
create_if_not_exists table(:phoenix_kit_entity_data, prefix: prefix) do
add :entity_id, :integer, null: false
add :title, :string, null: false
add :slug, :string
add :status, :string, null: false, default: "draft"
add :data, :map, null: false, default: "{}"
add :metadata, :map, null: true
add :created_by, :integer, null: false
add :date_created, :utc_datetime_usec, null: false, default: fragment("NOW()")
add :date_updated, :utc_datetime_usec, null: false, default: fragment("NOW()")
end
create_if_not_exists index(:phoenix_kit_entity_data, [:entity_id],
name: :phoenix_kit_entity_data_entity_id_idx,
prefix: prefix
)
create_if_not_exists index(:phoenix_kit_entity_data, [:slug],
name: :phoenix_kit_entity_data_slug_idx,
prefix: prefix
)
create_if_not_exists index(:phoenix_kit_entity_data, [:status],
name: :phoenix_kit_entity_data_status_idx,
prefix: prefix
)
create_if_not_exists index(:phoenix_kit_entity_data, [:created_by],
name: :phoenix_kit_entity_data_created_by_idx,
prefix: prefix
)
create_if_not_exists index(:phoenix_kit_entity_data, [:title],
name: :phoenix_kit_entity_data_title_idx,
prefix: prefix
)
# Add foreign key constraint only if it doesn't exist
execute """
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'phoenix_kit_entity_data_entity_id_fkey'
AND conrelid = '#{prefix_table_name("phoenix_kit_entity_data", prefix)}'::regclass
) THEN
ALTER TABLE #{prefix_table_name("phoenix_kit_entity_data", prefix)}
ADD CONSTRAINT phoenix_kit_entity_data_entity_id_fkey
FOREIGN KEY (entity_id)
REFERENCES #{prefix_table_name("phoenix_kit_entities", prefix)}(id)
ON DELETE CASCADE;
END IF;
END $$;
"""
execute """
INSERT INTO #{prefix_table_name("phoenix_kit_settings", prefix)} (key, value, module, date_added, date_updated)
VALUES
('entities_enabled', 'false', 'entities', NOW(), NOW()),
('entities_max_per_user', '100', 'entities', NOW(), NOW()),
('entities_allow_relations', 'true', 'entities', NOW(), NOW()),
('entities_file_upload', 'false', 'entities', NOW(), NOW())
ON CONFLICT (key) DO NOTHING
"""
execute """
COMMENT ON COLUMN #{prefix_table_name("phoenix_kit_entities", prefix)}.fields_definition IS
'JSONB array of field definitions. Each field has type, key, label, validation rules.'
"""
execute """
COMMENT ON COLUMN #{prefix_table_name("phoenix_kit_entities", prefix)}.settings IS
'JSONB storage for entity-specific settings (permissions, display options, etc.).'
"""
execute """
COMMENT ON COLUMN #{prefix_table_name("phoenix_kit_entities", prefix)}.display_name_plural IS
'Plural form of the entity display name (e.g., "Blog Posts" for "Blog Post"). Used in UI for collections and navigation.'
"""
execute """
COMMENT ON COLUMN #{prefix_table_name("phoenix_kit_entity_data", prefix)}.data IS
'JSONB storage for all field values based on entity definition. Structure matches fields_definition.'
"""
execute """
COMMENT ON COLUMN #{prefix_table_name("phoenix_kit_entity_data", prefix)}.metadata IS
'JSONB storage for additional metadata (tags, categories, search keywords, etc.).'
"""
execute "COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '17'"
end
@doc """
Rollback the V17 migration.
"""
def down(%{prefix: prefix} = _opts) do
# Drop foreign key constraint if it exists
execute """
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'phoenix_kit_entity_data_entity_id_fkey'
AND conrelid = '#{prefix_table_name("phoenix_kit_entity_data", prefix)}'::regclass
) THEN
ALTER TABLE #{prefix_table_name("phoenix_kit_entity_data", prefix)}
DROP CONSTRAINT phoenix_kit_entity_data_entity_id_fkey;
END IF;
END $$;
"""
drop_if_exists index(:phoenix_kit_entity_data, [:title],
name: :phoenix_kit_entity_data_title_idx,
prefix: prefix
)
drop_if_exists index(:phoenix_kit_entity_data, [:created_by],
name: :phoenix_kit_entity_data_created_by_idx,
prefix: prefix
)
drop_if_exists index(:phoenix_kit_entity_data, [:status],
name: :phoenix_kit_entity_data_status_idx,
prefix: prefix
)
drop_if_exists index(:phoenix_kit_entity_data, [:slug],
name: :phoenix_kit_entity_data_slug_idx,
prefix: prefix
)
drop_if_exists index(:phoenix_kit_entity_data, [:entity_id],
name: :phoenix_kit_entity_data_entity_id_idx,
prefix: prefix
)
drop_if_exists index(:phoenix_kit_entities, [:status],
name: :phoenix_kit_entities_status_idx,
prefix: prefix
)
drop_if_exists index(:phoenix_kit_entities, [:created_by],
name: :phoenix_kit_entities_created_by_idx,
prefix: prefix
)
drop_if_exists index(:phoenix_kit_entities, [:name],
name: :phoenix_kit_entities_name_uidx,
prefix: prefix
)
drop_if_exists table(:phoenix_kit_entity_data, prefix: prefix)
drop_if_exists table(:phoenix_kit_entities, prefix: prefix)
execute """
DELETE FROM #{prefix_table_name("phoenix_kit_settings", prefix)}
WHERE key IN ('entities_enabled', 'entities_max_per_user', 'entities_allow_relations', 'entities_file_upload')
"""
execute "COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '16'"
end
defp prefix_table_name(table_name, nil), do: table_name
defp prefix_table_name(table_name, prefix), do: "#{prefix}.#{table_name}"
end