One to One

An one to one bi-directional relationship between entities can be defined as follows.

Entity Structure

app/Domain/Entities/Country.php

<?php declare(strict_types=1);

namespace App\Domain\Entities;

use Dms\Core\Model\Object\ClassDefinition;
use Dms\Core\Model\Object\Entity;

class Country extends Entity
{
    const NAME = 'name';
    const CAPITAL_CITY = 'capitalCity';

    /**
     * @var string
     */
    public $name;

    /**
     * @var CapitalCity
     */
    public $capitalCity;

    /**
     * Country constructor.
     *
     * @param string      $name
     * @param CapitalCity $capitalCity
     */
    public function __construct(string $name, CapitalCity $capitalCity)
    {
        parent::__construct();
        $this->name           = $name;
        $this->capitalCity    = $capitalCity;
        $capitalCity->country = $this;
    }

    /**
     * Defines the structure of this entity.
     *
     * @param ClassDefinition $class
     */
    protected function defineEntity(ClassDefinition $class)
    {
        $class->property($this->name)->asString();

        $class->property($this->capitalCity)->asObject(CapitalCity::class);
    }
}

app/Domain/Entities/CapitalCity.php

<?php declare(strict_types=1);

namespace App\Domain\Entities;

use Dms\Core\Model\Object\ClassDefinition;
use Dms\Core\Model\Object\Entity;

class CapitalCity extends Entity
{
    const NAME = 'name';
    const COUNTRY = 'country';

    /**
     * @var string
     */
    public $name;

    /**
     * @var Country
     */
    public $country;

    /**
     * CapitalCity constructor.
     *
     * @param string  $name
     * @param Country $country
     */
    public function __construct(string $name, Country $country)
    {
        parent::__construct();
        $this->name           = $name;
        $this->country        = $country;
        $country->capitalCity = $this;
    }

    /**
     * Defines the structure of this entity.
     *
     * @param ClassDefinition $class
     */
    protected function defineEntity(ClassDefinition $class)
    {
        $class->property($this->name)->asString();

        $class->property($this->country)->asObject(Country::class);
    }
}

Mapper Configuration

app/Infrastructure/Persistence/CountryMapper.php

<?php declare(strict_types = 1);

namespace App\Infrastructure\Persistence;

use Dms\Core\Persistence\Db\Mapping\Definition\MapperDefinition;
use Dms\Core\Persistence\Db\Mapping\EntityMapper;
use App\Domain\Entities\Country;
use App\Domain\Entities\CapitalCity;

/**
 * The App\Domain\Entities\Country entity mapper.
 */
class CountryMapper extends EntityMapper
{
    /**
     * Defines the entity mapper
     *
     * @param MapperDefinition $map
     *
     * @return void
     */
    protected function define(MapperDefinition $map)
    {
        $map->type(Country::class);
        $map->toTable('countries');

        $map->idToPrimaryKey('id');

        $map->property(Country::NAME)->to('name')->asVarchar(255);

        $map->column('capital_city_id')->asUnsignedInt();
        $map->relation(Country::CAPITAL_CITY)
            ->to(CapitalCity::class)
            ->manyToOne()
            ->withBidirectionalRelation(CapitalCity::COUNTRY)
            ->withRelatedIdAs('capital_city_id');
    }
}

app/Infrastructure/Persistence/CapitalCityMapper.php

<?php declare(strict_types = 1);

namespace App\Infrastructure\Persistence;

use Dms\Core\Persistence\Db\Mapping\Definition\MapperDefinition;
use Dms\Core\Persistence\Db\Mapping\EntityMapper;
use App\Domain\Entities\CapitalCity;
use App\Domain\Entities\Country;

/**
 * The App\Domain\Entities\CapitalCity entity mapper.
 */
class CapitalCityMapper extends EntityMapper
{
    /**
     * Defines the entity mapper
     *
     * @param MapperDefinition $map
     *
     * @return void
     */
    protected function define(MapperDefinition $map)
    {
        $map->type(CapitalCity::class);
        $map->toTable('capital_cities');

        $map->idToPrimaryKey('id');

        $map->property(CapitalCity::NAME)->to('name')->asVarchar(255);

        $map->relation(CapitalCity::COUNTRY)
            ->to(Country::class)
            ->toOne()
            ->identifying()
            ->withBidirectionalRelation(Country::CAPITAL_CITY)
            ->withParentIdAs('capital_city_id');
    }
}

Module Configuration

app/Cms/Modules/CountryModule.php

<?php declare(strict_types=1);

namespace App\Cms\Modules;

use App\Domain\Entities\CapitalCity;
use App\Domain\Entities\Country;
use App\Domain\Services\Persistence\ICapitalCityRepository;
use App\Domain\Services\Persistence\ICountryRepository;
use Dms\Common\Structure\Field;
use Dms\Core\Auth\IAuthSystem;
use Dms\Core\Common\Crud\CrudModule;
use Dms\Core\Common\Crud\Definition\CrudModuleDefinition;
use Dms\Core\Common\Crud\Definition\Form\CrudFormDefinition;
use Dms\Core\Common\Crud\Definition\Table\SummaryTableDefinition;

