I'm trying to render an edit form in a modal for each element on the same page I create them, but it seems not possible to render a same form multiple times.
This is my controller:
/**
* #Route("/outcome", name="outcome")
*/
public function index(OutcomeRepository $repo, Request $request, EntityManagerInterface $manager): Response
{
$outcomes = $repo->findAll();
$outcome = new Outcome();
$form = $this->createForm(OutcomeType::class, $outcome);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$manager->persist($outcome);
$manager->flush();
$this->addFlash(
'success',
"Income <strong>{$outcome->getTitle()}</strong> has been added."
);
return $this->redirectToRoute('outcome');
}
return $this->render('outcome/outcome.html.twig', [
'outcomes' => $outcomes,
'form' => $form->createView(),
]);
}
This is the OutcomeType:
class OutcomeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, [
'label' => "Title",
'attr' => [
'placeholder' => "Write a title"
]
])
->add('amount', MoneyType::class, [
'label' => "Amount",
'attr' => [
'placeholder' => "Write an amount"
]
])
->add('save', SubmitType::class, [
'label' => 'Send',
'attr' => [
'class' => 'btn btn-primary'
]
]);
}
And this is the twig part:
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#newOutcomeModal">
New outcome
</button>
<div class="modal fade" id="newOutcomeModal" tabindex="-1" aria-labelledby="newOutcomeModal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="newOutcomeModal">Add a new outcome</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{form_start(form)}}
{{form_widget(form)}}
{{form_end(form)}}
</div>
</div>
</div>
</div>
{% for outcome in outcomes %}
<div class="card m-4">
<div class="card-body">
<h5 class="card-title">{{outcome.title}}</h5>
<p class="card-text">{{outcome.amount}}</p>
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#editOutcomeModal{{outcome.id}}">Edit</button>
</div>
</div>
<div class="modal fade" id="editOutcomeModal{{outcome.id}}" tabindex="-1" aria-labelledby="editOutcomeModal{{outcome.id}}" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editOutcomeModal{{outcome.id}}">Edit {{outcome.title}} outcome</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{form_start(form)}}
{{form_widget(form)}}
{{form_end(form)}}
</div>
</div>
</div>
</div>
{% endfor %}
I get the error: "An exception has been thrown during the rendering of a template ("Field "outcome" has already been rendered, save the result of previous render call to a variable and output that instead.")."
I don't know how to solve this. I tried to turn the fields of OutcomeType in a collectionType but it didn't work.
Can someone help me please ?
Related
I work with Symfony to build and send a contact form by mail.
I use swiftMailer.
The problem is that my form is not submitted.
So, my swiftmailer can't work because the isSumbitted will stay at false and $mailer->send(message) will not be activate
My controller :
public function index (Request $request, Swift_Mailer $mailer)
{
$form = $this->createForm(ContactType::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$contactData = $form->getData();
$name = $contactData['name'];
$content = $contactData['message'];
$email = $contactData['email'];
$entreprise = $contactData['entreprise'];
$zip = $contactData['zip'];
$message = (new Swift_Message('Vous avez un nouveau message sur le site AMC Industrie.fr'))
->setFrom('test#example.com')
->setTo('test#gmail.com')
->setSubject('Demande d\'information')
->setCharset('utf-8')
->setContentType('text/html')
->setBody(
"
<p> Vous avez reçu un message de <strong>$name</strong>, de l'entreprise <strong>$entreprise</strong>
<br>(code postal : $zip ) <br>
Son email est : <strong>$email</strong>. </p>
<p> Son message :</p>
<p>$content
</p>
"
)
;
$mailer->send($message);
return $this->redirectToRoute('map_contact');
}
return $this->render('contact/map_contact.html.twig', [
'form' => $form->createView()
]);
}
My Form :
{
$builder
->add('name',TextType::class,
['label'=>false,
'constraints' => new NotBlank,
'attr' => [
'class'=> 'form-control',
'placeholder' => 'Nom et prénom',
]
])
->add('email',EmailType::class,
['label'=>false,
'constraints' => new NotBlank,
'attr' => [
'class' => 'form-control',
'placeholder' => 'adresse e-mail'
]
])
->add('entreprise',TextType::class,
['label'=>false,
'constraints' => new NotBlank,
'attr' => [
'class'=> 'form-control',
'placeholder' => 'Nom de votre entreprise'
]
])
->add('zip',NumberType::class,
['label'=>false,
'constraints' => new NotBlank,
'attr' => [
'class'=> 'form-control',
'placeholder' => 'Code postal'
]
])
->add('message',TextareaType::class,
['label'=>false,
'constraints' => new NotBlank,
'attr' => [
'class'=> 'form-control',
'placeholder' => 'Décrivez-nous votre projet'
]
])
->add('save', SubmitType::class,
['label' => 'Envoyer',
'attr'=> [
//'class' => 'waves-effect waves-light btn-large btn btn-primary mt-1 mb-5 lift',
'id' => 'form-submit'
]])
;
}
My template :
<form>
{{ form_start(form) }}
<div class="row">
<div class="col-12 col-md-6">
<div class="form-group mb-5">
{{form_label(form.name, 'Nom et prénom')}}
{{form_widget(form.name)}}
</div>
</div>
<div class="col-12 col-md-6">
<div class="form-group mb-5">
{{form_label(form.email, 'Email') }}
{{form_widget(form.email)}}
</div>
<div class="col-12 col-md-6">
<div class="form-group mb-5">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12 col-md-6">
<div class="form-group mb-5">
{{form_label(form.entreprise, 'Entreprise')}}
{{form_widget(form.entreprise)}}
</div>
</div>
<div class="col-12 col-md-6">
<div class="form-group mb-5">
{{form_label(form.zip, 'Code Postal')}}
{{form_widget(form.zip)}}
</div>
<div class="col-12 col-md-6">
<div class="form-group mb-5">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-group">
{{form_label(form.message, 'Comment pouvons-nous vous aider ?')}}
{{form_widget(form.message)}}
</div>
</div>
</div> <!-- / .row -->
<div class="row justify-content-center">
<div class="col-auto py-5">
<!-- Submit -->
<div>
{{form_widget(form.save)}}
</div>
</div>
</div>
{{ form_end(form) }}
</form>
My debug tool :
Form in get parameter OK. I have all my input with informations.
Debug tool - get parameters:
BUT
there is not sumbitted data. form is not submit
debug tool - form is not submit:
I don't understand where is my problem.
Ok, it's resolved !
The problem : I already have a html balise for my form, in my template.
When I delete , it's ok !
I would like to have a form available on every page in my web app. So I created a widget with a form.
class AddNewURL extends Widget{
public $model;
public function init()
{
parent::init();
if ($this->model === null) {
$this->model = new ProductUrlForm();
}
}
public function run()
{
return $this->render('addNewURLForm', [
'model' => $this->model,
]);
}
}
and widget view
<div class="modal fade bs-example-modal-lg" id="newURL" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<?php Pjax::begin(); ?>
<?php $form = ActiveForm::begin([
'action' => Url::to(['site/new-url']),
'options' => ['data' => ['pjax' => true]],
]) ?>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">Add new URL</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<?= $form->field($model, 'url', ['errorOptions' => ['class' => 'help-block', 'encode' => false]])
->textInput(['maxlength' => false])
?>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Add</button>
</div>
<?php ActiveForm::end() ?>
<?php Pjax::end(); ?>
</div>
</div>
</div>
and this how I use this widget in my layouts/main.php
<?= \app\widgets\AddNewURL::widget() ?>
in my site/new-url action I have
public function actionNewUrl()
{
$model = new ProductUrlForm();
$model->status = STATUS_NEW;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
//return success message
}
return $this->render('index', [
'model' => $model,
]);
}
and now when I submit form data goes to my action, the form is reset and that's all. How to show validation error or success message using pjax? In my model I have few custom validation rules.
Maybe there is better solution to use one form on all pages?
Action actionNewUrl() must render view addNewURLForm so Pjax can replace old form with new html recieved from the action.
It is better to place your widget, controller and form model to separate module.
I'm currently running into a very confusing problem with Symfony 4 (or not even that, I don't know). I've set up a login/registration system manually, which has been working great, up until now. I've developed a form where one can create branches for their main company. I used a FormType for that exact manner:
<?php
// src/Form/UserType.php
namespace App\Form;
use App\Entity\Filiale;
use App\Entity\Kammer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class FilialType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('apothekenname', TextType::class)
->add('email', EmailType::class)
->add('anrede', TextType::class)
->add('titel', TextType::class, array(
'required' => false
))
->add('vorname', TextType::class)
->add('name', TextType::class)
->add('adresszusatz', TextType::class, array(
'required' => false
))
->add('strasse', TextType::class)
->add('hausnummer', TextType::class)
->add('plz', NumberType::class)
->add('ort', TextType::class)
//->add('kammer', TextType::class)
->add('kammer', EntityType::class, array(
'class' => Kammer::class,
'choice_label' => 'name',
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Filiale::class,
));
}
}
?>
The view rendering the FormType looks like this, where there's a separate button triggering the Bootstrap modal dialog:
<div class="modal fade" id="modal-add-filiale" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Neue Filialapotheke anlegen</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Schließen">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{{ form_start(form_add_filiale) }}
<div class="form-group row">
<label class="col-xl-3" for="filiale-apothekenname">Apothekenname</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.apothekenname) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-anrede">Anrede</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.anrede) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-titel">Titel</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.titel) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-vorname">Vorname</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.vorname) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-name">Name</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.name) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-strasse">Straße</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.strasse) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-hausnummer">Hausnummer</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.hausnummer) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-adresszusatz">Adresszusatz</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.adresszusatz) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-plz">PLZ</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.plz) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-stadt">Stadt</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.ort) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-email">Email</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.email) }}
</div>
</div>
<div class="form-group row">
<label class="col-xl-3" for="filiale-kammerbezirk">Kammerbezirk</label>
<div class="col-xl-9">
{{ form_widget(form_add_filiale.kammer) }}
</div>
</div>
<button type="submit">Hu</button>
{{ form_end(form_add_filiale) }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button>
<button id="modal-add-filiale-save" type="submit" class="btn btn-primary">Speichern</button>
</div>
</div>
</div>
</div>
The Controller function processing the form, renderung and submission looks like this:
public function login(Request $request, AuthenticationUtils $authUtils, AuthorizationCheckerInterface $authChecker) {
$isLoggedIn = ($authChecker->isGranted('ROLE_USER') || $authChecker->isGranted('ROLE_USER_MO'));
if($isLoggedIn) {
$filiale = new Filiale();
$form = $this->createForm(FilialType::class, $filiale);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$filiale->setUser($this->getUser());
$em = $this->getDoctrine()->getManager();
$em->persist($filiale);
$em->flush();
return $this->render('default/startpage_loggedin.html.twig', array(
'breadcrumb' => 'BROTKRUMEN',
'form_add_filiale' => $form->createView(),
'toast_finished' => 1,
));
}
return $this->render('default/startpage_loggedin.html.twig', array(
'breadcrumb' => 'BROTKRUMEN',
'form_add_filiale' => $form->createView(),
));
}
else {
// get the login error if there is one
$error = $authUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authUtils->getLastUsername();
return $this->render('default/startpage_loggedout.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
));
}
}
While the code isn't perfectly arranged and I'm probably missing some best practices, I still find the problem I'm facing weird. The form is only accessible while logged in - when I click the button to open the modal, then fill the form out and then press the submit button, I'm automatically getting logged out and the error message "invalid credentials" is displayed.
However, when I'm outsourcing the form rendering and form handling to a new view and a new controller function, the code does exactly what it's supposed to do, without touching the logged in user.
I'm basically running out of ideas and would appreciate any hint you guys could offer. Thank you in advance!
Edit: Security.yml
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
encoders:
App\Entity\User:
algorithm: bcrypt
legacy_encoder:
algorithm: md5
encode_as_base64: false
iterations: 1
providers:
in_memory: { memory: ~ }
db_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
#pattern: ^/
#http_basic: ~
anonymous: ~
provider: db_provider
user_checker: App\Security\UserChecker
logout:
path: /logout
target: /
form_login:
login_path: home
check_path: home
access_control:
- { path: ^/motivwelten, roles: ROLE_USER }
- { path: ^/services/.*, roles: ROLE_USER }
- { path: ^/shop, roles: ROLE_USER }
- { path: ^/shop/.*, roles: ROLE_USER }
erase_credentials: false
While not a direct solution to the initial question I asked, I fixed the problem by moving every logic but the login away from the login method in my controller.
This should probably be the better practice anyways. I believe submitting to the /login route tirggers a login event and, as there are no credentials, the service can't verify the user and terminates the session while displaying "invalid credentials" as well.
I have created a REST API for inserting data from Modal. After submitting my form I get data from my "store" controller. But when I call a Model function to insert that data to Database then I am getting 500 internal server error at console.
I have tried with two ways 1. Using Eloquent ORM
2. Using Query Builder
My controller:
public function store(Request $request)
{
$category_name = $request->category_name;
$category_entry_date = $request->category_entry_date;
$category_info = array(
'Supplier_Name' => $category_name,
'Supplier_Des' => $category_entry_date,
'Shop_Id' => 1
);
// Insert data into database
Product::CreateProductCategory($category_info);
return response()->json(['success_massege'=>'Category Added Successfully']);
}
Js:
$("#category_submit").click(function (e) {
event.preventDefault();
var category_name = $('#category_name_id').val();
var category_entry_date = $('#category_entry_date_id').val();
if (category_name && category_entry_date) {
$.post("/api/api/product_category", {category_name: category_name, category_entry_date: category_entry_date}).done(function (data) {
$('#category_name_id').val("");
$('#category_entry_date_id').val("");
var success_massege_dialogbox = '';
success_massege_dialogbox += '<div class="alert alert-success fade in">';
success_massege_dialogbox += '×';
success_massege_dialogbox += '<strong>Success!</strong>' + data.success_massege + '</div>';
$('#success_massege').append(success_massege_dialogbox);
});
}
else {
alert('Please give a category name and entry date');
}
});
Model:
static function CreateProductCategory($category_info){
DB::table('product_category_info')->insert($category_info);
}
View:
<div class="modal fade" id="addCategory" tabindex="-1" role="dialog" aria-labelledby="addCategoryLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="addCategoryLabel">Add Category</h4>
</div>
<form action="{{ route('product_category.store') }}" method="POST">
<div class="modal-body">
<div class="modal-body">
<div class="box-body">
<div class="row form-group">
<div class="col-md-3 form-level"></div>
<div class="col-md-9" id="success_massege"> </div>
</div>
<div class="row form-group">
<div class="col-md-3 form-level"><label>Category Name<b class="mandetory_star">*</b></label></div>
<div class="col-md-9" id="email-error-dialog">
{{Form::text('category_name','', $attributes = array('class' => 'form-control',
'id' => 'category_name_id',
'data-validation'=>'alphanumeric', 'data-validation-allowing'=>'-_',
'data-validation-error-msg'=>'Please Enter a Valid Category Name',
'data-validation-error-msg-container'=>'#email-error-dialog',
))}}
</div>
</div>
<div class="row form-group">
<div class="col-md-3 form-level"><label>Entry Date<b class="mandetory_star">*</b></label></div>
<div class="col-md-9" id="date-error-dialog">
{{Form::date('entry_date','', $attributes = array('class' => 'form-control',
'id' => 'category_entry_date_id',
'data-validation'=>'date',
'data-validation-error-msg'=>'Please Enter a Valid Date',
'data-validation-error-msg-container'=>'#date-error-dialog'))}}
</div>
</div>
</div><!-- /.box-body -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button id="category_submit" type="submit" class="btn btn-primary save-category"> Save category </button>
</div>
</form>
</div>
</div>
</div>
*I speak English not well. So i'm going to post the code now.*
Form code:
protected $elementDecorators = array('ViewHelper','Errors','Description','Label',
array('HtmlTag',array('tag' => 'div','class' => '_wrapperElement')
));
public function init(){
$this->addElement('text','mytext',array(
'class' => '_inputText',
'label' => 'Mytext',
'required' => true,
'decorators' => $this->elementDecorators
));
$this->setDecorators(array('FormElements',array('HtmlTag',array('tag' => 'div','class' => '_formWrapper')),'Form'));
}
Output:
<form method="post" action="" enctype="application/x-www-form-urlencoded">
<div class="_formWrapper">
<div class="_wrapperElement">
<label class="required" for="mytext">Mytext</label>
<input type="text" class="_inputText" value="" id="mytext" name="mytext">
</div>
</div>
</form>
Now i want a div wraps Label and Input element like this:
<form method="post" action="" enctype="application/x-www-form-urlencoded">
<div class="_formWrapper">
<div class="_wrapperElement">
<div class="_wrapperLabel">
<label class="required" for="mytext">Mytext</label>
</div>
<div class="_wrapperInput">
<input type="text" class="_inputText" value="" id="mytext" name="mytext">
</div>
</div>
</div>
</form>
How to do that?
I tried many times but i can't do it.
Thanks!
protected $elementDecorators = array('ViewHelper','Errors','Description', array('Label', array('tag' => 'div', 'class' => '_wrapperLabel')
),
array('HtmlTag',array('tag' => 'div','class' => '_wrapperInput')
));
i found the solution that render decorators to ViewScript.