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

时间:2017-09-03 10:42:59

标签: php doctrine-orm

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

  

创建实体类时,应保护所有字段或   私有(非公开),每个都有getter和setter方法   ($ id除外)。使用mutators允许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

使用它的缺点是什么?我在这里承担了一定的风险吗?

2 个答案:

答案 0 :(得分:4)

  

假设我没有必要挂钩操纵实体的调用,如手册中所述。

不需要挂钩这些调用,但 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对象。有关配置代理目录的详细信息,请参阅Advanced Configuration部分。另请参阅Working with Objects以获取有关代理对象以及Doctrine如何使用它们的更多信息。

答案 1 :(得分:2)

  

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

实际上你要问的是:

  

这个吸气剂/孵化器来自哪里?

它来自OOP中的封装。

  

那么,什么是封装?

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

  

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

NO!他们不应该直接达到id属性。

  

为什么不呢?

示例:如果您只接受以“IX”开头的发票ID并且其他类可以访问您的类并且他们直接设置invoice_id会怎样?

如果他们不小心将invoice_id设置为“XX12345”会怎样?

当然,你不能做任何事情(比如验证输入),因为你使用的是公共属性而没有setter。

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

private $invoiceId;

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

    $this->invoiceId = $invoiceId;

    return $this;
}

我希望它显而易见。我会尝试扩展答案