Adding a custom action to Sonata Admin - php

I have created a custom action that renders a small form at the bottom of my show template for orders. The form is a basic checkbox and a select field to with tow buttons. It works perfectly but the rendering is not right.
I know the way I render the show template is not 100% correct, because when it renders, the left hand side menu doesn't work anymore.
Here is my custom controller with action;
namespace Qi\Bss\FrontendBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Qi\Bss\FrontendBundle\Crud\Crud;
use Qi\Bss\BaseBundle\Entity\Business\PmodOrder;
use Symfony\Component\HttpFoundation\RedirectResponse;
class PmodOrderController extends Controller
{
/**
* #Route("/{id}/approve", name = "order_approve")
* #Security("is_granted('IS_AUTHENTICATED_FULLY')")
* #Method({"GET", "POST"})
*/
public function approveAction(Request $request, $id){
$em = $this->getDoctrine()->getManager();
$order = $em->getRepository('QiBssBaseBundle:PmodOrder')->find($id);
$approveForm = $this->createFormBuilder($order)
->add('requireApproval', 'checkbox', array('label' => 'Require second Approval', 'required' => false, 'mapped' => false))
->add('secondApprover', 'choice', array('choices' => Crud::enumStatus(), 'label' => 'User', 'required' => false))
->getForm();
$approveForm->handleRequest($request);
if ($approveForm->isSubmitted() && $approveForm->isValid()) {
$secondApproval = $request->request->get('form');
$approval = $approveForm->getData();
if (isset($secondApproval['requireApproval'])) {
$approval->setStatus(PmodOrder::STATUS_PARTLY_APPROVED);
$em->persist($approval);
$em->flush();
return new RedirectResponse($this->container->get('router')->generate('admin_bss_base_business_pmodorder_show', array('id' => $order->getId())));
} else {
$approval->setSecondApprover(NULL);
$approval->setStatus(PmodOrder::STATUS_APPROVED);
$em->persist($approval);
$em->flush();
return new RedirectResponse($this->container->get('router')->generate('admin_bss_base_business_pmodorder_show', array('id' => $order->getId())));
}
}
return $this->render('QiBssFrontendBundle:PmodOrder:order_approve.html.twig', array(
'order' => $order,
'form' => $approveForm->createView(),
));
}
}
What bothers me is the fact that I'm actually suppose to extend from Sonata's CRUDController. And when I do that I get an error;
An exception has been thrown during the rendering of a template
("There is no _sonata_admin defined for the controller
Path\To\Controller\PmodOrderController and the current
route ``")
And I am also aware that I'm actually suppose to use a return like return new RedirectResponse($this->admin->generateUrl('show'));
At this point I don't know what to do anymore. If somebody can please guide me how to extend correctly from CRUDController in my scenario, it would be really appreciated

Here an example, I don't know if it's the best solution but I hope that can help you :
1- Create a custom CRUDcontroller :
# CustomCRUDcontroller.php :
class CustomCRUDDController extends Controller
{
/**
* Show action.
*
* #param int|string|null $id
* #param Request $request
*
* #return Response
*
* #throws NotFoundHttpException If the object does not exist
* #throws AccessDeniedException If access is not granted
*/
public function showAction($id = null)
{
$request = $this->getRequest();
// DO YOUR LOGIC IN THE METHOD, for example :
if(isset($request->get('yourFormParam'))){
$this->doTheJob();
}
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id : %s', $id));
}
$this->admin->checkAccess('show', $object);
$preResponse = $this->preShow($request, $object);
if ($preResponse !== null) {
return $preResponse;
}
$this->admin->setSubject($object);
return $this->render($this->admin->getTemplate('show'), array(
'action' => 'show',
'object' => $object,
'elements' => $this->admin->getShow(),
), null);
}
}
2- Register it in admin.yml :
# admin.yml :
x.admin.x:
class: Namespace\YourAdminClass
arguments: [~, Namespace\Entity, Namespace:CustomCRUD]
tags:
- {name: sonata.admin, manager_type: orm, group: X, label: X}
3- Create your own custom_show.html.twig (just a copy and paste of the original template base_show.html.twig located in the sonata-admin folder), here you can display extra elements to the view :
# custom_show.html.twig :
{% extends base_template %}
{% import 'SonataAdminBundle:CRUD:base_show_macro.html.twig' as show_helper %}
{% block actions %}
{% include 'SonataAdminBundle:CRUD:action_buttons.html.twig' %}
{% endblock %}
{% block tab_menu %}
{{ knp_menu_render(admin.sidemenu(action), {
'currentClass' : 'active',
'template': sonata_admin.adminPool.getTemplate('tab_menu_template')
}, 'twig') }}
{% endblock %}
{% block show %}
<div class="sonata-ba-view">
{{ sonata_block_render_event('sonata.admin.show.top', { 'admin': admin, 'object': object }) }}
{% set has_tab = (admin.showtabs|length == 1 and admin.showtabs|keys[0] != 'default') or admin.showtabs|length > 1 %}
{% if has_tab %}
<div class="nav-tabs-custom">
<ul class="nav nav-tabs" role="tablist">
{% for name, show_tab in admin.showtabs %}
<li{% if loop.first %} class="active"{% endif %}>
<a href="#tab_{{ admin.uniqid }}_{{ loop.index }}" data-toggle="tab">
<i class="fa fa-exclamation-circle has-errors hide"></i>
{{ admin.trans(name, {}, show_tab.translation_domain) }}
</a>
</li>
{% endfor %}
</ul>
<div class="tab-content">
{% for code, show_tab in admin.showtabs %}
<div
class="tab-pane fade{% if loop.first %} in active{% endif %}"
id="tab_{{ admin.uniqid }}_{{ loop.index }}"
>
<div class="box-body container-fluid">
<div class="sonata-ba-collapsed-fields">
{% if show_tab.description != false %}
<p>{{ show_tab.description|raw }}</p>
{% endif %}
{{ show_helper.render_groups(admin, object, elements, show_tab.groups, has_tab) }}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% elseif admin.showtabs is iterable %}
{{ show_helper.render_groups(admin, object, elements, admin.showtabs.default.groups, has_tab) }}
{% endif %}
</div>
{{ sonata_block_render_event('sonata.admin.show.bottom', { 'admin': admin, 'object': object }) }}
{% endblock %}
4- Then indicate to your adminController to display your custom_show template when the current route is "show" (instead of the default template base_show.html.twig) :
# YourEntityAdminController.php :
class YourEntityAdminController extends Controller
{
// allows you to chose your custom showAction template :
public function getTemplate($name){
if ( $name == "show" )
return 'YourBundle:Admin:custom_show.html.twig' ;
return parent::getTemplate($name);
}
}

