Overriding SensioGeneratorBundle's twig template: trying to translate the delete button - symfony

I'm trying to override Resources/crud/actions/delete.php.twig from SensioGeneratorBundle.
I created this file located at app/Resources/SensioGeneratorBundle/skeleton/crud/actions/delete.php.twig:
{% extends "#SensioGenerator/Resources/crud/actions/delete.php.twig" %}
{% block form %}
/**
* Creates a form to delete a {{ entity }} entity by id.
*
* #param mixed $id The entity id
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm($id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('{{ route_name_prefix }}_delete', array('id' => $id)))
->setMethod('DELETE')
->add('submit', 'submit', array('label' => 'Supprimer'))
->getForm()
;
}
{% endblock form %}
Problem is that #SensioGenerator is not recognized:
There are no registered paths for namespace "SensioGenerator" in "crud/controller.php.twig" at line 58.
I tried to manually register the namespace in app/config/config.yml:
twig:
[...]
paths:
"%kernel.root_dir%/../vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle/": SensioGenerator
But still not working. Idea?

Why you didn't do it like in docs ? SensioGeneratorDocs
{% extends "skeleton/crud/actions/delete.php.twig" %}
{% block form %}
/**
* Creates a form to delete a {{ entity }} entity by id.
*
* #param mixed $id The entity id
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm($id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('{{ route_name_prefix }}_delete', array('id' => $id)))
->add('submit', 'submit', array('label' => 'Supprimer'))
->getForm()
;
}
{% endblock form %}

If you're using a project that's based-on or is similar-to the Symfony Standard Edition, then the SensioGeneratorBundle is only loaded for the dev environment.
This is configured in both the composer.json and the AppKernel.
So, make sure your execution is using the dev environment or alter these configurations so that the bundle is always loaded.

Related

Symfony 4. ManyToMany association with attribute. Custom form to insert attribute value, how do I?

A Symfony 4 project. I am not able to see ho to code the following spec.
Say I have two entities Alpha and Beta.
When I create an Alpha object alpha, I wish to associate (ManyToMany) to it one or more Beta objects. I know how to render new and edit Forms to do so.
I wish to enrich the AlphaBeta join table with an attribute, say the Cost to associate a Beta to an Alpha. The issue is that I am not able to enrich the forms above as to insert or edit a Cost value, when I create a Alpha object and associate to it a Beta object.
Is there a standard way to code a situation of this kind?
I read that the way to go is to have two OneToMany associations Alpha->AlphaBeta, and Beta->AlphaBeta, but even doing so I am not able to define/render a Form for Alpha as to create a new Alpha object, to associate to it a Beta object (or more) and to assign to such association a Cost value.
Your advise is very welcome. Thanks
Generally, if the relation/association itself should have an attribute, then the many-to-many mapping in doctrine isn't sufficient anymore, since associations can't store additional data. So you correctly note, that you need an extra AlphaBeta entity/class that holds the data. (As already posted by Ali Kazemi, there is a tutorial for this)
But you wonder about how the cost field can be added to your form...
Since the cost is part of the AlphaBeta entity/class in your case, the form field should be in a form type AlphaBetaType that - probably depending on the options provided - should render an AlphaType and/or BetaType sub form, and a cost form field. Custom form themeing can display it in a way, that it doesn't appear as if it was a subform, if that is a concern. (However, it should be noted that custom form theming can be annoying at times...)
In general, the form structure/hierarchy usually very much resembles the entity structure/hierarchy. And only sporadically hiding data or mapping/transforming it to be displayed or handled differently.
Alternatively you can add an unmapped form field and later store that in your AlphaBeta, but that on average isn't simpler, since it involves "manual" handling.
It seems I did find a way, indeed it is pretty straightforward Synfony 4 code.
Hope it could be useful to somebody.
(note: I used php bin/console make:entity/crud/form to write the needed scripts. Below how I needed to modify the code I got from make)
So, say I have Alpha and Beta entities.
I wish to have a form to create a new Alpha object, to associate to it one or more Beta objects, and to fill in a Cost value for each Alpha-Beta association. I want a edit form too.
First I create a new AlphaBeta entity, whose fields are:
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Alpha", inversedBy="alphabetas", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
private $alpha;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Beta", inversedBy="betaalphas", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
private $beta;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $cost;
Within class Alpha, I need
/**
* #ORM\OneToMany(targetEntity="App\Entity\AlphaBeta", mappedBy="alpha", orphanRemoval=true, cascade={"persist"})
*/
private $alphabetas;
with usual 'getAlphaBeta, addAlphaBeta and removeAlphaBeta methods. (similarly for Beta)
I create usual CRUD controllers for the new AlphaBeta entity. To have an AlphaBeta Form which could be used as a subform too, I define
class `AlphaBetaEmbeddedForm` extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder ->add('Beta', EntityType::class, array(
'class' => Beta::class,
'multiple' => false,
'expanded' => true,
'choice_label' => 'betaTitle' ))
->add('cost', TextType::class);
if(empty($options['remove_alpha_field'])) {
$builder->add('Alpha', EntityType::class, array(
'class' => Alpha::class,
'multiple' => false,
'expanded' => true,
'choice_label' => 'alphaTitle'
));}}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => AlphaBeta::class,
'remove_alpha_field' => false,
]);
}}
remove_alpha_field is the trick that let me use the form above as a subform within a form to create an Alpha object:
class AlphaType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('alphaTitle')
->add('AlphaBetas', CollectionType::class, array(
'entry_type' => AlphaBetaEmbeddedForm ::class,
'entry_options' => ['label' => true, 'remove_alpha_field' => true],
'allow_add' => true,
'label' => 'Betas',
'by_reference' => false
));}
To render the subforms within the main form, I need some JS, as suggested here, to be inserted within the new.html.twig and edit.html.twig templates for Alpha:
{% block javascripts %} <script type="text/javascript">
jQuery(document).ready(function () {
$("#add-another-collection-widget").click(function(e){
var list = jQuery(jQuery(this).attr('data-list-selector'));
var counter = list.data('widget-counter') | list.children().length;
var newWidget = list.attr('data-prototype');
newWidget = newWidget.replace(/__name__/g, counter);
counter++;
list.data('widget-counter', counter);
var newElem = jQuery(list.attr('data-widget-tags')).html(newWidget);
newElem.appendTo(list);
});});</script>{% endblock %}
To apply the latter, it seems you need to write each single row in the main form template _form.html.twig:
{{ form_start(form) }}
<div class="my-custom-class-for-errors">
{{ form_errors(form) }}
</div>
<div id="alphaTitle">
{{ form_row(form.alphaTitle) }}
</div>
{% if form.AlphaBetas %}
<b>Betas</b> </br></br>
{% for Beta in form.AlphaBetas %}
{% for BetaField in Beta %}
{{ form_row(BetaField) }}
{% endfor %}
{% endfor %}
{% endif %}
<div id="AlphaBeta-fields-list"
data-prototype="{{ form_widget(form.AlphaBetas.vars.prototype)|e }}"
data-widget-tags="{{ '<p></p>' |e }}"
data-widget-counter="{{ form.children|length }}">
{% for AlphaBetaField in form.AlphaBetas.vars.prototype.children %}
{{ form_row(AlphaBetaField) }}
{% endfor %}
</div>
<button type="button" id="add-another-collection-widget" data-list-selector="#AlphaBeta-fields-list">Insert Beta</button>
</br>
<button class="btn">{{ button_label|default('Save') }}</button>
{{ form_end(form) }}
That's all.
Note: within the main form to edit an Alpha object, I would like to insert a delete buttons for each Beta associated object. As much as I can see, it is not possible, since it would mean to insert a html form within an html form. Since I may delete a AlphaBeta association by the corresponding AlphaBeta delete action, it is not a big deal.
If you see how I can improve my code, you are welcome to advise me.

