Zend FrameWork 2在表单中获取ServiceLocator并填充下拉列表

时间:2012-09-17 12:29:17

标签: zend-framework2

我需要从表单中获取适配器,但仍然不能。

在我的控制器中,我可以使用以下方法恢复适配器:

// module/Users/src/Users/Controller/UsersController.php
public function getUsersTable ()
{
    if (! $this->usersTable) {
        $sm = $this->getServiceLocator();
        $this->usersTable = $sm->get('Users\Model\UsersTable');
    }
    return $this->usersTable;
}

在我的模块中,我这样做了:

// module/Users/Module.php  
public function getServiceConfig()
{
    return array(
            'factories' => array(
                    'Users\Model\UsersTable' =>  function($sm) {
                        $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                        $uTable     = new UsersTable($dbAdapter);
                        return $uTable;
                    },
                    //I need to get this to the list of groups
                    'Users\Model\GroupsTable' =>  function($sm) {
                        $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                        $gTable     = new GroupsTable($dbAdapter);
                        return $gTable;
                    },
            ),
    );
}

有人能举例说明如何从组表单中获取适配器吗?

我已将此示例关注到我的表单用户: http://framework.zend.com/manual/2.0/en/modules/zend.form.collections.html

从这里开始编辑......

也许我表示自己错了提问。

我真正需要做的是使用我的表组中的信息填充选择(下拉列表)。

所以我需要通过ServiceLocatorAwareInterface(see this link)实现我的userForm类中的服务,因为默认情况下,Zend Framework MVC注册一个初始化器,它将注入ServiceManager实例ServiceLocatorAwareInterface实现任何类。

从表组中检索值并填充选择。

问题在于我尝试过的所有方法,getServiceLocator()都会返回:

Call to a member function get() on a non-object in
D:\WEBSERVER\htdocs\Zend2Control\module\Users\src\Users\Form\UsersForm.php
on line 46

我只想在我的UserForm中执行此操作...

namespace Users\Form;

use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Form\Element;
use Zend\Form\Form;

class UsersForm extends Form implements ServiceLocatorAwareInterface
{

    protected $serviceLocator;

    public function getServiceLocator ()
    {
        return $this->serviceLocator;
    }

    public function setServiceLocator (ServiceLocatorInterface $serviceLocator)
    {
        $this->serviceLocator = $serviceLocator;
    }

