Laravel Form : Creating default object from empty value - object

i'm creating a form to update my project but i get this error Creating default object from empty value
Here is my route :
Route::get('admin/projects/edit/{id}','ProjectsController#edit');
Route::put('admin/projects/update/{id}','ProjectsController#update');
The edit view :
#extends ('layout.admin')
#section ('content')
{{ Form::open(array('url'=>'admin/projects/update/'.$project->id.'','enctype'=>'multipart/form-data','method'=>'put','style'=>'margin:0!important;')) }}
{{ Form::token() }}
<div class="form-group">
{{ Form::label('name','Nom :') }}
{{ Form::text('name',$project->name,array('class'=>'form-control')) }}
#if($errors->first('name'))
<div class="alert alert-danger" role="alert">{{ $errors->first('name') }}</div>
#endif
</div>
<div class="form-group">
{{ Form::label('slug','Slug :') }}
{{ Form::text('slug',$project->slug,array('class'=>'form-control')) }}
#if($errors->first('slug'))
<div class="alert alert-danger" role="alert">{{ $errors->first('slug') }}</div>
#endif
</div>
<div class="form-group">
{{ Form::label('category_id','Category :') }}
{{ Form::select('category_id',$cats,$project->category_id) }}
</div>
<div class="form-group">
{{ Form::label('thumbnail','Image :') }}
{{ Form::file('thumbnail',array('class'=>'form-control')) }}
#if($errors->first('thumbnail'))
<div class="alert alert-danger" role="alert">{{ $errors->first('thumbnail') }}</div>
#endif
</div>
<div class="form-group">
{{ Form::label('description','Description :') }}
{{ Form::textarea('description',$project->description,array('class'=>'form-control')) }}
#if($errors->first('description'))
<div class="alert alert-danger" role="alert">{{ $errors->first('description') }}</div>
#endif
</div>
{{ Form::submit('Modifier', array('class'=>'btn btn-primary','style'=>'float:left')) }}
{{ Form::close() }}
<button class="btn btn-danger" style="float:left;margin-left:20px;">Supprimer</button>
#stop
And my controller
public function update($id){
$inputs = Input::all();
$rules = array(
'name'=>'required|min:5',
'description'=>'required|min:10'
);
$validation = Validator::make($inputs,$rules);
if($validation->fails()){
return Redirect::to('admin/projects/edit/'.Input::get('id').'')->withInput()->withError();
}
$project=Project::find($id);
$project->name = Input::get('name');
$project->slug = Slug::create(Input::get('slug'));
$project->category_id = Input::get('category_id');
$project->description = Input::get('description');
if(Input::hasFile('thumbnail')){
$img = Str::slug(Input::get('name').'png');
$post->thumbnail = 'img/'.$img.'';
}
if(Input::hasFile('thumbnail')){
Input::file('thumbnail')->move('img/',$img);
}
return Redirect::to('admin/projects')->with('Alert_succes','Votre projet a bien été mis à jour');
}
public function edit($id)
{
$project = Project::find($id);
$categories = Category::all();
$cats = array();
foreach ($categories as $category){
$cats[$category->id] = $category->name;
}
return View::make('projects.edit')->with(array('project'=>$project,'cats'=>$cats));
}
I Dont really understand where the errors is , the problem come from this line according to laravel debuger : $post->thumbnail = 'img/'.$img.'';
Thank You for your help

You are using $post->thumbnail = 'img/'.$img.''; but you didn't create a $post object. Instead you have $project object from this code:
$project = Project::find($id);
Where is $post in your code ? It should be $project and also make sure if you got an object, using something like this:
$project = Project::find($id);
if($project) {
//...
}
It looks that, $post->thumbnail should be $project->thumbnail in your update method.

Related

'Request missing required body param' when submitting front end entry form - Craft CMS

