How can i save and render array in symfony database? - php

i have an app for pizza ordering. i have entity orders. When i am submitting order i have problem, because i have checkboxes for ingredients and i am saving it as array.
Entity/Orders
/**
* #Assert\NotBlank(
* message="please select!")
* #Assert\NotNull(
* message="please select!")
* #ORM\Column(name="ingredients", type="array")
*
*/
protected $ingredients;
public static function getIngredientsOptions(){
return array('cheese','tomatoes','salami','onions','mushroom', 'bacon','ham','vegetables','peppers','olives');
}
DefaultController
private function buildForm($order) {
return $this->createFormBuilder($order)
->add('name','text',array('required'=>false))
->add('address','textarea',array('required'=>false))
->add('phone','number',array('required'=>false))
->add('email','text',array('required'=>false))
->add('box','choice',array(
'choices' => Orders::getBoxOptions()))
->add('ingredients','choice',array(
'choices' => Orders::getIngredientsOptions(),
'expanded' => true,
'multiple' => true))
->add('delivery','choice',array(
'choices' => Orders::getDeliveryOptions(),
'expanded' => true))
->add('save','submit')
->getForm();
}
/**
* #Route("/new", name="new_order")
* #Template()
*/
public function orderAction(Request $request)
{
$order = new Orders();
$form = $this->buildForm($order);
$form->handleRequest($request);
if ($form->isValid()) {
$order = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($order);
$em->flush();
return $this->forward('S1500238090PizzaBundle:Default:index');
}
return array('form' => $form->createView());
}
index.html.twig
<h3>Orders</h3>
<table border>
<tr>
<th>ID</th>
<th>Name</th>
<th>Address</th>
<th>Phone</th>
<th>E-Mail</th>
<th>Box</th>
<th>Ingredients</th>
<th>Delivery</th>
</tr>
{% for order in orders %}
<tr>
<td>{{order.id}}</td>
<td>{{order.name}}</td>
<td>{{order.address}}</td>
<td>{{order.phone}}</td>
<td>{{order.email}}</td>
<td>{{boxOptions[order.box]}}</td>
<td>{{ingredientsOptions[order.ingredients]}}</td>
<td>{{deliveryOptions[order.delivery]}}</td>
<td>
Edit
Delete
</td>
</tr>
{% endfor %}
</table>
New order
i don't know how to save ingredients array correctly and after that render it.

I found out that,I have to change index.html.twig ingredients tag with the following:
<td>{% for ingredient in order.ingredients %}
{{ingredient}}
{% endfor %}
</td>

Related

Symfony2 - Requirement to provide a value value for an argument

