I'm writing an extension for textblocks on my site. Therefore I made a twig extensions. As every textblock needs to be unique I included the template name in the filename of the textblock as well as a hook name. However I need to get the current template filename that called a twig extension.
It seems to be in the environment variable of twig, but is is not accessible.
My only workaround is to make the current template name a parameter of the function itself: {{ textblock_render('textblock_name', _self) }}, which seems a bit unnecessary.
So is there a way to get the current twig template filename, that called the extension?
class TextBlockExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('textblock_render', [$this, 'textblock'], ['is_safe' => ['html'], 'needs_environment' => true])
];
}
public function textblock(Environment $twig, string $hook, string $filename)
{
$loader = $twig->getLoader();
if (preg_match(TextblockController::REGEX_TEXTBLOCK, $hook) === 1) {
$path = '_partial/textblocks/' . md5('templates/' . $filename) . '-' . $hook . '.html.twig';
if ($loader->exists($path)) {
return $twig->render($path);
}
}
return false;
}
}
Related
i try to create some kind of formbuilder that outputs html of generated forms using the symfony form extenstion (using all the nice stuff like valdation, error hightlighting and such).
is use symfony 5 with twig 3.0
the created class
class FormBuilder{
private $twig;
private $formFactory;
public function __construct()
{
$defaultFormTheme = 'contact.html.twig';
$loader = new FilesystemLoader($_SERVER['DOCUMENT_ROOT'].'../template/forms');
$this->twig = new Environment($loader, ['cache' => $_SERVER['DOCUMENT_ROOT'].'../var/cache/'.$_ENV['APP_ENV'].'/twig']);
$formEngine = new TwigRendererEngine([$defaultFormTheme], $this->twig);
$this->twig->addRuntimeLoader(new FactoryRuntimeLoader([
FormRenderer::class => function () use ($formEngine) {
return new FormRenderer($formEngine);
},
]));
$this->twig->addExtension(new FormExtension());
$this->formFactory = Forms::createFormFactoryBuilder()
->getFormFactory();
}
/**
* #return string
*/
public function getContactForm():string
{
$form = $this->formFactory->createBuilder()
->add('content', TextareaType::class)
->getForm();
return $this->twig->render('contact.html.twig', [
'contact_form' => $form->createView(),
]);
}
}
and call it somewhere else with
$fb = new FormBuilder();
var_dump($fb->getContactForm());
it doesn't matter if the template looks like
{{ form_start(contact_form) }}
{{ form_widget(contact_form) }}
<input type="submit"/>
{{ form_end(contact_form) }}
or
{{ form(contact_form) }}
there is always the and runtime error:
An exception has been thrown during the rendering of a template ("No block "form" found while rendering the form.").
or in first template example instead of "form" it yells about "form_start".
searching for hours now, but seems i to blind to find the missing spot..
any suggestions or tips how to include the form function in twig outside the symfony controller?
with adding themes and (because of translation in the themes) translation it now works
public function __construct()
{
// name of theme template
$defaultFormTheme = 'form_div_layout.html.twig';
$appVariableReflection = new \ReflectionClass('\Symfony\Bridge\Twig\AppVariable');
$vendorTwigBridgeDirectory = dirname($appVariableReflection->getFileName());
//where the forms reside
$viewsDirectory = realpath($_SERVER['DOCUMENT_ROOT'].'../template/forms');
//init twig with directories
$this->twig = new Environment(new FilesystemLoader([
$viewsDirectory,
$vendorTwigBridgeDirectory.'/Resources/views/Form',
]));
//apply theme to twig/renderer
$formEngine = new TwigRendererEngine([$defaultFormTheme], $this->twig);
$this->twig->addRuntimeLoader(new FactoryRuntimeLoader([
FormRenderer::class => function () use ($formEngine) {
return new FormRenderer($formEngine);
},
]));
//add form extenstion
$this->twig->addExtension(new FormExtension());
// this is for the filter |trans
$filter = new TwigFilter('trans', function ($context, $string) {
return Translation::TransGetText($string, $context);
}, ['needs_context' => true]);
// load the i18n extension for using the translation tag for twig
// {% trans %}my string{% endtrans %}
$this->twig->addFilter($filter);
$this->twig->addExtension(new Translation());
//prepare factory for later use
$this->formFactory = Forms::createFormFactoryBuilder()
->getFormFactory();
}
just needs additional php extension 'gettext' and the translation extension for twig from https://github.com/JBlond/twig-trans (the original seems to support only twig up to v2.x)
thanks again Jakumi for the solving hint :)
I need to get all View data passed from controller in custom helper or directive, which was called in this View blade template.
So in blade template there is translation:
#lang($periodName . '.H1 title', ['time' => $time])
I want to make it shorter. For this purpose I created helper periodTrans('H1 Title').
function periodTrans($title) {
return __($periodName . '.' . $title, ['time' => $time]);
}
It there is a way to get acces inside helper function to $periodName and $time variables, to not pass them like arguments and make function shorter?
AFAIK this should work.
In your controller:
public function __construct()
{
.....
\View::share('periodName', $periodName);
\View::share('time', $time);
}
Your helper:
function periodTrans($title) {
$data = \View::getShared();
return __($data['periodName'] . '.' . $title, ['time' => $data['time']]);
}
I'm trying to configure routes in my OctoberCMS app. I configure routes in Plugin.php file of my plugin.
At the moment my code:
public function boot()
{
Validator::extend('numeric_for_repeater', function($attribute, $value, $parameters) {
foreach ($value as $v)
{
$validator = Validator::make(
$v,
[
'stock_quantity' => 'sometimes|numeric',
'stock_votes_quantity' => 'sometimes|numeric',
'value' => 'sometimes|numeric',
],
$parameters
);
if ($validator->fails())
return false;
}
return true;
});
\Route::get('/oferty/{id}', function ($id = null) {
$theme = Theme::getActiveTheme();
$path = \Config::get('cms.themesPath', '/themes').'/'.$theme->getDirName();
$this->assetPath = $path;
$offer = new Offer();
return \View::make(self::MAMF_PAGE_DIR . 'oferta.htm', ['offer' => $offer->getOfferById($id)]);
});
}
but I got an error:
View [.var.www.plugins.mamf.mamf2017..........themes.mamf2017.pages.oferta.htm] not found. because by default October expects views files in plugin directory.
How can I render view outside of plugin dir, for ex in themes path like this app/themes/mamf2017/pages/oferta.htm
I guess self::MAMF_PAGE_DIR is full base path your application. for example like
/var/www/vhosts/octdev/themes/responsiv-flat/
In short \View::make need absolute path from root
now it will try to look file with configured extensions for october-cms its .htm. others are .blade and .htm.blade etc ..
so in your case (view)file name is 'oferta.htm' that .(dot) is translated to '/' path separator so just don't use it and just use 'oferta' so it will check all possible values in pages directory
oferta.htm
oferta.blade
oferta.htm.balde
this adding .htm is automatic thing so you just need to provide name of view then it will find and work automatically
\Route::get('/oferty/{id}', function ($id = null) {
$theme = \Cms\Classes\Theme::getActiveTheme();
$path = \Config::get('cms.themesPath', '/themes').'/'.$theme->getDirName();
$this->assetPath = $path;
$offer = new Offer();
return \View::make(base_path() . $path . '/pages/' . 'oferta', ['offer' => $offer->getOfferById($id)]);
});
this is tested and working fine hope this will help you.
if its not working please comment.
I have a custom Module that creates a custom Block which has field elements.
This all works fine but I need to theme this block. I have checked the other posts on here and tried with no luck.
I have enabled twig debug and got theme suggestions. Still no luck.
Can anyone please point me in the right direction.
This is what I have so far:
my_module/my_module.module
// nothing related in here
my_module/src/Plugin/Block/myModuleBlock.php
<?php
namespace Drupal\my_module\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a 'ModuleBlock' block.
*
* #Block(
* id = "module_block",
* admin_label = #Translation("My Module"),
* )
*/
class ModuleBlock extends BlockBase {
public function blockForm($form, FormStateInterface $form_state) {
$form['test'] = array(
'#type' => 'select',
'#title' => $this->t('test'),
'#description' => $this->t('test list'),
'#options' => array(
'Test' => $this->t('Test'),
),
'#default_value' => isset($this->configuration['test']) ? $this->configuration['test'] : 'Test',
'#size' => 0,
'#weight' => '10',
'#required' => TRUE,
);
return $form;
}
/**
* {#inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['test'] = $form_state->getValue('test');
}
/**
* {#inheritdoc}
*/
public function build() {
$build = [];
$build['module_block_test']['#markup'] = '<p>' . $this->configuration['test'] . '</p>';
return $build;
}
}
my_module/templates/block--my-module.html.twig // as suggested by twig debug
<h1>This is a test</h1>
<div id="test-widget">{{ content }}</div>
I should also note that in my my_theme.theme I have this but I don;t think its relevant:
// Add content type suggestions.
function my_theme_theme_suggestions_page_alter(array &$suggestions, array $variables) {
if ($node = \Drupal::request()->attributes->get('node')) {
array_splice($suggestions, 1, 0, 'page__node__' . $node->getType());
}
}
As for what I've tried is this:
public function build() {
return array(
'#theme' => 'block--my-module'
);
}
But still no go.
Any help here is very much appreciated.
UPDATE: So I just got it to work but I still need help. I moved the template block--my-module.html.twig to my theme directory and it worked.
How do I get it to work in my module directory?
UPDATE: So I just got it to work but I still need help. I moved the
template block--my-module.html.twig to my theme directory and it
worked.
How do I get it to work in my module directory?
You can create a directory called templates/ in your modules root.
Place your template here.
Now let Drupal know you store the template in your module.
in your_module.module add this function:
function YOUR_MODULE_theme($existing, $type, $theme, $path) {
return array(
'block__my_module' => array(
'render element' => 'elements',
'template' => 'block--my-module',
'base hook' => 'block'
)
);
}
This is not tested. It´s the way it worked for my custom block.
Don´t forget to clear the cache.
To be able to add the twig file in your module, you need to make sure the module defines the reference, not the theme.
You can still implement hook_theme() in the module's .module file as follows:
function mymodule_theme($existing, $type, $theme, $path) {
return [
'mymodule_block' => [
'variables' => [
// define defaults for any variables you want in the twig file
'attributes' => [
'class' => ['my-module-class'],
], //etc
],
],
];
}
Then in your block's build() implementation you can add a reference to the new theme function:
public function build() {
// Load the configuration from the form
$config = $this->getConfiguration();
$test_value = isset($config['test']) ? $config['test'] : '';
$build = [];
$build['#theme'] = 'mymodule_block';
// You would not do both of these things...
$build['#test_value'] = $test_value;
$build['module_block_test']['#markup'] = '<p>' . $test_value . '</p>';
return $build;
}
Finally be careful about where you place your twig file and what you name it. Create a templates directory in your module directory, and replace the _ in the theme function name with -: mymodule-block.html.twig
Given an html/javascript 'widget' which needs to have certain fields customized before use. For example, the css class ids need to be unique as the widget may appear more than once on the same page.
Let's say I want to keep the markup (js/html) of the widget stored as a template so that I can fill in the values that need to be customized during resuse.
I know that Zend Framework's views give you at least part of this functionality, but each view is generally associated with a particular controller. Given that this widget could be created from any controller, yes still needs to be able to access some properties stored in a controller (or model). Where should I put the widget markup and how then do I fill in the custom values?
Can I create a custom view that can be reused within the same page (appear more than once) as well as on other pages? If so, how do I set that up?
Sounds like you need a ViewHelper http://framework.zend.com/manual/en/zend.view.helpers.html. Create a custom helper that will fetch the data from a model and just simply output it. This way it won't depend on any controller, can be called in either the layout or in any view script. Example:
// views/helpers/Widget.php
class Zend_View_Helper_Widget extends Zend_View_Helper_Abstract
{
protected $_model = null;
protected $_view = null;
public function widget()
{
$data = $this->_getDataFromModel();
return $this->_view->partial('widget.phtml', array('data' => $data));
}
public function setView(Zend_View_Interface $view)
{
if($this->_view === null) {
$this->_view = $view;
}
return $this->_view;
}
protected function _getDataFromModel()
{
$this->_model = $this->_getModel();
return $this->_model->getDataForWidget();
}
protected function _getModel()
{
if($this->_model === null) {
$this->_model = new Model_Widget(); // or whatever it's called
}
return $this->_model;
}
The partial script:
// views/scripts/widget.phtml
<div class="widget-class"><?php echo $this->data; ?></div>
And when you need it in your views just call it like <?php echo $this->widget(); ?>
Note that I'm rendering the widget in a separate partial view script, just to avoid having html/css in the helper itself.
Hope this helps to get you started :)
Zend_View_Helper_Partial
Example:
<?php echo $this->partial('partial.phtml', array(
'css_id' => 'foobar')); ?>
To run this from any other module:
<?php echo $this->partial('partial.phtml', 'partials_module', array(
'css_id' => 'foobar')); ?>
In your partial view script (partial.html) you would then have access to $this->css_id.