Related

Symfony forms problem- Variable form does not exist

I am building a crud app using symfony 4. Here is the controller code:
<?php
namespace App\Controller;
use App\Entity\Taskslist;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class TodoController extends Controller{
/**
* #Route("/",name="todo_list")
*/
public function listAction(){
$todos=$this->getDoctrine()->getRepository('App:Taskslist')->findAll();
return $this->render('todo/index.html.twig',array('todos' => $todos));
}
/**
* #Route("/todo/create",name="todo_create")
*/
public function createAction(Request $request){
$todo = new Taskslist;
$form = $this->createFormBuilder($todo)
->add('id', TextType::class)
->add('title', TextType::class)
->add('description', TextareaType::class)
// ->add('priority', ChoiceType::class, array('choices' => array('Low' => 'Low', 'Normal' => 'Normal', 'High'=>'High'), 'attr' => array('class' => 'form-control', 'style' => 'margin-bottom:15px')))
// ->add('status', BooleanType::class, array('attr' => array('class' => 'form-control', 'style' => 'margin-bottom:15px')))
->add('Save', SubmitType::class, array('label'=> 'Create Todo'))
->getForm();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$name = $form['id']->getData();
$category = $form['title']->getData();
$description = $form['description']->getData();
//$due_date = $form['status']->getData();
//$name = $form['name']->getData();
//$now = new\DateTime('now');
$todo->setId($name);
$todo->setTitle($category);
$todo->setDescription($description);
$sn = $this->getDoctrine()->getManager();
$sn -> persist($todo);
$sn -> flush();
return $this->redirectToRoute('todo_list');
}
return $this->render('todos/create.html.twig', array(
'form' => $form->createView()
));
}
/**
* #Route("/todo/edit/{id}",name="todo_edit")
*/
public function editAction($id,Request $request){
return $this->render('todo/edit.html.twig');
}
/**
* #Route("/todo/details/{id}",name="todo_details")
*/
public function detailsAction($id){
return $this->render('todo/details.html.twig');
}
}
And here is the edit.html.twig:
{% extends 'base.html.twig' %}
{% if form is defined %}
{% block body %}
{{form_start(form)}}
{{form_widget(form)}}
{{form_end(form)}}
{% endblock %}
{% endif %}
I followed all the tutorials but got this error when i click the edit button :
variable form does not exist.
Although my code steps into the if statement.
Can you please help me?Thanks in advance.i couldn't find any working solution
I think the issue is here:
{% extends 'base.html.twig' %}
{% if form is defined %}
{% block body %}
{{form_start(form)}}
{{form_widget(form)}}
{{form_end(form)}}
{% endblock %}
{% endif %}
use this instead:
{% extends 'base.html.twig' %}
{% block body %}
{{form_start(form)}}
{{form_widget(form)}}
{{form_end(form)}}
{% endblock %}

