ZF2 创建子目录以更好地管理与每个视图关联的文件

ZF2 Creating Sub Directories To Better Manage Files Associated With Each View

我开始为我的网站设计一个会员区。我创建了一个成员模块。由于我在会员区内有许多部分的计划,因此我在会员模块内创建了一系列子目录,以包含与用户将看到的每个菜单选项相关的文件组。我想使用子目录作为一种组织形式。

我目前收到错误消息:

Zend\Form\FormElementManager::get was unable to fetch or create an instance for Member\Form\NextPasswordChange

我无法弄清楚 ZF2 是否正在访问子目录中的文件,或者我是否遗漏了一个非常简单的错误。我想知道我是否导致 ZF2 无法工作,因为我没有坚持使用默认文件结构。

"NextPasswordChange" 视图的实际文件存储在这些目录中:

./Member/src/Member/Form/Member/NextPasswordChange.php
./Member/src/Member/Form/Member/Factory/NextPasswordChangeFactory.php
./Member/src/Member/Service/Form/NextPasswordChange.php
./Member/src/Member/Service/Form/Factory/NextPasswordChangeFactory.php
./Member/src/Member/Service/Form/NextPasswordChangeInterface.php
./Member/src/Member/InputFilter/Member/NextPasswordChange.php

如何让 ZF2 接受我的文件结构?

我在 module.config.php 中为 NextPasswordChange 创建了以下条目:

'controllers'     => [
    'factories' => [
        'Member\Controller\Member' => 'Member\Controller\Factory\MemberControllerFactory',
    ]
],

'form_elements'   => [
    'factories'  => [
        'Member\Form\Member\NextPasswordChange' => 'Member\Form\Member\Factory\NextPasswordChangeFactory',
    ],
],

'input_filters'   => [
    'invokables' => [
        'Member\InputFilter\Member\NextPasswordChange' => 'Member\InputFilter\Member\NextPasswordChange',
    ]
],
'service_manager' => [
    'factories' => [
        'Member\Service\Member' => 'Member\Service\Factory\MemberFactory',
        'Member\Service\Form\NextPasswordChange' => 'Member\Service\Form\Factory\NextPasswordChangeFactory',
    ]
],

我的MemberControllerFactory.php看起来像这样

namespace Member\Controller\Factory;

use Member\Controller\MemberController;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

/**
 * Class MemberControllerFactory
 *
 * @package Member\Controller\Factory
 */
class MemberControllerFactory implements FactoryInterface
{
    /**
     * @param ServiceLocatorInterface $serviceLocator
     *
     * @return MemberController
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        /**
         * @var \Zend\ServiceManager\ServiceLocatorAwareTrait $serviceLocator
         */
        $sl = $serviceLocator->getServiceLocator();

        /**
         * @var \Member\Form\NextPasswordChange $formNextPasswordChange
         */
        $formNextPasswordChange = $sl->get('FormElementManager')
                        ->get('Member\Form\NextPasswordChange');

        /**
         * @var \Member\Service\Member $serviceMember
         */
        $serviceMember = $sl->get('Member\Service\Member');

        /**
         * @var \Member\Service\NextPasswordChange $serviceNextPasswordChange
         */
        $serviceNextPasswordChange = $sl->get('Member\Service\NextPasswordChange');


        return new MemberController(
            $formNextPasswordChange,
            $serviceMember,
            $serviceNextPasswordChange
        );
    }
}

MemberController.php 中的相应部分如下所示:

namespace Member\Controller;

use Member\Form\NextPasswordChange as FormNextPasswordChange;
use Member\Service\Member as ServiceMember;
use Member\Service\NextPasswordChange as ServiceNextPasswordChange;
use Zend\Http\Request;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

/**
 * Class MemberController
 *
 * @package Member\Controller
 *
 * @method Request getRequest()
 */
class MemberController extends AbstractActionController
{
    /**
     * @var FormNextPasswordChange $formNextPasswordChange
     */
    private $formNextPasswordChange;

    /**
     * @var ServiceMember $serviceMember
     */
    private $serviceMember;

    /**
     * @var ServiceNextPasswordChange $serviceNextPasswordChange
     */
    private $serviceNextPasswordChange;

