Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 如何通过datamapper将受保护的属性保存到数据库?_Php_Oop - Fatal编程技术网

Php 如何通过datamapper将受保护的属性保存到数据库?

Php 如何通过datamapper将受保护的属性保存到数据库?,php,oop,Php,Oop,我试图理解datamapper的概念,我希望这是与受保护属性结合使用的正确术语 我正在建立一个认证系统。我有一个用户类 class User { protected $id; public $first_name; public $mail; protected $password; 如您所见,我选择对$id和$password进行保护。事实上,我不太确定这是否正确,但我确实读过,应该尽可能地保持属性的范围是封闭的 我还构建了一个数据映射器,将用户对象保存到数据库中。映射器通过构造函数依赖项

我试图理解datamapper的概念,我希望这是与受保护属性结合使用的正确术语

我正在建立一个认证系统。我有一个用户类

class User {

protected $id;
public $first_name;
public $mail;
protected $password;
如您所见,我选择对$id和$password进行保护。事实上,我不太确定这是否正确,但我确实读过,应该尽可能地保持属性的范围是封闭的

我还构建了一个数据映射器,将用户对象保存到数据库中。映射器通过构造函数依赖项注入注入到用户类中。我以这种方式从用户类内部调用mappers save方法

public function save () {
    return $this->dep['mapper']->saveUser($this);
}
在我的mappers saveUser方法中,我正在构建一个值数组以传递给我的数据库类

public function saveUser($obj) {

    $insert_array;

    foreach ( $obj as $key => $value ) {
        $insert_array[$key] = $obj->get($key);
    }
这并没有按预期的方式工作,因为我的映射程序无法迭代受保护的属性。因此,这些属性不会传递给数据库。如果上述房产是公共的,那么它就可以正常使用

所以我的问题是:如何设置我的类和方法,使我的映射器能够获得它所需要的所有值,而不公开我的所有属性


额外:我已经使用了uu get来避免这个问题,但是这是一个好的编码实践吗?

对于这个问题没有唯一正确的答案,但是在我看来,您不希望数据对象中的字段具有不同的可见性。这里有一些想法

如果设置了对用户类上的字段具有不同的可见性,则可以这样更改,以允许映射器使用在用户类的save方法中构建的数组来保存数据

<?php

    class User
    {
        protected $id;
        public    $first_name;
        public    $mail;
        protected $password;

        private $dep = [];

        public function __construct()
        {
            $this->dep['mapper'] = new Mapper();
        }

        public function save()
        {
            $data = [
                'id' => $this->id,
                'first_name' => $this->first_name,
                'mail' => $this->mail,
                'password' => $this->password
            ];

            return $this->dep['mapper']->saveUser($data);
        }
    }

    class Mapper
    {
        public function saveUser($data)
        {
            foreach($data as $field=>$value)
            {
                echo $field.': '.$value.PHP_EOL;
            }
        }
    }

    $myUser = new User();

    $myUser->first_name = 'Lando';
    $myUser->mail = 'lando@cloudcity.gov';

    $myUser->save();
更正式的选择是使用数据传输对象DTO,这是一个只封装数据的死简单类。然后,您可以控制对业务对象中字段的访问

<?php

class User
{
    private $dto;
    private $dep = [];

    public function __construct(UserDto $dto)
    {
        $this->dto           = $dto;
        $this->dep['mapper'] = new Mapper();
    }

    public function __get($propName)
    {
        if($propName=='password')
        {
            throw new Exception('No password for you');
        }
        elseif(property_exists($this->dto, $propName))
        {
            return $this->dto->$propName;
        }

        throw new InvalidArgumentException('No property '.$propName.' found in object');
    }

    public function __set($propName, $value)
    {
        if($propName=='id')
        {
            throw new Exception('ID may not be changed');
        }
        elseif($propName=='password')
        {
            throw new Exception('Password may not be changed');
        }
        elseif(property_exists($this->dto, $propName))
        {
            $this->dto->$propName = $value;
        }
        else
        {
            $this->$propName = $value;
        }
    }

    public function __isset($propName)
    {
        return (property_exists($this->dto, $propName));
    }

    public function save()
    {
        return $this->dep['mapper']->saveUser($this->dto);
    }
}

class UserDto
{
    public $id;
    public $first_name;
    public $mail;
    public $password;
}

class Mapper
{
    public function saveUser(UserDto $dto)
    {
        foreach ($dto as $key => $value)
        {
            $insert_array[$key] = $dto->$key;

            echo $key.': '.$value.PHP_EOL;
        }
    }
}

try
{
    $dto    = new UserDto();
    $myUser = new User($dto);

    $myUser->first_name = 'Lando';
    $myUser->mail       = 'lando@cloudcity.gov';

    echo $myUser->password;
    $myUser->password = 'foobar';

    $myUser->save();
}
catch(Exception $e)
{
    echo $e->getMessage().PHP_EOL;
}
控制属性访问的更好选项是使用get/set/has方法。这是冗长的,但有一个好处,就是在获取和设置数据时向数据添加逻辑或转换。这种方法的一个主要好处是,功能齐全的代码编辑器将完成所有这些getter和setter的代码编写,这是魔术方法无法做到的。当然,您可以将其与DTO结合使用

<?php

class User
{
    private $data = [
        'id'=>null,
        'first_name'=>null,
        'mail'=>null,
        'password'=>null
    ];

    private $dep = [];

    public function __construct($data)
    {
        $validData = array_intersect_key($data, $this->data);

        foreach($validData as $currKey=>$currValue)
        {
            $this->data[$currKey] = $currValue;
        }

        $this->dep['mapper'] = new Mapper();
    }

    public function getId()
    {
        return $this->data['id'];
    }

    //Notice there is no setter for ID!

    public function hasId()
    {
        return (!empty($this->data['id']));
    }

    public function getFirstName()
    {
        return $this->data['first_name'];
    }

    public function setFirstName($val)
    {
        $this->data['first_name'] = $val;
    }

    public function hasFirstName()
    {
        return (!empty($this->data['first_name']));
    }

    public function getMail()
    {
        return $this->data['mail'];
    }

    public function setMail($val)
    {
        $this->data['mail'] = $val;
    }

    public function hasMail()
    {
        return (!empty($this->data['mail']));
    }

    //Notice there is no getter for ID!

    public function setPassword($val)
    {
        $hashed = md5($val); //Just an example, don't do this
        $this->data['password'] = $hashed;
    }

    public function hasPassword()
    {
        return (!empty($this->data['password']));
    }

    public function save()
    {
        return $this->dep['mapper']->saveUser($this->data);
    }
}

class Mapper
{
    public function saveUser($data)
    {
        foreach($data as $field=>$value)
        {
            echo $field.': '.$value.PHP_EOL;
        }
    }
}

try
{
    $dataFromDb = [
        'id'=>123,
        'first_name'=>'Lando',
        'mail'=>'lando@cloudcity.gov',
    ];

    $myUser = new User($dataFromDb);

    $myUser->setFirstName('Chewie');
    $myUser->setMail('wookie@kashyyyk.net');

    if(!$myUser->hasPassword())
    {
        $myUser->setPassword('AAAAAARRRRRRGHHHH');
    }

    $myUser->save();
}
catch(Exception $e)
{
    echo $e->getMessage().PHP_EOL;
}
我更喜欢这样做,所有详细的样板文件都被降级为数据访问对象,这些对象封装了数据并处理加载和保存单个记录,而单个记录的应用程序逻辑包含在主业务对象中。它们可以是超类或特质,任何能让你的船漂浮的东西。就我个人而言,我有基于数据库模式为我编写所有DAO和业务对象类的代码,所以我只需要担心应用程序逻辑

<?php

trait UserDao
{
    private $data = [
        'id'=>null,
        'first_name'=>null,
        'mail'=>null,
        'password'=>null
    ];

    private $deps;

    public function getId()
    {
        return $this->data['id'];
    }

    //Notice there is no setter for ID!

    public function hasId()
    {
        return (!empty($this->data['id']));
    }

    public function getFirstName()
    {
        return $this->data['first_name'];
    }

    public function setFirstName($val)
    {
        $this->data['first_name'] = $val;
    }

    public function hasFirstName()
    {
        return (!empty($this->data['first_name']));
    }

    public function getMail()
    {
        return $this->data['mail'];
    }

    public function setMail($val)
    {
        $this->data['mail'] = $val;
    }

    public function hasMail()
    {
        return (!empty($this->data['mail']));
    }

    private function _getPassword()
    {
        return $this->data['password'];
    }

    private function _setPassword($val)
    {
        $this->data['password'] = $val;
    }

    public function hasPassword()
    {
        return (!empty($this->data['password']));
    }

    public function load($data)
    {
        $validData = array_intersect_key($data, $this->data);

        foreach($validData as $currKey=>$currValue)
        {
            $this->data[$currKey] = $currValue;
        }
    }

    private function _save()
    {
        return $this->dep['mapper']->saveUser($this->data);
    }
}

class User
{
    use UserDao;

    public function __construct()
    {
        $this->dep['mapper'] = new Mapper();
    }

    public function setPassword($val)
    {
        $hashed = str_rot13($val); //Just an example, don't do this
        $this->_setPassword($hashed);
    }

    public function getPassword()
    {
        return str_rot13($this->_getPassword()); //Just an example, don't do this
    }

    public function save()
    {
        echo 'Do some complex validation here...'.PHP_EOL;

        $this->_save();
    }
}

class Mapper
{
    public function saveUser($data)
    {
        foreach($data as $field=>$value)
        {
            echo $field.': '.$value.PHP_EOL;
        }
    }
}

try
{
    $dataFromDb = [
        'id'=>123,
        'first_name'=>'Lando',
        'mail'=>'lando@cloudcity.gov',
    ];

    $myUser = new User();
    $myUser->load($dataFromDb);

    $myUser->setFirstName('Chewie');
    $myUser->setMail('wookie@kashyyyk.net');

    if(!$myUser->hasPassword())
    {
        $myUser->setPassword('AAAAAARRRRRRGHHHH');
    }

    $myUser->save();

    echo 'Unfutzed Password: '.$myUser->getPassword().PHP_EOL;
}
catch(Exception $e)
{
    echo $e->getMessage().PHP_EOL;
}

我建议对这个主题做一些研究,有很多模式,每个人都有不同的观点。

如果有关于用户对象的值得了解的内容,至少应该是可读的;否则,为什么要保存它?我通常的方法是,在保存对象时,保存输入不一定是对象属性,因此可以绕过对象上的设置属性,然后读取保存阶段的属性,只保存原始输入。如果一个属性需要在保存前由用户对象处理,它会返回到我的第一句话。如果值得了解,它应该是可读的。