The following exception is being returned when trying to use the method editAction() of a custom ProfileController herited from FOSuserbundle:
Controller "UserBundle\Controller\ProfileController::editAction()" requires that you provide a value for the "$id" argument (because there is no default value or because there is a non optional argument after this one).
UserBundle\Controller\ProfileController::editAction():
/**
* Edit the user
*/
public function editAction($id)
{
$user = $this
->getDoctrine()
->getManager()
->getRepository('UserBundle:User')
->find($id);
if (!is_object($user) || !($user instanceof UserInterface)) {
throw new AccessDeniedException('This user does not have access to this section.');
}
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$form = $this->createForm(new RegistrationEditFormType(), $user);
$form->handleRequest($request);
if ($form->isValid()) {
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
// Updating the user
$userManager->updateUser($user);
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_SUCCESS, $event);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('lld_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
$request->getSession()->getFlashBag()->add('info-edit', 'Your account has been updated!');
return $this->render('UserBundle:Profile:edit.html.twig', array(
'form' => $form->createView(),
'user' => $user
));
}
code snippet from admin.html.twig:
<tbody>
{% for user in users %}
<tr class="">
<td>{{ user.id }}</td>
<td>{{ user.firstName }}</td>
<td>{{ user.lastName }}</td>
<td>{{ user.amount }}</td>
<td>
{% if user.isAccountNonLocked %}
<span class="label label-success">Enabled</span>
{% else %}
<span class="label label-default">Disabled</span>
{% endif %}
</td>
<td>{{ user.date|date('m-d-Y H:i:s') }}</td>
<td>{{ user.updatedAt|date('m-d-Y H:i:s') }}</td>
<td></td>
<td>Delete Disable Enable Edit</td>
</tr>
{% endfor %}
</tbody>
The specific line being used:
<td>Delete Disable Enable Edit </td>
adminAction():
public function adminAction()
{
$user = $this->getUser();
if (!$this->container->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY') || !$this->container->get('security.context')->isGranted('ROLE_ADMIN') || !$this->container->get('security.context')->isGranted('ROLE_MANUFACTURER')) {
return $this->redirect($this->generateUrl('fos_user_security_login'));
}
$userManager = $this->container->get('fos_user.user_manager');
$users = $userManager->findUsers();
return $this->render('UserBundle:Admin:admin.html.twig', array(
'users' => $users
));
}
public function deleteAction(Request $request, $id)
{
if (!$this->container->get('security.context')->isGranted('ROLE_ADMIN')) {
return $this->redirect($this->generateUrl('fos_user_security_login'));
}
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('id' => $id));
if (null === $user) {
throw new NotFoundHttpException("User with id " . $id . " doesn't exist.");
}
$userManager->deleteUser($user);
$request->getSession()->getFlashBag()->add('info-delete', 'The user ' . strtoupper($user->getCompanyName()) . ' has been deleted successfully!');
return $this->redirect($this->generateUrl('admin'));
}
So, I got stuck and I'm not seeing where this issue is coming from since the argument is logically being retrieved from the line where the click is being made. Obviously the right action (the custom editAction()) is being called.
Is there something I'm missing? Any suggestions please? I tried the check out similar posts but none could help me.
The reason is that your are using native FOS routing, which does not have an id parameter at all.
<route id="fos_user_profile_show" path="/" methods="GET">
<default key="_controller">fos_user.profile.controller:showAction</default>
</route>
It's a Profile Edit page, which allows you to edit your own profile, so the id must be taken from a session.
If you are trying to provide some kind of CRUD functionality, you have to either switch to a ready-to-use datagrid / admin systems (e.g. SonataUserBundle) or write your custom routing and controller.

Add dropdown to symfony 3 form

