Partials in lithium - php

Normally I use the Zend Framework and this is something I miss in Lithium. Partials. There is a render method in the view where you can use 'elements' which is the closest I got.
<?php $this->_render('element', 'form); ?>
This does work, however it requires that the form.html.php file is in the /views/elements folder. Is it possible to let it search in another path? Like /views/users/ so it gets the file /views/users/form.html.php.
I have tried the following, since I found out that the render method does accept an options argument wherein you can specify a path. So I made an Helper to fix this problem for me.
namespace app\extensions\helper;
use lithium\template\TemplateException;
class Partial extends \lithium\template\Helper
{
public function render($name, $folder = 'elements', $data = array())
{
$path = LITHIUM_APP_PATH . '/views/' . $folder;
$options['paths']['element'] = '{:library}/views/' . $folder . '/{:template}.{:type}.php';
return $this->_context->view()->render(
array('element' => $name),
$data,
$options
);
}
}
However it still only searches in the /view/elements folder, not in the path I specified.
Is there something I am doing wrong?

Why using plugins when this stuff can hopefully be done by Lithium :-)
I don't know Zend, but here is an exemple to configure elements default paths differently, to load them from the related view folder, instead of a shared path.
And let's add one more thing: we want to differentiate elements/partials from a normal view, by appending un underscore to the name of the file (mimic Rails partials)
First, reconfigure Media during the bootstrap process (config/bootstrap/media.php)
Media::type('default', null, array(
'view' => 'lithium\template\View',
'paths' => array(
'layout' => '{:library}/views/layouts/{:layout}.{:type}.php',
'template' => '{:library}/views/{:controller}/{:template}.{:type}.php',
'element' => array(
'{:library}/views/{:controller}/_{:template}.{:type}.php',
'{:library}/views/elements/{:template}.{:type}.php'
)
)
));
Then, use it
Suppose a controller Documents. Call on a view:
<?= $this->_render('element', 'foo', $data, array('controller' => 'documents')); ?>
This will look for a file inside views/documents/_foo.html.php and if doesn't exists, fallback to /views/elements/foo.html.php
This kind of simple re-configuration of framework defaults, can be done in Lithium for a bunch of stuffs (default controllers paths to create namespaces, views paths, libraries, etc ...)
One more example to re-maps your template paths so you can have stuff like pages/users_{username}.php instead of the Lithium default:
https://gist.github.com/1854561

Fixed it. Works like a charm. Zend like Partials in Lithium.
<?php
namespace app\extensions\helper;
use lithium\template\View;
class Partial extends \lithium\template\Helper
{
public function render($name, $folder = 'elements', array $data = array())
{
$view = new View(array(
'paths' => array(
'template' => '{:library}/views/' . $folder . '/' . $name . '.{:type}.php'
)
));
return $view->render('all', $data);
}
}
Can be used in templates like:
<?php echo $this->partial->render('filename', 'foldername', compact('foo', 'bar')); ?>

There is a plugin for partials. https://github.com/dmondark/li3_partials

Related

Where can I store, and how can i load Widget string templates in CakePHP 3.9?