What is the better solution to get the Entity I want in a biderectional OneToMany relation?

I have one entity Article and an other entity Image with a bidrectional relation OneToMany and ManyToOne :
class Article
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Image", mappedBy="article")
*/
private $images;
}
class Image
{
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Article", inversedBy="images")
* #ORM\JoinColumn(nullable=true)
*/
private $article;
}
In my controller I use #paramconverter to get the article I want :
/**
* #Route("/blog/{slug}", name="article")
* #ParamConverter("article", class="AppBundle:Article")
*/
public function articleAction(Article $article)
{
return $this->render('default/article.html.twig', array(
'article' => $article,
));
}
Now my problem is that I want to identify the ONLY image with the attributes "main = true" in all the "article.images" I have.
What is the best solution?
In my wiew I can do somehting like this but it's not the best I think :
{% for image in article.images %}
{% if image.main %}
<img src="{{ asset( image.src ) }}" alt="{{ image.alt }}" title="{{ image.title }}">
{% endif %}
{% endfor %}
I'd like to use something like :
{{ article.mainImg }}
How can I achieve this please? And is this the best solution?
Doctrine provides a collection filter mechanism you could use to get the "main image":
public function articleAction(Article $article)
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq("main", true))
->setMaxResults(1);
$mainImg = $article->getImages()->matching($criteria)->first();
return $this->render('default/article.html.twig', array(
'article' => $article,
'mainImg' => $mainImg
));
}
More information on filtering doctrine collections: Filtering collections
I did not test the code myself, but it should convey the idea of how it can be done.

