Is it possible to let Mustache (php) partials contain their own data? - php

I want to have a Mustache template reference a partial where the partial adds data to the context as well. Instead of having to define the data in the data to the initial Mustache rendering.
I have a mockup in https://gist.github.com/lode/ecc27fe1ededc9b4a219
It boils down to:
<?php
// controller
$options = array(
'partials' => array(
'members_collection' => new members_collection
)
);
$mustache = new Mustache_Engine($options);
$template = '
<h1>The team</h1>
{{> members_collection}}
';
echo $mustache->render($template);
// viewmodel
class members_collection {
public $data;
public function __toString() {
$template = '
<ul>
{{# data}}
{{.}}
{{/ data}}
</ul>
';
$mustache = new Mustache_Engine();
return $mustache->render($template, $this);
}
public function __construct() {
$this->data = array(
'Foo Bar',
'Bar Baz',
'Baz Foo',
);
}
}
This gives an error like Cannot use object of type members_collection as array.
Is there a way to make this work? Or is using __toString not the right way? And would using a partials_loader or __invoke help? I got it working with neither but might miss something.

In your example above, members_collection isn't a partial, it's a subview. Two really small changes make it work: in the options array, change the partials key to helpers; and, in the parent template, change from a partial tag to an unescaped interpolation tag ({{> members_collection}} -> {{{members_collection}}}).
<?php
require '/Users/justin/Projects/php/mustache/mustache.php/vendor/autoload.php';
// controller
$options = array(
'helpers' => array(
'members_collection' => new members_collection
)
);
$mustache = new Mustache_Engine($options);
$template = '
<h1>The team</h1>
{{{members_collection}}}
';
echo $mustache->render($template);
// viewmodel
class members_collection {
public $data;
public function __toString() {
$template = '
<ul>
{{# data}}
{{.}}
{{/ data}}
</ul>
';
$mustache = new Mustache_Engine();
return $mustache->render($template, $this);
}
public function __construct() {
$this->data = array(
'Foo Bar',
'Bar Baz',
'Baz Foo',
);
}
}

I'm assuming you're using the bobthecow PHP implementation Mustache templating in PHP.
As of last time I checked, Mustache PHP it didn't support data driven partials. You want sort of a 'controller' backed a partial... however, currently partials just simple include-this-file style partials.
You're gonna have to build this yourself. Good luck!

Related

Adding a WordPress filter in a class/spacename

I have a main plugin and a sub-plugin. The function of the sub-plugin is to extract information from a 3rd-party plugin and pass it to the main plugin.
Extracting the information is no problem, and I can pass hardcoded information through a filter.
But when I try to make the filter dynamic, it stops functioning.
What's the best approch for this situation?
Main plugin uses this code:
namespace Essif\Testing
class Hooks extends Flow
public static function options(): array {
$res = apply_filters('custom_filter_testing', '');
return $res;
}
My subplugin looks like this:
public function getTarget() {
return ['foo' => 'foo', 'bar' => 'bar'];
}
public function essif_hook_data()
{
$context = ['CF7' => 'CF7'];
$target = ['foo' => 'foo', 'bar' => 'bar'];
//$target = $this->getTarget();
$res = ['context' => $context, 'target' => $target];
return $res;
}
Using hardcoded info, it works. But if I use $this->getTarget(), it throws an error.
EDIT:
Errors:
ManageHooks::options() must be of the type array, string returned
$this
Using $this when not in object context

ZF2 add params to setPartial submenu

I use this partial to generate my submenu.
<?php foreach ($this->container as $page): ?>
<?php foreach ($page->getPages() as $child): ?>
<a href="<?php echo $child->getHref(); ?>" class="list-group-item">
<?php echo $this->translate($child->getLabel()); ?>
</a>
<?php endforeach; ?>
<?php endforeach; ?>
Which is called like this:
$this->navigation('navigation')->menu()->setPartial('partial/submenu')->render();
But when i render the menu the "$child->getHref()" renders the url without the needed "slug/id" parameter.
I tried to create the url with "$this->url()" in ZF1 you could pass the params in an array to the partial but in ZF2 that doesn't seem to work anymore.
Can anybody tell me how to add the params to the menu urls?
Thanks in advance!
PS!
I'm not referring to $this->Partial, i'm talking about $this->navigation('navigation')->menu()->setPartial('partial/submenu')->render() which apparently doesn't support a param array.
If I'm understanding your question, yes, you can pass params to partials. Example:
<?php echo $this->partial('partial.phtml', array(
'from' => 'Team Framework',
'subject' => 'view partials')); ?>
See http://framework.zend.com/manual/2.3/en/modules/zend.view.helpers.partial.html
I'm not sure this completely solves your issue, since you are not showing what the menu helper is. Is it your own view helper? Are you saying that setPartial method only accepts one argument?
All that said, have you considered Spiffy Navigation?
https://github.com/spiffyjr/spiffy-navigation
It's been sometime since this question was asked, however today I came across the same problem (using version 2.4).
If you have a segment route to be included within the menu that requires some parameters there is no way to pass these through to the navigation's view partial helper.
The change I've made allows a ViewModel instance to be passed to the menu navigation helper's setPartial() method. This view model will be the context for the navigation's partial template rendering; therefore we can use it to set the variables we need for the route creation and fetch them just like within other views using $this->variableName.
The change requires you to extend the Menu helper (or which ever navigation helper requires it).
namespace Foo\Navigation;
use Zend\Navigation\AbstractContainer;
use Zend\View\Model\ViewModel;
class Menu extends \Zend\View\Helper\Navigation\Menu
{
public function renderPartial($container = null, $partial = null)
{
if (null == $container) {
$container = $this->getContainer();
}
if ($container && $partial instanceof ViewModel) {
$partial->setVariable('container', $container);
}
return parent::renderPartial($container, $partial);
}
public function setPartial($partial)
{
if ($partial instanceof ViewModel) {
$this->partial = $partial;
} else {
parent::setPartial($partial);
}
return $this;
}
}
Because this extends the default implementation of the helper updated configuration is required in module.config.php to ensure the extend class is loaded.
'navigation_helpers' => [
'invokables' => [
'Menu' => 'Foo\Navigation\Menu',
],
],
The menu helper will then accept a view model instance.
$viewModel = new \Zend\View\Model\ViewModel;
$viewModel->setTemplate('path/to/partial/template')
->setVariable('id', $foo->getId());
echo $this->navigation()
->menu()
->setPartial($viewModel)
->render();
The only change in the actual partial script will require you to create the URL's using the URL view helper.
foreach ($container as $page) {
//...
$href = $this->url($page->getRoute(), ['id' => $this->id]);
//...
}

In Zf2, how to add a error css class on validation failed

I want to add red border around Input Text Box when ZF2 Validation failed. I am thinking if ZF2 provide way to add/set css class in case of validation failure.
It is better if it is possible using only Zend Validation/Form API. I am not considering client side (javascript/jQuery) solutions.
UPDATE :
I am using Form Row element like that
<div class="row">
<div><?php echo $this->formLabel($form->get('first_name')); ?></div>
<div><?php echo $this->formElement($form->get('first_name')); ?></div>
<div><?php echo $this->formElementErrors($form->get('first_name'), array('class' => "field-validation-error")); ?></div>
</div>
You can use FormRow view helper, which will render non-valid elements with CSS class ("input-error" by default).
Usage is very simple, in your template:
echo $this->formRow($element);
or if you want custom class:
echo $this->formRow()->setInputErrorClass('my-error-class')->render($element);
If you wish to add specific 'error' classes to the input will need to modify the related Zend\Form\View\Helper\Form* classes as these are what introspect the Zend\Form\ElementInterface objects and render the required HTML.
For example:
// \MyModule\Form\View\Helper\MyCustomFormElement.php
class MyCustomFormElement extends \Zend\Form\View\Helper\FormElement
{
public function render(Element $element)
{
$errors = $element->getMessages();
if (! empty($errors)) {
$classes = $element->getAttribute('class');
if (null === $classes) $classes = array();
if (! is_array($classes)) $classes = explode(' ', $classes);
$classes = array_unique(array_merge($classes, array('my-error-class')));
$element->setAttribute('class', implode(' ', $classes));
}
return parent::render($element);
}
}
Then just replace the default form element helper by registering a invokable with the same name.
// Module.php
public function getViewHelperConfig()
{
return array(
'invokables' => array(
'form_element' => 'MyModule\Form\View\Helper\MyCustomFormElement',
),
);
}

How to render ZF2 view within JSON response?

So far, I have figured out how to return a typical JSON response in Zend Framework 2. First, I added the ViewJsonStrategy to the strategies section of the view_manager configuration. Then, instead of returning a ViewModel instance from the controller action, I return a JsonModel instance with all my variables set.
Now that I've figured that piece out, I need to understand how to render a view and return it within that JSON response. In ZF1, I was able to use $this->view->render($scriptName), which returned the HTML as a string. In ZF2, the Zend\View\View::render(...) method returns void.
So... how can I render an HTML view script and return it in a JSON response in one request?
This is what I have right now:
if ($this->getRequest()->isXmlHttpRequest()) {
$jsonModel = new JsonModel(...);
/* #todo Render HTML script into `$html` variable, and add to `JsonModel` */
return $jsonModel;
} else {
return new ViewModel(...);
}
OK, i think i finally understood what you're doing. I've found a solution that i think matches your criteria. Though i am sure that there is room for improvement, as there's some nasty handwork to be done...
public function indexAction()
{
if (!$this->getRequest()->isXmlHttpRequest()) {
return array();
}
$htmlViewPart = new ViewModel();
$htmlViewPart->setTerminal(true)
->setTemplate('module/controller/action')
->setVariables(array(
'key' => 'value'
));
$htmlOutput = $this->getServiceLocator()
->get('viewrenderer')
->render($htmlViewPart);
$jsonModel = new JsonModel();
$jsonModel->setVariables(array(
'html' => $htmlOutput,
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1,2,3,4,5,6)
));
return $jsonModel;
}
As you can see, the templateMap i create is ... nasty ... it's annoying and i'm sure it can be improved by quite a bit. It's a working solution but just not a clean one. Maybe somehow one would be able to grab the, probably already instantiated, default PhpRenderer from the ServiceLocator with it's template- and path-mapping and then it should be cleaner.
Thanks to the comment ot #DrBeza the work needed to be done could be reduced by a fair amount. Now, as I'd initially wanted, we will grab the viewrenderer with all the template mapping intact and simply render the ViewModel directly. The only important factor is that you need to specify the fully qualified template to render (e.g.: "$module/$controller/$action")
I hope this will get you started though ;)
PS: Response looks like this:
Object:
html: "<h1>Hello World</h1>"
jsonArray: Array[6]
jsonVar1: "jsonVal2"
You can use more easy way to render view for your JSON response.
public function indexAction() {
$partial = $this->getServiceLocator()->get('viewhelpermanager')->get('partial');
$data = array(
'html' => $partial('MyModule/MyPartView.phtml', array("key" => "value")),
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1, 2, 3, 4, 5, 6));
$isAjax = $this->getRequest()->isXmlHttpRequest());
return isAjax?new JsonModel($data):new ViewModel($data);
}
Please note before use JsonModel class you need to config View Manager in module.config.php file of your module.
'view_manager' => array(
.................
'strategies' => array(
'ViewJsonStrategy',
),
.................
),
it is work for me and hope it help you.
In ZF 3 you can achieve the same result with this code
MyControllerFactory.php
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$renderer = $container->get('ViewRenderer');
return new MyController(
$renderer
);
}
MyController.php
private $renderer;
public function __construct($renderer) {
$this->renderer = $renderer;
}
public function indexAction() {
$htmlViewPart = new ViewModel();
$htmlViewPart
->setTerminal(true)
->setTemplate('module/controller/action')
->setVariables(array('key' => 'value'));
$htmlOutput = $this->renderer->render($htmlViewPart);
$json = \Zend\Json\Json::encode(
array(
'html' => $htmlOutput,
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1, 2, 3, 4, 5, 6)
)
);
$response = $this->getResponse();
$response->setContent($json);
$response->getHeaders()->addHeaders(array(
'Content-Type' => 'application/json',
));
return $this->response;
}
As usual framework developer mess thing about AJAX following the rule why simple if might be complex Here is simple solution
in controller script
public function checkloginAction()
{
// some hosts need to this some not
//header ("Content-type: application/json"); // this work
// prepare json aray ....
$arr = $array("some" => .....);
echo json_encode($arr); // this works
exit;
}
This works in ZF1 and ZF2 as well
No need of view scrpt at all
If you use advise of ZF2 creator
use Zend\View\Model\JsonModel;
....
$result = new JsonModel($arr);
return $result;
AJAX got null as response at least in zf 2.0.0

