Symfony forward with #Template - symfony

I have an entry point controller (editAction) which depending on a route variable ({property}) will display different templates. I am using annotation "#Template".
I tried a lots of differtings settings but none is working. The closing that I got is with the following :
Error is " The merge filter only works with arrays or hashes "
public function editAction(Request $request, $property, $id)
{
// controller logic
$form = $this->createForm('ObjectEdit'.ucfirst($property), $object);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return $this->redirect($this->generateUrl('acme-demo-dashboard-show'));
}
$response = $this->forward('AcmeDemoBundle:Dashboard:edit'. ucfirst($property),
array('form' => $form)
);
return $response;
}
/**
* Edit Title property for Object
* #Template
*/
public function editTitleAction($form)
{
return array('form' => $form->createView());
}
Yaml Routing:
acme-demo-dashboard-edit:
path: /dashboard/edit/{property}/{id}
defaults: { _controller: AcmeDemoBundle:Dashboard:edit }
requirements:
acme-demo-dashboard-edit-title:
path: /dashboard/forward/edit/title/{form}
defaults: { _controller: AcmeDemoBundle:Dashboard:editTitle }
Edit: More info about the error :
at twig_array_merge (null, array('_locale' => 'en'))
Edit2: Twig Template for editTitle
{% extends "AcmeDemoBundle::layout.html.twig" %}
{% trans_default_domain "AcmeDemoBundle" %}
{% block AcmeBundleContent %}
<div class="col-sm-12">
<div class="alert alert-info">
{{ (ycRoute~'.intro') | trans }}
</div>
{{ form_start(form, { 'attr': {'class': 'form-horizontal', 'role': 'form'}}) }}
{% if form_errors(form) %}
<div class="alert alert-danger">
{{ form_errors(form) }}
</div>
{% endif %}
<div class="form-group {% if form_errors(form.title) %}has-error{% endif %}">
{{ form_label(form.title, '', {'label_attr': {'class': 'col-sm-3 control-label'}}) }}
<div class="col-sm-9">
{{ form_widget(form.title, { 'attr': {'class': 'form-control', 'placeholder': (ycRoute~'.titre') } }) }}
<p class="help-block">{{ form_errors(form.title) }}</p>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
{{ form_widget(form.save, { 'attr': {'class': 'btn btn-primary'} }) }}
</div>
</div>
<div class="row">
<div class="col-sm-9 col-sm-offset-3">
(<i class="fa fa-asterisk yc-fa-sm"></i>) {{ (ycRoute~'.champRequis')|trans }}
</div>
</div>
{{ form_end(form)}}
</div>
{% endblock AcmeBundleContent %}

Did you try such syntax to render template? You will need remove #Template annotation.
return $this->render(
'AcmeDemoBundle:Dashboard:edit'. ucfirst($property),
array('form' => $form)
);
"forward" used for forwards the request to another controller.

Related

Symfony 4.4 object not found by the #ParamConverter annotation

I know there are lots of similar topics already.
What I tried:
checking my routes (php bin/console router:match url)
Overriding the ParamConverter "App\Entity\User object not found by the #ParamConverter annotation"
Understanding #ParamConverter & #security annotations
And more solutions that didn't work for me.
Here is not working fragment of the controller:
/**
* Edit action.
*
* #param Request $request HTTP request
* #param User $user User entity
* #param UserPasswordEncoderInterface $passwordEncoder Password encoder
*
* #return Response HTTP response
*
* #throws ORMException
* #throws OptimisticLockException
*
* #Route(
* "/{id}/password",
* methods={"GET", "PUT"},
* requirements={"id": "[1-9]\d*"},
* name="app_password",
* )
*/
public function editPasswordUser(Request $request, User $user, UserPasswordEncoderInterface $passwordEncoder): Response
{
if (($this->getUser() == $user) || (is_array($this->getUser()->getRoles()) && in_array(
'ROLE_ADMIN',
$this->getUser()->getRoles()
))) {
$role = $user->getRoles();
$form = $this->createForm(NewPasswordType::class, $user, ['method' => 'PUT']);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->setPassword(
$passwordEncoder->encodePassword(
$user,
$user->getPassword()
)
);
$user->setUpdatedAt(new DateTime());
$this->userService->save($user);
$this->addFlash('success', 'message_updated_successfully');
return $this->redirectToRoute('detail_show');
}
return $this->render(
'security/password.html.twig',
[
'form' => $form->createView(),
'user' => $user,
'role' => $role,
]
);
} else {
return $this->redirectToRoute('detail_show');
}
}
Twig file:
{% extends 'base.html.twig' %}
{% block title %}
{{ 'title.user_editpasswd'|trans({'%id%': user.id|default('')}) }}
{% endblock %}
{% block body %}
<h1>{{ 'title.user_editpasswd'|trans({'%id%': user.id|default('')}) }}</h1>
{{ form_start(form, { method: 'PUT', action: url('app_password', {id: user.id}) }) }}
{{ form_widget(form) }}
<div class="form-group row float-sm-right">
<input type="submit" value="{{ 'action.save'|trans }}" class="btn btn-primary" />
</div>
<div>
{% if role[0] == 'ROLE_ADMIN' %}
<a href="{{ url('user_view', {id: user.id} ) }}" title="{{ 'action.back_to_view'|trans }}">
{{ 'action.back_to_view'|trans }}
</a>
{% endif %}
</div>
<div>
{% if role[0] == 'ROLE_ADMIN' %}
<a href="{{ url('user_index') }}" title="{{ 'action.index'|trans }}">
{{ 'action.index'|trans }}
</a>
{% endif %}
</div>
{{ form_end(form) }}
{% endblock %}
My app is almost finished, and that's the first and only time I have such an error.
Change argument type in controller action from User to \Symfony\Component\Security\Core\User\UserInterface and everything should work. Current user is registered in a container by this service id.

