Zend Framework 2 db Validator Zend\Validate\Db\NoRecordExists - 我只是不能让事情为我工作



我一直在到处搜索,找到了一些"解决方案",有些甚至让我重写了大部分InputFilter,并在Module.php和/或Module.config.php中添加了很多东西……运气不好。。。只是不能让它为我工作,仍然有各种各样的错误。我决定撤消所有内容,从头开始(在根据数据库验证表单条目之前,我的代码最初看起来是这样的),并在这里问我的问题。

我正在办理注册手续。当然,我需要根据用户表中的现有记录验证电子邮件地址(不允许有两个相同的电子邮件地址)。当然,在我的数据库中,我将该列设置为只接受唯一值。。。但在实际使用数据库之前,我还必须验证它,并在表单提交中向用户提供适当的消息。

如何使用Db\NoRecordExists(或任何其他Db验证器)?我应该在代码中进一步写些什么(添加/编辑)?我已将所有代码粘贴到下面。我需要添加Db\NoRecordExists验证器的表单元素是"user_identifier"。

这是我的/config/autoload/global.php:

return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=my_database_name;host=localhost',
'username' => 'my_user',
'password' => 'my_password',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES 'UTF8'',
),
),
'service_manager' => array(
'factories' => array(
'ZendDbAdapterAdapter' => 'ZendDbAdapterAdapterServiceFactory',
),
), 
);

这是我的注册表格(/module/User/src/User/form/RegisterForm.php):

namespace UserForm;
use ZendFormForm;
class RegisterForm extends Form {
public function __construct() {
parent::__construct('register');
$this->setHydrator(new ZendStdlibHydratorReflection());
$this->setObject(new UserEntityUsers());
$this->setAttributes(array(
// not important for my issue
));
$this->setInputFilter(new UserFormRegisterFilter);
// User Identifier
$identifier = new ZendFormElementEmail();
$identifier->setName('user_identifier');
$identifier->setAttributes(array(
'id' => 'user-email',
'placeholder' => 'Email',
'class' => 'form-control'
));
$identifier->setLabel('Your email:');
/*
* Many other fields were here, but to make the sample code
* shorter here, I've only left one of the fields I need to
* validate against the database
*/
$this->add($identifier); // User's email - used for login
// Submit
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Register',
'class' => 'btn btn-primary',
),
));
}
}

这是我的RegisterFilter(/module/User/src/User/Form/RegisterFilter.php):

namespace UserForm;
use ZendFormForm;
use ZendInputFilterInputFilter;
use ZendInputFilterInputFilterAwareInterface;
use ZendInputFilterInputFilterInterface;
use ZendDbAdapterAdapter;
class RegisterFilter extends InputFilter {
public function __construct() {
// User Identifier (Email)
$this->add(array(
'name' => 'user_identifier',
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
),
'validators' => array(
/*
* Some validators here (NotEmpty, EmailAddress)
*/
array(
'name' => 'DbNoRecordExists',
'options' => array(
'table' => 'users',
'field' => 'user_identifier',
/*
* 'adapter' => had many examples for what to put here, like:
* ZendDbTableGatewayFeatureGlobalAdapterFeature::getStaticAdapter()
* and for that I also had to put:
'ZendDbAdapterAdapter' => function ($sm) {
$adapterFactory = new ZendDbAdapterAdapterServiceFactory();
$adapter = $adapterFactory->createService($sm);
ZendDbTableGatewayFeatureGlobalAdapterFeature::setStaticAdapter($adapter);
return $adapter;
}
* in my /config/autoload/global.php (and can't remember anything else) BUT, while
* following the example to the letter, I still got errors (like "no static adapter blah-blah - can't remember) and didn't work
* and so on... followed quite a few different examples/methods, rewrote/added many lines in my code
* (in the Model and/or Controller and/or Module.php) but still couldn't make things work for me.
*/
),
),
),
));
/*
* Filters and validators for the rest of the form elements here
* Removed them so I would keep the code focused on my question
*/
}
}

这是我的用户模块module.php(/module/User/module.php):

