ORM 映射不同类型的用户

问题描述 投票:0回答:1

我有一个包含不同类型用户的应用程序。所有类型的用户都共享共同的属性,例如用户名、名字和姓氏。不同类型的用户是:

  • 孩子
  • 守护者
  • 老师

教师拥有用户的所有属性,还有电子邮件和电话号码。儿童和监护人同上,但这两个实体之间存在多对多关系。一个孩子可以有多个父母,一个父母可以有多个孩子。

我设计的模型有一个具有公共属性的父类

User
,并且每种类型都有一个类作为子类。这个想法是它会生成数据库表“用户”、“儿童”、“监护人”和“教师”。最后三个对用户来说是外键,因此当我检索例如教师时,我还可以检索它的名字和姓氏。

这些是我的模型: 用户.php

<?php

namespace App\Domain\Model\User;

use App\Domain\Model\Timestampable;
use App\Domain\Model\HasUuid;

class User
{
    use Timestampable;
    use HasUuid;

    public function __construct(
        private string $username,
        private string $firstName,
        private string $lastName,
        private string $passcode,
    )
    {
        $this->username = $username;
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->passcode = $passcode;
    }

    public function getUsername(): string
    {
        return $this->username;
    }

    public function setUsername(string $username): void
    {
        $this->username = $username;
    }

    public function getFirstName(): string
    {
        return $this->firstName;
    }

    public function setFirstName(string $firstName): void
    {
        $this->firstName = $firstName;
    }

    public function getLastName(): string
    {
        return $this->lastName;
    }

    public function setLastName(string $lastName): void
    {
        $this->lastName = $lastName;
    }

    public function getFullName(): string
    {
        return $this->firstName . ' ' . $this->lastName;
    }

    public function getPasscode(): string
    {
        return $this->passcode;
    }

    public function setPasscode(string $passcode): void
    {
        $this->passcode = $passcode;
    }
}

Child.php

<?php

namespace App\Domain\Model\User;

use App\Domain\Model\HasUuid;
use App\Domain\Model\User\User;
use App\Domain\Model\User\Guardian;

class Child extends User
{
    use HasUuid;

    public function __construct(
        private string $username,
        private string $firstName,
        private string $lastName,
        private string $passcode,
        private array $guardians,
    )
    {
        parent::__construct($username, $firstName, $lastName, $passcode);
    }

    public function getGuardians(): array
    {
        return $this->guardians;
    }

    public function addGuardian(Guardian $guardian): void
    {
        $this->guardians[] = $guardian;
    }
}

守护者.php

<?php

namespace App\Domain\Model\User;

use App\Domain\Model\HasUuid;
use App\Domain\Model\User\User;
use App\Domain\Model\User\Child;

class Guardian extends User
{
    use HasUuid;

    public function __construct(
        private string $username,
        private string $firstName,
        private string $lastName,
        private string $passcode,
        private string $email,
        private string $telnr,
        private array $children,
    )
    {
        parent::__construct($username, $firstName, $lastName, $passcode);
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function setEmail(string $email): void
    {
        $this->email = $email;
    }

    public function getTelnr(): string
    {
        return $this->telnr;
    }

    public function setTelnr(string $telnr): void
    {
        $this->telnr = $telnr;
    }

    public function getChildren(): array
    {
        return $this->children;
    }

    public function addChild(Child $child): void
    {
        $this->children[] = $child;
    }
}

老师.php

<?php

namespace App\Domain\Model\User;

use App\Domain\Model\Timestampable;
use App\Domain\Model\HasUuid;
use App\Domain\Model\User\User;

class Teacher extends User
{
    use HasUuid;

    public function __construct(
        private string $username,
        private string $firstName,
        private string $lastName,
        private string $passcode,
        private string $email,
        private string $telnr,
    )
    {
        parent::__construct($username, $firstName, $lastName, $passcode);
        $this->email = $email;
        $this->telnr = $telnr;
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function setEmail(string $email): void
    {
        $this->email = $email;
    }

    public function getTelnr(): string
    {
        return $this->telnr;
    }

    public function setTelnr(string $telnr): void
    {
        $this->telnr = $telnr;
    }
}

现在我使用 XML 格式的 Doctrine/ORM 来生成数据库方案。这是我当前的配置: 用户.orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
    http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