Showing different entities in Sonata Admin list view

I've got this entity, which contains entityName property and entityId property:
/**
* #var string
*
* #ORM\Column(name="entityName", type="string", length=255)
*/
private $entityName;
/**
* #var integer
* #ORM\Column(name="entityId", type="integer")
*/
private $entityId;
Instead of showing this entity using __toString() function, I wanted to actually return the entity with name and id. and show that in sonata admin list view.
for now, here is __toString:
public function __toString()
{
return $this->entityName . ":" . $this->entityId;
}
which should return something like:
public function __toString()
{
return $em->getRepository($this->entityName)->find($this->entityId);
}
I hope that I've described my problem well.
tnx
One workaround is to use custom list block for sonata.
create a new twig filter called entityFilter, this filter will convert FQCN of an sonata admin object to a readable route name generated by sonata. like admin_blablabla_show:
public function entityFilter($entityName)
{
$str = str_replace('\\', '_', $entityName);
$str = str_replace('Bundle', '', $str);
$str = str_replace('_Entity', '', $str);
$str = 'Admin' . $str . '_Show';
return strtolower($str);
}
public function getName()
{
return 'my_extension';
}
in you admin class, set the template of the desired field to a new twig template:
->add('orderItems', null, array(
'template' => 'AcmeBundle::order_items_list.html.twig'
))
And in your new twig template (order_items_list.html.twig):
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
<div>
{% for item in object.orderItems %}
{% if entity(item.entityName) == 'admin_first_entity_show' %}
{% set foo = 'Apple ID' %}
{% elseif entity(item.entityName) == 'admin_second_entity_show' %}
{% set foo = 'Device Accessory' %}
{% else %}
{% set foo = 'Not defiend' %}
{% endif %}
<a target="_blank" class="btn btn-default btn-xs" href="{{ path(entity(item.entityName), {'id': item.entityId}) }}"><i class="fa fa-external-link"></i> {{ foo }}</a>
{% endfor %}
</div>
{% endblock %}

Symfony, embedded 5 or 6 entities without relation in the one form

In my case i have 30 entities that there are no relation between them and every entities have two columns. I wanted to have 6 or 7 entities in the one form, but i don't know what is the best way to do it? this is my code ...
this is my Controller:
public function general1Action(Request $request)
{
$example1 = new Example1();
$example2 = new Example2();
$example3 = new Example3();
$example4 = new Example4();
$formexample1 = $this->createForm('...Bundle\Form\Example1Type', $example1);
$formexample2 = $this->createForm('...Bundle\Form\ Example2Type', $example2);
$formexample3 = $this->createForm('...Bundle\Form\ Example3Type', $example3);
$fprmexample4 = $this->createForm('...Bundle\Form\Example4Type', $example4);
$example1->handleRequest($request);
$example2->handleRequest($request);
$example3->handleRequest($request);
$example4->handleRequest($request);
$em = $this->getDoctrine()->getManager();
if ($example1->isSubmitted() && $example1->isValid()) {
/**
* To generate the value example1 first column(e1fc) and second column(e1sc)
*/
$prefix=$this->container->getParameter('prefix');
$e1fcCle=$em->getRepository("...Bundle:Example1")->genereCle('Example1',$prefix);
$example1->sete1fc($e1fcCle);
/**
* To generate the value example1 second column(e1sc)
*/
$example1->sete1sc("e1sc".($e1fcCle));
/**
* to check, the fields are not empty
*/
if(($formexample2["e2sc"]->getData())!=""){
$example2->sete1fc($e1fcCle);
$em->persist($example2);
}
if(($formexample3["e3sc"]->getData())!=""){
$example3->sete1fc($e1fcCle);
$em->persist($example3);
}
if(($formexample4["e4sc"]->getData())!=""){
$example4-> sete1fc($e1fcCle);
$em->persist($example4);
}
$em->persist($example1);
$em->flush();
return $this->forward('...Bundle:General...: general2',
array('E1FC' => $e1fcCle));
}
return $this->render('.../general1.html.twig', array(
'example1' => $example1,
'formExample1' => $formexample1->createView(),
'formExample2' => $formexample2->createView(),
'formExample3' => $formexample3->createView(),
'formExample4' => $formexample4->createView(),
));
}`
and this is my general1.html.twig :
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(formExample1) }}
{{ form_row(formExample2.e2sc) }}
{{ form_row(formExample3.e3sc) }}
{{ form_row(formExample4.e4sc) }}
<input type="submit" value="Next" />
{{ form_widget(formExample1._token) }}
{{ form_end(formExample1, {"render_rest":false}) }}
<ul>
<li>
Back to the list
</li>
</ul>
{% endblock %}`,
I have another question : when i try to use isValid() for the other form like this
if(($formexample2["e2sc"]->getData())!=""&& $example2->isValid())
i have this error: Fatal error: Call to a member function get...() on null

