当前位置: 首页 > 知识库问答 >
问题:

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

卓雅达
2023-03-14

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

创建实体类时,所有字段都应该是受保护的或私有的(不是公共的),每个字段都有getter和setter方法(除了$id)。使用变异子可以让条令钩住调用,这些调用以一种如果直接使用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;

优点是:

  • 始终使用完全相同的名称

使用此选项的缺点是什么?我在这里是不是在冒险?

共有2个答案

宰父夕
2023-03-14

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

实际上,你要问的是:

这个能手/二传手来自哪里?

它来自于OOP中的封装。

那么,什么是封装?

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

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

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

为什么不呢?

示例:如果您只接受以“IX”开头的发票标识,而其他类可以访问您的类,并且它们直接设置invoice_id,会发生什么?

如果他们不小心将发票id设置为“XX12345”,会发生什么情况?

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

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

private $invoiceId;

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

    $this->invoiceId = $invoiceId;

    return $this;
}

我希望一切都很清楚。我将尝试扩展答案

南宫龙野
2023-03-14

假设我不需要挂接调用来操纵实体,如手册中所述。

您不需要挂接这些调用,但Doctrine确实需要。条令在内部覆盖这些getter和setter,以尽可能透明地实现ORM。

是的,它过于复杂,但这是PHP语言的错,而不是教义。使用公共属性本身没有什么问题,只是PHP语言不允许像其他一些语言(如静态编程语言、C#)那样创建自定义getters/setters。

学说到底是做什么的?它生成所谓的代理对象来实现延迟加载。这些对象扩展了类并覆盖了某些方法。

// 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 ...
}

(这并不是重点,重点是条令必须能够覆盖getter和setter来实现ORM)

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

 类似资料:
  • 问题内容: 在JPA 2.0规范 说,第22页: 类的实例变量必须是私有的,受保护的或程序包可见性,而与使用字段访问还是属性访问无关。使用属性访问时,属性访问器方法必须是公共的或受保护的。 为什么不允许公共访问? 问题答案: 对于公共字段,将没有办法使代理可靠地工作-如果有人直接访问字段,那么持久性框架就没有简单的方法来拦截该调用并(例如)初始化包含对象(如果它是代理) 。 如果无法进行字段访问,

  • 我正在尝试使用文件系统。我的< code>CMakeLists.txt中有< code>-std=c 11 -std=c 1y。GCC版本为4.9.2。然而,我得到了一个错误: 使用的正确方法是什么?

  • 今天我对封装感兴趣,对此我有一个基本问题。我理解为什么需要为私有字段添加setter和getter方法。但我看到为public int编写,字符串是其他的setter和getter。为什么我们需要为公共字段添加属性?我已经可以从任何地方访问公共变量。你怎么能用塞特和盖特控制我?谢谢

  • 从TensorArray读取: 使用: 问题: 回溯(最近一次调用last):RLU培训中第130行的文件“\main.py”。train()文件“C:\Users\user\Documents\Projects\rl toolkit\rl_training.py”,第129行,在train self中_rpm,赛尔夫。批量大小,自行确定。梯度步数,记录步数b=self。在call result=

  • 我试图使用Java8Javadoc工具,但它抱怨是一个未知标记: 我看到有一些方法可以禁用doclint,但我真的想知道哪些标签列表被支持(或者为什么这个不支持)。 更多信息在这个问题,这个问题和从这个博文。