I want to create a CakePHP Widget in order to create a custom form control. The end goal is to make it a plugin, but for now I am trying to determine the general structure of a Widget. I have created a file in src/View/Widget/DateTimeWidget.php containing
<?php
namespace App\View\Widget;
use Cake\View\Form\ContextInterface;
use Cake\View\Widget\WidgetInterface;
class DateTimeWidget implements WidgetInterface
{
protected $_templates;
public function __construct($templates)
{
$this->_templates = $templates;
}
public function render(array $data, ContextInterface $context)
{
$data += [
'name' => '',
];
return $this->_templates->format('DateTime', [
'name' => $data['name'],
'attrs' => $this->_templates->formatAttributes($data, ['name'])
]);
}
public function secureFields(array $data)
{
return [$data['name']];
}
}
?>
I load the Widget in a View with the code
$this->Form->addWidget(
'datetime',
['DateTime']
);
and then create a form control with it using
echo $this->Form->control('end_time', ['type' => 'datetime']);
However, I get the error Cannot find template named 'DateTime'.
I have created the basic template code
<?php
$this->Form->setTemplates([
'DateTime' => '<p {{attrs}}>Test template</p>'
]);
But I have no idea where in the folder structure to put it? In most plugins I have looked at it is in a helper file, but I wonder if this is the default way to do it? What are my options? And how do i tell CakePHP to load it? What is the preferred way of doing this?
Thank you!
If you want your widget to come with default string templates, then you could for example define them in the widget itself, by adding it to the string template instance that is being passed to the widget's constructor. You'd do it in the widget's render() method though, it wouldn't work properly in the constructor, as widget instances are being reused, ie they are only being constructed once, for example:
public function render(array $data, ContextInterface $context)
{
if (!array_key_exists('customDateTime', $this->_templates->getConfig())) {
$this->_templates->add([
'customDateTime' => '<p {{attrs}}>Test template</p>',
// ...
]);
}
// ...
}
Another option is to put the string templates in a config file:
// in path_to_your_plugin/config/form_helper_templates.php
<?php
return [
'customDateTime' => '<p {{attrs}}>Test template</p>',
// ...
];
and ask the users to load the form helper string templates in their view templates when they want to use your widgets:
$this->Form->templater()->load('YourPluginName.form_helper_templates');
Both options will integrate properly with the form helper, so that users can still override the templates by setting custom templates either via FormHelper::setTemplates(), StringTemplate::load()/add(), or the templates option for FormHelper::control().
I think you should use Cells for it.
Take a look at: https://book.cakephp.org/3/en/views/cells.html

Timber::render does not appear to work correctly on kinsta

I am migrating and troubleshooting a wordpress theme. I have a gutenberg block and an alert module set up to use the Timber composer package that allows the use of the twig templating engine.
I have it configured in a class
ProcessorTable.php
<?php
namespace CRG\Blocks;
class ProcessorTable
{
public function __construct()
{
$this->createProcessorTable();
}
public function createProcessorTable()
{
if (function_exists('acf_register_block')) {
// register a custom vue gravity forms block
acf_register_block(array(
'name' => 'processor-table-block',
'title' => __('Processor Table Block'),
'description' => __('A Block for displaying a contracted database processors table'),
'category' => 'crg-custom-blocks',
'icon' => 'welcome-write-blog',
'render_callback' => array($this, 'render_processor_table'),
'keywords' => array( 'table' ),
));
}
}
public function render_processor_table($block, $content = '', $is_preview = false)
{
$context = \Timber\Timber::context();
// Store block values.
$context['block'] = $block
// Store field values.
$context['fields'] = get_fields();
// Store $is_preview value.
$context['is_preview'] = $is_preview;
$twigPath = TEMPLATEPATH . "/src/views/blocks/block-processor-table.html.twig";
\Timber\Timber::render($twigPath, $context, 600);
}
}
AlertNotification.php
<?php
namespace CRG\Controllers\SiteWide;
class AlertNotification {
public function AlertModal(){
$context = \Timber\Timber::context();
$context['alert_header_text'] = get_field('alert_header_text', 'options');
$context['alert_text'] = get_field('alert_text', 'options');
$context['alert_icon'] = get_field('alert_icon', 'options');
$context['alert_color'] = get_field('alert_color', 'options');
$context['alert_toggle'] = get_field('alert_toggle', 'options');
\Timber\Timber::render( TEMPLATEPATH . "/src/views/sitewide/alert-modal.html.twig", $context );
}
}
this code worked on a cpanel server and on nexcess managed wordpress hosting, but when I migrated it to kinsta the code stopped rendering. It looks like the Timber::context() works, and the method can find the html.twig files, but it can't render the twig template, and produces no errors
I have tried troubleshooting this by checking the composer package versions, reinstalling the timber composer package, testing the code outside of the class directly into the functions.php file, and verifying the code can reach and output the file contents as a string. I checked the error log files and was unable to find a solution, or a cause of the error.
Hosting configuration:
Kinsta Caching: disabled
Wordpress debugging is enabled
Running on PHP 7.4
Using MySQL
I added this to my functions.php file
\Timber\Timber::$locations = TEMPLATEPATH . "/src/views";
so everything in the functions.php file for timber to work would look like this
require_once(__DIR__ . '/vendor/autoload.php');
$timber = new Timber\Timber();
\Timber\Timber::$locations = TEMPLATEPATH . "/src/views";
then in my ProcessorTable.php file I can call the Twig file path like this
$twigPath = "/blocks/block-processor-table.html.twig";
return \Timber\Timber::render( $twigPath, $context);
My guess about what went wrong is that the timber package was looking in the wrong director when rendering, based on incorrect locations information the Timber:locations method allows you to set up a custom location to store the templates: https://timber.github.io/docs/guides/template-locations/
Not sure why this would have worked on other systems, but not Kinsta. However, this appears to be a good practice going forward by explicitly setting the views.

