3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Mvc\Controller\Plugin
;
12 use Zend\Http\Header\Accept\FieldValuePart\AbstractFieldValuePart
;
13 use Zend\Http\Request
;
14 use Zend\Mvc\InjectApplicationEventInterface
;
15 use Zend\Mvc\MvcEvent
;
16 use Zend\Mvc\Exception\DomainException
;
17 use Zend\Mvc\Exception\InvalidArgumentException
;
18 use Zend\View\Model\ModelInterface
;
21 * Controller Plugin to assist in selecting an appropriate View Model type based on the
22 * User Agent's accept header.
24 class AcceptableViewModelSelector
extends AbstractPlugin
28 * @var string the Key to inject the name of a viewmodel with in an Accept Header
30 const INJECT_VIEWMODEL_NAME
= '_internalViewModel';
34 * @var \Zend\Mvc\MvcEvent
40 * @var \Zend\Http\Request
45 * Default array to match against.
49 protected $defaultMatchAgainst;
53 * @var string Default ViewModel
55 protected $defaultViewModelName = 'Zend\View\Model\ViewModel';
58 * Detects an appropriate viewmodel for request.
60 * @param array $matchAgainst (optional) The Array to match against
61 * @param bool $returnDefault (optional) If no match is available. Return default instead
62 * @param AbstractFieldValuePart|null $resultReference (optional) The object that was matched
63 * @throws InvalidArgumentException If the supplied and matched View Model could not be found
64 * @return ModelInterface|null
66 public function __invoke(
67 array $matchAgainst = null,
68 $returnDefault = true,
69 & $resultReference = null
71 return $this->getViewModel($matchAgainst, $returnDefault, $resultReference);
75 * Detects an appropriate viewmodel for request.
77 * @param array $matchAgainst (optional) The Array to match against
78 * @param bool $returnDefault (optional) If no match is available. Return default instead
79 * @param AbstractFieldValuePart|null $resultReference (optional) The object that was matched
80 * @throws InvalidArgumentException If the supplied and matched View Model could not be found
81 * @return ModelInterface|null
83 public function getViewModel(
84 array $matchAgainst = null,
85 $returnDefault = true,
86 & $resultReference = null
88 $name = $this->getViewModelName($matchAgainst, $returnDefault, $resultReference);
94 if (!class_exists($name)) {
95 throw new InvalidArgumentException('The supplied View Model could not be found');
102 * Detects an appropriate viewmodel name for request.
104 * @param array $matchAgainst (optional) The Array to match against
105 * @param bool $returnDefault (optional) If no match is available. Return default instead
106 * @param AbstractFieldValuePart|null $resultReference (optional) The object that was matched.
107 * @return ModelInterface|null Returns null if $returnDefault = false and no match could be made
109 public function getViewModelName(
110 array $matchAgainst = null,
111 $returnDefault = true,
112 & $resultReference = null
114 $res = $this->match($matchAgainst);
116 $resultReference = $res;
117 return $this->extractViewModelName($res);
120 if ($returnDefault) {
121 return $this->defaultViewModelName
;
126 * Detects an appropriate viewmodel name for request.
128 * @param array $matchAgainst (optional) The Array to match against
129 * @return AbstractFieldValuePart|null The object that was matched
131 public function match(array $matchAgainst = null)
133 $request = $this->getRequest();
134 $headers = $request->getHeaders();
136 if ((!$matchAgainst && !$this->defaultMatchAgainst
) ||
!$headers->has('accept')) {
140 if (!$matchAgainst) {
141 $matchAgainst = $this->defaultMatchAgainst
;
144 $matchAgainstString = '';
145 foreach ($matchAgainst as $modelName => $modelStrings) {
146 foreach ((array) $modelStrings as $modelString) {
147 $matchAgainstString .= $this->injectViewModelName($modelString, $modelName);
151 /** @var $accept \Zend\Http\Header\Accept */
152 $accept = $headers->get('Accept');
153 if (($res = $accept->match($matchAgainstString)) === false) {
161 * Set the default View Model (name) to return if no match could be made
162 * @param string $defaultViewModelName The default View Model name
163 * @return AcceptableViewModelSelector provides fluent interface
165 public function setDefaultViewModelName($defaultViewModelName)
167 $this->defaultViewModelName
= (string) $defaultViewModelName;
172 * Set the default View Model (name) to return if no match could be made
175 public function getDefaultViewModelName()
177 return $this->defaultViewModelName
;
181 * Set the default Accept Types and View Model combinations to match against if none are specified.
183 * @param array $matchAgainst (optional) The Array to match against
184 * @return AcceptableViewModelSelector provides fluent interface
186 public function setDefaultMatchAgainst(array $matchAgainst = null)
188 $this->defaultMatchAgainst
= $matchAgainst;
193 * Get the default Accept Types and View Model combinations to match against if none are specified.
197 public function getDefaultMatchAgainst()
199 return $this->defaultMatchAgainst
;
203 * Inject the viewmodel name into the accept header string
205 * @param string $modelAcceptString
206 * @param string $modelName
209 protected function injectViewModelName($modelAcceptString, $modelName)
211 $modelName = str_replace('\\', '|', $modelName);
212 return $modelAcceptString . '; ' . self
::INJECT_VIEWMODEL_NAME
. '="' . $modelName . '", ';
216 * Extract the viewmodel name from a match
217 * @param AbstractFieldValuePart $res
220 protected function extractViewModelName(AbstractFieldValuePart
$res)
222 $modelName = $res->getMatchedAgainst()->params
[self
::INJECT_VIEWMODEL_NAME
];
223 return str_replace('|', '\\', $modelName);
230 * @throws DomainException if unable to find request
232 protected function getRequest()
234 if ($this->request
) {
235 return $this->request
;
238 $event = $this->getEvent();
239 $request = $event->getRequest();
240 if (!$request instanceof Request
) {
241 throw new DomainException(
242 'The event used does not contain a valid Request, but must.'
246 $this->request
= $request;
254 * @throws DomainException if unable to find event
256 protected function getEvent()
262 $controller = $this->getController();
263 if (!$controller instanceof InjectApplicationEventInterface
) {
264 throw new DomainException(
265 'A controller that implements InjectApplicationEventInterface '
266 . 'is required to use ' . __CLASS__
270 $event = $controller->getEvent();
271 if (!$event instanceof MvcEvent
) {
272 $params = $event->getParams();
273 $event = new MvcEvent();
274 $event->setParams($params);
276 $this->event
= $event;