Php 如何通过datamapper将受保护的属性保存到数据库?
我试图理解datamapper的概念,我希望这是与受保护属性结合使用的正确术语 我正在建立一个认证系统。我有一个用户类Php 如何通过datamapper将受保护的属性保存到数据库?,php,oop,Php,Oop,我试图理解datamapper的概念,我希望这是与受保护属性结合使用的正确术语 我正在建立一个认证系统。我有一个用户类 class User { protected $id; public $first_name; public $mail; protected $password; 如您所见,我选择对$id和$password进行保护。事实上,我不太确定这是否正确,但我确实读过,应该尽可能地保持属性的范围是封闭的 我还构建了一个数据映射器,将用户对象保存到数据库中。映射器通过构造函数依赖项
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;
}
我建议对这个主题做一些研究,有很多模式,每个人都有不同的观点。如果有关于用户对象的值得了解的内容,至少应该是可读的;否则,为什么要保存它?我通常的方法是,在保存对象时,保存输入不一定是对象属性,因此可以绕过对象上的设置属性,然后读取保存阶段的属性,只保存原始输入。如果一个属性需要在保存前由用户对象处理,它会返回到我的第一句话。如果值得了解,它应该是可读的。