Details list in symfony2, from two bundles

I have little in the development of Symfony and I have a question: To show a list with details of two Bundles.
My project have a Owner, but can have 1 o more consultants. The Entity has been related but when the results are listed only can view the user id. How can see the user name?
I will attach a image with my entities.
Thanks,
View
Entities
I need show the name of the differents consultants in a project. These are users too. I have already related the owner of the project, but this consultant is a field in my entity.
Attachment images to reference, and the php and twig files content. See the image: Database relation, and view.
UserProjectController
public function showAction(UserProject $userProject, User $user) {
$id = $userProject->getId();
$em = $this->getDoctrine()->getManager();
$project = $em->getRepository('ProjectBundle:Project')->find($id);
$userProjects = $em->getRepository('ProjectBundle:UserProject')->findby(array('idproject' => $id, 'status' => '1'));
if (empty($userProjects)) {
return $this->redirectToRoute('userproject_new');
}
$users = $em->getRepository('UserBundle:User')->findby(array('id' => 1));
return $this->render('ProjectBundle:Userproject:show.html.twig', array(
'userProjects' => $userProjects, 'project' => $project, 'User' => $user,
));
}
Entity User
/**
* #ORM\OneToMany(targetEntity="ProjectBundle\Entity\UserProject", mappedBy="user")
*/
protected $uproject;
Entity UserProject
/**
* #ORM\ManyToOne(targetEntity="ProjectBundle\Entity\Project", inversedBy="userproject")
* #ORM\JoinColumn(name="project_id", referencedColumnName="id")
* #Assert\NotBlank()
*/
protected $idproject;
/**
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="uproject")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* #Assert\NotBlank()
*/
protected $user;
Entity Project
/**
* #ORM\OneToMany(targetEntity="ProjectBundle\Entity\UserProject", mappedBy="idproject")
*/
protected $userproject;
View show.html.twig
{% for userProject in userProjects %}
<br>
<dl>
<dt><span class="text-primary">{{'User_id'|trans}}</span></dt>
<dd>
{{ userProject.user }}
</dd>
<br>
<dt><span class="text-primary">{{'Consultor_id'|trans}}</span></dt>
<dd>
{{ userProject.consultorId }}
</dd>
<br>
<dt><span class="text-primary">{{'Status'|trans}}</span></dt>
<dd>
{% if userProject.status == 1 %}
<span class="text-success">{% trans %}Enabled{% endtrans %}</span>
{% elseif userProject.status == 0 %}
<span class="text-danger">{% trans %}Disabled{% endtrans %}</span>
{% endif %}
</dd>
<br>
</dl>
{% endfor %}

Sonata admin tabs in page

