All important class methods of these classes can be derived automatically using Generics and GHC's DefaultSignatures extension,
but you can override any method if necessary.
All database types must be of kind '(Type -> Type) -> Type'. If
the type parameter is named f, each field must be of the type
of f applied to some type for which an IsDatabaseEntity
instance exists.
The be type parameter is necessary so that the compiler can
ensure that backend-specific entities only work on the proper
backend.
When parameterized by this entity tag, a database type will hold
meta-information on the Haskell mappings of database entities. Under the
hood, each entity type is transformed into its DatabaseEntityDescriptor
type. For tables this includes the table name as well as the corresponding
TableSettings, which provides names for each column.
Represents a meta-description of a particular entityType. Mostly, a wrapper
around 'DatabaseEntityDescriptor be entityType', but carries around the
IsDatabaseEntity dictionary.
Database entities represent things that can go into databases. Each entity in
your database that you want to access from Haskell must be given a field in
your database type. Each type of entity gets a particular entity tag, such as
TableEntity or DomainTypeEntity
The big Kahuna! All beam tables implement this class.
The kind of all table types is '(Type -> Type) -> Type'. This is
because all table types are actually table type constructors.
Every table type takes in another type constructor, called the
column tag, and uses that constructor to instantiate the column
types. See the documentation for Columnar.
This class is mostly Generic-derivable. You need only specify a
type for the table's primary key and a method to extract the
primary key given the table.
An example table:
data BlogPostT f = BlogPost
{ _blogPostSlug :: Columnar f Text
, _blogPostBody :: Columnar f Text
, _blogPostDate :: Columnar f UTCTime
, _blogPostAuthor :: PrimaryKey AuthorT f
, _blogPostTagline :: Columnar f (Maybe Text)
, _blogPostImageGallery :: PrimaryKey ImageGalleryT (Nullable f) }
deriving Generic
instance Beamable BlogPostT
instance Table BlogPostT where
data PrimaryKey BlogPostT f = BlogPostId (Columnar f Text) deriving Generic
primaryKey = BlogPostId . _blogPostSlug
instance Beamable (PrimaryKey BlogPostT)
We can interpret this as follows:
The _blogPostSlug, _blogPostBody, _blogPostDate, and _blogPostTagline fields are of types Text, Text, UTCTime, and 'Maybe Text' respectfully.
Since _blogPostSlug, _blogPostBody, _blogPostDate, _blogPostAuthor must be provided (i.e, they cannot contain Nothing), they will be given SQL NOT NULL constraints.
_blogPostTagline is declared Maybe so Nothing will be stored as NULL in the database. _blogPostImageGallery will be allowed to be empty because it uses the Nullable tag modifier.
blogPostAuthor references the AuthorT table (not given here) and is required.
blogPostImageGallery references the ImageGalleryT table (not given here), but this relation is not required (i.e., it may be Nothing. See Nullable).
A data type representing the types of primary keys for this table.
In order to play nicely with the default deriving mechanism, this type must be an instance of Generic.
Given a table, this should return the PrimaryKey from the table. By keeping this polymorphic over column,
we ensure that the primary key values come directly from the table (i.e., they can't be arbitrary constants)
Provides a number of introspection routines for the beam library. Allows us
to "zip" tables with different column tags together. Always instantiate an
empty Beamable instance for tables, primary keys, and any type that you
would like to embed within either. See the
manual for more
information on embedding.
Return a TableSettings for the appropriate table type where each column
has been given its default name. See the
manual for
information on the default naming convention.
A type family that we use to "tag" columns in our table datatypes.
This is what allows us to use the same table type to hold table data, describe table settings,
derive lenses, and provide expressions.
The basic rules are
Columnar Identity x = x
Thus, any Beam table applied to Identity will yield a simplified version of the data type, that contains
just what you'd expect.
The Nullable type is used when referencing PrimaryKeys that we want to include optionally.
For example, if we have a table with a PrimaryKey, like the following
data BeamTableT f = BeamTableT
{ _refToAnotherTable :: PrimaryKey AnotherTableT f
, ... }
we would typically be required to provide values for the PrimaryKey embedded into BeamTableT. We can use
Nullable to lift this constraint.
data BeamTableT f = BeamTableT
{ _refToAnotherTable :: PrimaryKey AnotherTableT (Nullable f)
, ... }
Now we can use just_ and nothing_ to refer to this table optionally. The embedded PrimaryKey in _refToAnotherTable
automatically has its fields converted into Maybe using Nullable.
Use this rule if you'd like to parameterize your table type over any other functor. For example, this is used
in the query modules to write expressions such as 'TableT QExpr', which returns a table whose fields have been
turned into query expressions.
The other rules are used within Beam to provide lenses and to expose the inner structure of the data type.
If you declare a function 'Columnar f a -> b' and try to constrain your
function by a type class for f, GHC will complain, because f is
ambiguous in 'Columnar f a'. For example, 'Columnar Identity (Maybe a) ~
Maybe a' and 'Columnar (Nullable Identity) a ~ Maybe a', so given a type
'Columnar f a', we cannot know the type of f.
Thus, if you need to know f, you can instead use Columnar'. Since its a
newtype, it carries around the f paramater unambiguously. Internally, it
simply wraps 'Columnar f a'
group_ for any Beamable type. Adds every field in the type to the
grouping key. This is the equivalent of including the grouping expression
of each field in the type as part of the aggregate projection
Essentially a wrapper over the field name, but with a phantom type
parameter, so that it forms an appropriate column tag.
Usually you use the defaultDbSettings function to generate an appropriate
naming convention for you, and then modify it with withDbModification if
necessary. Under this scheme, the field n be renamed using the IsString
instance for TableField, or the fieldNamed function.
Represents a table that contains metadata on its fields. In particular,
each field of type 'Columnar f a' is transformed into 'TableField table a'.
You can get or update the name of each field by using the fieldName lens.
Automatically provide names for tables, and descriptions for tables (using
defTblFieldSettings). Your database must implement Generic, and must be
auto-derivable. For more information on name generation, see the
manual
A newtype wrapper around 'f e -> f e' (i.e., an endomorphism between entity
types in f). You usually want to use modifyTable or another function to
contstruct these for you.
A newtype wrapper around 'Columnar f a -> Columnar f a' (i.e., an
endomorphism between Columnars over f). You usually want to use
fieldNamed or the IsString instance to rename the field, when 'f ~
TableField'
Modify a database according to a given modification. Most useful for
DatabaseSettings to change the name mappings of tables and fields. For
example, you can use this to modify the default names of a table
db :: DatabaseSettings MyDb
db = defaultDbSettings `withDbModification`
dbModification {
-- Change default name "table1" to "Table_1". Change the name of "table1Field1" to "first_name"
table1 = setEntityName "Table_1" <> modifyTableFields tableModification {
table1Field1 = "first_name"
}
}
Modify a table according to the given field modifications. Invoked by
modifyTable to apply the modification in the database. Not used as often in
user code, but provided for completeness.
Return a table modification (for use with modifyTable) that does nothing.
Useful if you only want to change the table name, or if you only want to
modify a few fields.
For example,
tableModification { field1 = "Column1" }
is a table modification (where 'f ~ TableField tbl') that changes the
column name of field1 to Column1.
Deprecated. Instead of 'modifyTable fTblNm fFields', use 'modifyEntityName _ <> modifyTableFields _'
Provide an EntityModification for TableEntitys. Allows you to modify
the name of the table and provide a modification for each field in the
table. See the examples for withDbModification for more.
Automatically deduce lenses for a table over any column tag. lenses at
global level by doing a top-level pattern match on tableLenses, replacing
every column in the pattern with `LensFor nameOfLensForField'. The lenses
are generated per-column, not per field in the record. Thus if you have
nested Beamable types, lenses are generated for each nested field.
For example,
data AuthorT f = AuthorT
{ _authorEmail :: Columnar f Text
, _authorFirstName :: Columnar f Text
, _authorLastName :: Columnar f Text }
deriving Generic
data BlogPostT f = BlogPost
{ _blogPostSlug :: Columnar f Text
, _blogPostBody :: Columnar f Text
, _blogPostDate :: Columnar f UTCTime
, _blogPostAuthor :: PrimaryKey AuthorT f
, _blogPostTagline :: Columnar f (Maybe Text) }
deriving Generic
instance Table BlogPostT where
data PrimaryKey BlogPostT f = BlogPostId (Columnar f Text)
primaryKey = BlogPostId . _blogPostSlug
instance Table AuthorT where
data PrimaryKey AuthorT f = AuthorId (Columnar f Text)
primaryKey = AuthorId . _authorEmail
Note: In order to have GHC deduce the right type, you will need to turn off
the monomorphism restriction. This is a part of the Haskell standard that
specifies that top-level definitions must be inferred to have a monomorphic
type. However, lenses need a polymorphic type to work properly. You can
turn off the monomorphism restriction by enabling the
NoMonomorphismRestriction extension. You can do this per-file by using
the {-# LANGUAGE NoMonomorphismRestriction #-} pragma at the top of the
file. You can also pass the -XNoMonomorphismRestriction command line flag
to GHC during compilation.