I have overridden the Sensio Generator Bundle for CRUD in order to better suit my needs.
What I would like to do is to be able to loop through the entity fields.
It is done by default in show.html.twig but not in new and edit views.
When I implement the same logic in new.html.twig.twig it doesn't work though it does for edit.html.twig.twig.
{#app/Resources/SensioGeneratorBundle/skeleton/crud/views/new.html.twig.twig#}
{% for field, metadata in fields %}
{% if field not in 'id' %}
{{ '{{ form_row(edit_form.' ~ field ~ ')}}' }}
{% endif %}
{% endfor %}
When running the generator, the error is: Variable "fields" does not exist in "crud/views/new.html.twig.twig" at line 9
Ok, in fact it is an issue in Sensio Generator Bundle.
In the file: sensio\generator-bundle\Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator.php the generateNewView function is missing a paramter. It is not passing the fields as opposed to generateShowView.
Here is the comparison:
protected function generateNewView($dir)
{
$this->renderFile('crud/views/new.html.twig.twig', $dir.'/new.html.twig', array(
'bundle' => $this->bundle->getName(),
'entity' => $this->entity,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'actions' => $this->actions,
));
}
versus
protected function generateShowView($dir)
{
$this->renderFile('crud/views/show.html.twig.twig', $dir.'/show.html.twig', array(
'bundle' => $this->bundle->getName(),
'entity' => $this->entity,
'fields' => $this->metadata->fieldMappings,
'actions' => $this->actions,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
));
}
I'll try to post this as an improvement.
Related
With Sonata, when I create a contract with a choiceType, the user can choose contract1 or contract2 and in my database I would get "451" for contract1 and "678" for contract2.
In my Field List all my data are displayed but for my contract I've got either "451" or "678" and I would like instead of those numbers, contract1 or contract2.
This is my field for creating the contract :
$mapper
->add('contract', ChoiceType::class, [
'choices' => [
'contract1' => '451',
'contract2' => '678',
],
])
And in my code for the field, I don't know how to tell it if 451 then 'contract1'. I started like that :
->add('contract', null, [
'label' => 'Contract',
])
Any idea ?
You can use the form entity type which would solve your problem:
$builder->add('contract', EntityType::class, [
// looks for choices from this entity
'class' => Contract::class,
// uses the Contrzct.name property as the visible option string
'choice_label' => 'name',
// Query builder to select your to specific contract
'query_builder' => function (ContractRepositoty $contractRepository) {
return $contractRepository->createQueryBuilder('support_time_slot')
->where('contract.id in :ids')
->setParameter('ids', [461,678])
->orderBy('contract.name');
},
// used to render a select box, check boxes or radios
'multiple' => true,
'expanded' => true,
]);
I found a solution. I created a specific template and in it I translated the value I wanted:
->add('contract', null, [
'label' => 'Contract',
'template' => 'AdminBundle:ContractAdmin:list__operation.html.twig'
])
And my Twig :
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
{% if value %}
{{ ('contract.operation.'~value~'.value')|trans }}
{% endif %}
{% endblock %}
I would like in the form builder controller to say this field belongs to this group then in the view/theme organize all of group1 into div1, group2 into div2, etc..
I tried something like this (creating sub forms) but that is not working; I am not able to display the group
$builder->add(
$builder->create('group1', FormType::class, array('inherit_data' => true))
->add('brand', TextType::class, array(
'label' => 'brand',
'invalid_message' => 'Enter a brand',
))
);
twig
{{ form_row(form.group1) }}
In case someone else is looking; my error was that I forgot to loop through
{% for row in form.group1 %}
{{ form_row(row) }}
{% endfor %}
What is best practice to include another info in form from another entity?
I have Student Entity, Group Entity, StudentsGroup and Attendance Entity.
Student have id, code, and name.
Group have id and name.
StudentsGroup have id, group_id, and student_id.
Attendance have id, students_group_id, date and status.
Group can have many Student which saved in StudentsGroup. Why I make StudentsGroup? Because actually 1 Group can have some sub groups like SubjectsGroup etc. And Attendance save student information by StudentsGroup Id which same student can have different students_group_id.
Now, the problem is : How to show Student information in collection form of attendance?
All Entity relationship is declared as object, so actually we can access em freely from any entity. But I don't know how to do that in form. Here my form :
<?php
/* Collection Form */
$tanggal = new \DateTime($request->request->get('sifo_adminbundle_studentsgrouping')['tanggal']);
$attendances = new StudentsGrouping();
foreach ($entities as $temp) {
$entity = new Attendance();
$entity = $em->getRepository('SifoAdminBundle:Attendance')->findOneBy(array('studentsGrouping' => $temp, 'date' => $tanggal));
if ($entity){
$attendances->getAttendances()->add($entity);
}
}
$form = $this->createCollectionForm($attendances, $id, $tanggal);
return $this->render('SifoAdminBundle:DftAbsensi:manage.html.twig', array(
'form' => $form->createView(),
));
This is how I render it in twig :
{{ form_start(form_collection) }}
{{ form_row(form_collection.tanggal) }}
{% for attendance in form_collection.attendances %}
{{ form_row(attendance.status) }}
{% endfor %}
{{ form_end(form_collection) }}
## Concept ##
I'm thinking about creating entity and pass it into form like this :
foreach ($entities as $temp) {
$entity = new Student();
$entity = $em->getRepository('SifoAdminBundle:Student')->find($temp->getId());
if ($entity){
$entities[i] = $entity;
}
$i++
}
and then in twig show it like this :
{{ form_start(form_collection) }}
{{ form_row(form_collection.tanggal) }}
{% for key, attendance in form_collection.attendances %}
{{ entities[key].code }}
{{ entities[key].name }}
{{ form_row(attendance.status) }}
{% endfor %}
{{ form_end(form_collection) }}
But I feel not comfort with this. Am I really need to make new entity just for showing name and code from Student Entity? Is there a best practice to do this?
You can use entity field.
$builder->add('users', 'entity', array(
'class' => 'AcmeHelloBundle:User',
'multiple' => true, /* you can choose more than one */
'mapped' => false, /* if you are using the form with an entity */
'query_builder' => function(EntityRepository $er) {
/* use query builder to get correct results */
return $er->createQueryBuilder('u')->orderBy('u.username', 'ASC');
},
));
There are two important keys in above.
'multiple' => true ----- you can able to choose more than one
'mapped' => false ----- if you are using form with an entity, your form will automatically looks for a connection between these entities and if can not found, throws exception. to avoid of that problem you should set this option to false
http://symfony.com/doc/current/reference/forms/types/entity.html
There is the following code
$form->with('Item')->add('parent', null, array(
'label' => 'Category',
'required' => true,
'query_builder' =>
function($er) use ($id) {
$qb = $er->createQueryBuilder('p');
if ($id){
$qb->where('p.id <> :id')
->setParameter('id', $id);
}
$qb->orderBy('p.root, p.lft', 'ASC');
return $qb;
}
.........
Result is the entity-objects collection which is given to the string (__toString method).
It return name-field.
But I need get the another field - url.
How to get url value instead the name in the select-list form?
The query_builder type return object => how to change this form that it works likewise query_builder?
I didn't work with SonataAdminBundle forms, but I think that it works absolutely like symfony forms. All that you need here is to add 'class' and 'property' values to your options list:
$form->with('Item')->add('parent', null, array(
'class' => 'Acme\DemoBundle\Entity\Category',
'property' => 'url',
'label' => 'Category',
'required' => true,
'query_builder' =>
function($er) use ($id) {
$qb = $er->createQueryBuilder('p');
if ($id){
$qb->where('p.id <> :id')
->setParameter('id', $id);
}
$qb->orderBy('p.root, p.lft', 'ASC');
return $qb;
}
property - is the name of the field in your entity that will represent your Entity's value instead of calling __toString().
But also... If you need always represent your Entity as URL you can simply override __toString() method in the Entity class to something like that:
public function __toString() {
return $this->url;
}
In my case, ->orderBy() used in query_builder worked, but was overwritten for unknown reasons "somewhere" later. Fun fact: when I used extended => true, everything was rendered like sorted in the query_builder. But by using extended => false my <option>s are re-sorted before select2 touched it.
As a workaround I did this:
config/packages/twig.yaml
twig:
paths:
'%kernel.project_dir%/templates': '%kernel.project_dir%/templates'
# This is to prevent a infinite loop when extending the parent template
'vendor/sonata-project/admin-bundle/src/Resources/views': SonataAdminBundleOriginal
And then I done the sorting I wanted again in twig for the project entity:
templates/bundles/SonataAdminBundle/Form/form_admin_fields.html.twig:
{% extends '#SonataAdminBundleOriginal/Form/form_admin_fields.html.twig' %}
{%- block choice_widget_options -%}
{% if name == 'project' %}
{% set options = options|sort((a, b) => a.data.numberSortable <=> b.data.numberSortable)|reverse %}
{% endif %}
{{ parent() }}
{%- endblock choice_widget_options -%}
Just in case if someone needs to "filter" an entity by a FK (foreign key) just like i needed (and searched for 3 days), here is the solution:
In the __construct you can set/find what you need. In my case i work this "sessions" (year). So i find the session->status=true:
$this->sessionActive = $this->sessionRepository->findOneBy(['status'=>true]);
Then in the QueryBuilder:
protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
{
$rootAlias = current($query->getRootAliases());
$query
->select($rootAlias)
->where($rootAlias.'.session = :id')
->addOrderBy($rootAlias.'.dateStart', 'ASC')
->setParameter('id', $this->sessionActive->getId());
return $query;
}
Note: I have always one Session->status set to "true". If you might have null values don't forget to make a condition so that it doesn't give you an error!
Regards.
Im new to Symfony / Twig and am having problems passing object values to my twig templates.
Here is some of my controller code that shows the content of the object:
$prevArticles = $section->getArticles();
print_r($prevArticles);
die()
Displays:
Array
(
[0] => Imagine\NewsletterBundle\Entity\Article Object
(
[id:protected] =>
[title:protected] =>
[headline:protected] =>
[link:protected] =>
[image:protected] =>
[excerpt:protected] =>
[check:protected] =>
[attachment:protected] =>
[field1:protected] =>
[field2:protected] =>
[field3:protected] =>
[magazines:protected] =>
[top_logo_advert:protected] => /uploaded_images/cece0b1859ea2b1af95f1f274620ba77.jpg
[top_logo_alt:protected] => Picture of blomange
[top_logo_link:protected] => www.google.com
)
)
So then I pass my object to my twig template like so:
return $this->render('ImagineNewsletterBundle:Section:'.$builder->getTemplate(), array('prevArticles' => $prevArticles));
Then in my twig template I want to display the value of 'top_logo_advert' but its not working:
{% for article in prevArticles %}
{{ article.top_logo_advert }}
{% endfor %}
I get the error message:
Method "top_logo_advert" for object "Imagine\NewsletterBundle\Entity\Article" does not exist in ImagineNewsletterBundle:Section:build_advert.html.twig at line 62
You must access it via :
{{ article.topLogoAdvert }} or {{ article.getTopLogoAdvert() }}
Both solutions works. Next time, just reminder that properties like 'my_property_1' is converted into myProperty1 in the twig engine.