为什么我不能在 Doctrine 实体中使用公共属性?

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

假设我有一个非常简单的 PHP CRUD 系统来管理数据库。假设它包含产品列表。使用 Doctrine ORM,我想查询数据库并查看/编辑/添加记录。根据入门手册,

创建实体类时,所有字段都应受到保护或 私有(非公开),每个都有 getter 和 setter 方法 ($id 除外)。变异器的使用允许 Doctrine 挂钩调用 它以一种你无法做到的方式操纵实体 直接使用entity#field = foo;设置值

这是提供的样本:

// src/Product.php
class Product
{
    /**
     * @var int
     */
    protected $id;
    /**
     * @var string
     */
    protected $name;

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }
}

// Recording a new title
$product->setName("My new name");
$db->persist($product);
$db->flush();

// Echoing the title
echo $product->getName();

但是,这似乎过于复杂。假设我不需要挂钩来操作实体,如手册中所述。我可以使这段代码更短并执行以下操作:

// src/Product.php
class Product
{
    /**
     * @var int
     */
    public $id;
    /**
     * @var string
     */
    public $name;
}

这将允许这样的事情:

$product = $db->getRepository('Product')->find(1);

// Recording a new name
$product->name = "My new title";
$db->persist($product);
$db->flush();

// Echoing the title
echo $product->name;

优点是:

  • 始终使用完全相同的名称
  • 实体类中没有额外的 setter 和 getter

使用这个有什么缺点?我在这里承担某些风险吗?

php doctrine-orm
3个回答
7
投票

假设我不需要挂钩来操作实体,如手册中所述。

不需要加入这些调用,但Doctrine需要。 Doctrine 在内部重写这些 getter 和 setter 以尽可能透明地实现 ORM。

是的,它过于复杂,但这是 PHP 语言的错误,而不是 Doctrine 的错误。使用公共属性本身并没有什么问题,只是 PHP 语言不允许像其他语言那样创建自定义 getter/setter(例如KotlinC#)。

Doctrine 到底是做什么的?它生成所谓的 Proxy 对象来实现延迟加载。这些对象

extend
您的类并覆盖某些方法。

对于您的代码:

// src/Product.php
class Product
{
    /**
     * @var int
     */
    protected $id;
    /**
     * @var string
     */
    protected $name;

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }
}

教义可能会产生:

class Product extends \YourNamespace\Product implements \Doctrine\ORM\Proxy\Proxy
{

    // ... Lots of generated methods ...

    public function getId(): int
    {
        if ($this->__isInitialized__ === false) {
            return (int)  parent::getId();
        }


        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getId', []);

        return parent::getId();
    }

    public function getName(): string
    {

        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getName', []);

        return parent::getName();
    }

    // ... More generated methods ...
}

(这到底做什么不是重点,重点是 Doctrine 必须能够重写 getter 和 setter 来实现 ORM)

要亲自查看,请检查 Doctrine 代理目录中生成的 Proxy 对象。有关配置代理目录的更多信息,请参阅高级配置部分。另请参阅使用对象以获取有关代理对象以及 Doctrine 如何使用它们的更多信息。


1
投票

为什么不在 Doctrine 中使用公共实体类?

其实你想问的是:

这个 getter/setter 从哪里来?

来自OOP中的封装。

那么,什么是封装?

这是我们定义属性和方法可见性的方式。当你创建类时,你必须问自己哪些属性和方法可以在类之外访问。

假设我们有一个名为 id 的属性。如果一个类扩展了你的类,是否允许直接操作和访问 id?如果有人创建你的类的实例怎么办?他们是否被允许操纵和访问 id?

不!他们不应该能够直接访问 id 属性。

为什么不呢?

示例:如果您只接受以“IX”开头的发票 ID,并且其他类可以访问您的类并直接设置发票 ID,会发生什么情况?

如果他们不小心将invoice_id设置为“XX12345”会发生什么?

当然,您不能执行任何操作(例如验证输入),因为您使用的是公共属性并且没有设置器。

但是如果你的类中有一个 setter 方法,你就可以这样做

private $invoiceId;

public function setInvoiceId($invoiceId) 
{
    if (is_null($invoiceId) || preg_match("#^IX(.*)$#i", $invoiceId) === 0)
        return false;

    $this->invoiceId = $invoiceId;

    return $this;
}

我希望它足够清楚。我会尝试扩展答案


0
投票

请注意,从版本 2.4 开始,Doctrine 现在支持具有公共属性的实体的代理对象

请参阅此答案了解更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.