namespace User;
use UserEntityUsers;
use UserEntityUsersTable;
use ZendDbResultSetResultSet;
use ZendDbTableGatewayTableGateway;
class Module {
public function getAutoloaderConfig() {
// ...
}
public function getConfig() {
// ...
}
public function getViewHelperConfig() {
// ...
}
public function getServiceConfig() {
return array(
'factories' => array(
'UserEntityUsersTable' => function($sm) {
$tableGateway = $sm->get('UsersTableGateway');
$table = new UsersTable($tableGateway);
return $table;
},
'UsersTableGateway' => function($sm) {
$dbAdapter = $sm->get('ZendDbAdapterAdapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Users());
return new TableGateway('users', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
}

这是我的模型(/module/User/src/User/Entity/Users.php):

namespace UserEntity;
class Users {
public $user_id;
public $other_id;
public $user_identifier;
public $user_credential;
public $user_type;
public $user_alias;
public $active;
public $enabled;
public $token;
public $created;
public function exchangeArray($data) {
$this->user_id = (isset($data['user_id'])) ? $data['user_id'] : null;
$this->other_id = (isset($data['other_id'])) ? $data['other_id'] : 0;
$this->user_identifier = (isset($data['user_identifier'])) ? $data['user_identifier'] :  'what?';
$this->user_credential = (isset($data['user_credential'])) ? md5($data['user_credential']) : 'not-possible';
$this->user_type = (isset($data['user_type'])) ? $data['user_type'] : 'client';
$this->user_alias = (isset($data['user_alias'])) ? $data['user_alias'] : 'Anonymus';
$this->active = (isset($data['active'])) ? $data['active'] : 0;
$this->enabled = (isset($data['enabled'])) ? $data['enabled'] : 1;
$this->token = (isset($data['token'])) ? $data['token'] : 'no-token';
$this->created = (isset($data['created'])) ? $data['created'] : date('Y-m-d h:m:s', time());
}
public function getArrayCopy() {
return get_object_vars($this);
}
}

以及TableGateway(/module/User/src/User/Entity/UsersTable.php):

namespace UserEntity;
use ZendDbTableGatewayTableGateway;
class UsersTable {
protected $tableGateway;
public function __construct(TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
}
/*
* fetchAll(), getUserById(), deleteUser() etc.
* Different methods here...
*/
public function saveUser(Users $user) {
$data = array(
'user_id' => $user->user_id,
'other_id' => $user->other_id,
'user_identifier' => $user->user_identifier,
'user_credential' => $user->user_credential,
'user_type' => $user->user_type,
'user_alias' => $user->user_alias,
'active' => $user->active,
'enabled' => $user->enabled,
'token' => $user->token,
'created' => $user->created
);
$user_id = (int)$user->user_id;
if ($user_id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getUser($user_id)) {
$this->tableGateway->update($data, array('user_id' => $user_id));
} else {
throw new Exception("User with id {$user_id} does not exist");
}
}
}
}

最后,但同样重要的是,这是我的控制器(/module/User/src/User/controller/IndexController.php):

namespace UserController;
use ZendMvcControllerAbstractActionController;
use ZendViewModelViewModel;
use UserEntityUsers;
class IndexController extends AbstractActionController {
public function registerAction() {
$registerForm = new UserFormRegisterForm;
if ($this->getRequest()->isPost()) {
// Form processing
$formData = $this->getRequest()->getPost();
$registerForm->setData($formData);
if ($registerForm->isValid()) {
// Insert into DB here
$user = new Users();
$user->exchangeArray($formData->user);
$this->getUsersTable()->saveUser($user);
}
return new ViewModel(array(
'form' => $registerForm,
));
} else {
return new ViewModel(array(
'form' => $registerForm,
));
}
}
/*
* Other methods go here
* login, logout, editAccount, emailConfirmation
* etc.
*/
public function getUsersTable() {
if (!$this->usersTable) {
$sm = $this->getServiceLocator();
$this->usersTable = $sm->get('UserEntityUsersTable');
}
return $this->usersTable;
}
}

首先,我觉得有必要在这里大喊:如果在我的/config/autoload/global.php中,我已经设置了数据库连接,那么我到底为什么要在应用程序的任何其他地方指定任何关于数据库的(其他)内容,除了我想要使用的表?为什么我必须在模块级别(有时甚至在控制器中)处理setter和getter、工厂和服务经理等等,并使代码变得如此丰富?根据我从各种例子中看到的(我从中在Module.php中的getServiceConfig()中编写了代码),我需要为每个实体都这样做。wtf?这真的很糟糕!伟大的时刻!那么,如果我在控制器中使用#n个表,我必须有#n个函数,比如"public function getUsersTable(){}"?还有#n个工厂,比如Module.php中我的getServiceConfig()中的"User\Entity\UsersTable"?那是垃圾!这是最好的路吗?或者我只是运气不好,在学习zf2时一直在寻找最糟糕的例子?为什么我的控制器中应该有一个getTableNameTable()函数?模特不应该担心我说的是哪张桌子吗?既然每个模型都是为一个特定的表设计的?就像在zf1中一样,在我的模型中我只需要"protected$_name='users';",这就是我所需要的。

为什么在我的应用程序中,这些连接设置不能"神奇地"(简单地)在任何地方都可用,比如在zf1中?否则我为什么要把它放在的配置中?我真的不明白为什么我需要Module.php中的getServiceConfig()中的所有这些,我该如何避免呢?

zf1中的东西更加紧凑。在zf2中,大多数时候我根本不知道自己在做什么,我复制狙击手并在表单提交或F5上捕食,它们开箱即用,我没有错误。我可能还应该提到,我对OOP没有很强的理解,我只是一个试图学习zf2的新手(在两个项目中使用了zf1,这些项目与数据库、内容管理、ajax、使用facebookapi、谷歌地图有很大关系)。有了zf1,即使我对OOP有一个很好的想法,我仍然可以做我需要做的任何事情。在zf2中,每一件事都有1000种开箱即用的方法。几乎每次我在寻找解决我遇到的问题的方法时,我都会发现很多例子。。。但大多数时候,没有一个例子有与我相似的基本代码可以构建,所以我必须重写很多(因为我不能适应,我是一个新手,如果我适应了我发现的东西,我会立即出现错误,即使我根据我发现的例子重写,有时也会出现错误)。

所以,我要问的是:

  1. 在上面粘贴的代码中,为了对数据库进行验证,我应该添加/修改什么?因为,现在,我得到"不存在数据库适配器"<em>当然我会<em>

  2. 这可能超出了本文最初的范围,但我如何才能避免如此多的代码和配置散布在各处?我不想在我的应用程序中指定任何关于数据库连接的内容,它真的应该在一个地方完成,所有其他模块、实体和控制器都应该知道关于数据库的所有信息(不告诉他们在每个控制器/模型中找哪里,他们应该只从配置中知道),他们在模型(实体)/控制器级别需要知道的任何其他信息都应该是我想要使用的表,我不需要重复自己的话,不需要到处说"这是我的适配器,在那里-从>这里<得到它"-这太糟糕了)不要再干扰getter和setter、工厂和服务经理,"让这个从这里到那里"等等。我不应该在我的InputFilters或global/local.php之外的任何地方指定适配器。我应该在输入过滤器中告诉验证器的只是要验证的表和列,这不是更自然吗?

我通过将适配器传递到我的接口来实现这一点:

控制器代码:

if ($request->isPost()) {
$dbAdapter = $this->getServiceLocator()->get('ZendDbAdapterAdapter');
$admins = new Admins($dbAdapter);
.
.
.
}

我的接口代码:

class Admins implements InputFilterAwareInterface 
{
public $id;
public $first_name;
public $last_name;
/* etc */
private $gatewayAdapter;
public function __construct($dbAdapter = null) {
$this->gatewayAdapter = $dbAdapter;
}
/* etc*/
public function getInputFilter() {
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory     = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name'     => 'id',
'required' => true,
'filters'  => array(
array('name' => 'Int'),
),
)));
/* etc */
$inputFilter->add($factory->createInput(array(
'name'     => 'email',
'required' => true,
'filters'  => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array('name'    => 'NotEmpty',),
array(
'name' => 'DbNoRecordExists',
'options' => array(
'table' => 'admins',
'field' => 'email',
'adapter' => $this->gatewayAdapter
),
),
),
)));
/* etc */
}
/* etc */
}

最新更新