I fully edited this post after doing research:
I'd like to realize a sidebar in the admin section which is integrated in every page, f.e. http://example.com/admin/index/:
class MyController extends Controller {
protected $modules = array();
public function __construct(){
$this->modules[] = "ModuleController:mainAction";
$this->modules[] = "OtherModuleController:mainAction";
}
public function indexAction(Request $request){
// do stuff here
return $this->render("MyBundle:My:index.html.twig",$data);
}
}
In the view should happen something like:
{% block modules %}
{% for module in modules %}
{% render module %}
{% endfor %}
{% endblock %}
So far so good, but these modules can contain forms which send post requests. I'd like to stay on the same page (http://example.com/admin/index/), so the action attribute of the form stays empty. The problem is: The post request will never be recognized by the Modules. So one idea was to hide a field in the according form that contains the name of the route, transform it to the according uri and send a sub request (in MyController):
public function indexAction(Request $request){
if($request->request->has('hidden_module_route')){
// collect all parameters via $request->request->keys()
$uri = $router->generate($request->request->get('hidden_module_route'), $parameters);
// 1: resolve the uri to the according controller and replace the target in the $this->modules array
// or 2: (after step 1)
$req = $request->create($uri, 'POST');
$subresponse = $this->get('kernel')->handle($req,HttpKernelInterface::SUB_REQUEST);
// inject the response into the modules and handle it in the view
}
[...]
}
That would work for me, but I'm not happy to have these responsibilities in the controller and it feels like there should be a better solution (one Idea is to register a kernel.controller listener that handles sub requests and injects the paths to the controller (which perhaps is marked via interface...)).
What do you think?
You could try to send the main request to your modules, so that they can bind the form with it.
{% block modules %}
{% for module in modules %}
{% render module with {'mainRequest': app.request} %}
{% endfor %}
{% endblock %}
And the module:
public function moduleAction(Request $request, Request $mainRequest)
{
$form = ...;
if ($mainRequest->isMethod('POST')) {
$form->bindRequest($mainRequest);
if ($form->isValid()) {
// You can have a beer, that worked
}
}
}
Related
How can I pass custom options directly to the Twig template of a custom Symfony 3.4 FormType?
Context:
I have created a custom FormType which also uses a custom Twig template to render its form widget. The build-in options (e.g. translation_domain, lable_attr, etc.) are directly accessible in the template. How can I achieve the same for my custom options?
// MyCustomType.php
class MyCustomType extends AbstractType {
public function getBlockPrefix() {
return 'my_custom_field';
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'custom_option' => 'customOptionValue',
'translation_domain' => 'customTranslationDomain',
]);
$resolver->setAllowedTypes('custom_option', ['string']);
}
}
// form_widgets.html.twig
{% block my_custom_field_widget %}
{{ dump() }}
{{ translation_domain }}
{#{ custom_option }#}
{% endblock %}
Of course this form widget is not very useful, it only dumps the available variables and the content of translation_domain which is directly accessible. Adding {{ custom_option }} will lead to an error, since this option/variable is not available. Neither directly nor via form.vars.custom_option.
Only when setting the option as form var, it can be accessed in the template:
// MyCustomType.php
class MyCustomType extends AbstractType {
...
public function buildView(FormView $view, FormInterface $form, array $options) {
$view->vars['custom_option'] = $options['custom_option'];
}
}
// form_widgets.html.twig
{% block my_custom_field_widget %}
// fails
{{ custom_option }}
// works
{{ form.vars.custom_option }}
{% endblock %}
So, all built-in options are available in form_vars but can also be accessed directly (e.g. as form.vars.translation_domain' AND as translation_domain`).
How do I achieve the same for my custom options?
Of course accessing the option via form.vars... is no problem. However, I wonder if this is the correct solution since the build-in types are accessible directly. Are these just shortcuts (how are they added?) to the form.vars... or is this some other configuration?
I need to include template dynamically in twig template. So the template will included the page that defined in routes.php
I try to concatenate string and variable like this code bellow, but still not working.
routes.php:
$app->get('/home', function ($request, $response, $args) {
$data['page'] = "home";
return $this->view->render($response, 'Home/layout.html', $data);
});
Home/layout.html:
{% include 'Home/_header.html' %}
{% include 'Home/_topbar.html' %}
{% include 'Home/_sidebar.html' %}
{% include 'Home/' ~ data.page ~ '.html' %}
{% include 'Home/_footer.html' %}
Error message:
Unable to find template "Home/.html" (looked into: ../App/Templates) in "Home/layout.html" at line 4.
I found my own solution, but it's not the one i want. So I edited routes.php and layout.html like this:
routes.php:
$app->get('/home', function ($request, $response, $args) {
$data['page'] = "{% include 'Home/home.html' %}";
return $this->view->render($response, 'Home/layout.html', $data);
});
Home/layout.html:
{% include 'Home/_header.html' %}
{% include 'Home/_topbar.html' %}
{% include 'Home/_sidebar.html' %}
{% include template_from_string(page) %}
{% include 'Home/_footer.html' %}
What I want is to send variable that contain name of the template file from routes.php, not the template syntax instead.
You are accessing the wrong variable. You are passing the following to twig,
$data['page'] = "home";
return $this->view->render($response, 'Home/layout.html', $data);
Which means to access the variable page inside twig you would just need to call page and not data.page as the array data is not even passed towards to your template
Which means your template should look like the following
{% include 'Home/_header.html' %}
{% include 'Home/_topbar.html' %}
{% include 'Home/_sidebar.html' %}
{% include 'Home/' ~ page ~ '.html' %}
{% include 'Home/_footer.html' %}
You could actually tell this by yourself when looking at the error message
Unable to find template "Home/.html"
As there is nothing in between Home/ and .html, this mean the variable data.page doesn't exist. I recommend to enable debug mode when developing then u would get an error you are trying to access an undefined variable
Page code:
{% partial 'content/main' %}
Partial:
[services]
{% component 'services' %}
Component:
public function prepareVars()
{
$this->page['servicesList'] = $this->getProperty(); // function returns 123
}
Component template:
{{ servicesList }} //does not display anything =(
Why is not the variable being passed?
Hmm seems something odd, not sure about your onRun method in component
prepareVars will not call automatically we need to call it manually.
did you add prepareVars inside onRun as when page life-cycle called , onRun is called automatically so we need to add prepareVars there as well, so its code get executed.
public function onRun()
{
$this->prepareVars();
}
public function prepareVars()
{
$this->page['servicesList'] = $this->getProperty(); // function returns 123
}
if you are already doing this then please notify it in comment so we can look further.
I have this controller where \Exception is raised (I haven't figured out which SF2 Exception to use yet) upon certain condition. Here is it:
<?php
namespace My\AppBundle\Controller;
use ....
class MyController extends Controller
{
const EXCEPTION_MESSAGE = <<<EOF
My <b>HTML</b>
<br/>
<small>Small phrase</small>
EOF;
public function indexAction()
{
// my logic
if(in_array($data, $array))
throw new \Exception(self::EXCEPTION_MESSAGE);
// the rest of my logic
return ....
}
}
And in app/Resources/TwigBundle/views/Exception/error.html.twig
{% extends '::base.html.twig' %}
{% block body %}
<h2>Error</h2>
<p>{{ exception.message }}</p>
{% endblock %}
The problem is HTML is not rendered when seeing the error page in prod environement.
I tried {{ exception.message|raw }} and also setting autoescape to false as per this answer but it seems to have no effect.
How can I do to make HTML works when displaying the \Exception message in Twig?
Where ever in the code you catch the exception is where it is needed to be added to the array you pass to twig. for example
$vars = [];
try {
$a->indexAction();
//fill $vars
} catch (Exception $e) {
$vars['exception'] = $e;
}
//pass $vars to twig
I'm using OctoberCms and trying to return a page content from an ajax request. For example, when clicking some internal link, i want to get from ajax the page object like the twig {% page %}.
public function onInternalLink(){
$href = post('href');
return [
'title'=>'', //here i want {{ page.title }}
'content' => '', //and here {% page %} like this variable in layout.
];
}
}
my js code is
$.request('onInternalLink', {
data: {href: u}, // var u is the requested url to return
success: function() {
console.log('Almost october');
}
})
}
I tried to create new CmsObject and try to use parseMarkup() method, and try pageCycle() with no success.
I didn't find a way to get the {% page %} object from the php script, is there way to do something like this?
in your PHP, you can use $this->page to access the current page. So your php would become:
public function onInternalLink() {
$href = post('href');
return [
'title'=> $this->page->title,
'content' => $this->getContentsFromFile($this->page->baseFileName),
];
}
All you would need after this would be to write the logic to fetch html content from the baseFileName of the page (I have wrapped this as $this->getContentsFromFile() in the example above).
There are more variables provided by $this-> page - Read about them here - https://octobercms.com/docs/cms/pages#page-variables