Sonata Field Type in Symfony Form

I need to create a custom form but used within Symfony so I, therefore, have to create a Symfony form. However, I'd like to be able to use the functionality Sonta field types provide like ModeListType::class.
So far I have created a custom route on my admin, within the controller action I create a new Symfony form. The controller action then returns a view with the form which extends the Sonata base edit layout.
class ExampleController
{
public function exampleAction(Request $request)
{
$order = new FooBar();
$modelManager = $this->get('sonata.admin.manager.orm');
$form = $this->createForm(ExampleType::class, $order, [
'model_manager' => $modelManager,
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//
}
return $this->renderWithExtraParams('admin/test.html.twig', [
'form' => $form->createView(),
'action' => 'create',
'object' => $order,
'objectId' => null,
]);
}
}
My form:
class ExampleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('job', ModelListType::class, [
'model_manager' => $options['model_manager'],
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => FooBar::class,
]);
$resolver->setRequired([
'model_manager',
]);
}
}
And my template:
{% extends 'bundles/SonataAdminBundle/CRUD/base_edit.html.twig' %}
{% import "#SonataAdmin/CRUD/base_edit_form_macro.html.twig" as
form_helper %}
{% block title %}
Here
{% endblock %}
{% block sonata_tab_content %}
<div class="col-md-12">
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-body">
{{ form(form) }}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
This renders an input without the additional buttons like: List, Add, Delete. Clicking into the input also doesn't do anything, so it isn't treated as a Sonata input.
Any help on solving this would be great.
As you override {% block sonata_tab_content %}, maybe you should try to put inside
{% block formactions %}{{parent()}}{{% endblock %}
{% block sonata_tab_content %}
<div class="col-md-12">
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-body">
{{ form(form) }}
</div>
</div>
</div>
</div>
</div>
{% block formactions %}
{{parent()}}
{{% endblock %}
{% endblock %}

Key "createdAt" for array with keys "0, 1" does not exist in note/show.html.twig at line 7

I can show my createdAt field of all other tables, except this one in my show view. The code is
{% block body %}
<div class="row">
<div class="col-md-12">
<h1>{{ title }}</h1>
<small>Criado {{ notes.createdAt | date('d/m/Y') }}</small>
{% if notes.updatedAt != notes.createdAt %}
<small>Editado {{ notes.updatedAt | date('d/m/Y') }}</small>
{% endif %}
</div>
</div>
...
showAction
public function showAction($id)
{
return $this->render('note/show.html.twig', array(
'notes' => $this->getDoctrine()->getRepository('CDGBundle:Note')->findAllByBudget($id),
'title' => 'Notas do cliente ' . $id
));
}
The findAllByBudget method was created inside of the repository:
public function findAllByBudget($id)
{
$qb = $this->createQueryBuilder('n');
return $qb
->orderBy('n.id', 'DESC')
->where(
$qb->expr()->eq('n.budget', '?1')
)
->setParameter(1, $id)
->getQuery()
->getResult();
}
Somehow I have problems on displaying from this specific entity only.
You get an array of notes, so in twig template you have to use some loop to display each note. For example:
<div class="col-md-12">
<h1>{{ title }}</h1>
{% for note in notes %}
<small>Criado {{ note.createdAt | date('d/m/Y') }}</small>
{% if note.updatedAt != note.createdAt %}
<small>Editado {{ note.updatedAt | date('d/m/Y') }}</small>
{% endif %}
{% endfor %}
</div>

Symfony how do you access form fields and pass to rendered forms?

I have a form that has two rendered controllers (forms) inside it. I want to pass a variable from the main form to the rendered. I know I can do it with something like this:
{{ render(controller(
'CompanyNameofBundle:Search:shortjq', {'orgid':entity.orgId})) }}
But I am having troubles accessing the 'orgid' which is in the main form.
my.html.twig
{% extends 'CompanyNameofBundle::base.html.twig' %}
{% block body -%}
<h1>Organization Edit</h1>
{{ form_start(edit_form, {'attr': {'novalidate': 'novalidate'}}) }}
<div class="form-group'">
<div class="col-md-2">{{ form_label(edit_form.orgName, 'Organization Name') }}</div>
<div class="col-md-4">{{ form_widget(edit_form.orgName) }}</div>
<div class="hidden">{{ form_widget(edit_form.orgId) }}</div>
<div> </div><div> </div>
</div>
<ul class="record_actions">
<li>{{ form_end(edit_form) }}</li>
<li>{{ form(delete_form) }}</li>
<li>
<a href="{{ path('org') }}">
Back to the list
</a>
</li>
</ul>
{{ render(controller(
'CompanyNameofBundle:Search:shortjq', {'orgid':entity.orgId})) }}
{% if entity.orgId is not null %}
{{ render(controller(
'CompanyNameofBundle:OrgMember:test', {'orgid':entity.orgId})) }}
{% endif %}
{% endblock %}
SearchController.php
/**
* #Route("/shortjq", name="shortjq")
* #Template()
*/
public function shortjqAction()
{
$form = $this->createForm(new JqueryType(), null, [
'action' => '',
'method' => 'POST'
]);
return array(
'form' => $form->createView(),
);
}
JqueryType.php
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setMethod('POST')
->add('name', 'text')
->add('orgid', 'integer')
->add('search', 'submit')
;
}
/**
* #return string
*/
public function getName()
{
return 'companynameofbundle_jquery';
}
To access the form widgets into the views I use this:
{{ form_widget(form.orgid) }}
If you also want to add attributes to that widget you can add them like that (for example to add a class to the widget):
{{ form_widget(form.org, { 'attr': {'class': 'the-css-class-you-want'} }) }}
Here you can search for more information Twig Template Form Function and Variable Reference

Label collection rendered incorrectly

I have the following form where questionaire.Questions is a collection of QuestionType which is just a yes/no <select>.
Here's what the twig looks like:
Expected:
{{ form_start(questionaire) }}
{% for question in questionaire.Questions %}
<div class="question">
{{ form_label(question) }}
</div>
<div>
{{ form_widget(question) }}
</div>
{% endfor %}
{{ form_end(questionaire) }}
However it gets rendered like this:
<div class="question">
//This is where I want the label. But instead I get this:
<label></label>//Unsure why it's empty. Maybe it's questionaire.Question's label?
</div>
<div>
<label>lorem ipsum...</label> //Wrong place. Label gets rendered here instead.
<select>...</select> //Selection widget is correctly rendered.
</div>
I think the label is getting rendered along with the widget. Here's my QuestionType just in case.
class QuestionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
$question = $event->getData();
$form = $event->getForm();
$form->add('Answer', 'choice', array(
'label' => $question->getQuestion(),
'choices' => array(
'' => 'Select One',
'Yes',
'No'
)
)
);
}
);
}
...
}
How can I get the label to where I want it?
You have to call the form_widget and form_label for the answer type
{{ form_label(question.Answer) }}
{{ form_widget(question.Answer) }}
You need to define the block question_row in a form theme, and use {{ form(questionaire_form) }} to render the entire form.
Acme/DemoBundle/Form/Type/QuestionType.php
// ...
public function getName(){
return 'question';
}
// ...
Acme/DemoBundle/Controller/DefaultController.php
// ...
public function questionaireAction(){
$form = $this->createForm(new BriefQuestionaireType());
return $this->render('AcmeDemoBundle:Default:brief_questionaire.html.twig', array(
'questionaire_form' => $form->createView()
));
}
// ...
Acme/DemoBundle/Resources/views/Default/brief_questionaire.html.twig
<html>
<head>
<title>Questionaire</title>
</head>
<body>
{% form_theme questionaire_form 'AcmeDemoBundle:Form:form.html.twig' %}
{{ form(questionaire_form) }}
</body>
</html>
Acme/DemoBundle/Resources/views/Form/form.html.twig
We create a block named [block_prefix]_row, where block_prefix is derived from getName() in QuestionType above. When this form theme is used, all QuestionType rows are rendered this way.
{% block question_row %}
<div class="question">
{{ form_label(form) }}
</div>
<div>
{{ form_widget(form) }}
{{ form_error(form) }}
</div>
{% endblock %}

Resources