Zend: wrap FormErrors with user-defined html tags

Zend talk.By default,using the FormErrors decorator the generated list of errors has the following markup:
<ul class="form-errors>
<li>
<b>[element label or name]</b>
<ul>
<li>[error message]</li>
<li>[error message]</li>
</ul>
</li>
</ul>
Question: How can I use the following structure instead?
<span class='myErrors'>
•[error message]</br>
</span>
Update: I tried with:
array('FormErrors', array(
'markupListStart' => "<span class='myErrors'>",
'markupListEnd' => '</span>',
'markupListItemStart' => '',
'markupListItemEnd' => '</br>',
'ignoreSubForms'=> false,
'showCustomFormErrors' => true,
'onlyCustomFormErrors'=> false,
'markupElementLabelEnd' => '',
'markupElementLabelStart' => ''
));
But I still have unwanted tags and labels.This is the source code:
<span class='myErrors'>
[element label or name]
<ul class="errors">
<li>
[error message]
</li>
</ul>
</br>
</span>
The simples thing would be to create own decorator. You can take Zend decorator and modify the code. But I would recommend to put the messages in element instead of for the sake of semantics, span is after all an in-line element and you want to make multiple lines.
If you do not want to create new decorator you can try pass an array with data like in the Zend FormErrors options
protected $_defaults = array(
'ignoreSubForms' => false,
'showCustomFormErrors' => true,
'onlyCustomFormErrors' => false,
'markupElementLabelEnd' => '</b>',
'markupElementLabelStart' => '<b>',
'markupListEnd' => '</ul>',
'markupListItemEnd' => '</li>',
'markupListItemStart' => '<li>',
'markupListStart' => '<ul class="form-errors">',
);
Create similar array with appropriate tags.
You need two things: A decorator and a view helper. By default Zend Form uses the 'Errors' decorator, which uses the FormErrors view helper. You need to override this to achieve what you need. Take this simple example to illustrate this:
class Admin_Form_Authors extends Zend_Form
{
public function init()
{
$name = new Zend_Form_Element_Text('name');
$name->setLabel('name')
->setRequired(true)
->addFilter('StripTags')
->addFilter('StringTrim')
->addValidator('NotEmpty')
->setDecorators(array(
'ViewHelper',
array(new My_Form_Error_Decorator(),array('class' => 'my-error','escape' => true)),
'Label',
'HtmlTag'
));
$submit = new Zend_Form_Element_Submit('submit');
$submit->setAttrib('id','submitbutton');
$this->addElements(array($name,$submit));
}
}
As you can see, the second decorator I'm using for my $name form element, is an new object of My_Form_Error_Decorator class, which looks something like this:
class My_Form_Error_Decorator extends Zend_Form_Decorator_Abstract
{
public function render($content)
{
$element = $this->getElement();
$view = $element->getView();
if (null === $view) {
return $content;
}
$errors = $element->getMessages();
if (empty($errors)) {
return $content;
}
$separator = $this->getSeparator();
$placement = $this->getPlacement();
$viewhelp = new My_Form_Error_View_Helper();
$errors = $viewhelp->myErrorViewer($errors, $this->getOptions());
switch ($placement) {
case self::APPEND:
return $content . $separator . $errors;
case self::PREPEND:
return $errors . $separator . $content;
}
}
}
This is in fact almost the same decorator as the default 'Errors' decorator, the only line I change is this $errors = $viewhelp->myErrorViewer($errors, $this->getOptions()); Here I'm telling the decorator to use the My_Form_Error_View_Helper view helper, which in turn look something like this:
class My_Form_Error_View_Helper extends Zend_View_Helper_FormElement
{
public function myErrorViewer($errors, array $options = null)
{
if (empty($options['class'])) {
$options['class'] = 'errors';
}
$start = "<span%s>";
$end = "</br></span>";
if (strstr($start, '%s')) {
$start = sprintf($start, " class='{$options['class']}'");
}
$html = $start
. array_pop($errors)
. $end;
return $html;
}
}
Again I borrow most of this function from the original FormErrors View Helper. This is by no means the solution you should implement, but hopefully this sets you on the right path on how you can use decorators and view helper to customize the output of your form. I recommend the following two reads about Decorators and View Helpers (They are a huge topic by themselves)
Decorators
View Helpers

Categories