how to use forward in view zend 2

I want to write a plugin in ZF2,
An example of the plugin is a like button that shows in every post. It should for example print in PostsAction,
I know I can use:
$like = $this->forward()->dispatch('Application\Controller\Index', array(
'action' => 'like',
'postId' => $Id
));
$like variable returns a button that users can click on.
But I want to echo this in the view. In forward the view is not defined.
Also if I use
return $this->getView()->render('application/index/like', array('postId' => $Id));
I don't have access to postId in likeController, because it is set in the view. How I can implement these type of plugins that need a dynamic variables?
Looks like you only need partials. A partial in ZF2 is only a view which you print in another view and give some params to it.
So you could define a View:
// application/partials/button.phtml
<button data-postId="<?php echo $this->postId ?>">Like It!</button>
And use it in other View:
echo $this->partial('application/partials/button.phtml', array(
'postId' => $thePostId
));
Official Documentation
Nice Answer on SO to implement with template_map
Solution using view helper
I think what you are looking for is a custom view helper. You can read on this in the official ZF2 documentation.
You have to write your custom button view helper, register it and then you can use it in your view.
The helper class:
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class LikeButtonHelper extends AbstractHelper
{
public function __invoke($post)
{
//return here your button logic, you will have access to $post
}
}
Register your helper within a configuration file:
'view_helpers' => array(
'invokables' => array(
'likeButtonHelper' => 'Application\View\Helper\LikeButtonHelper',
),
)
And finally in the view you can use it like this:
foreach($posts as $post){
echo( ... your code to show the post ...);
echo $this->likeButtonHelper($post);
}
UPDATE - Solution using forward plugin
I think I get what you mean now. I also think the example you are talking about is what in the ZF2 forward plugin documentation is referred to as “widgetized” content.
I think you are doing it correctly. You can attach the return value $like as a child to the view of the original controller (from where you forwarded in the first place).
So in your WidgetController:
use Zend\View\Model\ViewModel;
class WidgetController extends AbstractActionController
{
public function likeAction()
{
$post= $this->params()->fromRoute('post');
$viewModel = new ViewModel(array('post' => $post));
$viewModel->setTemplate('view/widgets/like');
return $viewModel;
}
}
So in your PostController:
use Zend\View\Model\ViewModel;
class PostController extends AbstractActionController
{
public function postsAction()
{
$likeWidget = $this->forward()->dispatch('Application\Controller\WidgetController', array(
'action' => 'like',
'post' => $post
));
$viewModel = new ViewModel();
$viewModel->setTemplate('view/posts/post');
$viewModel = new ViewModel(array(
//...add your other view variables...
));
// Add the result from the forward plugin as child to the view model
if ($likeWidget instanceof ViewModel)
{
$viewModel->addChild($likeWidget , 'likeWidget');
}
return $view;
}
}
And finally in your post view template add:
echo($this->likeWidget);
That is where the widget will eventually output.
The problem remains that you can not do this inside a foreach loop (a loop for printing your posts) in the view. That is why I suggested using a view helper and #copynpaste suggests using a partial, those are more suitable for adding additional logic inside a view.
Note:
Personally I don't like this forward solution for something so simple as a like button. There is hardly any logic in the controller and it seems overly complicated. This is more suitable for reusing a whole view/page that will be both rendered by itself as well as nested in another view.
The partials or view helpers seem much more suitable for what you want to do and those are very proper ZF2 solutions.
I found it ,developed by Mohammad Rostami,Special thanks to him :
Plugin In ZF2

how to auto load mobile templates by agent in codeigniter?

