代码之家  ›  专栏  ›  技术社区  ›  BMBM

如何在Zend框架中使用依赖项注入?

  •  16
  • BMBM  · 技术社区  · 14 年前

    目前我正在努力学习Zend框架,因此我买了《Zend框架在行动》一书。

    class IndexController extends Zend_Controller_Action 
    {
        public function indexAction()
        {
            $this->view->title = 'Welcome';
            $placesFinder = new Places();
            $this->view->places = $placesFinder->fetchLatest();
        }
    }
    

    Places IndexController .

    class IndexController extends Zend_Controller_Action 
    {
        private $placesFinder;
    
        // Here I can inject anything: mock, stub, the real instance
        public function setPlacesFinder($places)
        {
            $this->placesFinder = $places;
        }
    
        public function indexAction()
        {
            $this->view->title = 'Welcome';
            $this->view->places = $this->placesFinder->fetchLatest();
        }
    }
    

    不能单独测试。第二个更好。现在我只需要一些方法将模型实例注入控制器对象。

    5 回复  |  直到 14 年前
        1
  •  10
  •   takeshin    14 年前

    首先,值得一提的是,控制器应该只需要功能测试,尽管所有逻辑都属于模型。

    • 允许向操作注入任何依赖项
    • 验证操作参数,例如,您可能无法传入数组 $_GET

    DocBlock注释正在成为一个非常著名的行业标准,并且出于评估目的对其进行解析变得越来越流行(例如,条令2)。我在很多应用程序中使用了这种技术,效果很好。

    Actions, now with params! Jani Hartikainen's blog post

    下面是代码:

    <?php
    
    /**
     * Enchanced action controller
     *
     * Map request parameters to action method
     *
     * Important:
     * When you declare optional arguments with default parameters, 
     * they may not be perceded by optional arguments,
     * e.g.
     * @example
     * indexAction($username = 'tom', $pageid); // wrong
     * indexAction($pageid, $username = 'tom'); // OK
     * 
     * Each argument must have @param DocBlock
     * Order of @param DocBlocks *is* important
     * 
     * Allows to inject object dependency on actions:
     * @example
     *   * @param int $pageid
     *   * @param Default_Form_Test $form
     *   public function indexAction($pageid, Default_Form_Test $form = null)
     *
     */
    abstract class Your_Controller_Action extends Zend_Controller_Action
    {  
        /**
         *
         * @var array
         */
        protected $_basicTypes = array(
            'int', 'integer', 'bool', 'boolean',
            'string', 'array', 'object',
            'double', 'float'
        );
    
        /**
         * Detect whether dispatched action exists
         * 
         * @param string $action
         * @return bool 
         */
        protected function _hasAction($action)
        {
            if ($this->getInvokeArg('useCaseSensitiveActions')) {
                trigger_error(
                        'Using case sensitive actions without word separators' .
                        'is deprecated; please do not rely on this "feature"'
                );
    
                return true;
            }
    
            if (method_exists($this, $action)) {
    
                return true;
            }
    
            return false;
        }
    
        /**
         *
         * @param string $action
         * @return array of Zend_Reflection_Parameter objects
         */
        protected function _actionReflectionParams($action)
        {
            $reflMethod = new Zend_Reflection_Method($this, $action);
            $parameters = $reflMethod->getParameters();
    
            return $parameters;
        }
    
        /**
         *
         * @param Zend_Reflection_Parameter $parameter
         * @return string
         * @throws Your_Controller_Action_Exception when required @param is missing
         */
        protected function _getParameterType(Zend_Reflection_Parameter $parameter)
        {
            // get parameter type
            $reflClass = $parameter->getClass();
    
            if ($reflClass instanceof Zend_Reflection_Class) {
                $type = $reflClass->getName();
            } else if ($parameter->isArray()) {
                $type = 'array';
            } else {
                $type = $parameter->getType();
            }
    
            if (null === $type) {
                throw new Your_Controller_Action_Exception(
                        sprintf(
                                "Required @param DocBlock not found for '%s'", $parameter->getName()
                        )
                );
            }
    
            return $type;
        }
    
        /**
         *
         * @param Zend_Reflection_Parameter $parameter 
         * @return mixed
         * @throws Your_Controller_Action_Exception when required argument is missing
         */
        protected function _getParameterValue(Zend_Reflection_Parameter $parameter)
        {
            $name = $parameter->getName();
            $requestValue = $this->getRequest()->getParam($name);
    
            if (null !== $requestValue) {
                $value = $requestValue;
            } else if ($parameter->isDefaultValueAvailable()) {
                $value = $parameter->getDefaultValue();
            } else {
                if (!$parameter->isOptional()) {
                    throw new Your_Controller_Action_Exception(
                            sprintf("Missing required value for argument: '%s'", $name));
                }
    
                $value = null;
            }
    
            return $value;
        }
    
        /**
         *
         * @param mixed $value 
         */
        protected function _fixValueType($value, $type)
        {
            if (in_array($type, $this->_basicTypes)) {
                settype($value, $type);
            }
    
            return $value;
        }
    
        /**
         * Dispatch the requested action
         *
         * @param   string $action Method name of action
         * @return  void
         */
        public function dispatch($action)
        {
            $request = $this->getRequest();
    
            // Notify helpers of action preDispatch state
            $this->_helper->notifyPreDispatch();
    
            $this->preDispatch();
            if ($request->isDispatched()) {
                // preDispatch() didn't change the action, so we can continue
                if ($this->_hasAction($action)) {
    
                    $requestArgs = array();
                    $dependencyObjects = array();
                    $requiredArgs = array();
    
                    foreach ($this->_actionReflectionParams($action) as $parameter) {
                        $type = $this->_getParameterType($parameter);
                        $name = $parameter->getName();
                        $value = $this->_getParameterValue($parameter);
    
                        if (!in_array($type, $this->_basicTypes)) {
                            if (!is_object($value)) {
                                $value = new $type($value);
                            }
                            $dependencyObjects[$name] = $value;
                        } else {
                            $value = $this->_fixValueType($value, $type);
                            $requestArgs[$name] = $value;
                        }
    
                        if (!$parameter->isOptional()) {
                            $requiredArgs[$name] = $value;
                        }
                    }
    
                    // handle canonical URLs here
    
                    $allArgs = array_merge($requestArgs, $dependencyObjects);
                    // dispatch the action with arguments
                    call_user_func_array(array($this, $action), $allArgs);
                } else {
                    $this->__call($action, array());
                }
                $this->postDispatch();
            }
    
            $this->_helper->notifyPostDispatch();
        }
    
    }
    

    要使用它,只需:

    Your_FineController extends Your_Controller_Action {}
    

    /**
     * @param int $id Mandatory parameter
     * @param string $sorting Not required parameter
     * @param Your_Model_Name $model Optional dependency object 
     */
    public function indexAction($id, $sorting = null, Your_Model_Name $model = null) 
    {
        // model has been already automatically instantiated if null
        $entry = $model->getOneById($id, $sorting);
    }
    

        2
  •  5
  •   BMBM    14 年前

    this component of the symfony framework /library/ioc/lib/ .

    我将这些init函数添加到我的 Bootstrap.php

    protected function _initIocFrameworkAutoloader()
    {
        require_once(APPLICATION_PATH . '/../library/Ioc/lib/sfServiceContainerAutoloader.php');
    
        sfServiceContainerAutoloader::register();
    }
    

    application.ini

    ioc.controllers.wiringXml = APPLICATION_PATH "/objectconfiguration/controllers.xml"
    ioc.controllers.enableIoc = 1
    

    sfServiceContainerBuilder /library/MyStuff/Ioc/Builder.php /library/MyStuff/

    class MyStuff_Ioc_Builder extends sfServiceContainerBuilder
    {
      public function initializeServiceInstance($service)
      {
          $serviceClass = get_class($service);
          $definition = $this->getServiceDefinition($serviceClass);
    
    
        foreach ($definition->getMethodCalls() as $call)
        {
          call_user_func_array(array($service, $call[0]), $this->resolveServices($this->resolveValue($call[1])));
        }
    
        if ($callable = $definition->getConfigurator())
        {
          if (is_array($callable) && is_object($callable[0]) && $callable[0] instanceof sfServiceReference)
          {
            $callable[0] = $this->getService((string) $callable[0]);
          }
          elseif (is_array($callable))
          {
            $callable[0] = $this->resolveValue($callable[0]);
          }
    
          if (!is_callable($callable))
          {
            throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', get_class($service)));
          }
    
          call_user_func($callable, $service);
        }
    
      }
    }
    

    /library/MyStuff/Controller.php 我的所有控制器都从中继承:

    class MyStuff_Controller extends Zend_Controller_Action {
        /**
         * @override
         */
        public function dispatch($action)
        {
            // NOTE: the application settings have to be saved 
            // in the registry with key "config"
            $config = Zend_Registry::get('config');
    
            if($config['ioc']['controllers']['enableIoc'])
            {
                $sc = new MyStuff_Ioc_Builder();
    
                $loader = new sfServiceContainerLoaderFileXml($sc);
                $loader->load($config['ioc']['controllers']['wiringXml']);
    
                $sc->initializeServiceInstance($this);
            }
    
            parent::dispatch($action);
        }
    }
    

    $this

        3
  •  3
  •   Vovkin    12 年前

    http://symfony.com/doc/current/book/service_container.html .

    public function init()
    {
        $sc = $this->getInvokeArg('bootstrap')->getContainer();
        $this->placesService = $sc->get('PlacesService');
    }
    

    http://blog.starreveld.com/2009/11/using-symfony-di-container-with.html

        4
  •  2
  •   Krzysztof Karski    8 年前

    http://php-di.org/doc/frameworks/zf1.html

    我知道这个问题很老,但在搜索引擎中查找ZF1中的DI时,它会在搜索引擎中出现得很高,所以我想我应该添加一个解决方案,它不需要您自己编写。

        5
  •  0
  •   Trekatz    8 年前

    与Zend Framework 3的服务经理一起。

    正式文件:

    https://zendframework.github.io/zend-servicemanager/

    class JsonController extends AbstractActionController
    {
        private $_jsonFactory;
        private $_smsRepository;
        public function __construct(JsonFactory $jsonFactory, SmsRepository $smsRepository)
        {
            $this->_jsonFactory = $jsonFactory;
            $this->_smsRepository = $smsRepository;
        }
    ...
    }
    

    Creates the Controller

    class JsonControllerFactory implements FactoryInterface
    {
        /**
         * @param ContainerInterface $serviceManager
         * @param string $requestedName
         * @param array|null $options
         * @return JsonController
         */
        public function __invoke(ContainerInterface $serviceManager, $requestedName, array $options = null)
        {
            //improve using get method and callable
            $jsonModelFactory = new JsonFactory();
            $smsRepositoryClass = $serviceManager->get(SmsRepository::class);
            return new JsonController($jsonModelFactory, $smsRepositoryClass);
        }
    }
    

    https://github.com/fmacias/SMSDispatcher

    我希望它能帮助别人