I have trouble rendering a template from a controller in a custom Drupal 8 module.
I am calling this controller method :
public function displayEngineUI() {
$build['#theme'] = 'bretagnecom-search-engine';
return $build;}
There is no problem reaching controller, i can var_dump inside. But the content of the template is not rendered.
My module file structure look like this :
bretagnecom_search_engine.module src
./src:
Controller
./src/Controller:
DefaultController.php
./templates:
bretagnecom-search-engine.html.twig
Any idea about what i'm doing wrong? I usually render a few html directly from controller with inline-template but i would like to isolate my html in his template file this time.
Thank's for the help everyone!
I guess template is not defined in hook_theme().
First just change hyphens to underscores:
public function displayEngineUI() {
$build['#theme'] = 'bretagnecom_search_engine';
return $build;
}
and in bretagnecom_search_engine.module add:
/**
* Implements hook_theme().
*/
function bretagnecom_search_engine_theme() {
$themes = [
'bretagnecom_search_engine' => [
'variables' => [
'your_custom_variable_1' => NULL,
'your_custom_variable_2' => NULL
]
];
If you do not have variables, just remove that part of code.
You can find more info here: https://www.drupal.org/docs/8/theming/twig/create-custom-twig-templates-for-custom-module
Related
I'm brand new to CodeIgniter, so apologies if I'm missing something obvious here.
I'm comfortable with sending data from a controller to a view file using return view('default/blog/index', $data);. My issue is accessing the same data in a layout file which is extended by the view file using <?= $this->extend('layouts/default'); ?>.
For example, if I insert <?= $data['content'] ?> in my view file, it displays as expected. If I insert the same code in the layout file that is extended by my view file, I get the "Trying to access array offset on value of type null" exception.
What am I missing that will allow me to access my data from within the layout file?
Thanks in advance.
Update:
So in my BlogController I've got
class BlogController extends BaseController
{
public function index()
{
$model = new Blog();
$data = $model->getBlog();
return view('default/blog/index', ['data' => $data]);
}
public function item($slug = null){
$model = new Blog();
$data = $model->getBlog($slug);
return view('default/blog/item', ['data' => $data]);
}
}
And then in my item.php and index.php files, I have
<?= $this->extend('layouts/default', ['data' => $data]); ?>
My Blog Model's getBlog() method:
public function getBlog($slug = false)
{
if ($slug === false) {
return $this->orderBy('bs_created_dt', 'desc')->findAll();
}
return $this->where(['bs_slug' => $slug])->first();
}
When I use the debug toolbar to inspect the data, it is showing as expected, and I can display it in the view files, but not in the layout file.
In Codeigniter, you need to pass data also in an extended file called layouts.
Because you want to access data inside the extended file and for that, you just need to pass data to that file.
So replace the line of code of extended view with this :
$this->extend('layouts/default', ['data' => $data]);
Figured this out - absolute rookie mistake.
I'm working off of a pre-existing template, and the previous developer had overwritten the $data variable in layout file before where I was trying to use it.
I'm off to stand in the corner for a while.
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
Is there any way to use something like this?
$foo = "{{ object|filter }}";
Because I'm trying to write a dynamic image converter that needs to output something like the example, but when in my twig I use {{ foo }}, it just outputs a raw string {{ object|filter }} instead of executhing the filter on the object as intended.
I've tried to use {{ foo | raw }} but same result.
What I'm trying to do exactly
CONTROLLER
$image = $em->getRepository('AcmeDemo:Media')->find($id);
$image_src = sprintf("{{ %s | imagine_filter('%s') }}", $image->getWebPath(), 'front_small');
return $this->render('image.html.twig', array(
'image_src' => $image_src
));
TWIG
<img src="{{ image_src }}"/>
So, I have a twig function inside a PHP variable $image_src, that Twig function could be, once formatted with sprintf {{ '/uploads/foo.jpg' | imagine_filter('front_small') }}.
That is a string for now, because it's inside a php variable $image_src, that variable is sent to my Twig template with the name image_src so, for now it is a string as I've said, if I do
My variable contains "{{ image_src }}" It will output a string that says:
My variable contains "{{ '/uploads/foo.jpg' | imagine_filter('front_small') }}"
Because, as I've said, image_src is just a string, but I want to acutally execute inside my Twig, the string that contains image_src, because yes, it is a string (to the eyes of the compiler) but we all know it is or it is pretended to be a Twig function (because of the syntax).
So, why | raw will not work?, because it is inteded to be used with strings containing HTML code, if it were HTML syntax it would work, but it's a Twig syntax, so It doesn't work.
Resuming, there should be a | compile twig function that executes Twig code inside a variable like | raw does with HTML, but, as this function doesn't exists, I'm wondering if there's a way to achieve it...
As #joshua said, it's like a Javascript eval.
I hope I've explained good what is the problem and what I need.
EDIT
I've used my own twig extension Compile in order to achieve what I needed.
class CompileExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
'compile' => new \Twig_Filter_Method($this, 'compile', array(
'needs_environment' => true,
'needs_context' => true,
'is_safe' => array('compile' => true)
)),
);
}
public function compile(\Twig_Environment $environment, $context, $string)
{
$loader = $environment->getLoader();
$compiled = $this->compileString($environment, $context, $string);
$environment->setLoader($loader);
return $compiled;
}
public function compileString(\Twig_Environment $environment, $context, $string)
{
$environment->setLoader(new \Twig_Loader_String());
return $environment->render($string, $context);
}
public function getName()
{
return 'compile';
}
}
UPDATE
Accepting #Benjamin Paap answer because it does exactly what I wanted in this case with better code, but my custom Twig class works for every situation.
What you want to do is not possible in twig without a TwigExtension which renders your string separately.
But looking at your code you're trying to use the LiipImagineBundle the wrong way. It seems tempting to use it this way, but the correct way to generate a url for your thumbnails would be this:
class MyController extends Controller
{
public function indexAction()
{
// RedirectResponse object
$imagemanagerResponse = $this->container
->get('liip_imagine.controller')
->filterAction(
$this->request, // http request
'uploads/foo.jpg', // original image you want to apply a filter to
'my_thumb' // filter defined in config.yml
);
// string to put directly in the "src" of the tag <img>
$cacheManager = $this->container->get('liip_imagine.cache.manager');
$srcPath = $cacheManager->getBrowserPath('uploads/foo.jpg', 'my_thumb');
// ..
}
}
https://github.com/liip/LiipImagineBundle#using-the-controller-as-a-service
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
I've created a new form element class for a special, complex purpose (text input field with an add-on button to open a "search wizard" popup).
To render this element properly, I've also created a form view helper. Everything works and is fine so far.
However, if I try to render the form using the FormCollection view helper, the element is rendered as a basic input element. That's because the FormElement view helper, which the FormCollection helper relies on, uses a hard-coded series of if clauses to map the element's type to a specific form view helper. It can't map my element's class and thus falls back to FormInput.
I.e. (taken from Zend/Form/View/Helper/FormElement.php, line 41-49):
if ($element instanceof Element\Button) {
$helper = $renderer->plugin('form_button');
return $helper($element);
}
if ($element instanceof Element\Captcha) {
$helper = $renderer->plugin('form_captcha');
return $helper($element);
}
...
$helper = $renderer->plugin('form_input');
return $helper($element);
and so on.
I've got a little stuck here because this architecture doesn't really promote extensibility.
The only solution that came to my mind (except rendering the form by hand) is to extend the FormElement view helper class and thus create my own CustomFormElement view helper. However, because of its complexity, I've put the custom element into an own module. So I'd have to write this CustomFormElement helper dynamically to add custom elements from any module. I don't think this is a recommended procedure.
Is there another solution or is maybe even my complete approach unrecommended? Thanks in advance!
I think the simplest way is to extend Zend\Form\View\Helper\FormElement, handle your field types in your render() method and register your FormElement as default FormElement for your application/module. Assuming that you have your custom TestField that you would like to render:
namespace Application\Form\View\Helper;
use \Zend\Form\ElementInterface;
use \Zend\Form\View\Helper\FormElement
use \Application\Form\Element\TestField;
class MyFormElement extends FormElement
{
public function render(ElementInterface $element)
{
$renderer = $this->getView();
if (!method_exists($renderer, 'plugin')) {
// Bail early if renderer is not pluggable
return '';
}
//your custom fields go here...
if ($element instanceof TestField) {
$helper = $renderer->plugin('\Application\Form\View\Helper\FormTestField');
return $helper($element);
}
return parent::render($element);
}
}
And in Application/config/module.config.php:
'view_helpers' => array(
'invokables' => array(
'form_element' => 'Application\Form\View\Helper\MyFormElement',
)
)
Get your hands on the FormElement view helper any way you can and addType to overwrite the view helper used. i.e. in view, just before you render your form:
<?php $this->plugin('FormElement')->addType('text', 'formcustom'); ?>
This will overwrite the view helper used in the FormRow,FormCollection helpers using your view helper by the key name:
in your config
'view_helpers' => array(
'invokables' => array(
'formcustom' => 'Application\Form\View\Helper\FormCustom',
)
),
When this question was asked the method may not have been there. But it is now.
The following is what I've done and feels like the right level of keeping things separate and neat.
Given:
A new element: MyModule\Form\MyElement which extends Zend\Form\Element
A new view helper class for MyElement: MyModule\Form\View\Helper\FormMyElement which extends Zend\Form\View\Helper\AbstractHelper
Here's how you register your view helper to be used to render your element by adding the following to module.config.php:
'view_helpers' => array(
'invokables'=> array(
'formMyElement' => 'MyModule\Form\View\Helper\FormMyElement',
),
'factories' => array(
'formElement' => function($sm) {
$helper = new \Zend\Form\View\Helper\FormElement();
$helper->addClass('MyModule\Form\MyElement', 'formMyElement');
return $helper;
}
),
),
The key is that you are providing a new factory method for FormElement that still creates the same, standard class (no need to override it), but also calls the addClass method to register your custom helper as the proper helper for your custom element. If you don't want to make the short-name for your helper, you can drop the invokables section and put the FQCN in the addClass call, but I like having the short name available.
This is the best method I've found so far. Ideally, you wouldn't have to take over the construction of the FormElement and could just modify a config that gets passed to it. The downside of this approach is that if you have multiple modules that define custom form elements they are going to clash if they all try to re-define the FormElement factory. You can't specify additions in multiple modules this way. So, if someone finds a better config that can be set that simply gets passed to the FormElement::addClass() method, please let me know.
BTW, I found this page which doesn't address the view helper side of the equation, but talks about registering new form element classes and how to over-ride the built in classes: http://framework.zend.com/manual/current/en/modules/zend.form.advanced-use-of-forms.html
----custom form element-----
namespace App\Form\View\Helper;
use Zend\Form\View\Helper\FormElement as ZendFormElement;
/**
* Description of FormElement
*/
class FormElement
extends ZendFormElement
{
public function addTypes(array $types)
{
foreach ($types as $type => $plugin) {
$this->addType($type, $plugin);
}
}
}
---- application module.config.php--------------
//..........
'view_helpers' => array(
'invokables' => array(
'formRTE' => 'App\Form\View\Helper\FormRTE',
),
'factories' => array(
'formElement' => function($sm) {
$helper = new App\Form\View\Helper\FormElement();
$helper->addTypes(array(
'rte' => 'formRTE',
));
return $helper;
}
),
),
//.........
Seems like we're both running into Form issues with Zend. I think that it could be better integrated with the whole MVC structure.
I think that your approach is sound. What I might think of doing is the following
Give your elements a variable named helper like in ZF1.
Create the custom form element renderer that will ALSO check the renderer attribute of a form element to decide on how to render it.
You could re-use the ViewHelperProviderInterface or create your own interface:
class CustomElement implements ViewHelperProviderInterface
{
public function getViewHelperConfig()
{
return array('type' => '\My\View\Helper');
}
}
or
class CustomElement implements FormHelperProviderInterface
{
public function getFormHelperConfig()
{
return '\My\View\Helper';
// or
return new My\View\Helper();
}
}
Then in your FormElement class you can do the following:
if ('week' == $type) {
$helper = $renderer->plugin('form_week');
return $helper($element);
}
if ($element instanceof THEINTERFACE) {
return $renderer->plugin($element->getFormHelperConfig());
}
$helper = $renderer->plugin('form_input');
return $helper($element);
This is probably what you had in mind anyway.
You'd probably be better off creating your own interface since the first one already has some sort of meaning behind it and it might confuse someone.
Aside from that, each module would then ONLY have to provide a helper_map key in the module configuration to have it's view helpers available during rendering with the MVC components.