Symfony 4 - KnpPaginatorBundle - This form should not contain extra fields - php

I'm using KnpPaginatorBundle for a page.
In this page, I use a hand-created filter, which gives this
So if I enter something in the form, it will process the request and show as agreed.
The problem is when I ask to sort by 'ID'.
This problem occurs when I make a filter on the name or service, and I ask to order by ID
I have the following error message:
This form should not contain extra fields
What should I do to stop this error?
My controller is :
/**
* Undocumented function
*
* #Route("/rh3", name="rh_index3")
*
* #param PaginatorInterface $paginator
* #param Request $request
* #param ObjectManager $manager
* #param UserRepository $repo
* #return void
*/
public function index3(PaginatorInterface $paginator, Request $request, UserRepository $repo)
{
$search = new UserSearch();
$form = $this->createForm(UserSearchType::class, $search);
$form->handleRequest($request);
$users = $repo->getUsersSearch($search);
$pagination = $paginator->paginate(
$users,
$request->query->getInt('page', 1),
7
);
return $this->render('rh/index3.html.twig', [
'pagination' => $pagination,
'form' => $form->createView(),
]);
}
And my Twig:
{% extends "base.html.twig" %}
{% block body %}
<div class="jumbotron">
<div class="container">
{{form_start(form)}}
<div class="form-row align-items-end">
<div class="col">
{{form_row(form.name)}}
</div>
<div class="col">
{{form_row(form.service)}}
</div>
<div class="col">
<div class="form-group">
<button type="submit" class="btn btn-primary">Rechercher</button>
</div>
</div>
</div>
{{form_end(form)}}
</div>
</div>
<table class="table table-hover align-items-center">
<thead>
<tr>
<th>{{ knp_pagination_sortable(pagination, 'Id', 'u.id') }}</th>
<th>Nom</th>
<th>Service</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for user in pagination %}
<tr {% if loop.index is odd %} class="color" {% endif %}>
<td>{{user.id}}</td>
<td>{{user.fullName}}</td>
<td>{{user.groupe.nom}}</td>
<td>
Voir
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="navigation">
{{knp_pagination_render(pagination)}}
</div>
{% endblock %}
Thanks for your help !

in your UserSearchType add this option:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'allow_extra_fields' => true,
));
}

Related

symfony insert multiple rows insted of one