How filter data inside entity object in Symfony 2 and Doctrine

I have two entities: Product and Feature. Product has many other Features (relation one to many). Every Feature has a name and an important status (true if feature is important, false if not). I want to get in TWIG all important features for my product.
Solution below is very ugly:
Product: {{ product.name }}
Important features:
{% for feature in product.features %}
{% if feature.important == true %}
- {{ feature.name }}
{% endif %}
{% endfor %}
So I want to get:
Product: {{ product.name }}
Important features:
{% for feature in product.importantFeatures %}
- {{ feature.name }}
{% endfor %}
I must filter data in entity object, but how?
// MyBundle/Entity/Vehicle.php
class Product {
protected $features; // (oneToMany)
// ...
protected getFeatures() { // default method
return $this->features;
}
protected getImportantFeatures() { // my custom method
// ? what next ?
}
}
// MyBundle/Entity/Feature.php
class Feature {
protected $name; // (string)
protected $important; // (boolean)
// ...
}
You can use Criteria class to filter out the Arraycollection of related features
class Product {
protected $features; // (oneToMany)
// ...
protected getFeatures() { // default method
return $this->features;
}
protected getImportantFeatures() { // my custom method
$criteria = \Doctrine\Common\Collections\Criteria::create()
->where(\Doctrine\Common\Collections\Criteria::expr()->eq("important", true));
return $this->features->matching($criteria);
}
}
In twig
Product: {{ product.name }}
Important features:
{% for feature in product.getImportantFeatures() %}
- {{ feature.name }}
{% endfor %}
You can do it from repository
$featureEntityRepository->findBy(array(
'impoertant' => true,
'product' => $product->getId()
));

symfony form : how to show the parameter in the view, "variable does not exist..."

I'm using a form with 2 classes ("ArticleType" and "ArticleHandler") for my class Article.
I would like to send the id of the article I've just created, but I can't manage to show this id parameter in the view :
In the controller, I send my article's id :
$handler = new ArticleHandler($form, $request, $em);
if ($handler->process()){
return $this->redirect($this->generateUrl('myproject_show', array('id' => $article->getId())) );
}
and in the view, I've got an error with :
{% block body %}
<p>the id :</p>
{{ id }}
{% endblock %}
or entity.id (as in the CRUD) :
Variable "id" does not exist...
Variable "entity.id" does not exist...
Do you know how to fix this?
Thanks
EDIT :
here's my method :
public function addAction()
{
$article = new Article();
$form = $this->createForm(new ArticleType(), $article);
$request = $this->getRequest();
$em = $this->getDoctrine()->getEntityManager();
$handler = new ArticleHandler($form, $request, $em);
if ($handler->process()){
return $this->redirect($this->generateUrl('myproject_show', array('id' => $article->getId())) );
}
return $this->render('ProjBlogBundle:Blog:add.html.twig', array(
'form' => $form->createView(),
));
}
and here's the view :
{% extends "ProjBlogBundle::layout.html.twig" %}
{% block title %}
the title - {{ parent() }}
{% endblock %}
{% block sousbody %}
<p>here's the article i've just created :</p>
{{ id }}
{% endblock %}
EDIT N°2 :
myproject_show:
pattern: /show/{id}
defaults: { _controller: ProjBlogBundle:Blog:show, id:5 }
To use a variable in a template you need to pass it when you render your template:
//ProjBlogBundle:Blog:show
public function showAction($id)
{
return $this->render('ProjBlogBundle:Blog:show.html.twig', array(
'id' => $id
));
}
$this->redirect($this->generateUrl('myproject_show', array('id' => $article->getId())) ); returns only HTTP 302-response without rendering a template, and the browser is redirected to the generated url...

Categories