I'm pretty new to craft and creating my first front end entry form using the demo code provided by craft. For some reason I'm getting a 'Request missing required body param' error. I've tried adding <input type="hidden" name="entryId" value=""> but that didn't solve the issue.
Here's my code:
{% extends "_layout.twig" %}
{% macro errorList(errors) %}
{% if errors %}
{{ ul(errors, {class: 'errors'}) }}
{% endif %}
{% endmacro %}
{# If there were any validation errors, an `entry` variable will be
passed to the template, which contains the posted values
and validation errors. If that’s not set, we’ll default
to a new entry. #}
{% set entry = entry ?? create('craft\\elements\\Entry') %}
{# Add `enctype="multipart/form-data"` to `<form>` if you’re
uploading files. #}
<form method="post" accept-charset="UTF-8" enctype="multipart/form-data">
{{ csrfInput() }}
{{ actionInput('entries/save-entry') }}
{{ redirectInput('viewentry/{slug}') }}
{{ hiddenInput('friends', '2') }}
<input type="hidden" name="entryId" value="">
<div class="mb-6">
<label for="title" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your Dog's Name</label>
{{ input('text', 'title', entry.title, {
id: 'title',
class:'bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500',
}) }}
{{ _self.errorList(entry.getErrors('title')) }}
</div>
<div class="mb-6">
<label for="featureImage" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Feature Image</label>
{{ input('file', 'featureImage', entry.featureImage, {
id: 'featureImage',
}) }}
{{ _self.errorList(entry.getErrors('featureImage')) }}
</div>
<div class="mb-6">
<label for="postContent" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Tell us about your dog</label>
{{ tag('textarea', {
id: 'postContent',
name: 'fields[postContent]',
class:'bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500',
}) }}
{{ _self.errorList(entry.getErrors('postContent')) }}
</div>
<div class="mb-8">
<label for="postCategories" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Breed</label>
<select name="fields[postCategories]" id="postCategories">
{# Create a category query with the 'group' parameter #}
{% set myCategoryQuery = craft.categories()
.group('blog') %}
{# Fetch the categories #}
{% set categories = myCategoryQuery.all() %}
{# display post categories #}
{% if entry.postCategories|length %}
<div class="border-t py-2 mb-6">
{% for category in entry.postCategories.all() %}
<a href="{{ category.url }}" class="inline-block border rounded px-2 py-1 text-sm">
{{- category.title -}}
</a>
{% endfor %}
</div>
{% endif %}
{% nav category in categories %}
<li>
<option>{{ category.title }}</option>
</li>
{% endnav %}
</select>
</div>
{#
{% set field = craft.app.fields.getFieldByHandle('postCategories') %}
{{ hiddenInput('fields[postCategories]', '') }}
<select multiple name="fields[postCategories][]">
{% for option in field.options %}
{% set selected = entry is defined
? entry.postCategories.contains(option.value)
: option.default %}
<option value="{{ option.value }}"
{% if selected %} selected{% endif %}
>
{{ option.label }}
</option>
{% endfor %}
</select>
#}
<div class="mb-6">
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Publish</button>
</div>
</form>
{% endblock %}
I didn't realise that the 2 in {{ hiddenInput('sectionId', '2') }} had to be changed for the ID of the channel - not the handle.
For anyone else stuck with this, the ID can be found in the URL when you're editing the channel in the dashboard settings.
More details here >>

Symfony4: render cannot pass parameters

After updating from Symfony 2.X to Symfony 4.4 and verifying the operation, I found that Twig does not recognize modal.
Since it does not recognize modal, it will be displayed even when the code that is not displayed in modal in the if statement is displayed in modal.
When I tried "if modal is empty", the code in the if statement was displayed, so it seems that the modal is not passed.
The code that passes the modal has changed with the Symfony update. This may have an effect.
Do you have any idea?
Old_code
<div id="imageDialog" title="Image management">
{{ render(controller("AppBundle:Shop/Image:manager", {"modal": true})) }}
</div>
Changed_code
<div id="imageDialog" title="Image management">
{{ render(controller("AppBundle\\Controller\\Shop\\ImageController::managerAction", {"modal": true})) }}
</div>
Code in question
<div class="tabpanel selected" id="imageFolder" >
<div class="tabcontent">
<div class="operations">
{% if not modal %}
<div class="pull-right">
<form method="post" action="{{ path('app_shop_image_delete') }}">
<input type="hidden" name="methods" value="delete">
<input type="hidden" name="_csrf_token" value="{{ csrf_token("authenticate") }}">
<input type="hidden" name="ids" value="[]">
<button type="submit" class="delete btn btn-danger"><i class="icon-trash"></i> Delete</button>
</form>
</div>
{% endif %}
</div>
</div>
</div>
ImageController
*
* #Method("GET")
* #Route("/manager")
*
* #Template("#AppBundle/Shop/Image/manager.html.twig")
*/
public function managerAction(Request $request)
{
$routeParams = $request->get('_route_params');
$uploadUrl = $this->generateUrl("app_shop_image_save");
return array(
'modal' => isset($routeParams["modal"]) ? $routeParams["modal"] : false,
'form' => $this->createUploadForm($uploadUrl)->createView(),
);
}
It worked fine with the following method.
I couldn't get $routeParams["modal"], but please let me know if there is a way to get it.
public function managerAction(Request $request)
{
$routeParams = $request->query->get('modal');
$uploadUrl = $this->generateUrl("app_hq_image_save");
return array(
'modal' => isset($routeParams) ? $routeParams : false,
'form' => $this->createUploadForm($uploadUrl)->createView(),
);
}
<div id="imageDialog" title="Image management">
{{ render(controller("AppBundle\\Controller\\Shop\\ImageController::managerAction", {}, {"modal": true})) }}
</div>
public function managerAction(Request $request)
{
$isModal = $request->query->get('modal') != null ? $request->query->get('modal') : false;
$uploadUrl = $this->generateUrl("app_hq_image_save");
return array(
'modal' => $isModal,
'form' => $this->createUploadForm($uploadUrl)->createView(),
);
}
But I wonder if you shouldn't make modal as a real route param with a default value false.
https://symfony.com/doc/4.4/routing.html#optional-parameters

How to add Bootstrap Validation to WTForms

I am using WTForms in conjunction with Flask and I would like to integrate the Bootstrap Form Validation for errors in my form. I have a basic login form setup something like this:
class LoginForm(FlaskForm):
"""Login form."""
email = EmailField(
"Email Address", validators=[DataRequired(), Email(), Length(min=6, max=40)]
)
password = PasswordField(
"Password", validators=[DataRequired()]
)
def __init__(self, *args, **kwargs):
"""Create instance."""
super(LoginForm, self).__init__(*args, **kwargs)
self.user = None
def validate(self):
"""Validate the form."""
initial_validation = super(LoginForm, self).validate()
if not initial_validation:
return False
self.user = User.query.filter_by(email=self.email.data).first()
if not self.user:
self.email.errors.append("Unknown email address!")
return False
if not self.user.check_password(self.password.data):
self.password.errors.append("Invalid password!")
return False
if not self.user.verified:
self.email.errors.append("Please verify your email address!")
return False
return True
My login.html template is setup like this:
<form method="POST" action="{{ url_for('public.login') }}" role="form">
{{ form.csrf_token }}
<div class="form-group">
{{ form.email.label(class_="form-control-label") }}
<div class="input-group input-group-merge">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-user"></i></span>
</div>
{{ form.email(placeholder="name#example.com", class_="form-control") }}
</div>
</div>
<div class="form-group mb-4">
<div class="d-flex align-items-center justify-content-between">
<div>
{{ form.password.label(class_="form-control-label") }}
</div>
<div class="mb-2">
Lost password?
</div>
</div>
<div class="input-group input-group-merge">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-key"></i></span>
</div>
{{ form.password(placeholder="Password", class_="form-control") }}
<div class="input-group-append" onclick="togglePassword()">
<span class="input-group-text">
<i class="fas fa-eye"></i>
</span>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-auto mt-1 mb-2" align="center">
<button type="submit" class="btn btn-sm btn-primary btn-icon rounded-pill">
<span class="btn-inner--text">Sign in</span>
<span class="btn-inner--icon"><i class="fas fa-long-arrow-alt-right"></i></span>
</button>
</div>
<div class="col-md-auto text-center mt-2">
<p class="text-secondary-dark">or</p>
</div>
<div class="col-md-auto" align="center">
<button type="button" class="btn btn-md btn-secondary btn-icon-only">
<span class="btn-inner--icon">
<i class="fab fa-google"></i>
</span>
</button>
<button type="button" class="btn btn-md btn-secondary btn-icon-only">
<span class="btn-inner--icon">
<i class="fab fa-linkedin"></i>
</span>
</button>
</div>
</div>
</form>
I would like to display the errors that I validate using WTForms, but I am unsure of how to change the class of the original form element to is-invalid or is-valid, and how to create the labels for each error. I have looked into macros, but they don't seem to be able to modify the form element either.
Can someone point me in the right direction?
I faced the same problem and I wanted to avoid the use of any third package (flask-bootstrap), so I came up with this simple solution:
<div class="form-group">
{{ form.email.label }} <span class="text-danger">*</span>
{# her #}
{{ form.email(class="form-control" + (" is-invalid" if form.email.errors else "") + " rounded-0 shadow-none", **{"placeholder": "Your Email", "aria-describedby": "emailHelp", "autocomplete": "off"}) }}
<small id="emailHelp" class="form-text text-muted">We'll never share your data with anyone else.</small>
{% if form.email.errors %}
{% for error in form.email.errors %}
<div class="invalid-feedback">{{ error }}</div>
{% endfor %}
{% endif %}
</div>
The trick is to use a simple ternary expression combined with string concatenation.
#cizario's answer is a great start. However, I found a better implementation for the errors. Using WTForm's Custom Widgets, you get the following widgets:
from wtforms.widgets import PasswordInput, CheckboxInput, TextInput
from wtforms.widgets.html5 import EmailInput
class BootstrapVerifyEmail(EmailInput):
"""Bootstrap Validator for email"""
def __init__(self, error_class=u"is-invalid"):
super(BootstrapVerifyEmail, self).__init__()
self.error_class = error_class
def __call__(self, field, **kwargs):
if field.errors:
c = kwargs.pop("class", "") or kwargs.pop("class_", "")
kwargs["class"] = u"%s %s" % (self.error_class, c)
return super(BootstrapVerifyEmail, self).__call__(field, **kwargs)
class BootstrapVerifyPassword(PasswordInput):
"""Bootstrap Validator for password"""
def __init__(self, error_class=u"is-invalid"):
super(BootstrapVerifyPassword, self).__init__()
self.error_class = error_class
def __call__(self, field, **kwargs):
if field.errors:
c = kwargs.pop("class", "") or kwargs.pop("class_", "")
kwargs["class"] = u"%s %s" % (self.error_class, c)
return super(BootstrapVerifyPassword, self).__call__(field, **kwargs)
class BootstrapVerifyBoolean(CheckboxInput):
"""Bootstrap Validator for boolean"""
def __init__(self, error_class=u"is-invalid"):
super(BootstrapVerifyBoolean, self).__init__()
self.error_class = error_class
def __call__(self, field, **kwargs):
if field.errors:
c = kwargs.pop("class", "") or kwargs.pop("class_", "")
kwargs["class"] = u"%s %s" % (self.error_class, c)
return super(BootstrapVerifyBoolean, self).__call__(field, **kwargs)
class BootstrapVerifyText(TextInput):
"""Bootstrap Validator for text"""
def __init__(self, error_class=u"is-invalid"):
super(BootstrapVerifyText, self).__init__()
self.error_class = error_class
def __call__(self, field, **kwargs):
if field.errors:
c = kwargs.pop("class", "") or kwargs.pop("class_", "")
kwargs["class"] = u"%s %s" % (self.error_class, c)
return super(BootstrapVerifyText, self).__call__(field, **kwargs)
This will add the invalid tag so that bootstrap can mark it as invalid. In the HTML, do something like this to add the error message:
{{ login_user_form.email.label(class_="form-control-label") }}
<div class="input-group input-group-merge">
<div class="input-group-prepend">
<span class="input-group-text" id="user"><i class="fas fa-user"></i></span>
</div>
{{ login_user_form.email(placeholder="name#example.com", class_="form-control", **{"aria-describedby": "inputGroupPrepend3", "required": ""}) }}
{% for error in login_user_form.email.errors %}
<div class="invalid-feedback">{{ error }}</div>
{% endfor %}
</div>
I ended up using a different library that provides Bootstrap widgets:
https://github.com/agdsn/wtforms-widgets
The usage gets really simple really fast:
from wtforms import validators
from wtforms.validators import Email
from wtforms_widgets.base_form import BaseForm
from wtforms_widgets.fields.core import StringField, PasswordField
class RegisterForm(BaseForm):
email = StringField('Email Address', [Email(), validators.DataRequired(message='Forgot your email address?')])
password = PasswordField('Password', [validators.DataRequired(message='Must provide a password. ;-)')])
<form method="POST" action="{{ url_for('auth.register') }}" accept-charset="UTF-8" role="form">
{% for field in form %}
{{ field(render_mode='horizontal', autocomplete='off') }}
{% endfor %}
<input type="submit" value="submit">
</form>
But this doesn't include validation.
To add validation, I put this in Jinja:
<form method="POST" action="{{ url_for('auth.register') }}" accept-charset="UTF-8" role="form">
{% for field in form %}
{{ field() }}
{% for error in field.errors %}
<div class="invalid-feedback">{{ error }}</div>
{% endfor %}
{% endfor %}
<input type="submit" value="submit">
</form>
And for flashed messages I use this in my base layout.html that I extend everywhere:
<div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
{% if category == 'message' %}
<div class="alert alert-warning" role="alert">
{% else %}
<div class="alert alert-{{ category }}" role="alert">
{% endif %}
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
When rendering your template using render_template, pass the 'is-valid' class to all the elements you want. Use this
class = '{{email_valid_class}} all other classes here'
Then in python return render_template('login.html',email_valid_class='is-valid')

Flash a error/success message on flask after .validate_on_submit

Newbie to Flask and a very short time to learn to do something decent(College).
I have a survey that I would like to use FLASH to flash the success and error messages(category), though I haven't been able to do so, I gave it a good research and I did get how it works, my code seems fine comparing to the examples I have found, but it is clearly not. If someone could please help me out, it would be much appreciated.
This is the .py file
def index():
form = Form()
if form.validate_on_submit():
flash(u'Thank you for your collaboration!', 'success')
if not form.validate_on_submit():
flash(u'Something went wrong!', 'error')
return redirect(url_for('index'))
return redirect(url_for('index'))
#submission_successful = True #or False. you can determine this.
return render_template('index1.html', form=form) #submission_successful=submission_successful)
the .html file
<center>
<form methods='POST'>
{{ form.csrf_token }}
{{ form.hidden_tag() }}
<div style = font-size:18px; font-weight:bold; margin-left:200px; class="form-field"> {{ form.Email.label }} <br> {{ form.Email }} </div> <br>
<div style = font-size:18px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.sex.label }} {{ form.sex }} </div> <br>
<div style = font-size:18px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.age.label }} {{ form.age }} </div> <br>
<div style = font-size:18px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.Marital.label }} {{ form.Marital }} </div> <br>
<div style = font-size:18px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.county.label}} {{ form.county }} </div> <br>
<div style = font-size:18px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.Property.label }} {{ form.Property }} </div> <br>
<div style = font-size:18px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.PropertyStatus.label }} {{ form.PropertyStatus }} </div> <br>
<div style = font-size:18px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.Rooms.label }} {{ form.Rooms }} </div> <br>
<div style = font-size:18px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.People.label}} <br> {{ form.People }} </div> <br>
<div style = font-size:17px; font-weight:bold; margin-left:100px; class="form-field"> {{ form.submit}} </div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
{% if category == 'success'%}
<div class='alert alert-success mb-3' role='alert' >
{{ message }}
</div>
{% endif %}
{% if category == 'error'%}
<div class='alert alert-danger mb-3' role='alert' >
{{ message }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
</center>
</form>
This is a simple template using flash messages...customize this for Ur case
app.py
from flask import Flask, render_template, redirect, url_for,session, flash
from flask_wtf import FlaskForm
from wtforms import (StringField, SubmitField)
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecretkey'
class InfoForm(FlaskForm):
name = StringField('What are you?')
submit = SubmitField('Submit')
#app.route('/', methods=['GET', 'POST'])
def index():
# Create instance of the form.
form = InfoForm()
# If the form is valid on submission (we'll talk about validation next)
if form.validate_on_submit():
# Grab the data from the breed on the form.
session['name'] = form.name.data
flash(f"You just changed your name to: {session['name']}")
return redirect(url_for("index"))
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
#index.html
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</head>
<div class="container">
{# get_flashed_messages() is auto sent to the template with the flash() call #}
{% for mess in get_flashed_messages() %}
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close" class="fade close">
<span aria-hidden="true">×</span>
</button>
{{mess}}
</div>
{% endfor %}
<form method="POST">
{# This hidden_tag is a CSRF security feature. #}
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
</div>
</html>
This is what I usually do for message flashing.
https://hastebin.com/behopumivu.cs
Sorry formatting is bad I am phone. But if you would put this in your base template, when you do “flash” it will always flash a message at the top of whatever page the user is currently on. Hope this helped!

wrap checkboxes with custom html in Twig template using form entity fieldtype (Symfony3)

I would like to wrap the checkboxes with custom HTML in twig template when Symfony3 renders the form.
Instead of this:
<input type="checkbox" id="form_role_1" name="form[role][]" value="1" />
<label for="form_role_1">ROLE 1</label>
<input type="checkbox" id="form_role_2" name="form[role][]" value="2" />
<label for="form_role_2">ROLE 2</label>
<input type="checkbox" id="form_role_3" name="form[role][]" value="3" />
<label for="form_role_3">ROLE 3</label>
I would like to get something like this:
<div class="input-group">
<input type="checkbox" id="form_role_1" name="form[role][]" value="1" />
<label for="form_role_1">ROLE 1</label>
</div>
<div class="input-group">
<input type="checkbox" id="form_role_2" name="form[role][]" value="2" />
<label for="form_role_2">ROLE 2</label>
</div>
<div class="input-group">
<input type="checkbox" id="form_role_3" name="form[role][]" value="3" />
<label for="form_role_3">ROLE 3</label>
</div>
in the controller action:
$form = $this->createForm(RolesFormType::class, $roles);
$form->handleRequest($request);
return $this->render(
'role/edit_roles.html.twig',
array(
'form' => $form->createView()
)
);
in the form type:
class RolesFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('role', EntityType::class, array(
'label' => 'form.elements.roles',
'class' => 'RoleBundle\Entity\Role',
'choice_label' => 'role_name',
'expanded' => true,
'multiple' => true
));
}
}
the classic template looks like this:
<div class="container-fluid">
<div class="row">
{{ form_start(form, {'method': "POST"}) }}
<div class="col-md-12">
<div class="form-group">
{{ form_errors(form.role) }}
{{ form_widget(form.role, {}) }}
</div>
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
</div>
I tried it this way in the twig template, but only the labels were rendered:
<div class="container-fluid">
<div class="row">
{{ form_start(form, {'method': "POST"}) }}
<div class="col-md-12">
{% for role in form.role %}
<div class="input-group">
{{ form_label(role) }}
{{ form_widget(role) }}
</div>
{% endfor %}
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
</div>
Is ii possible without creating a new field type?
solution
concerning #xabbuh's answer I found the solution at How to Customize Form Rendering in Symfony 3
I created a folder in my Bundle:
src/RoleBundle/Resources/views/form
and I put a new file (fields.html.twig) into it with this content
{%- block choice_widget_expanded -%}
<div {{ block('widget_container_attributes') }}>
{%- for child in form %}
<div class="input-group">
{{- form_widget(child) -}}
{{- form_label(child, null, {translation_domain: choice_translation_domain}) -}}
</div>
{% endfor -%}
</div>
{%- endblock choice_widget_expanded -%}
In the meantime I found another question which deals with this problem:
Overriding symfony radio widget
You can customise the rendering of your form using a custom form theme. You can so either on the project level or even do that just for a particular form (see How to Customize Form Rendering and How to Work with Form Themes for more information).
Your example looks like you need to override the choice_widget and choice_label
When I play with symfony is not very skilled, I don't often use this accepted solution to solve it
view:
<label class="layui-form-label">所属地市:</label>
<div class="layui-inline">
<select name="{{ form.cityId.vars.full_name }}" lay-verify="" id="{{ form.cityId.vars.id }}" lay-filter="case_city">
<option value="0">请选择</option>
{% if citylist is not empty %}
{% for list in citylist %}
<option value="{{ list.id is defined ? list.id : 0 }}">{{ list.areaName is defined ? list.areaName : '' }}</option>
{% endfor %}
{% endif %}
</select>
</div>
controller:
$caseReg = new CaseRegister();
$form = $this->createForm('Alarm\Component\Form\CaseRegisterType', $caseReg);
$form->handleRequest($request);
$citylist = $this->getDoctrine()->getRepository('AlarmComponent:OfficeArea')->findBy(array('pid'=>0,'status'=>0));
$caseType = $this->getDoctrine()->getRepository('AlarmComponent:CaseType')->findBy(array('status'=>0));
if ($form->isSubmitted()) {
$em = $this->getDoctrine()->getManager();
$param = $request->request->get('alarm_component_caseregister');
if($param['receiveId'] == 0)
{
$case = new CaseType();
$case->setCaseName($param['receiveName']);
$em->persist($case);
$em->flush($case);
$caseReg->setReceiveId($case->getId());
$caseReg->setReceiveName($case->getCaseName());
}
if($param['cityId'] != 0)
{
$city = $this->getDoctrine()->getRepository('AlarmComponent:OfficeArea')->find($param['cityId']);
$caseReg->setCityId($city);
}
if($param['subofficeId'] != 0)
{
$suboffice = $this->getDoctrine()->getRepository('AlarmComponent:OfficeArea')->find($param['subofficeId']);
$caseReg->setSubofficeId($suboffice);
}
$em->persist($caseReg);
$em->flush($caseReg);
return $this->redirectToRoute('case_register_index');
}
return $this->render('AlarmWebBundle:BackendBat\CaseRegister:new.html.twig', array(
'caseReg' => $caseReg,
'form' => $form->createView(),
'citylist' => $citylist,
'casetype' => $caseType
));
It is also can solve my problem, but when I was late to optimize this or they must be altered. But I hope this answer can solve your current problem.

Resources