Many to Many¶
A many to many bi-directional relationship between entities can be defined as follows.
Entity Structure¶
app/Domain/Entities/Article.php¶
<?php declare(strict_types=1);
namespace App\Domain\Entities;
use Dms\Core\Model\EntityCollection;
use Dms\Core\Model\Object\ClassDefinition;
use Dms\Core\Model\Object\Entity;
class Article extends Entity
{
const TITLE = 'title';
const TAGS = 'tags';
/**
* @var string
*/
public $title;
/**
* @var EntityCollection|Tag[]
*/
public $tags;
/**
* Article constructor.
*/
public function __construct()
{
parent::__construct();
$this->tags = Tag::collection();
}
/**
* Defines the structure of this entity.
*
* @param ClassDefinition $class
*/
protected function defineEntity(ClassDefinition $class)
{
$class->property($this->title)->asString();
$class->property($this->tags)->asType(Tag::collectionType());
}
}
app/Domain/Entities/Tag.php¶
<?php declare(strict_types=1);
namespace App\Domain\Entities;
use Dms\Core\Model\EntityCollection;
use Dms\Core\Model\Object\ClassDefinition;
use Dms\Core\Model\Object\Entity;
class Tag extends Entity
{
const NAME = 'name';
const ARTICLES = 'articles';
/**
* @var string
*/
public $name;
/**
* @var EntityCollection|Article[]
*/
public $articles;
/**
* Tag constructor.
*/
public function __construct()
{
parent::__construct();
$this->articles = Article::collection();
}
/**
* Defines the structure of this entity.
*
* @param ClassDefinition $class
*/
protected function defineEntity(ClassDefinition $class)
{
$class->property($this->name)->asString();
$class->property($this->articles)->asType(Article::collectionType());
}
}
Mapper Configuration¶
app/Infrastructure/Persistence/ArticleMapper.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\Article;
use App\Domain\Entities\Tag;
/**
* The App\Domain\Entities\Article entity mapper.
*/
class ArticleMapper extends EntityMapper
{
/**
* Defines the entity mapper
*
* @param MapperDefinition $map
*
* @return void
*/
protected function define(MapperDefinition $map)
{
$map->type(Article::class);
$map->toTable('articles');
$map->idToPrimaryKey('id');
$map->property(Article::TITLE)->to('title')->asVarchar(255);
$map->relation(Article::TAGS)
->to(Tag::class)
->toMany()
->withBidirectionalRelation(Tag::ARTICLES)
->throughJoinTable('article_tags')
->withParentIdAs('article_id')
->withRelatedIdAs('tag_id');
}
}
app/Infrastructure/Persistence/TagMapper.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\Tag;
use App\Domain\Entities\Article;
/**
* The App\Domain\Entities\Tag entity mapper.
*/
class TagMapper extends EntityMapper
{
/**
* Defines the entity mapper
*
* @param MapperDefinition $map
*
* @return void
*/
protected function define(MapperDefinition $map)
{
$map->type(Tag::class);
$map->toTable('tags');
$map->idToPrimaryKey('id');
$map->property(Tag::NAME)->to('name')->asVarchar(255);
$map->relation(Tag::ARTICLES)
->to(Article::class)
->toMany()
->withBidirectionalRelation(Article::TAGS)
->throughJoinTable('article_tags')
->withParentIdAs('tag_id')
->withRelatedIdAs('article_id');
}
}
Module Configuration¶
app/Cms/Modules/ArticleModule.php¶
<?php declare(strict_types = 1);
namespace App\Cms\Modules;
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;
use App\Domain\Services\Persistence\IArticleRepository;
use App\Domain\Entities\Article;
use Dms\Common\Structure\Field;
use App\Domain\Services\Persistence\ITagRepository;
use App\Domain\Entities\Tag;
/**
* The article module.
*/
class ArticleModule extends CrudModule
{
/**
* @var ITagRepository
*/
protected $tagRepository;
public function __construct(IArticleRepository $dataSource, IAuthSystem $authSystem, ITagRepository $tagRepository)
{
$this->tagRepository = $tagRepository;
parent::__construct($dataSource, $authSystem);
}
/**
* Defines the structure of this module.
*
* @param CrudModuleDefinition $module
*/
protected function defineCrudModule(CrudModuleDefinition $module)
{
$module->name('article');
$module->labelObjects()->fromProperty(Article::TITLE);
$module->crudForm(function (CrudFormDefinition $form) {
$form->section('Details', [
$form->field(
Field::create('title', 'Title')->string()->required()
)->bindToProperty(Article::TITLE),
//
$form->field(
Field::create('tags', 'Tags')
->entitiesFrom($this->tagRepository)
->labelledBy(Tag::NAME)
->mapToCollection(Tag::collectionType())
// Add this line if you want to load the
// options asynchronously via autocomplete
->searchableBy(Tag::NAME)
)->bindToProperty(Article::TAGS),
]);
});
$module->removeAction()->deleteFromDataSource();
$module->summaryTable(function (SummaryTableDefinition $table) {
$table->mapProperty(Article::TITLE)->to(Field::create('title', 'Title')->string()->required());
$table->view('all', 'All')
->loadAll()
->asDefault();
});
}
}
app/Cms/Modules/TagModule.php¶
<?php declare(strict_types = 1);
namespace App\Cms\Modules;
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;
use App\Domain\Services\Persistence\ITagRepository;
use App\Domain\Entities\Tag;
use Dms\Common\Structure\Field;
use App\Domain\Services\Persistence\IArticleRepository;
use App\Domain\Entities\Article;
/**
* The tag module.
*/
class TagModule extends CrudModule
{
/**
* @var IArticleRepository
*/
protected $articleRepository;
public function __construct(ITagRepository $dataSource, IAuthSystem $authSystem, IArticleRepository $articleRepository)
{
$this->articleRepository = $articleRepository;
parent::__construct($dataSource, $authSystem);
}
/**
* Defines the structure of this module.
*
* @param CrudModuleDefinition $module
*/
protected function defineCrudModule(CrudModuleDefinition $module)
{
$module->name('tag');
$module->labelObjects()->fromProperty(Tag::NAME);
$module->crudForm(function (CrudFormDefinition $form) {
$form->section('Details', [
$form->field(
Field::create('name', 'Name')->string()->required()
)->bindToProperty(Tag::NAME),
//
$form->field(
Field::create('articles', 'Articles')
->entitiesFrom($this->articleRepository)
->labelledBy(Article::TITLE)
->mapToCollection(Article::collectionType())
// Add this line if you want to load the
// options asynchronously via autocomplete
->searchableBy(Article::TITLE)
)->bindToProperty(Tag::ARTICLES),
//
]);
});
$module->removeAction()->deleteFromDataSource();
$module->summaryTable(function (SummaryTableDefinition $table) {
$table->mapProperty(Tag::NAME)->to(Field::create('name', 'Name')->string()->required());
$table->view('all', 'All')
->loadAll()
->asDefault();
});
}
}