Basic metadata structure

Metadata for entities are always organized into individual XML files. Each file uses the SimpleFM metadata namespace and the root element must specify at least the class name of the entity, including its namespace, as well as the layout on which the repository will operate on.

To support auto completion in IDEs, it is advised to include the schema location of the official XSD file. It will be included in the following example but excluded from all further ones for simplicity reasons:

<?xml version="1.0" encoding="utf-8"?>
<entity
    xmlns="uri:soliantconsulting.com:simplefm:entity-metadata"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="uri:soliantconsulting.com:simplefm:entity-metadata
        https://soliantconsulting.github.io/SimpleFM/entity-metadata-5-1.xsd"
    class-name="My\Entity\SampleEntity"
    layout="sample-layout"
/>

Additionally, you can define an interface name on the root element, which will be used for automatically creating entity proxies for lazy loading:

<?xml version="1.0" encoding="utf-8"?>
<entity … interface-name="My\Entity\SampleEntityInterface"/>

Field properties

The most common type you are going to define in your metadata are field properties. These are any kind of fields in the layout which can be mapped to scalar values, Decimal or DateTimeImmutable. When defining a field type, the following three attributes are mandatory:

name
The name of the field in your layout
property
The property name on your entity
type

The type to which the value will be cast to on your entity. The following built-in types are supported:

  • boolean: will treat any value other than "0" or "" as true
  • date-time: will treat the value as DateTimeImmutable
  • date: will treat the value as DateTimeImmutable and convert it to a pure date
  • decimal: will treat the numeric value as Decimal object
  • float: will treat the numeric value as float
  • integer: will treat the numeric value as integer
  • nullable-string: will treat the value as string but convert an empty string to null
  • stream: will treat the value as lazy loaded StreamInterface
  • string: will treat the value as string
  • time: will treat the value as DateTimeImmutable and convert it to a pure time
<?xml version="1.0" encoding="utf-8"?>
<entity …>
    <field name="Name" property="name" type="string"/>
</entity>

Additionally, the field element supports the following two boolean attributes which default to false:

read-only
Tells the repository to not persist this property
repeatable
Treats the field as a set of values

Record ID

If you need to access the record ID of a record, you can specify it via a special `' element:

<?xml version="1.0" encoding="utf-8"?>
<entity …>
    <record-id property="recordId"/>
</entity>

Relations

Sometimes your layout defines relations to other tables. For that reason, SimpleFM supports many-to-one, one-to-many and one-to-one relations. For one-to-many relations, SimpleFM will create lazy-loaded collections, while for both many-to-one and one-to-one relations it will create a lazy-loaded proxy.

Proxy interface

When using any to-one relation, you need to define an interface name on the target entity's metadata, so that the repository can create a proxy for it. The only exception is when the relation has eager hydration enabled.

One-to-many

A one-to-many relation is the simplest of the three relations. It is defined by the following attributes:

property
The property name on your entity
target-table
The table of the foreign entity as it shows on the layout
target-entity
The class name of the mapped entity
target-field-name
The name of the identifying ID field
<?xml version="1.0" encoding="utf-8"?>
<entity …>
    <one-to-many
        property="sampleChildren"
        target-table="children"
        target-entity="My\Entity\SampleEntityChild"
        target-field-name="ID"
    />
</entity>

Many-to-one

A many-to-one relation is set up similarly as the one-to-many relation and requires the same attributes including a few additional ones:

name
The name of the field containing the foreign ID
target-property-name
The property name on the mapped entity containing the ID

Optionally, you can mark the relation as read-only by setting the read-only property to true.

<?xml version="1.0" encoding="utf-8"?>
<entity …>
    <many-to-one
        name="ParentID"
        property="sampleParent"
        target-table="parents"
        target-entity="My\Entity\SampleEntityParent"
        target-field-name="ID"
        target-property-name="id"
    />
</entity>

One-to-one

When it comes to one-to-one relations, there are two types of it: the owning side and the inverse side.

Owning side

The owning side takes the exact same attributes as the many-to-one relation, including the optional read-only attribute.

<?xml version="1.0" encoding="utf-8"?>
<entity …>
    <one-to-one-owning
        name="ParentID"
        property="sampleParent"
        target-table="parents"
        target-entity="My\Entity\SampleEntityParent"
        target-field-name="ID"
        target-property-name="id"
    />
</entity>

Inverse side

While the owning side mirrors the behavior of the many-to-one relation, the inverse side mirrors that of the one-to-many relation and requires the same arguments:

<?xml version="1.0" encoding="utf-8"?>
<entity …>
    <one-to-one-inverse
        property="sampleChildren"
        target-table="children"
        target-entity="My\Entity\SampleEntityChild"
        target-field-name="ID"
    />
</entity>

Eager hydration

Every relation can be eagerly hydrated when the sparse record contains all information to fully hydrate the parent or child entity. To enable eager hydration, set the eager-hydration property on the relation to true.

While this gives you additional hydration overhead for each relation, it also saves you from doing additional queries against FileMaker. Using eager hydration should only be considered when you are using specific relations often enough.