I am migrating from clean php views to smarty, and I have a problem converting the following template into smarty:
<?php
use \framework\core;
?>
<h1><?= Core::translate('some string to translate'); ?></h1>
So I want to use some class that's loaded via autoloader, and then use its translate method. How can I do that in smarty?
This is how I did this same task in one of my older projects using Smarty API extension:
// register additional smarty modifiers for I18N
$this->smarty->register_block("translate", array($this, "smarty_i18n_translate"));
// better alias
$this->smarty->register_block("_", array($this, "smarty_i18n_translate"));
// translation function
public function smarty_i18n_translate($params, $content, &$smarty, &$repeat)
{
if (isset($content))
{
if (isset($params['resourceID']))
{
$resourceID = $params['resourceID'];
unset($params['resourceID']);
}
else
$resourceID = NULL;
// setting context vars if specified
foreach ($params as $key => $val)
{
$this->setContextVar($key, $val);
}
// Core::translate($content); ?
return $this->translate($content, $resourceID);
}
return '';
}
This allows writing in views (I used {? as smarty tag delimiter):
<span class="label">{?_?}Operation type{?/_?}:</span>
One note: this was for some pretty ancient version of Smarty, they may have changed details of API, but it seems register methods are still there.
Btw in the docs there is this same problem illustrated as example: http://www.smarty.net/docsv2/en/api.register.block.tpl
try this logic/way, hope you might get some idea..
<?php
$my_obj = new MyClass();
$smarty->left_delimeter('{{');
$smarty->right_delimeter('}}');
$smarty->assign('my_obj', $my_obj);
$smarty->display('your.html');
?>
Then on your html
<h1>{{ $my_obj->translate('some string to translate'); }}</h1>
Related
I have an old project I'm working on using Slim version 2. I can not upgrade to 3.
I'm trying to integrate twig into slim 2 while also keeping the old default slim2 renderer.
Currently I have this.
class TwigView extends \Slim\View
{
public function rendertwig($template,$data = array()){
global $twig;
$twigResults = $twig->render($template,array('test' => '1'));
$data = array_merge($this->data->all(), $data);
return $this->render($twigResults, $data);
}
}
$view = new TwigView();
$config['view'] = $view; //#JA - This command overides the default render method.
//#JA - Intialize Slim
$app = new \Slim\Slim($config);
The idea is that I would call this saying $app->view->rendertwig('file.twig') when I need to render the twig templates and use $app->render('template.php') for all the other templates that use the default slim2 method of templating.
However, I get an error because in my rendertwig function $this->render() function requires a template name for the first parameter. Is there a way I can render directly the results from twig into the slim engine without needing a template file?
I'm aware this is bad form to have two templating engines but eventually I will switch everything to Twig but I need this as a temporary solution till I can patch everything over.
When I inspected slim's view object it has this defined as its render method which will explain the issue.
protected function render($template, $data = null)
{
$templatePathname = $this->getTemplatePathname($template);
if (!is_file($templatePathname)) {
throw new \RuntimeException("View cannot render `$template` because the template does not exist");
}
$data = array_merge($this->data->all(), (array) $data);
extract($data);
ob_start();
require $templatePathname;
return ob_get_clean();
}
I don't know if this is bad form but I did this as a temporary solution.
class TwigView extends \Slim\View
{
public function rendertwig($template,$data = array()){
global $twig;
$twigResults = $twig->render($template,array('test' => '1'));
echo $twigResults;
}
}
I saw that all the render method did was just require the template so I figured its safe to just echo the results from the twig templating engine? This seemed to work from my test.
I am looking to create an extension api for my web application.
Example extension file:
function echoCustomHeaders(){
echo '<li>Header Link</li>';
}
There would be several files similar to the example extension file (with the same function name, for user friendlyness when programming addons).
for($x=0;$x<count($extension_files);$x++){
//This would obviosely break when it gets to the second file, as functions cannot be declared twice in php
require_once($extension_files[$x]);
}
//some code later...
//this should call echoCustomHeaders() in ALL of the extension files, what code should I put here to make this happen?
echoCustomHeaders();
In case you are wondering about what the question is, read the comments in the code above and it should be fairly easy to see.
Return closures (lambda expressions) in your extension files as follows:
return function(){
echo '<li>Header Link</li>';
}
In PHP the include/require statement is really a function and therefore has a return value, hence you can collect those closures into an array:
$closures = array();
for($x=0;$x<count($extension_files);$x++){
$closures[$i]=include($extension_files[$x]);
}
// Do whatever you want with your closures, e.g. execute them:
foreach($closures as $closure) {
$closure();
}
ADDED CONTENT:
In the case if you would like to return multiple closures with each include, you may return an array of closures, indexed by the name of them:
return array(
'echoCustomHeaders' => function() {
echo '<li>Header Link</li>';
},
// ...
);
Then you can still execute some of them by their name:
$closureArray = array();
foreach($extension_files as $file) {
$closureArray[] = include($file);
}
foreach($closureArray as $closure) {
if(isset($closure['echoCustomHeaders'])) // Maybe there wasn't an echoCustomHeaders in each extension file ...
$closure['echoCustomHeaders']();
}
Maybe it would be a better idea to even separate the different kind of extension functions into distinct arrays:
$closureArray = array();
foreach($extension_files as $file) {
$functions = include($file);
foreach($functions as $name => $function) {
if(!isset($closureArray[$name]))
$closureArray[$name] = array();
$closureArray[$name][] = $function;
}
}
foreach($closureArray['echoCustomHeaders'] as $closure) {
$closure();
}
Another solution is to use a more object oriented way, and declare a new extension class in each extension file. However, if there would be no data sharing required between the extension methods in an extension file, then simply returning the functions as an array of closures is a more lightweight and cleaner solution in my opinion.
1.maybe you can use the new feature after php5.3:namespace http://www.php.net/manual/en/language.namespaces.php, then you can use the same name functions.
2.however you could think about the object oriented solution,for example,defined a base class who has a method echoCustomHeaders.
I've followed a Tutorial on Moodle.org on how to extend the custom menu and I'm receiving this error message in Moodle 2.5:
Function get_course_category_tree() is deprecated, please use course renderer or coursecat class, see function phpdocs for more info
The code works, but I would like to upgrade the code for Moodle 2.5. I've found documentation on replacement functions.
Below is the code directly from the tutorial. I need to replace get_category_tree().
class theme_mytheme_core_renderer extends core_renderer {
protected function render_custom_menu(custom_menu $menu) {
global $CFG;
require_once($CFG->dirroot.'/course/lib.php');
$branch = $menu->add(get_string('courses', 'theme_mytheme'), null, null, 10000);
$categorytree = get_course_category_tree();
foreach ($categorytree as $category) {
$this->add_category_to_custommenu($branch, $category);
}
return parent::render_custom_menu($menu);
}
}
Here is the documentation for the new functions. I've tried all three of the following functions with no success. Does anyone have any thoughts or pointers?
core_course_renderer::coursecat_coursebox()
core_course_renderer::coursecat_courses()
core_course_renderer::coursecat_tree()
You can use $courses = get_courses($categoryid) to get the result.
Having php code:
function getChildren($parent_id = 0)
{
....
return $SomeChildrenArray;
}
And Smarty assignation $smarty->assign('children', getChildren($id));
How to write smarty expression to pass some value to the function?
Smarty can be extended through its plugins API. See registerPlugin() for an introduction.
$smarty->registerPlugin('function', 'children', function($params, $template) {
return "some string:" . $params['foobar'];
});
and
{children foobar="hello world"}
would output
some string:hello world
Note that plugin functions have to return a string.
Since Smarty3 you can call and assign arbitrary functions from within a template:
{$children = getChildren(3)}
This way you can call any function and return any value/type you want. See Smarty_Security for details on how to control which functions may be called…
Oh, sorry! I'am was wrong in my post =) Try this please
php file
$smarty->registerPlugin("function","my_supper_closure_function", "my_supper_closure_function");
function my_supper_closure_function($params, $smarty){
$SomeChildrenArray=array($params["parent_id"]);
return print_r($SomeChildrenArray,true);
}
$smarty->display('index.tpl');
tpl file
{my_supper_closure_function parent_id=2}
PHP file:
$smarty->assign('children', function($parent_id = 0) {
....
return $SomeChildrenArray;
});
tpl file for parent_id=2
{children p1=2}
I am trying to attack this problem from a completely different angle, because it doesn't look like I can achieve my goal that way.
I want to loop over the item stack in the HeadScript View Helper, and make modifications to it. The documentation for this and some of the other view helpers makes this statement:
HeadScript overrides each of append(),
offsetSet(), prepend(), and set() to
enforce usage of the special methods
as listed above. Internally, it stores
each item as a stdClass token, which
it later serializes using the
itemToString() method. This allows
you to perform checks on the items in
the stack, and optionally modify these
items by simply modifying the object
returned.
So, where is this "object returned"? I am missing a piece of the puzzle here.
Thanks for your help!
In the toString() method of Zend_View_Helper_HeadScript I noticed a foreach() loop on $this, so I tried that and it worked. Here's a HeadScript extension I wrote that illustrates the solution:
class My_View_Helper_HeadScript extends Zend_View_Helper_HeadScript
{
public function toString($indent = null)
{
$files = array();
foreach ($this as $key => $item) {
if (!empty($item->attributes)
&& array_key_exists('src', $item->attributes)
&& ('scripts' == substr($item->attributes['src'], 1, 7))) {
$files[] = $item->attributes['src'];
unset($this[$key]);
}
}
if (0 < count($files)) {
$this->prependFile('/combo.php?type=scripts&files=' . implode(',', $files));
}
return parent::toString($indent);
}
}
In Bootstrap.php the following lines to point to my helpers:
$this->bootstrap('view');
$view = $this->getResource('view');
$view->addHelperPath('My/View/Helper', 'My_View_Helper');
In my layout, I have this line:
<?php echo $this->headScript(); ?>
If my solution is unclear in any way, let me know and I'll update it to clarify.