I want to add a dropdow field for category in my form in symfony version 3, I have tried to solutions but each one have their own problem
First got all categories and pass them to my view and show them:
Action:
/**
* Creates a new News entity.
*
* #Route("/new", name="news_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$news = new News();
$form = $this->createForm('AppBundle\Form\NewsType', $news);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($news);
$em->flush();
return $this->redirectToRoute('news_show', array('id' => $news->getId()));
}
$em = $this->getDoctrine()->getManager();
$categories = $em->getRepository('AppBundle:Category')->findAll();
return $this->render('news/new.html.twig', array(
'news' => $news,
'form' => $form->createView(),
'categories' => $categories,
));
}
View:
{% extends 'base.html.twig' %}
{% block body %}
<h1>News creation</h1>
{{ form_start(form) }}
<label for="news_content" class="required">Category</label>
<select name="news[categoryId]">
{% for category in categories %}
<option value="{{ category.id }}">{{ category.title }}</option>
{% endfor %}
</select>
{{ form_widget(form) }}
<input class="btn btn-sm btn-success" type="submit" value="Create" />
{{ form_end(form) }}
<ul>
<li>
<a class="label label-sm label-info" href="{{ path('news_index') }}">Back to the list</a>
</li>
</ul>
{% endblock %}
The form is created as I expected but when i want to submit it, it show an validation error as bellow:
This form should not contain extra fields.
second solution that I have tried is to generate the dropdown from my Type, so in NewsType I changed the buildForm function as bellow:
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('categoryId', EntityType::class, [
'class' => 'AppBundle:Category',
'choice_label' => 'title',
])
->add('title')
->add('content')
;
}
It this way, the form also have been created nicely but after submit, an database exception returned and said:
An exception occurred while executing 'INSERT INTO news (category_id, title, content) VALUES (?, ?, ?)' with params [{}, "asdf", "asdf"]:
Catchable Fatal Error: Object of class AppBundle\Entity\Category could not be converted to string
It mean that my category_id passed as an object !
What should I do?
BTW, my english is a little weak, please do not put and minus on my post, I had been ban multiple times.
Thanks in advance.
all symfony is trying to do is to find a string representation of the Category object, so it can populate the option fields.
you can solve this in a couple of ways:
In the Category object, you can make a __toString method.
public function __toString() {
return $this->name; // or whatever field you want displayed
}
or
you can tell symfony which field to use as the label for the field. From docs
$builder->add('attending', ChoiceType::class, array(
'choices' => array(
new Status(Status::YES),
new Status(Status::NO),
new Status(Status::MAYBE),
),
'choice_label' => 'displayName', // <-- where this is getDisplayName() on the object.
));

Symfony 2: Multiple forms generated list of objects

I would like to generate page with list of users names and next to every user I would like to have button which would activate/deactivate this user.
I could of course create link with user ID and GET method in controller which would perform operation when link would be clicked. As far as I know it is not recommended though to do it this way due to security concerns. So instead of links to route which would perform operation I would like to have forms and buttons to submit request to PUT route which would change user status.
QUESTION: how to generate such forms (buttons) based on list of users returned by Doctrine?
Form code used to create form/button in user profile:
/**
* Creates a form to activate/deactivate a User entity by id.
*
* #param mixed $id The entity id
*
* #return \Symfony\Component\Form\Form The form
*/
private function createActivationDeactivationForm($id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('user_activate', array('id' => $id)))
->setMethod('PUT')
->add('submit', 'submit', array('label' => 'Activate/Deactivate'))
->getForm()
;
}
Controller code used for user profile:
/**
* #Route("/user/{id}", name="user_show")
* #Method("GET")
* #Template()
*/
public function showUserAction($id)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('TestUserBundle:User')->find($id);
if (!$user) {
throw $this->createNotFoundException('Unable to find user');
}
$deleteForm = $this->createDeleteForm($id);
$activateForm = $this->createActivationDeactivationForm($id);
return array(
'user' => $user,
'delete_form' => $deleteForm->createView(),
'activate_form' => $activateForm->createView(),
);
}
Controller PUT method to perform operation from user profile:
/**
* Activate a user.
*
* #Route("/{id}", name="user_activate")
* #Method("PUT")
*/
public function activateAction(Request $request, $id)
{
$form = $this->createActivationDeactivationForm($id);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('TestUserBundle:User')->find($id);
if (!$user) {
throw $this->createNotFoundException('Unable to find user');
}
$current_user_activity_flag = $user->getActive();
$user->setActive(abs($current_user_activity_flag-1));
$em->persist($user);
$em->flush();
}
return $this->redirect($this->getRequest()->headers->get('referer'));
}
Controller code to be used for users list:
/**
* #Route("/users", name="users_list")
* #Method("GET")
* #Template()
*/
public function listUsersAction()
{
$em = $this->getDoctrine()->getManager();
$users = $em->getRepository('TestUserBundle:User')->findExistingUsers();
//$deleteForm = $this->createDeleteForm($id);
//$activateForm = $this->createActivationDeactivationForm($id);
return array(
'users' => $users,
//'delete_form' => $deleteForm->createView(),
//'activate_form' => $activateForm->createView(),
);
}
I can not pass ID to form like I did for operation from profile cause for every user there is different ID and more of that Symfony generates only first form and ignores rest.
Any idea how to handle it? Or maybe my approach with form/buttons is incorrect and I should just use links instead?
I found solution which works though I am not sure about if it's compliant with best practices.
Instead of passing one form object in controller I generated an array of them with keys based on user ID. Than when looping through array in TWIG template I use user ID to refer form object created for current user.
Mentioned in question controller for user listing should than look like this:
/**
* #Route("/users", name="users_list")
* #Method("GET")
* #Template()
*/
public function listUsersAction()
{
$em = $this->getDoctrine()->getManager();
$users = $em->getRepository('PSUserBundle:User')->findExistingUsers();
$activate_forms = array();
$delete_forms = array();
foreach($users as $user)
{
$activate_forms[$user->getId()] = $this->createActivationDeactivationForm($user->getId())->createView();
$delete_forms[$user->getId()] = $this->createDeleteForm($user->getId())->createView();
}
return array(
'users' => $users,
'delete_forms' => $delete_forms,
'activate_forms' => $activate_forms,
);
}
... and in TWIG form within foreach should be refered like this:
{{ form_start(activate_forms[user.id], {'attr': {'novalidate': 'novalidate'}}) }}
{% if user.active %}
{{ form_widget(activate_forms[user.id].submit, {'attr': {'class': 'btn btn-xs btn-warning btn-block'}, 'label' : 'Deactivate'}) }}
{% else %}
{{ form_widget(activate_forms[user.id].submit, {'attr': {'class': 'btn btn-xs btn-success btn-block'}, 'label' : 'Activate'}) }}
{% endif %}
{{ form_end(activate_forms[user.id]) }}