    <entity name="App\Domain\Model\User\User" table="users" inheritance-type="JOINED">
        <id name="id" type="guid" column="id">
            <generator strategy="NONE"/>
        </id>

        <field name="username" type="string" length="255" nullable="false"/>
        <field name="firstName" type="string" length="255" nullable="false"/>
        <field name="lastName" type="string" length="255" nullable="false"/>
        <field name="passcode" type="string" length="255" nullable="false"/>
        
        <field name="createdAt" type="datetime" nullable="false"/>
        <field name="updatedAt" type="datetime" nullable="false"/>
        <field name="deletedAt" type="datetime" nullable="true"/>
        
        <discriminator-column name="type" type="string" />
        <discriminator-map>
        <discriminator-mapping value="teacher" class="App\Domain\Model\User\Teacher" />
        <discriminator-mapping value="guardian" class="App\Domain\Model\User\Guardian" />
        <discriminator-mapping value="child" class="App\Domain\Model\User\Child" />
    </discriminator-map>

    </entity>
</doctrine-mapping>

Child.orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
    http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

    <entity name="App\Domain\Model\User\Child" table="children">

        <!-- Many-to-many relationship with Guardian -->
        <many-to-many field="guardians" target-entity="App\Domain\Model\User\Guardian">
            <join-table name="guardians_children">
                <join-columns>
                    <join-column name="child_id" referenced-column-name="id" nullable="false"/>
                </join-columns>
                <inverse-join-columns>
                    <join-column name="guardian_id" referenced-column-name="id" nullable="false"/> <!-- Ensure NOT NULL -->
                </inverse-join-columns>
            </join-table>
        </many-to-many>
    </entity>
</doctrine-mapping>

Guardian.orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
    http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

    <entity name="App\Domain\Model\User\Guardian" table="guardians">

        <!-- Many-to-many relationship with Child -->
        <many-to-many field="children" target-entity="App\Domain\Model\User\Child" inversed-by="guardians">
            <join-table name="guardian_child">
                <join-columns>
                    <join-column name="guardian_id" referenced-column-name="id" nullable="false"/> <!-- Ensure NOT NULL -->
                </join-columns>
                <inverse-join-columns>
                    <join-column name="child_id" referenced-column-name="id" nullable="false"/> <!-- Ensure NOT NULL -->
                </inverse-join-columns>
            </join-table>
        </many-to-many>
    </entity>
</doctrine-mapping>

教师.orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
    http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
    <entity name="App\Domain\Model\User\Teacher" table="teachers">
        <field name="email" type="string" length="255" nullable="false"/>
        <field name="telnr" type="string" length="50" nullable="false"/>
    </entity>
</doctrine-mapping>

由此生成迁移会创建以下表: [Database1

如您所见,表包含正确的字段,并且用户包含用于确定用户类型的鉴别器。但是代表不同类型用户的表缺少一些东西。当我在这个数据库中检索教师时,我不知道他实际上是什么用户,并且不可能获得名字、姓氏……因为表 Teachers 没有与表 Users 的外键。

我想我可以通过在xml的子表中添加多对一来解决这个问题:

    <entity name="App\Domain\Model\User\Teacher" table="teachers">
        <field name="email" type="string" length="255" nullable="false"/>
        <field name="telnr" type="string" length="50" nullable="false"/>
        
        <many-to-one field="user" target-entity="App\Domain\Model\User\User" inversed-by="teachers">
            <join-column name="id" referenced-column-name="id" nullable="false"/>
        </many-to-one>
    </entity>

但这会出现以下错误:

 Property App\Domain\Model\User\Teacher::$user does not exist

这可以通过向教师类添加属性

$user
来解决。但老师已经从用户扩展,所以这对我来说似乎没有意义。

我认为我做错了什么,我如何成功实现不同类型的用户?

php symfony doctrine-orm
1个回答
© www.soinside.com 2019 - 2024. All rights reserved.