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\View\Renderer
;
15 use Zend\Stdlib\ArrayUtils
;
16 use Zend\View\Exception
;
17 use Zend\View\Model\JsonModel
;
18 use Zend\View\Model\ModelInterface
as Model
;
19 use Zend\View\Renderer\RendererInterface
as Renderer
;
20 use Zend\View\Resolver\ResolverInterface
as Resolver
;
25 class JsonRenderer
implements Renderer
, TreeRendererInterface
28 * Whether or not to merge child models with no capture-to value set
31 protected $mergeUnnamedChildren = false;
39 * JSONP callback (if set, wraps the return in a function call)
43 protected $jsonpCallback = null;
46 * Return the template engine object, if any
48 * If using a third-party template engine, such as Smarty, patTemplate,
49 * phplib, etc, return the template engine object. Useful for calling
50 * methods on these objects, such as for setting filters, modifiers, etc.
54 public function getEngine()
60 * Set the resolver used to map a template name to a resource the renderer may consume.
62 * @todo Determine use case for resolvers when rendering JSON
63 * @param Resolver $resolver
66 public function setResolver(Resolver
$resolver)
68 $this->resolver
= $resolver;
72 * Set flag indicating whether or not to merge unnamed children
74 * @param bool $mergeUnnamedChildren
75 * @return JsonRenderer
77 public function setMergeUnnamedChildren($mergeUnnamedChildren)
79 $this->mergeUnnamedChildren
= (bool) $mergeUnnamedChildren;
84 * Set the JSONP callback function name
86 * @param string $callback
87 * @return JsonRenderer
89 public function setJsonpCallback($callback)
91 $callback = (string) $callback;
92 if (! empty($callback)) {
93 $this->jsonpCallback
= $callback;
99 * Returns whether or not the jsonpCallback has been set
103 public function hasJsonpCallback()
105 return (null !== $this->jsonpCallback
);
109 * Should we merge unnamed children?
113 public function mergeUnnamedChildren()
115 return $this->mergeUnnamedChildren
;
119 * Renders values as JSON
121 * @todo Determine what use case exists for accepting both $nameOrModel and $values
122 * @param string|Model $nameOrModel The script/resource process, or a view model
123 * @param null|array|\ArrayAccess $values Values to use during rendering
124 * @throws Exception\DomainException
125 * @return string The script output.
127 public function render($nameOrModel, $values = null)
129 // use case 1: View Models
130 // Serialize variables in view model
131 if ($nameOrModel instanceof Model
) {
132 if ($nameOrModel instanceof JsonModel
) {
133 $children = $this->recurseModel($nameOrModel, false);
134 $this->injectChildren($nameOrModel, $children);
135 $values = $nameOrModel->serialize();
137 $values = $this->recurseModel($nameOrModel);
138 $values = Json
::encode($values);
141 if ($this->hasJsonpCallback()) {
142 $values = $this->jsonpCallback
. '(' . $values . ');';
147 // use case 2: $nameOrModel is populated, $values is not
148 // Serialize $nameOrModel
149 if (null === $values) {
150 if (! is_object($nameOrModel) ||
$nameOrModel instanceof JsonSerializable
) {
151 $return = Json
::encode($nameOrModel);
152 } elseif ($nameOrModel instanceof Traversable
) {
153 $nameOrModel = ArrayUtils
::iteratorToArray($nameOrModel);
154 $return = Json
::encode($nameOrModel);
156 $return = Json
::encode(get_object_vars($nameOrModel));
159 if ($this->hasJsonpCallback()) {
160 $return = $this->jsonpCallback
. '(' . $return . ');';
165 // use case 3: Both $nameOrModel and $values are populated
166 throw new Exception\
DomainException(sprintf(
167 '%s: Do not know how to handle operation when both $nameOrModel and $values are populated',
173 * Can this renderer render trees of view models?
179 public function canRenderTrees()
185 * Retrieve values from a model and recurse its children to build a data structure
187 * @param Model $model
188 * @param bool $mergeWithVariables Whether or not to merge children with
189 * the variables of the $model
192 protected function recurseModel(Model
$model, $mergeWithVariables = true)
195 if ($mergeWithVariables) {
196 $values = $model->getVariables();
199 if ($values instanceof Traversable
) {
200 $values = ArrayUtils
::iteratorToArray($values);
203 if (! $model->hasChildren()) {
207 $mergeChildren = $this->mergeUnnamedChildren();
208 foreach ($model as $child) {
209 $captureTo = $child->captureTo();
210 if (! $captureTo && ! $mergeChildren) {
211 // We don't want to do anything with this child
215 $childValues = $this->recurseModel($child);
217 // Capturing to a specific key
218 // TODO please complete if append is true. must change old
219 // value to array and append to array?
220 $values[$captureTo] = $childValues;
221 } elseif ($mergeChildren) {
222 // Merging values with parent
223 $values = array_replace_recursive($values, $childValues);
230 * Inject discovered child model values into parent model
232 * @todo detect collisions and decide whether to append and/or aggregate?
233 * @param Model $model
234 * @param array $children
236 protected function injectChildren(Model
$model, array $children)
238 foreach ($children as $child => $value) {
239 // TODO detect collisions and decide whether to append and/or aggregate?
240 $model->setVariable($child, $value);