symfony2 edit more entities in same page

How can edit more products entities in same page (no 1 to many).
In My editaction :
$entities=$em->getRepository('MyBundle:Product')->findAll();
$editForm=array();
$deleteForm=array();
foreach ($entities as $product )
{
$editForm [$port->getId()]= $this->createEditForm($product);
$deleteForm[$port->getId()] = $this->createDeleteForm($product->getId());
}
return $this->render('MyBundle:Product:edit.html.twig', array(
'entities' => $entities,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
I have this error :
Error: Call to a member function createView() on a non-object
And how update the edit.thml.twig to show all form update as table with only one submit ?
This is not about Symfony2, but about PHP, you are calling a method onto an array...
Consider something like this :
$entities = $em->getRepository('MyBundle:Product')->findAll();
$editForms = array();
$deleteForms = array();
foreach ($entities as $product)
{
$editForms[$port->getId()] = $this->createEditForm($product)
->createView();
$deleteForms[$port->getId()] = $this->createDeleteForm($product->getId())
->createView();
}
return $this->render('MyBundle:Product:edit.html.twig', array(
'entities' => $entities,
'edit_forms' => $editForms,
'delete_forms' => $deleteForms,
));
With a template like this one :
{% for form in edit_foms %}
{{ form(form) }}
{% endfor %}
{% for form in delete_foms %}
{{ form(form) }}
{% endfor %}
I have fix this :
change the name of form:
public $name;
/**
* #return string
*/
public function getName()
{
return (string)'port_'.$this->name;
}
public function __construct($name=0) {
$this->name=$name;
}
and in my controller
editAction :
$entities = $em->getRepository('InfraProductBundle:InfraPortDdf')->findAll();
foreach ($entities as $port )
{
$editForm [$port->getId()]= $this->createEditForm($port);
$edit_view[$port->getId()]=$editForm[$port->getId()]->createView();
}
return $this->render('......:edit.html.twig', array(
'entities' => $entities,
'edit_form' => $edit_view,
));
in update action
foreach ($array_id as $key=>$id){
if(!is_numeric(str_replace('port_','',$id)))
continue;
$entity = $em->getRepository('InfraProductBundle:InfraPortDdf')->find(str_replace('port_','',$id));
if (!$entity) {
throw $this->createNotFoundException('Unable to find InfraPortDdf entity.');
}
$editForm = $this->createEditForm($entity);
if($editForm->submit($request->request->get($id)))
$em->flush();
}
in my edit.html.twig:
{% for key, edit in edit_form %}
...
{{form_widget(edit.description,{name:'['~key~'][description]', 'attr': { 'name' : '['~key~'][description]' } } ) }}
....
{%endfor%}

Sonata Admin custom template

I am trying to create a custom page in Sonata where I basically read the current month created records of a particular table (Quotes). There is also money involved in every quote so at the end of the table I add an extra row for the Total of the month.
Now, since this is a custom template, I wanted to be able to put the Show action button that Sonata has on every quote, but I cannot find a way to do it... Any ideas? Thanks!
Im not using the Admin class since I am overriding the listAction method in a custom CRUDController as you can see here...
public function listAction()
{
if (false === $this->admin->isGranted('LIST')) {
throw new AccessDeniedException();
}
$datagrid = $this->admin->getDatagrid();
$formView = $datagrid->getForm()->createView();
// set the theme for the current Admin Form
$this->get('twig')->getExtension('form')->renderer->setTheme($formView, $this->admin->getFilterTheme());
$data = array();
$em = $this->getDoctrine()->getManager();
$request = $this->getRequest();
$form = $this->createFormBuilder(null, array('label' => 'Month: '))
->add('month', 'choice', array(
'choices' => $this->getMonths(),
'label' => false
))
->add('search', 'submit', array(
'attr' => array('class' => 'btn-small btn-info'),
))
->getForm();
$form->handleRequest($request);
if($request->isMethod('POST')){
$startDate = new \DateTime($form->get('month')->getData());
$endDate = new \DateTime($form->get('month')->getData());
$endDate->modify('last day of this month');
} else {
$endDate = new \DateTime('now');
$startDate = new \DateTime('now');
$startDate->modify('first day of this month');
}
$dql = 'SELECT q FROM AcmeQuoteBundle:Quote q WHERE q.date BETWEEN \' '
. $startDate->format('Y-m-d') . '\' AND \'' . $endDate->format('Y-m-d')
. '\' ORDER BY q.date DESC';
$query = $em->createQuery($dql);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$query,
$this->get('request')->query->get('page', 1)/*page number*/,
50/*limit per page*/
);
$data['logrecords'] = $pagination;
return $this->render('AcmeQuoteBundle:Admin:quoteReport.html.twig',
array('data' => $data,
'action' => 'list',
'form' => $form->createView(),
'datagrid' => $datagrid
));
}
and here is the twig that I am using:
{% block list_table %}
{% set logrecords = data.logrecords %}
{# total items count #}
<div class="count">
<h4>Total number of records founded: {{ logrecords.getTotalItemCount }} </h4>
</div>
<table class="table table-striped table-hover table-bordered ">
<thead class="info">
<tr >
<th>#</th>
<th>{{ knp_pagination_sortable(logrecords, 'Created at', 'q.date') }}</th>
<th>{{ knp_pagination_sortable(logrecords, 'Quote token', 'q.viewToken') }}</th>
<th>Business name</th>
<th>$AUD</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% set grandTotal = 0.0 %}
{% for quote in logrecords %}
{% set total = 0.0 %}
{% for item in quote.items %}
{% set total = (item.unit * item.rate) %}
{% endfor %}
{% set grandTotal = grandTotal + total %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ quote.date | date('Y-m-d') }}</td>
<td>{{ quote.viewToken }}</td>
<td>{{ quote.request.business.name }}</td>
<td>{{ total }}</td>
<td>
<!-- THIS IS WHERE I WANT TO SHOW THE SHOW BUTTON -->
<i class="icon-search"></i> SHOW
</td>
</tr>
{% endfor %}
<tr>
<td colspan="3"></td>
<td>TOTAL (AUD): </td>
<td>{{ grandTotal }}</td>
<td></td>
</tr>
</tbody>
</table>
{# display navigation #}
<div class="navigation">
{{ knp_pagination_render(logrecords) }}
</div>
{% endblock list_table %}
If I understand correctly, you wanna add a new route with custom action and templates to your list view.
The first you have to do is to modify your Admin as following:
// Add a new route to your admin
protected function configureRoutes(RouteCollection $collection)
{
$collection->add('custom_show_action', $this->getRouterIdParameter().'/customshow');
}
// Final Step is to add your custom route and a template to the listMapper
protected function configureListFields(ListMapper $listMapper)
{
// Optional you can change the list layout with the following function
// and to there some calculation on the visible entries.
$this->setTemplate('list', '::custom_list_layout.html.twig');
// ...
$listMapper
// ...
->add('_action', 'actions', array(
'actions' => array(
'view' => array(),
'edit' => array(),
'custom_show_action' => array('template' => '::your_template.html.twig'),
)
))
;
// ...
}
I recommend to calculate values in the controller. In my case I've done this for an invoice bundle in the following way:
// Acme/DemoBundle/Controller/AdminController
public function listAction()
{
if (false === $this->admin->isGranted('LIST')) {
throw new AccessDeniedException();
}
$datagrid = $this->admin->getDatagrid();
$formView = $datagrid->getForm()->createView();
$money_open = 0;
$money_payed = 0;
$money_referal = 0;
foreach($datagrid->getResults() as $key => $object) {
if ($object->getPayedAt()) {
$money_payed += $object->getPrice();
if ($object->getReferal() && $object->getReferalPrice() > 0) {
$money_referal += $object->getReferalPrice();
}
} else {
$money_open += $object->getPrice();
}
}
// set the theme for the current Admin Form
$this->get('twig')->getExtension('form')->renderer->setTheme($formView, $this->admin->getFilterTheme());
return $this->render($this->admin->getTemplate('list'), array(
'action' => 'list',
'form' => $formView,
'datagrid' => $datagrid,
'money_open' => $money_open,
'money_payed' => $money_payed,
'money_referal' => $money_referal,
));
}
As you can see my money calculation is done in controller and I only have to set it to the template.

Categories