1

我最近开始学习 ZF2,希望有人可以帮助我。

我正在学习 Rob Allen 的 Zend Framework 2 教程(非常感谢@rob-allen)。

我还使用了@AlloVince 和@Maciej How to set db adapter to Validator RecordExists in Zend Framework 2的解决方案(非常感谢两位作者),我很困惑,因为没有在editAction中使用这个解决方案。

Fatal error: Call to a member function get() on a non-object'adapter' => $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter').

1)在Module.php中添加

public function getServiceConfig()
{
    return array(
        'invokables' => array(
            'RegionModel' => 'FcLibraries\Model\Region', //<-- added it
        ),
        'factories' => array(
            'FcLibraries\Model\RegionTable' => function ($sm) {
                $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                $table = new RegionTable($dbAdapter);
                return $table;
            },
        ),
    );
}

2)在Region.php中添加

/**
 * @var
 */
protected $serviceLocator;
/**
 * @param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator
 * @return Library
 */
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
    $this->serviceLocator = $serviceLocator;
    return $this;
}
/**
 * @return \Zend\ServiceManager\ServiceLocatorInterface
 */
public function getServiceLocator()
{
    return $this->serviceLocator;
}

        $inputFilter->add($factory->createInput(array(
            'name' => 'name',
            'required' => true,
            'filters' => $this->_filters,
            'validators' => array(
                array(
                    'name' => 'StringLength',
                    'options' => array(
                        'encoding' => 'UTF-8',
                        'min' => 1,
                        'max' => 30,
                    ),
                ),
                array(
                    'name'    => 'Db\NoRecordExists',
                    'options' => array(
                        'table' => $this->table,
                        'field' => 'name',
                        //'exclude' => array(
                        //    'field' => 'id',
                        //    'value' => $this->id
                        //),
                        'adapter' => $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'),
                    ),
                ),
            ),
        )));

3)在 addAction 中的 RegionController.php 中使用

$model = $this->getServiceLocator()->get('RegionModel');

而不是$model = new Region();.

这适用于 addAction,但我不明白应该如何在 editAction 中使用它。

我的

public function editAction()
{
    $id = (int)$this->params()->fromRoute('id', 0);
    if (!$id) {
        return $this->redirect()->toRoute('zfcadmin/region', array(
            'action' => 'add'
        ));
    }
    $data = $this->getRegionTable()->get($id);
    $form = new RegionForm();
    $form->bind($data);
    $form->get('submitBtn')->setAttribute('value', 'Save');
    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setInputFilter($data->getInputFilter());
        $form->setData($request->getPost());
        if ($form->isValid()) {
            $this->getRegionTable()->save($form->getData());
            return $this->redirect()->toRoute('zfcadmin/regions');
        }
    }
    return array(
        'id' => $id,
        'form' => $form,
    );
}

我的 RegionTable 有以下代码:

/**
 * @param \Zend\Db\Adapter\Adapter $adapter
 */
public function __construct(Adapter $adapter)
{
    $this->adapter = $adapter;
    $this->resultSetPrototype = new ResultSet();
    $this->resultSetPrototype->setArrayObjectPrototype(new Region());
    $this->initialize();
}
public function get($id)
{
    $id = (int)$id;
    $rowSet = $this->select(array('id' => $id));
    $row = $rowSet->current();
    if (!$row) {
        throw new \Exception("Could not find row $id");
    }
    return $row;
}

非常感谢所有回答我问题的人。

最好的问候,鲁斯兰。

4

2 回答 2

3

您应该通过服务管理器实例化一个新实体以使用数据库适配器,而不是使用从表中收集的实体中的表单过滤器。

你有几个选择:

  1. 将输入过滤器移动到它自己的类并通过服务管理器进行实例化,以便注入数据库适配器。
  2. 将表网关工厂中的原型对象更改为通过服务管理器工厂实例化。
  3. 通过服务管理器实例化一个单独的实体并从那里获取输入过滤器。

我个人会选择选项 1,因为它可以更好地分隔代码。

一些例子:

选项1(我的选择):

这涉及将过滤器移动到它自己的文件和类中,在注入数据库适配器的同时为其创建一个工厂。然后,我们将在控制器中通过服务管理器获取过滤器并将过滤器应用于表单。

因此,首先将您的过滤器移动到 ModName\src\ModName\Form\RegionFilter.php 中的文件,显然将 ModName 替换为您的模块名称。

并将代码更改为:

<?php
namespace Region\Form;

use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
use Zend\Db\Adapter\Adapter;

class RegionFilter implements InputFilterAwareInterface {

    /**
     * @var inputFilter
     */
    protected $inputFilter;

    /**
     * @var Database Adapter
     */
    protected $dbAdapter;

    /**
     * @param \Zend\InputFilter\InputFilterInterface $inputFilter
     * @throws \Exception
     */
    public function setInputFilter(InputFilterInterface $inputFilter) {
        throw new \Exception("Not used");
    }