/**
 * The country module.
 */
class CountryModule extends CrudModule
{
    /**
     * @var ICapitalCityRepository
     */
    protected $capitalCityRepository;

    public function __construct(ICountryRepository $dataSource, IAuthSystem $authSystem, ICapitalCityRepository $capitalCityRepository)
    {
        $this->capitalCityRepository = $capitalCityRepository;
        parent::__construct($dataSource, $authSystem);
    }

    /**
     * Defines the structure of this module.
     *
     * @param CrudModuleDefinition $module
     */
    protected function defineCrudModule(CrudModuleDefinition $module)
    {
        $module->name('country');

        $module->labelObjects()->fromProperty(Country::NAME);

        $module->crudForm(function (CrudFormDefinition $form) {
            $form->section('Details', [
                $form->field(
                    Field::create('name', 'Name')->string()->required()
                )->bindToProperty(Country::NAME),
                //
                //
            ]);

            if ($form->isCreateForm()) {
                $form->continueSection([
                    $form->field(
                        Field::create('capital_city', 'Capital City')->string()->required()
                    )->bindToCallbacks(
                        function () {
                            // Unused
                        },
                        function (Country $country, string $cityName) {
                            $country->capitalCity = new CapitalCity($cityName, $country);
                        }
                    ),
                ]);
            } else {
                $form->continueSection([
                    $form->field(
                        Field::create('capital_city', 'Capital City')
                            ->entityFrom($this->capitalCityRepository)
                            ->required()
                            ->labelledBy(CapitalCity::NAME)
                            // Add this line if you want to load the
                            // options asynchronously via autocomplete
                            ->searchableBy(Country::NAME)
                    )->bindToProperty(Country::CAPITAL_CITY),
                ]);
            }
        });

        $module->removeAction()->deleteFromDataSource();

        $module->summaryTable(function (SummaryTableDefinition $table) {
            $table->mapProperty(Country::NAME)->to(Field::create('name', 'Name')->string()->required());

            $table->view('all', 'All')
                ->loadAll()
                ->asDefault();
        });
    }
}

app/Cms/Modules/CapitalCityModule.php

<?php declare(strict_types=1);

namespace App\Cms\Modules;

use App\Domain\Entities\CapitalCity;
use App\Domain\Entities\Country;
use App\Domain\Services\Persistence\ICapitalCityRepository;
use App\Domain\Services\Persistence\ICountryRepository;
use Dms\Common\Structure\Field;
use Dms\Core\Auth\IAuthSystem;
use Dms\Core\Common\Crud\CrudModule;
use Dms\Core\Common\Crud\Definition\CrudModuleDefinition;
use Dms\Core\Common\Crud\Definition\Form\CrudFormDefinition;
use Dms\Core\Common\Crud\Definition\Table\SummaryTableDefinition;

/**
 * The capital-city module.
 */
class CapitalCityModule extends CrudModule
{
    /**
     * @var ICountryRepository
     */
    protected $countryRepository;

    public function __construct(ICapitalCityRepository $dataSource, IAuthSystem $authSystem, ICountryRepository $countryRepository)
    {
        $this->countryRepository = $countryRepository;
        parent::__construct($dataSource, $authSystem);
    }

    /**
     * Defines the structure of this module.
     *
     * @param CrudModuleDefinition $module
     */
    protected function defineCrudModule(CrudModuleDefinition $module)
    {
        $module->name('capital-city');

        $module->labelObjects()->fromProperty(CapitalCity::NAME);

        $module->crudForm(function (CrudFormDefinition $form) {
            $form->section('Details', [
                $form->field(
                    Field::create('name', 'Name')->string()->required()
                )->bindToProperty(CapitalCity::NAME),
            ]);

            if ($form->isCreateForm()) {
                $form->continueSection([
                    $form->field(
                        Field::create('country', 'Country')->string()->required()
                    )->bindToCallbacks(
                        function () {
                             // Unused
                        },
                        function (CapitalCity $capitalCity, string $countryName) {
                            $capitalCity->country = new Country($countryName, $capitalCity);
                        }
                    )
                ]);
            } else {
                $form->continueSection([
                    $form->field(
                        Field::create('country', 'Country')
                            ->entityFrom($this->countryRepository)
                            ->required()
                            ->labelledBy(Country::NAME)
                            // Add this line if you want to load the
                            // options asynchronously via autocomplete
                            ->searchableBy(Country::NAME)
                    )->bindToProperty(CapitalCity::COUNTRY),
                ]);
            }
        });

        $module->removeAction()->deleteFromDataSource();

        $module->summaryTable(function (SummaryTableDefinition $table) {
            $table->mapProperty(CapitalCity::NAME)->to(Field::create('name', 'Name')->string()->required());

            $table->view('all', 'All')
                ->loadAll()
                ->asDefault();
        });
    }
}