    public function __construct ($name = null)
    {
        parent::__construct('users');

        $this->setAttribute('method', 'post');        

        $sm = $this->getServiceLocator();

        $groups = $sm->get('Users\Model\GroupsTable')->fetchAll(); // line 46       

        $select = new Element\Select('groups');

        $options = array();

        foreach ($groups as $group) {

            $options[$group->id] = $group->name;
        }

        $select->setValueOptions($options);

        $this->add($select);

        // and more elements here...

6 个答案:

答案 0 :(得分:7)

这里的其他各种答案通常是正确的,因为ZF< 2.1。

2.1出局后,框架有一个漂亮的nice solution。这或多或少正式化了DrBeza的解决方案,即:使用初始化程序,然后将任何表单引导移动到初始化所有依赖项后调用的init()方法。

我一直在玩开发分支,它运行得很好。

答案 1 :(得分:6)

这是我用来解决这个问题的方法。

首先,您希望使表单实现ServiceLocatorInterface。

然后你仍然需要手动注入服务定位器,并且因为整个表单是在contrstuctor中生成的,你也需要通过构造函数注入(不是理想的,不能在构造函数中构建它)

Module.php

/**
 * Get the service Config
 * 
 * @return array 
 */
public function getServiceConfig()
{
    return array(
        'factories' => array(
            /**
             * Inject ServiceLocator into our Form
             */
            'MyModule\Form\MyForm' =>  function($sm) {
                $form = new \MyModule\Form\MyFormForm('formname', $sm);
                //$form->setServiceLocator($sm);

                // Alternativly you can inject the adapter/gateway directly
                // just add a setter on your form object...
                //$form->setAdapter($sm->get('Users\Model\GroupsTable')); 

                return $form;
            },
        ),
    );
}

现在在您的控制器中,您可以获得这样的表单:

// Service locator now injected
$form = $this->getServiceLocator()->get('MyModule\Form\MyForm');

现在您可以访问表单中的完整服务定位器,以获取其他任何服务,例如:

$groups = $this->getServiceLocator()->get('Users\Model\GroupsTable')->fetchAll();

答案 2 :(得分:1)

在module.php中,我创建了两个服务。了解我如何将适配器提供给表单。

public function getServiceConfig()
{
    return array(
        'factories' => array(
            'db_adapter' =>  function($sm) {
                $config = $sm->get('Configuration');
                $dbAdapter = new \Zend\Db\Adapter\Adapter($config['db']);
                return $dbAdapter;
            },

            'my_amazing_form' => function ($sm) {
                return new \dir\Form\SomeForm($sm->get('db_adapter'));
            },

        ),
    );
}

在表单代码中,我将该Feed用于任何内容:

namespace ....\Form;

use Zend\Form\Factory as FormFactory;
use Zend\Form\Form;

class SomeForm extends Form
{

    public function __construct($adapter, $name = null)
    {
        parent::__construct($name);
        $factory = new FormFactory();

        if (null === $name) {
            $this->setName('whatever');
        }

    }
}

答案 3 :(得分:0)

我们通过添加接受表单

的方法在模型中处理此问题
public function buildFormSelectOptions($form, $context = null)
{
    /** 
     * Do this this for each form element that needs options added
     */
    $model = $this->getServiceManager()->get('modelProject');

    if (empty($context)){
        $optionRecords = $model->findAll();
    } else {
        /**
         * other logic for $optionRecords
         */
    }

    $options = array('value'=>'', 'label'=>'Choose a Project');
    foreach ($optionRecords as $option) {
        $options[] = array('value'=>$option->getId(), 'label'=>$option->getName());
    }

    $form->get('project')->setAttribute('options', $options);
}

当表单通过引用传递时,我们可以在构建表单的控制器中执行类似的操作:

    $builder = new AnnotationBuilder();
    $form = $builder->createForm($myEntity);
    $myModel->buildFormSelectOptions($form, $myEntity);

    $form->add(array(
        'name' => 'submitbutton',
        'attributes' => array(
            'type'  => 'submit',
            'value' => 'Submit',
            'id' => 'submitbutton',
        ),
    ));

    $form->add(array(
        'name' => 'cancel',
        'attributes' => array(
            'type'  => 'submit',
            'value' => 'Cancel',
            'id' => 'cancel',
        ),
    ));

注意:该示例假定基本表单是通过注释构建的,但是如何创建初始表单并不重要。

答案 4 :(得分:0)

其他答案的另一种方法是创建一个ServiceManager初始化程序。

现有Initializer的一个示例是,如果您的实例实现ServiceLocatorAwareInterface,将如何注入ServiceManager。

想法是创建一个在Initialiser中检查的界面,这个界面可能如下所示:

interface FormServiceAwareInterface
{
    public function init();
    public function setServiceManager(ServiceManager $serviceManager);
}

初始化程序的示例:

class FormInitializer implements InitializerInterface
{
    public function initialize($instance, ServiceLocatorInterface $serviceLocator)
    {
        if (!$instance instanceof FormServiceAwareInterface)
        {
            return;
        }

        $instance->setServiceManager($serviceLocator);
        $instance->init();
    }
}

init()中发生的任何事情都可以访问ServiceManager。当然,您需要将初始化程序添加到SM配置中。

它并不完美,但它可以满足我的需求,也可以应用于从ServiceManager中提取的任何Fieldsets。

答案 5 :(得分:0)

这是我用来解决这个问题的方式。

首先,在Module.php中,创建服务(就像你一样):

// module/Users/Module.php  
public function getServiceConfig()
{
    return array(
            'factories' => array(
                    'Users\Model\UsersTable' =>  function($sm) {
                        $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                        $uTable     = new UsersTable($dbAdapter);
                        return $uTable;
                    },
                    //I need to get this to the list of groups
                    'Users\Model\GroupsTable' =>  function($sm) {
                        $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                        $gTable     = new GroupsTable($dbAdapter);
                        return $gTable;
                    },
            ),
    );
}

然后在控制器中,我得到了对服务的引用:

$users = $this->getServiceLocator()->get('Test\Model\TestGroupTable')->fetchAll();
        $options = array();
        foreach ($users as $user)
           $options[$user->id] = $user->name;
        //get the form element
        $form->get('user_id')->setValueOptions($options);

中提琴,那工作。