    /**
     * @param \Zend\Db\Adapter $dbAdapter
     */
    public function __construct(Adapter $dbAdapter) {
        $this->dbAdapter = $dbAdapter;
    }

    /**
     * 
     * @return Zend\Db\Adapter
     */
    public function getDbAdapter() {
        return $this->dbAdapter;
    }

    /**
     * @return \Zend\InputFilter\InputFilter
     * 
     * Get the input filter (build it first)
     */
    public function getInputFilter() {
        if (!$this->inputFilter) {

            $inputFilter = new InputFilter();
            $factory = new InputFactory();

            $inputFilter->add($factory->createInput(array(
                'name' => 'name',
                'required' => true,
                'filters' => $this->_filters,
                'validators' => array(
                    array(
                        'name' => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'min' => 1,
                            'max' => 30,
                        ),
                    ),
                    array(
                        'name' => 'Db\NoRecordExists',
                        'options' => array(
                            'table' => $this->table,
                            'field' => 'name',
                            //'exclude' => array(
                            //    'field' => 'id',
                            //    'value' => $this->id
                            //),
                            'adapter' => $this->getDbAdapter(),
                        ),
                    ),
                ),
            )));
                    $this->inputFilter = $inputFilter;
        }

        return $this->inputFilter;
    }
}
?>

然后,您将在 Module.php 中创建一个工厂,如下所示:

public function getServiceConfig()
{
    return array(
        'factories' => array(
            'ModName\Form\RegionFilter' => function($sm) {
                $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                return new RegionFilter($dbAdapter);
            },
        ),
    );
}

最后在您的控制器中,只需执行以下操作:

if ($request->isPost()) {
    $filter = $this->getServiceLocator()->get('ModName\Form\RegionFilter');
    $form->setInputFilter($filter->getInputFilter());
    $form->setData($request->getPost());
    if ($form->isValid()) {
        $this->getRegionTable()->save($form->getData());
        return $this->redirect()->toRoute('zfcadmin/regions');
    }
}

选项 2:

这涉及使用Region注入的实例构建您的表。然后您可以将原型设置为此。

所以在你的表结构中:

public function __construct(Adapter $adapter, Region $region)
{
    $this->adapter = $adapter;
    $this->resultSetPrototype = new ResultSet();
    $this->resultSetPrototype->setArrayObjectPrototype($region);
    $this->initialize();
}

然后你的工厂:

public function getServiceConfig()
{
    return array(
        'invokables' => array(
            'RegionModel' => 'FcLibraries\Model\Region', 
        ),
        'factories' => array(
            'FcLibraries\Model\RegionTable' => function ($sm) {
                $region = $sm->get('RegionModel');
                $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                $table = new RegionTable($dbAdapter,$region);
                return $table;
            },
        ),
    );
}

您应该能够保持其余代码不变。例如控制器。现在我还没有测试过这种方法,所以我不是 100% 它会起作用,但我认为它应该。我自己以前使用过的其他两种方法。

选项3(最简单):

这涉及通过服务管理器获取一个单独的区域模型,并使用该模型将输入过滤器应用于表单。

public function editAction()
{
    $id = (int)$this->params()->fromRoute('id', 0);
    if (!$id) {
        return $this->redirect()->toRoute('zfcadmin/region', array(
            'action' => 'add'
        ));
    }
    $data = $this->getRegionTable()->get($id);
    $form = new RegionForm();
    $form->bind($data);
    $form->get('submitBtn')->setAttribute('value', 'Save');
    $request = $this->getRequest();
    if ($request->isPost()) {
        $region = $this->getServiceLocator()->get('RegionModel');
        $form->setInputFilter($region->getInputFilter());
        $form->setData($request->getPost());
        if ($form->isValid()) {
            $this->getRegionTable()->save($form->getData());
            return $this->redirect()->toRoute('zfcadmin/regions');
        }
    }
    return array(
        'id' => $id,
        'form' => $form,
    );
}

我没有测试过代码,但你应该明白要点。任何问题都可以问。

于 2013-03-30T12:46:47.070 回答
2

要对“用户名是否已存在”进行验证,请执行以下简单的 Service Manager 配置设置方法,例如:

// 配置/自动加载/global.php

return array(
   'db' => array(
   'driver'   => 'Pdo',
   'dsn'      => 'mysql:dbname=zf2tutorial;host=localhost',
),

 'service_manager' => array(
   'factories' => array(
     'Zend\Db\Adapter\Adapter' => function ($serviceManager) {
        $adapterFactory = new Zend\Db\Adapter\AdapterServiceFactory();
           $adapter = $adapterFactory->createService($serviceManager);

           \Zend\Db\TableGateway\Feature\GlobalAdapterFeature::setStaticAdapter($adapter);

           return $adapter;
     }
  ),
)

);

并将以下数组添加到 getInputFilter() 中:

array(
  'table'   => 'users',
  'field'   => 'username',
  'adapter' => \Zend\Db\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter();
)
于 2013-07-25T09:15:28.377 回答