【问题标题】:symfony doctrine using setters and getters dynamically动态使用 setter 和 getter 的 symfony 学说
【发布时间】:2014-05-29 12:08:08
【问题描述】:

我正在使用 symfony 和教义。

服务器收到对 URL /company/{id} 的 HTTP PATCH 请求,其中包含模型的属性及其值,例如 {"name": "My new name"} 新值需要持久化到数据库中。

$request = Request::createFromGlobals();
$requestContentJSON = $request->getContent();
$requestContentObj = json_decode($requestContentJSON);

$repository = $this->getDoctrine()->getRepository('MyBundle:Company');
$company = $repository->find($id);

现在我可以输入$company->setName($requestContentObj[0]);,但收到的属性会有所不同。现在我正在使用以下代码来处理每个属性:

foreach($requestContentObj as $key => $value){
    switch($key){
        case 'name':
            $company->setName($value);
            break;
        case 'department':
            $company->setDepartment($value);
            break;
        case 'origin':
            $company->setOrigin($value);
            break;
        case 'headquarters':
            $company->setHeadquarters($value);
            break;
        case 'email':
            $company->setEmail($value);
            break;
        case 'twitterid':
            $company->setTwitterId($value);
            break;
        case 'description':
            $company->setDescription($value);
            break;
    }
}

但这看起来不太聪明,尤其是因为我知道我将拥有其他实体,如新闻、产品、用户等,它们的属性将以相同的方式更新。我想做这样的事情:

$company->set("property", "value");

我首先想到的是将这个 switch 语句放在这个 set 函数中的 company 类中,以及我拥有的所有其他实体类中。但是有更好的方法吗?也许 symfony/doctrine 已经内置了解决方案,但我没有找到适合我的解决方案。

我仍然想使用 setter 和 getter 作为长期投资。

谢谢。

【问题讨论】:

    标签: php oop symfony doctrine


    【解决方案1】:

    假设您将拥有类似于方法名称的属性名称。

    你可以做这样的事情。设置多个属性。

    Class customer {
    
        protected $_email;
    
        public function __construct(array $config = array()){
             $this->setOptions($config);
         }
    
        public function getEmail(){
            return $this->_email;
        }
    
        public function setEmail($email){
            $this->_email = $email;
        }
    
        public function setOptions(array $options)
        {
            $_classMethods = get_class_methods($this);
            foreach ($options as $key => $value) {
                $method = 'set' . ucfirst($key);
                if (in_array($method, $_classMethods)) {
                    $this->$method($value);
                } else {
                    throw new Exception('Invalid method name');
                }
            }
            return $this;
        }
    
        public function setOption($key, $value){
            return $this->setOptions(array($key, $value));
        }
    
    }
    

    现在你可以简单地这样做了:

    $array = array('email' => 'abc.@gmail.com');
    $customer = new Customer($array);
    echo $customer->getEmail();
    

    【讨论】:

    • +1 因为这是一个很好的通用解决方案,可以在 symfony/doctrine 上下文之外工作。顺便说一句,您可以删除 ucfirst(),因为 PHP 方法和函数调用实际上不区分大小写!
    • 杰,谢谢你的帮助!尽管@Darragh 刚刚向我展示了一个替代 getter 和 setter 的方法,即使用 symfony 的注解验证。它支持回调,所以除了验证之外,如果我想要哪种渲染 getter 和 setter 无用,我还可以记录错误。否则,我会实施您解决问题的方法。
    • @yurihbss 如果您认为 Jay 的回答有用,请不要忘记为他点赞 :)
    • @yurihbss 这个方法应该在类上下文中使用。对不起,我忘了提到我已经更新了我的答案。
    • 我们也可以使用method_exists()
    【解决方案2】:

    我最初的想法是在你的类中添加一个merge 方法,如下所示:

    <?php
    
    // example Company entity
    class Company
    {
        private $name;
    
        function setName($name)
        {
            $this->name = $name;
        }
    
        function getName()
        {
            return $this->name;
        }
    
        function merge(\stdClass $obj)
        {
            // get the object vars of the passed object
            // iterate, and replace matching properties
            foreach (get_object_vars($obj) as $prop => $val) {
                if (property_exists($this, $prop)) {
                    $this->$prop = $val;
                }
            }
        }
    }
    
    $company = new Company();
    
    // mocking your request object
    $requestContentObj = new stdClass();
    $requestContentObj->name = 'acme';
    
    $company->merge($requestContentObj);
    
    var_dump($company);
    

    产量:

    class Company#1 (1) {
        private $name =>
        string(4) "acme"
    }
    

    这会静默转储任何与Company 类中的任何属性都不匹配的传递值,这可能是也可能不是您想要的。希望这会有所帮助:)

    【讨论】:

    • 嘿!谢谢您的帮助。这大概就是我想要的。如果我是,请纠正我,但我认为即使从实体类内部直接设置属性也是不明智的。如果有一种方法可以实现您的 merge 方法仍然将值重定向到一个更好的设置器,对吗?然后,如果需要,我可以检查在 setter 函数中传递的值。
    • 好点,这取决于!如果您有额外的输入验证(这当然很常见),调用该方法是有意义的。使用 Symfony/Doctrine,我倾向于在我的实体属性上使用验证注释,这在某种程度上否定了实体设置器中对附加逻辑的大部分需求。我使用@PrePersist@PreUpdate 钩子来计算/复合字段。也就是说,这只是一种方法 - 我会说你的观点是有效的 - 你可能非常希望仍然使用设置器。 @Jay 的回答巧妙地解决了这个问题,在这种情况下我可能会做一些非常相似的事情。
    【解决方案3】:

    我可以建议不使用设置器,但它似乎很适合您的问题。 在学说 1.2.4 中,您可以按如下方式使用 DQL:

    $q = Doctrine_Core::getTable("myTable")->createQuery("q")
            ->update()
            ->where("id = ?", $id);
    
        foreach($requestContentObj as $key => $value)
        {
            $q->set($key, "?", $value);
        }
    
        $q->execute();
    

    【讨论】:

    • 谢谢分享。这个例子将来可能对我有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多