假设我在PHP中有一个非常简单的CRUD系统来管理数据库。说它拥有一个产品清单。使用Doctrine ORM,我想查询数据库并查看/编辑/添加记录。根据入门手册,
创建实体类时,所有字段都应受保护或为私有字段(非公共字段),每个字段都应具有getter和setter方法($ id除外)。mutator的使用使Doctrine可以挂接到以实体#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();
Run Code Online (Sandbox Code Playgroud)
但是,这似乎过于复杂。假设我没有必要像实体手册中所述去处理实体的调用。我可以使这段代码更短并执行以下操作:
// src/Product.php
class Product
{
/**
* @var int
*/
public $id;
/**
* @var string
*/
public $name;
}
Run Code Online (Sandbox Code Playgroud)
这将允许这样的事情:
$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;
Run Code Online (Sandbox Code Playgroud)
优点是:
使用它的缺点是什么?我在这里冒险吗?
假设我不需要挂钩来操作实体的调用,如手册中所述。
您不需要挂接到这些调用,但Doctrine 可以。Doctrine 在内部覆盖了这些 getter 和 setter,以尽可能透明地实现 ORM。
是的,它过于复杂,但那是 PHP 语言的错,而不是 Doctrine。使用公共属性并没有本质上的错误,只是 PHP 语言不允许像其他语言(例如Kotlin、C#)那样创建自定义 getter/setter 。
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;
}
}
Run Code Online (Sandbox Code Playgroud)
学说可能会产生:
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 ...
}
Run Code Online (Sandbox Code Playgroud)
(这不是重点,重点是 Doctrine 必须能够覆盖 getter 和 setter 来实现 ORM)
要亲自查看,请检查 Doctrine 的代理目录中生成的 Proxy 对象。有关配置代理目录的更多信息,请参阅高级配置部分。另请参阅使用对象以获取有关代理对象以及 Doctrine 如何利用它们的更多信息。
\n\n\n为什么不在 Doctrine 中使用公共实体类?
\n
其实你想问的是:
\n\n\n\n\n这个 getter/setter 从哪里来?
\n
它来自OOP中的封装。
\n\n\n\n\n那么,什么是封装呢?
\n
它\xe2\x80\x99是我们定义属性和方法可见性的方式。当您\xe2\x80\x99创建类时,您必须问自己哪些属性和方法可以在类外部访问。
\n\n\n\n\n让\xe2\x80\x99s 说我们有一个名为 id 的属性。如果一个类扩展了你的类,是否允许直接操作和访问 id?如果有人创建你的类的实例怎么办?他们是否被允许操纵和访问 id?
\n
不!他们不应该能够直接访问 id 属性。
\n\n\n\n\n为什么不?
\n
示例:如果您只接受以“IX”开头的发票 ID,并且其他类可以访问您的类并且直接设置发票 ID,会发生什么情况?
\n\n如果他们不小心将发票 ID 设置为“XX12345”,会发生什么情况?
\n\n当然,您不能执行任何操作(例如验证输入),因为您使用的是公共属性并且没有设置器。
\n\n但如果你的类中有一个 setter 方法,你可以这样做
\n\nprivate $invoiceId;\n\npublic function setInvoiceId($invoiceId) \n{\n if (is_null($invoiceId) || preg_match("#^IX(.*)$#i", $invoiceId) === 0)\n return false;\n\n $this->invoiceId = $invoiceId;\n\n return $this;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我希望它足够清楚。我会尝试扩展答案
\n