    /**
     * @param FormNextPasswordChange    $formNextPasswordChange
     * @param ServiceMember             $serviceMember
     * @param ServiceNextPasswordChange $serviceNextPasswordChange
     */
    public function __construct(
        FormNextPasswordChange $formNextPasswordChange,
        ServiceMember $serviceMember,
        ServiceNextPasswordChange $serviceNextPasswordChange
    )
    {
        $this->formNextPasswordChange = $formNextPasswordChange;

        $this->serviceMember = $serviceMember;

        $this->serviceNextPasswordChange = $serviceNextPasswordChange;
    }

根据我收到的错误,我还想包含 NextPasswordChange 表单文件:

NextPasswordChangeFactory.php

namespace Member\Service\Factory;

use Member\Service\NextPasswordChange;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

/**
 * Class NextPasswordChangeFactory
 *
 * @package Member\Service\Factory
 */
class NextPasswordChangeFactory implements FactoryInterface
{
    /**
     * @param ServiceLocatorInterface $serviceLocator
     *
     * @return NextPasswordChange
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        /**
         * @var \Doctrine\ORM\EntityManager $entityManager
         */
        $entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');

        /**
         * @var \OTFunction\Service\Ipaddress $functionIpaddress
         */
        $functionIpaddress = $serviceLocator->get('OTFunction\Service\Ipaddress');

        /**
         * @var \Zend\Mvc\I18n\Translator $translator
         */
        $translator = $serviceLocator->get('translator');

        return new NextPasswordChange
        (
            $entityManager,
            $functionIpaddress,
            $translator
        );
    }
}

NextPasswordChange.php

namespace Member\Service;

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Query;
use OTFunction\Service\Ipaddress as FunctionIpaddress;
use Zend\Mvc\I18n\Translator;
use Zend\Session\Container;

/**
 * Class NextPasswordChange
 *
 * @package Member\Service
 */
class NextPasswordChange implements NextPasswordChangeInterface
{
    /**
     * @var EntityManager $entityManager
     */
    private $entityManager;

    /**
     * @var FunctionIpaddress $functionIpaddress
     */
    private $functionIpaddress;

    /**
     * @var Translator $translator
     */
    private $translator;

    /**
     * @param EntityManager     $entityManager
     * @param FunctionIpaddress $functionIpaddress
     * @param Translator        $translator
     */
    public function __construct(
        EntityManager $entityManager,
        FunctionIpaddress $functionIpaddress,
        Translator $translator
    ) {
        $this->entityManager = $entityManager;

        $this->functionIpaddress = $functionIpaddress;

        $this->translator = $translator;
    }

    /**
     * accountUpdate
     *
     * @param array $params
     *
     * @return string
     * @throws \PDOException
     */
    public function accountUpdate(array $params )
    {
        try {

            // Database query to process the form goes here

        } catch (\PDOException $e) {
            $this->entityManager->rollback();

            throw $e;
        }
    }

}

NextPasswordChangeInterface.php

namespace Member\Service;

/**
 * Interface NextPasswordChangeInterface
 *
 * @package Member\Service
 */
interface NextPasswordChangeInterface
{
    /**
     * accountUpdate
     *
     * @param array $params
     *
     * @return string
     * @throws \PDOException
     */
    public function accountUpdate(array $params );

}

我试过在对 NextPasswordChange 的引用中包含额外的 "Member" 子目录。当这不起作用时,我将其删除。我使用 PHP Storm 编程。 PHP Storm 识别子目录中包含的 类。不知道ZF2有没有效仿。我不确定如何进行。

您的服务定位器声明与您的实际文件/命名空间结构不匹配:

在您的 MemberControllerFactory 中,您正在获取 Member\Form\NextPasswordChange 的实例:

$formNextPasswordChange = $sl->get('FormElementManager')
                        ->get('Member\Form\NextPasswordChange');

然而您的服务定位器仅包含 Member\Form\Member\NextPasswordChange 的工厂条目(注意额外的 Member 命名空间):

'form_elements'   => [
    'factories'  => [
        'Member\Form\Member\NextPasswordChange' => 'Member\Form\Member\Factory\NextPasswordChangeFactory',
    ],
],

现在,当尝试定位 Member\Form\NextPasswordChange 时,服务定位器无法在此定义中找到它并抛出此错误。 您尝试获取的实例的名称确实必须与服务定位器配置中定义的键匹配。

其次,您所指的工厂在您提供的命名空间中也不存在。

本质上,服务定位器只是一个 key -> class 查找,其中键是您用于获取的服务的名称,值是它解析到的 class 或工厂。如果密钥未定义或解析的 class 不存在,则服务定位器无法知道您的想法。

另一方面,PHPStorm 可以解决此问题 class,因为它不查看服务定位器以加载 class 个实例,但通过查看文件系统了解所有 classes .

我强烈建议您花一些时间了解服务定位器的运作方式。虽然开始时相当复杂,但基本操作模式实际上非常简单:

  • 查看传递的名称是否作为键存在于配置中
  • Return class 或使用为键值指定的工厂。