I use Sonata admin Generator.
I'd like to create multiple lists from a symfony class.
For example I have a list of invoices and I would like to create a tab with paid bills, an other tab with pending bills and a last tab with invoices disabled.
This status is in the class.
I saw this page(admin/admin) in the Sonata demo who use context but I wouldn't like install mediabundle if it's possible.
It is possible without the mediabundle.
I had the same use-case as you with 'project-statuses'. These statuses are in my database.
A few steps are necessary:
Override the CRUDController. Maybe this step isn't needed, but I couldn't figure out how to without. I want to display the different statuses as tabs, so I inject a collection in the list-template.
Make your own CRUDController:
namespace Your\OwnBundle\Controller;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
class ProjectCrudController extends Controller
{
/**
* {#inheritdoc}
*
* #param Request $request
*/
public function render($view, array $parameters = array(), Response $response = null, Request $request = null)
{
$projectStatusRepo = $this->getDoctrine()->getRepository('EvinceObjectsBundle:ProjectStatus');
// here inject the params you'll need
// you can do it only when $parameters['action'] == 'list' if you want
$parameters['projectStatuses'] = $projectStatusRepo->findAll();
$parameters['activeProjectStatus'] = $request->get('status', 1);
return parent::render($view, $parameters, $response);
}
}
Inject your own CRUDController in the services.yml (or xml)
sonata.admin.project:
class: Your\OwnBundle\Admin\ProjectAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "project", label: "Project" }
arguments:
- ~
- Your\OwnBundle\Entity\Project
- YourOwnBundle:ProjectCrud
calls:
- [ setLabelTranslatorStrategy, ["#sonata.admin.label.strategy.underscore"]]
- [ setTemplate, [list, YourOwnBundle:ProjectAdmin:list.html.twig]]
Notice the setTemplate-call, so lets create your template
Create your own list-template
{% extends 'SonataAdminBundle:CRUD:base_list.html.twig' %}
{% block preview %}
<ul class="nav nav-pills">
<li><a><strong>{{ "label.select_projectstatus"|trans({}, 'SonataProjectBundle') }}</strong></a></li>
{% for projectStatus in projectStatuses %}
{% set active = false %}
{% if projectStatus.id == activeProjectStatus %}
{% set active = true %}
{% endif %}
<li class="{% if active %}active{% endif %}" >{{ projectStatus.status }}</li>
{% endfor %}
</ul>
{% endblock %}
Override the Admin::getFilterParameters function in your own admin class. Here you want to set the filter based on your requestparam:
/**
* {#inheritdoc}
*/
public function getFilterParameters()
{
$parameters = parent::getFilterParameters();
return array_merge(array(
'status' => array(
'type' => '',
'value' => $this->getRequest()->get('status', 1),
)
), $parameters);
}

How can I use two property on form_widget

I can show form by this codes.
$builder->add('icon', 'entity', array(
'class' => 'UserBundle:IconPics',
'property' => ‘label', 'expanded' => true, 'multiple' => false,
));
in twig
{{ form_label(form.icon) }}
{{ form_widget(form.icon) }}
There appeares radiobutton labeled 'PictureA' ,'PictureB','PictureC'....
But I want to use not only 'label' property but also 'pic' entity as well to make
the link to jpg file.
How can I use two property via one form_widget?
My code are below.
I have tables such as
in User.php
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\IconPics", inversedBy="icon")
* #ORM\JoinColumn(name="icon", referencedColumnName="id",nullable=true)
*/
private $icon;
in Icon.php
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $label;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $pic;
/** * * #ORM\OneToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="icon")
* #ORM\JoinColumn(name="icon", referencedColumnName="id")
*/
private $icon;
icon table is like
|id |pic |label
|1 |aaa.png |pictureA
|2 |bbb.png |pictureB
|3 |ccc.png |PictureC
This might not be the most elegant solution but it's a quick one. The proper solution would probably be writing a custom field type which would involve writing a lot of code.
There is a simple trick to achieve what you want. Just add a method to your entity that will be used to get both values at once:
public function getFormViewData()
{
return $this->getLabel() . ';' . $this->getPicture();
}
Then use this method in the property attribute:
$builder->add('icon', 'entity', array(
// ...
'property' => 'formViewData',
));
Finally use the twig split filter to separate get the two values in your template (see the example) and adapt your template (i.e. by overriding the form_label widget ) to use these instead of the original value.
{#
inside the overriden widget set the label correctly before rendering it
and extract the picture url.
#}
{% set label = label|split(';')|first|trans %}
{% set picture = label|split(';')|last %}
{{ label }}
{{ picture }}
Got the idea?

Resources