I have a wired situation here. On persisting symfony inserts multiple records instead of one. I can't find any problem with my code and I am not sure how to debug this error because it seems all fine.
Logic behind application is that user can select two bus station and create a route. Also he needs to define price and group (minimum and maximum people). Additionally, user needs to select bus vehicles that will drive this particular route
Does someone knows where is the problem?
Here is the output of entity when form is submitted
StationStandardPrice {#553 ▼
-id: null
-company: Company {#549 ▶}
-busVehicleGroupSize: BusVehicleGroupSize {#1233 ▶}
-departureStation: Stations {#1247 ▶}
-destinationStation: Stations {#1236 ▶}
-currency: Currencies {#1015 ▶ …2}
-price: 99.0
-busVehicles: ArrayCollection {#554 ▼
-elements: array:2 [▼
0 => BusVehicle {#1023 ▶}
1 => BusVehicle {#1208 ▶}
]
}
}
This is entity of route (shorted version)
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \AppBundle\Entity\Company
*
* #ORM\ManyToOne(targetEntity="Company")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="company_id", referencedColumnName="id")
* })
*/
private $company;
/**
* #var \AppBundle\Entity\BusVehicleGroupSize
*
* #ORM\ManyToOne(targetEntity="BusVehicleGroupSize")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="bus_vehicle_group_size_id", referencedColumnName="id")
* })
*/
private $busVehicleGroupSize;
/**
* #var \AppBundle\Entity\Stations
*
* #ORM\ManyToOne(targetEntity="Stations")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="departure_station_id", referencedColumnName="id")
* })
*/
private $departureStation;
/**
* #var \AppBundle\Entity\Stations
*
* #ORM\ManyToOne(targetEntity="Stations")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="destination_station_id", referencedColumnName="id")
* })
*/
private $destinationStation;
/**
* #var \AppBundle\Entity\Currencies
*
* #ORM\ManyToOne(targetEntity="Currencies")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="currency_id", referencedColumnName="id")
* })
*/
private $currency;
/**
* #var float
*
* #ORM\Column(name="price", type="decimal", precision=10, scale=2, nullable=false)
*/
private $price;
/**
* Many groups can have many bus vehicles
*
* #ORM\ManyToMany(targetEntity="BusVehicle", inversedBy="busVehicleGroup")
* #ORM\JoinTable(name="standard_station_price_bus_groups",
* joinColumns={#ORM\JoinColumn(name="station_standard_price_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="bus_vehicle_id", referencedColumnName="id")}
* )
**/
private $busVehicles;
/**
* Add bus vehicle
* #param BusVehicle $busVehicles
*/
public function addBusVehicles(BusVehicle $busVehicles)
{
if ($this->busVehicles->contains($busVehicles)) {
return;
}
$this->busVehicles->add($busVehicles);
$busVehicles->addBusVehicleGroup($this);
}
/**
* Remove bus vehicle
* #param BusVehicle $busVehicles
*/
public function removeBusVehicles(BusVehicle $busVehicles)
{
if (!$this->busVehicles->contains($busVehicles)) {
return;
}
$this->busVehicles->remove($busVehicles);
$busVehicles->removeBusVehicleGroup($this);
}
Controller:
/**
* Creates a new stationStandardPrice entity.
* #Template
*/
public function newAction(Request $request)
{
$stationStandardPrice = new Stationstandardprice();
$form = $this->createForm(StationStandardPriceType::class, $stationStandardPrice, array(
'user' => $this->getUser()
)
);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
dump($stationStandardPrice);
$em = $this->getDoctrine()->getManager();
$em->persist($stationStandardPrice);
$em->flush();
$this->addFlash('success', 'admin.stationstandardprice.created');
//return $this->redirectToRoute('stationstandardprice_show', array('id' => $stationStandardPrice->getId()));
}
return [
'stationStandardPrice' => $stationStandardPrice,
'form' => $form->createView(),
];
}
Database output:
Many to Many table status:
EDIT 1: Added twig
{% extends 'AdminBundle::layout.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link href="{{ asset('resources/public/css/datatables.min.css', 'busrent_admin') }}" rel="stylesheet"/>
<link href="{{ asset('resources/public/css/smart_wizard.min.css', 'busrent_admin') }}" rel="stylesheet"/>
<link href="{{ asset('resources/public/css/smart_wizard_theme_dots.min.css', 'busrent_admin') }}" rel="stylesheet"/>
{% endblock %}
{% block title %} {{ 'admin.stationstandardprice.new.title'|trans }} {% endblock %}
{% block breadcrumb %}
<div class="col-lg-10">
<h2>{{ 'admin.stationstandardprice.new.title'|trans }}</h2>
<ol class="breadcrumb">
<li>
{{ 'admin.dashboard.index.title'|trans }}
</li>
<li>
{{ 'admin.stationstandardprice.index.title'|trans }}
</li>
<li class="active">
<strong>{{ 'admin.stationstandardprice.new.title'|trans }}</strong>
</li>
</ol>
</div>
<div class="col-lg-2">
</div>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{{ 'admin.stationstandardprice.new.title'|trans }}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="row">
<div class="col-lg-12">
{{ form_start(form) }}
<div id="smartwizard">
<ul>
<li><a href="#step-1">Step Title<br/>
<small>Step description</small>
</a>
</li>
<li><a href="#step-2">Step Title<br/>
<small>Step description</small>
</a>
</li>
<li><a href="#step-3">Step Title<br/>
<small>Step description</small>
</a>
</li>
</ul>
<div>
<div id="step-1" class="">
{{ form_row(form.departureStation) }}
{{ form_row(form.destinationStation) }}
</div>
<div id="step-2" class="">
{{ form_row(form.busVehicleGroupSize) }}
{{ form_row(form.price) }}
{{ form_row(form.currency) }}
</div>
<div id="step-3" class="">
{% if app.user.company is not null %}
<div class="ibox-content">
<table id="busVehicleTable" class="table table-striped">
<thead>
<tr>
<th>{{ 'admin.busvehicle.form.licencePlate'|trans }}</th>
<th>{{ 'admin.busvehicle.form.chassisNumber'|trans }}</th>
<th>{{ 'admin.busvehicle.form.passengerSeatsNumber'|trans }}</th>
<th>{{ 'admin.busvehicle.form.busType'|trans }}</th>
<th>{{ 'admin.busvehicle.form.emissionClass'|trans }}</th>
<th>{{ 'admin.busvehicle.form.fullDayPrice'|trans }}</th>
<th>{{ 'admin.busvehicle.form.halfDayPrice'|trans }}</th>
<th>{{ 'admin.busvehicle.form.pricePerKm'|trans }}</th>
<th>{{ 'admin.busvehicle.form.amenities'|trans }}</th>
<th>{{ 'actions'|trans }}</th>
</tr>
</thead>
<tbody>
{% set inc = 0 %}
{% for busVehicle in app.user.company.busVehicle %}
<tr>
<td>{{ busVehicle.licencePlate }}</td>
<td>{{ busVehicle.chassisNumber }}</td>
<td>{{ busVehicle.passengerSeatsNumber }}</td>
<td>{{ busVehicle.busType.type }}</td>
<td>{{ busVehicle.emissionClass.name }}</td>
<td>{{ busVehicle.fullDayPrice }}</td>
<td>{{ busVehicle.halfDayPrice }}</td>
<td>{{ busVehicle.pricePerKm }}</td>
<td>
<i style="cursor: pointer;" class="fa fa-search"
aria-hidden="true"
data-toggle="collapse"
data-target="#amenities{{ inc }}"></i>
</td>
<td>
<div id="addBusVehicleDiv{{ busVehicle.id }}">
<button data-id="{{ busVehicle.id }}" href="#"
class="addBusVehicle btn btn-primary btn-sm">
<i class="fa fa-plus"></i>
<span class="bold"> Add bus vehicle</span>
</button>
</div>
<div id="removeBusVehicleDiv{{ busVehicle.id }}"
style="display: none;">
<div id="removeBusVehicleDiv{{ busVehicle.id }}">
<button data-id="{{ busVehicle.id }}"
href="#"
class="removeBusVehicle btn btn-danger btn-sm">
<i class="fa fa-times"></i>
<span class="bold"> Remove bus vehicle</span>
</button>
</div>
</div>
</td>
</tr>
<tr id="amenities{{ inc }}" class="collapse">
<td>
>{{ 'admin.busvehicle.form.amenities'|trans }}:
</td>
<td>
<div>
<p>
{% for busAmenity in busVehicle.busVehiclesAmenities %}
{% if loop.last %}
{{ busAmenity.name }}
{% else %}
{{ busAmenity.name }},
{% endif %}
{% endfor %}
</p>
</div>
</td>
</tr>
{% set inc = inc + 1 %}
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
</div>
</div>
{{ form_end(form) }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script src="{{ asset('resources/public/js/jquery.smartWizard.min.js', 'busrent_admin') }}"></script>
<script src="{{ asset('resources/public/js/busVehicleGroup.js', 'busrent_admin') }}"></script>
{% endblock %}
JS:
// Smart Wizard
$('#smartwizard').smartWizard({
selected: 0, // Initial selected step, 0 = first step
keyNavigation:true, // Enable/Disable keyboard navigation(left and right keys are used if enabled)
autoAdjustHeight:true, // Automatically adjust content height
cycleSteps: false, // Allows to cycle the navigation of steps
backButtonSupport: true, // Enable the back button support
showStepURLhash: true,
useURLhash: true, // Enable selection of the step based on url hash
lang: { // Language variables
next: 'Next',
previous: 'Previous'
},
toolbarSettings: {
toolbarPosition: 'bottom', // none, top, bottom, both
toolbarButtonPosition: 'right', // left, right
showNextButton: true, // show/hide a Next button
showPreviousButton: true, // show/hide a Previous button
toolbarExtraButtons: [
$('<button disabled id="finishForm" type="submit"></button>').text('Finish')
.addClass('btn btn-info')
]
},
anchorSettings: {
anchorClickable: false, // Enable/Disable anchor navigation
enableAllAnchors: false, // Activates all anchors clickable all times
markDoneStep: true, // add done css
enableAnchorOnDoneStep: true // Enable/Disable the done steps navigation
},
contentURL: null, // content url, Enables Ajax content loading. can set as data data-content-url on anchor
disabledSteps: [], // Array Steps disabled
errorSteps: [], // Highlight step with errors
theme: 'dots',
transitionEffect: 'fade', // Effect on navigation, none/slide/fade
transitionSpeed: '400'
});
$("#smartwizard").on("showStep", function(e, anchorObject, stepNumber, stepDirection) {
if (stepNumber == 2){
$('#finishForm').attr("disabled", false);
}
else{
$('#finishForm').attr("disabled", true);
}
});
the problem seems to be in:
public function addBusVehicles(BusVehicle $busVehicles)
{
if ($this->busVehicles->contains($busVehicles)) {
return;
}
$this->busVehicles->add($busVehicles);
$busVehicles->addBusVehicleGroup($this);
}
when call
$busVehicles->addBusVehicleGroup($this);
your are adding a new route to the relation, try without calling this method.

Doctrine - Create dynamical list in View

I am trying to create a view for display the total sales for all the suppliers and customers of a shop.
The total sales for a customer are easy to obtain, the problem is when I have to create the columns with the brands, I have been looking through this site, doctrine, etc, But I cannot find any solution.
SaleController.php
/**
* #Route("/widget/summarize")
*
* #return Render
*/
public function widgetSummarizeAction()
{
$suppliers = $this->getDoctrine()->getEntityManager()->createQueryBuilder()
->select(['su.name AS name'])
->from('AppBundle:Supplier', 'su')
->getQuery()
->getArrayResult()
;
$data = $this->getDoctrine()->getManager()->createQueryBuilder()->groupBy('c.name')
->select([
's.id AS id',
's.invoiceDate AS invoiceDate',
'c.name AS customer',
'SUM(ROUND(s.amount * s.price, 2)) AS totalSales',
'su.name AS supplier',
])
->from('AppBundle:Sale', 's')
->leftJoin('s.supplierCustomer', 'sc')
->leftJoin('sc.customer', 'c')
->leftJoin('sc.supplier', 'su')
->getQuery()
->getArrayResult()
;
return $this->render('AppBundle::home_widget.html.twig', [
'title' => 'Total Sales',
'icon' => 'money',
'urlList' => $this->generateUrl($this->getUrlList()),
'rowRoute' => $this->getUrlEdit(),
'data' => $data,
'columns' => [
'customer' => 'customer',
'totalSales' => 'Total Sales'
]
]);
home_widget.html.twig
<div class="panel panel-default home-widget">
<div class="panel-heading">
<h2 class="panel-title pull-left">
{% if icon is defined and icon is not empty %}<i class="fa fa-{{ icon }}"></i>{% endif %}
{{ title }}
</h2>
{% if urlList %}
<a class="btn btn-primary pull-right" href="{{ urlList }}">See All</a>
{% endif %}
<div class="clearfix"></div>
</div>
<div class="panel-body">
{% if data is not empty and data is iterable %}
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for column in columns %}
<td>{{ column }}</td>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in data %}
{% set href = url(rowRoute, {'id': row.id}) %}
<tr>
{% for key,column in columns %}
<td>
<a href="{{ href }}">
{{ row[key] | raw }}
</a>
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
Maybe try get colulmns name by:
$em = $this->getDoctrine()->getManager();
$columns = $em->getClassMetadata(your_entity_name::class)->getFieldNames();
and loop through the columns in twig template.
<thead>
<tr>
{% for column in columns %}
<td>{{ column }}</td>
{% endfor %}
</tr>
</thead>

Add a reply form textfield for each received message

I have a private message bundle/entity that allows my users to send messages between them.
Its fields are as follows:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #Assert\NotBlank(message="private_message.title.blank")
* #ORM\Column(name="title", type="string", length=50)
*/
protected $title;
/**
* #Assert\NotBlank(message="private_message.receiver.blank")
* #AcmeAssert\IsHimself(message="private_message.receiver.himself", groups={"new"})
* #ORM\ManyToOne(targetEntity="MedAppBundle\Entity\User")
* #ORM\JoinColumn(referencedColumnName="id")
*/
protected $receiver;
/**
* #ORM\ManyToOne(targetEntity="MedAppBundle\Entity\User")
* #ORM\JoinColumn(referencedColumnName="id")
*/
protected $sender;
/**
* #var string
* #Assert\NotBlank(message="private_message.content.blank")
* #ORM\Column(name="content", type="string")
*/
protected $content;
/**
* #var \DateTime
*
* #ORM\Column(name="sentAt", type="datetime")
*/
protected $sentAt;
/**
* #var boolean
*
* #ORM\Column(name="isSpam", type="boolean")
*/
protected $isSpam = false;
/**
* #var \DateTime
*
* #ORM\Column(name="seenAt", type="datetime",nullable=true)
*/
protected $seenAt = null;
/**
* #ORM\ManyToOne(targetEntity="PrivateMessageBundle\Entity\Message",inversedBy="replies")
* #ORM\JoinColumn(referencedColumnName="id",nullable=true)
*/
protected $replyof;
/**
* #ORM\OneToMany(targetEntity="PrivateMessageBundle\Entity\Message", mappedBy="replyof")
**/
private $replies;
public function __construct() {
$this->replies = new ArrayCollection();
}
Notice the replyof field, it references to another message, and the replies one references to an array of messages. If replyof is null, then the message is not a reply of any message.
I have a twig template with a macro that displays a user's message and all the replies of that message. What I'd like to do is have a reply textfield under each of these, exactly like Gmail has, that allows me to add a reply to each message.
But when I add it to the template, only one is rendered because it has one single Id. How can I add a reply form after each reply? What their FormType should look like?
Here is also my twig template:
{% macro displayReply(reply,replyform) %}
{% import _self as macros %}
{# <li> id: {{ reply.id }} </li>
<li> sent by: {{ reply.sender }} </li>
<li> title: {{ reply.title }} </li>
<li> content: {{ reply.content }} </li>
<li> date: {{ reply.sentAt|date('d-m-Y H:i:s') }} </li>
reply
<hr> #}
<div class="panel panel-default">
<div class="panel-body">
<div class="message-info">
<input type="hidden" name="messageid" id="messageId" value="{{ reply.id }}">
<div class="message-title clearfix">
<h4 class="pull-left">{{ reply.title }}</h4>
</div>
<hr class="lite-line">
<div class="message-sender clearfix">
<div class="pull-left sender">
{{ reply.sender }}
</div>
<div class="pull-right">
to <b>{{ (reply.receiver==app.user)?'me':reply.receiver }}</b> on <span
class="message-timestamp">{{ reply.sentAt|date('F d, Y H:i:s') }}</span>
<a class="btn btn-start-order" role="button"
href="{{ path('private_message_new',{'msg':reply.id}) }}">Reply</a>
</div>
</div>
<hr class="lite-line">
<div class="message-box clearfix">
<span>{{ reply.content | replace({"<script>" : "", "</script>" : ""}) | raw }}</span>
</div>
{{ form_start(replyform) }}
<input type="submit">
{{ form_end(replyform) }}
</div>
</div>
</div>
{% for reply in reply.replies %}
{% if loop.first %}<div>{% endif %}
{{ macros.displayReply(reply) }}
{% if loop.last %}</div>{% endif %}
{% endfor %}
{% endmacro %}
{% import _self as macros %}
{# use the macro #}
<div class="message-back">
<a class="btn btn-start-order-dark btn-block" role="button"
href="{{ path('private_message',{'page':'inbox'}) }}">
<span class="fa fa-undo"></span> Go back
</a>
</div>
<div class="messages">
<div class="panel panel-default">
<div class="panel-body">
<div class="message-info">
<input type="hidden" name="messageid" id="messageId" value="{{ message.id }}">
<div class="message-title clearfix">
<h4 class="pull-left">{{ message.title }}</h4>
</div>
<hr class="lite-line">
<div class="message-sender clearfix">
<div class="pull-left sender">
{{ message.sender }}
</div>
<div class="pull-right">
to <b>{{ (message.receiver==app.user)?'me':message.receiver }}</b> on <span
class="message-timestamp">{{ message.sentAt|date('F d, Y H:i:s') }}</span> <a
class="btn btn-start-order" role="button"
href="{{ path('private_message_new',{'msg':message.id}) }}">Reply</a>
</div>
</div>
<hr class="lite-line">
<div class="message-box clearfix">
<span>{{ message.content | replace({"<script>" : "", "</script>" : ""}) | raw }}</span>
</div>
{{ form_start(replyform) }}
<input type="submit">
{{ form_end(replyform) }}
</div>
</div>
</div>
</div>
{% for reply in message.replies %}
{% if loop.first %}<div class="replies">{% endif %}
{{ macros.displayReply(reply ,replyform) }}
{% if loop.last %}</div>{% endif %}
{% endfor %}
Notice that I first display the message, then apply the macro to it that displays all its replies as a tree. It will display the replies's replies, too, in a recursive manner, all the way until the leaf nodes. I add a 'replyform' after each, but I'm not sure how the FormType should be.
My reply form type is like this, but I'm pretty sure it is wrong.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('content', 'textarea')
;
}
As for the other fields of the reply, I take care of them in the controller. I think I should be doing this after receiving the message from the form though. Something like this, and get the title, content and replyof from the formdata.
$messages = $this->getDoctrine()->getRepository('PrivateMessageBundle:Message');
$isforme = $messages->findOneBy(array('receiver' => $this->getUser(), 'id' => $msg));
$message = new Message();
$message->setSender($this->getUser());
$message->setSentAt(new \Datetime('now'));
$message->setReplyof($isforme);
$message->setReceiver($isforme->getSender());
$form = $this->createForm(new MessageReplyType($em), $message);
EDIT
Ok, so I made something that works, by adding a hidden field and hardcoding multiple forms instead of using FormTypes, but I still think that this can be done in a better, more reusable way.
<form name="privatemessagebundle_message" method="post" action="" id="{{ reply.id }}">
<div><label for="privatemessagebundle_message_title" class="required">Title</label><input
type="text" id="privatemessagebundle_message_title"
name="privatemessagebundle_message[title]" required="required" maxlength="50"></div>
<div><label for="privatemessagebundle_message_content" class="required">Content</label><textarea
id="privatemessagebundle_message_content"
name="privatemessagebundle_message[content]" required="required"></textarea></div>
<input type="hidden" id="privatemessagebundle_message_replyof"
name="privatemessagebundle_message[replyof]" value="{{ reply.id }}">
<input type="submit">
<input type="hidden" id="privatemessagebundle_message__token"
name="privatemessagebundle_message[_token]"
value="{{ csrf_token('privatemessagebundle_message') }}">
</form>
Anyone got any better ideas?
I did it! I used the answer from this question.
Since I'm using foreach loops and they might be a bit low on performance, anyone with a better idea is welcomed. There is still the bounty to receive.
I'm creating a form for each of my forms through createNamedBuilder. They will have different names, thus different id's and Symfony will render them all. Then, I can render them where I want and handle their request just fine through their unique id taken from the database.
$genforms = $this->genReplyForms($isforme); // run the function for my message
$forms_views = $genforms['views']; // pass to the view
$forms= $genforms['forms']; // handle request...
This is the function that generated the form. It recursively generates them for each reply of my message.
public function genReplyForms(Message $message)
{
$id = $message->getId();
$msgreply[$id] = new Message();
$forms[$id] = $this->container
->get('form.factory')
->createNamedBuilder('form_'.$id, new MessageReplyType(), $msgreply[$id])
->getForm();
$forms_views[$id] = $forms[$id]->createView();
$result = array(array(), array());
$result['forms'][$id] = $forms[$id];
$result['views'][$id] = $forms_views[$id];
if (sizeof($message->getReplies())) {
foreach ($message->getReplies() as $reply) {
$child = $this->genReplyForms($reply);
$result['forms'] = $result['forms'] + $child['forms'];
$result['views'] = $result['views'] + $child['views'];
}
}
return $result;
}
MessageReplyType needs just user input. Everything else is handled in the controller
$builder
->add('title')
->add('content', 'textarea')
;
Also, my simplified twig. I've simplified the macro call, too. Was doing an unnecessary foreach loop for the first message instead of simply passing it to the macro.
{% macro displayReply(reply, forms) %}
{% import _self as macros %}
{# <li> id: {{ reply.id }} </li>
<li> sent by: {{ reply.sender }} </li>
<li> title: {{ reply.title }} </li>
<li> content: {{ reply.content }} </li>
<li> date: {{ reply.sentAt|date('d-m-Y H:i:s') }} </li>
reply
<hr> #}
<div class="panel panel-default">
<div class="panel-body">
<div class="message-info">
<input type="hidden" name="messageid" id="messageId" value="{{ reply.id }}">
<div class="message-title clearfix">
<h4 class="pull-left">{{ reply.title }}</h4>
</div>
<hr class="lite-line">
<div class="message-sender clearfix">
<div class="pull-left sender">
{{ reply.sender }}
</div>
<div class="pull-right">
to <b>{{ (reply.receiver==app.user)?'me':reply.receiver }}</b> on <span
class="message-timestamp">{{ reply.sentAt|date('F d, Y H:i:s') }}</span>
{# <a class="btn btn-start-order" role="button"
href="{{ path('private_message_new',{'msg':reply.id}) }}">Reply</a> #}
</div>
</div>
<hr class="lite-line">
<div class="message-box clearfix">
<span>{{ reply.content | replace({"<script>" : "", "</script>" : ""}) | raw }}</span>
</div>
{{ form_start(forms[reply.id]) }}
<input type="submit">
{{ form_end(forms[reply.id]) }}
{# NU STERGE! #}
{#
<form name="privatemessagebundle_message" method="post" action="" id="{{ reply.id }}">
<div><label for="privatemessagebundle_message_title" class="required">Title</label><input
type="text" id="privatemessagebundle_message_title"
name="privatemessagebundle_message[title]" required="required" maxlength="50"></div>
<div><label for="privatemessagebundle_message_content" class="required">Content</label><textarea
id="privatemessagebundle_message_content"
name="privatemessagebundle_message[content]" required="required"></textarea></div>
<input type="hidden" id="privatemessagebundle_message_replyof"
name="privatemessagebundle_message[replyof]" value="{{ reply.id }}">
<input type="submit">
<input type="hidden" id="privatemessagebundle_message__token"
name="privatemessagebundle_message[_token]"
value="{{ csrf_token('privatemessagebundle_message') }}"></form>#}
{# NU STERGE! #}
</div>
</div>
</div>
{% for reply in reply.replies %}
{% if loop.first %}<div>{% endif %}
{{ macros.displayReply(reply,forms) }}
{% if loop.last %}</div>{% endif %}
{% endfor %}
{% endmacro %}
{% import _self as macros %}
{# use the macro #}
<div class="message-back">
<a class="btn btn-start-order-dark btn-block" role="button"
href="{{ path('private_message',{'page':'inbox'}) }}">
<span class="fa fa-undo"></span> Go back
</a>
</div>
<div class="replies">
{{ macros.displayReply(message, forms) }}
</div>
Again, I'm still looking for better or more efficient alternatives, so please do post them.

Render partial twig in formmapper

Is it possible to render a view into a formmapper?
The situation is like this:
/**
* #param FormMapper $formMapper
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->tab('Company')
->with('Info')
->add('name')
->end()
->end()
->tab('Abonnementen')
->with('Abonnementen', array('class' => 'col-md-12'))
//Render a partial twig here
->end()
;
}
This is in my twig:
<form class="form-horizontal" action="" method="post" style="margin-top:15px;">
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header"><h4 class="box-title">Abonnementen</h4></div>
<div class="box-body">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>Active</th>
</tr>
</thead>
<tbody>
{% for abonnement in abonnementen %}
<tr>
<td>{{ abonnement.name }}</td>
<td>{{ abonnement.description }}</td>
<td>€ {{ abonnement.price }}</td>
<td>
<input type="checkbox" name="{{ abonnement.id }}[active]"
{% if abonnement.active %}
checked="checked"
{% endif %}>
</td>
</tr>
{% endfor %}
<tr>
<td colspan="4">
<input type="submit" class="btn btn-primary" value="Save" name="abonnement_save">
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</form>
Is it even possible to load this into my tab in my formmapper?
I think that is possible to do something similar. You want to inject your form into base sonata form. It is possible but at the end you get something like this:
<form> # form from sonata
<form> #your custom form
</form>
</form>
I don't recommend it.
Formmaper build form from formBuilder and you can define template(html) of your form field in standart way:
http://symfony.com/doc/current/cookbook/form/form_customization.html
I reccommend you to override sonata template name 'edit' and add there your code as another form.
public function configure()
{
$this->setTemplate('edit', 'ApplicationM2MNewsletterBundle:CRUD:empty_form.html.twig');
}

custom form widget Symfony 2 but return An exception in the controller

I'm trying to custom my form in symfony2 using {{ form_widget(form) }}
so my buildForm is like this :
$builder
->add('Status', 'choice', array(
'choices' => array('Required' => 'Required', 'Free' => 'Free', 'OnArrival' => 'OnArrival', 'Refused' => 'Refused'),
'required' => true,
'label' => 'Visa required?'
))
->add('Length', null, array('required' => false, 'label' => 'Max Stay'))
->add('Description', 'textarea',array('required' => false, 'label' => 'Additional information'));
my twig html :
{% block choice_widget %}
<div class="row">
<div class="col-md-8 ">
<select class="selectpicker " data-style="btn-primary">
{% spaceless %}
{% for choice in choices %}
<option value="{{ choice.value }}">{{ choice.value }}</option>
{% endfor %}
{% endspaceless %}
</select>
</div>
</div>
{% endblock %}
{% block text_widget %}
<div class="row">
<div class="col-md-8 ">
<input id="id_max" type="text" placeholder=" x Month(s)" class="form-control">
</div>
</div>
{% endblock %}
{% block textarea_widget %}
<textarea id="id_desc" type="" placeholder="" class="form-control"></textarea>
{% endblock %}
it shows me well the new style applied but when I execute my form it return me an exception and said that values are null ie : Status=null , length=null, description=null
PS : when I retrieve the custom html it works well
EDIT : add the controller part
/**
* Creates a new TravelInfoCorrection entity.
*
* #Route("/correct/create", name="correct_create")
* #Method("POST")
* #Template("TechyVisaBundle:edit:new.html.twig")
*/
public function createAction(Request $request)
{
$entity = new TravelInfoCorrection();
$form = $this->createForm(new TravelInfoCorrectionType(), $entity);
$form->bind($request);
$entity->setCreatedAt(new \DateTime('now'));
$entity->setProcessedFlag('Pending');
if (! $form->isValid()) throw new \Exception('Form not valid');
$em = $this->getDoctrine()->getManager();
$orientity = $em->getRepository('TechyVisaBundle:TravelInfo')
->findOneBy(array( 'TravFrom' => $entity->getTravFrom(), 'TravTo' => $entity->getTravTo() ));
$isdiff = false;
$isdiff = $isdiff || ($entity->getStatus() != $orientity->getStatus());
$isdiff = $isdiff || ($entity->getLength() != $orientity->getLength());
$isdiff = $isdiff || ($entity->getDescription() != $orientity->getDescription());
if ($isdiff){
$em->persist($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('thanks'));
}
and the caller html :
{% form_theme form 'TechyVisaBundle:Form:fields.html.twig' %}
{% block content %}
<br/>
<div id="sign_up1">
<div class="container">
<h2>Edit information {{ from.getName }} to {{ to.getName }} travelling information</h2>
<b style="color: red;">Please verify your informations <a target="_blank" rel="nofollow" href="http://www.gulfair.com/English/info/prepare/Pages/VisaInformation.aspx">here</a> before posting it</b>
<br/>
<form action="{{ path('correct_create') }}" method="post" {{ form_enctype(form) }} class="form-inline">
{{ form_widget(form) }}
<button type="submit">Submit correction</button>
</form>
</div>
</div>
{% endblock %}

Categories