dir:
application
-controllers
-models
-views
-mobile_views
How do I auto load templates at mobile_views when I use $this->load->view and view by iphone or other mobile phone?
Check this
You can do it in two way.
Way 1: Its very simple. In the above answer (the link I have given) add following line in the end of MyController function
$this->load->_ci_view_path . = $this->view_type .'/';
You are done. You can simply load view like normal view load.
Way 2:
To autoload a view based on user agent, I think you can implement it using hooks. To implement this hooks you need to follow the following steps
Autoload user agent library in autoload.php
$autoload['libraries'] = array('user_agent');
Enable hooks in config.php
$config['enable_hooks'] = TRUE;
Not implement hooks on post_controller_constructor. Add following codes to hooks.php
$hook['post_controller_constructor'][] = array('class' => 'Loadview',
'function' => 'load',
'filename' => 'loadview.php',
'filepath' => 'hooks'
);
Now create a page named loadview.php under hooks directory having following code
class Loadview
{
public static $MOBILE_PLATFORM = 'mobile';
public static $DEFAULT_PLATFORM = 'default';
public function load(){
$this->CI =& get_instance();
$view_type = $this->CI->agent->is_mobile() ? self::$MOBILE_PLATFORM : self::$DEFAULT_PLATFORM;
$this->CI->load->_ci_view_path = $this->CI->load->_ci_view_path . $view_type .'/';
}
}
You are done now. You can simply load view like normal view load.
to load views from another dir aside from "views", i found this forum topic to be helpful
http://codeigniter.com/forums/viewthread/132960/
function external_view($path, $view, $vars = array(), $return = FALSE)
{
$full_path = $path.$view.'.php';
if (file_exists($full_path))
{
return $this->_ci_load(array('_ci_path' => $full_path, '_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
}
else
{
show_error('Unable to load the requested module template file: '.$view);
}
}
and you can work the rest from the controller.
I do this in my controller:
public function index()
{
if($this->agent->is_mobile())
{
$this->load_mobile();
}
else
{
$this->load_web();
}
}
public function load_mobile()
{
$this->load->view('mobile/home');
}
public function load_web()
{
$this->load->view('web/home');
}
In this way I can add different data to mobile and to web pages.
I also extend the default controller and add some useful extra features:
Enables the usage of master page/templates.
Can add css and javascript files.
Uses the _output method for controlling the controllers output.
Can load relative content with in the form of modules (views)
So I can manage better the different pages.
Bye!!

Why does class get redeclared multiple times?

Ok here is a method I use for initializing models in my controller actions:
protected $_tables = array();
protected function _getTable($table)
{
if (false === array_key_exists($table, $this->_tables)) {
include APPLICATION_PATH . '/modules/'
. $this->_request->getModuleName() . '/models/' . $table . '.php';
$this->_tables[$table] = new $table();
echo 'test ';
}
return $this->_tables[$table];
}
Then when I call the _getTable() method two times (for example once in init() method and once in the controller action) it prints:
test test test test test test
On top of the page. Shouldn't it just return the object from the _tables array() because of the array_key_exists() check? In other words shouldn't the part inside the array_key_exists() function get executed only once when the method is called multiple times?
UPDATE:
So the problem is this - for some reason the layout gets printed twice (so it's layout printed and inside the layout where there is layout()->content; ?> it prints the layout again). I have no idea why it does this as it worked well on the previous server and also on localhost.
In the snippet you show:
protected $this->_tables = array();
This is not valid syntax, it should be:
protected $_tables = array();
Also, why not just use include_once and let PHP handle this for you? Alternatively, you could use the Zend_Loader. Don't reinvent the wheel.
What you are really looking for is the loading of module based resources. Instead of re-inventing the wheel, why not just use the (module) resource autoloaders of ZF? See the documentation at:
http://framework.zend.com/manual/en/zend.loader.autoloader-resource.html
When you use Zend_Application (I'm assuming you don't), you get these automatically. If you don't you could do something like
$loaders = array();
$frontController = Zend_Controller_Front::getInstance();
foreach($frontController->getControllerDirectory() as $module => $directory) {
$resourceLoader = new Zend_Application_Module_Autoloader(array(
'namespace' => ucfirst($module) . '_',
'basePath' => dirname($directory),
));
$resourceLoader->addResourceTypes(array(
'table' => array(
'path' => 'models/',
'namespace' => 'Table'
));
$loaders[$module] = $resourceLoader;
}
//build array of loaders
$loader = Zend_Loader_Autoloader::getInstance();
$loader->setAutoloaders($loaders);
//set them in the autoloader
This approach is a bit naive, but it should give you nice autoloading.

Categories