I create my form like this:
$siteContent2Form = $this->get('form.factory')->createNamedBuilder('cont_form_2', CKEditorType::class, $siteContent2, array(
'label' => false,
'config_name' => 'ckeditor_config_std',
))->getForm();
in twig:
{{ form_start(form_content_2) }}
{{ form_widget(form_content_2) }}
{{ form_end(form_content_2) }}
Is it possible to override the loaded config "ckeditor_config_std" and load another one in the template (twig)?
I don't think you can do this without creating a Twig extension. Here's what I found out:
I followed Symfony's tutorial to build a simple form with three fields. The task field is a CKEditor field:
$form = $this->createFormBuilder($task)
->add('task', CKEditorType::class, [
'config_name' => 'my_config',
'config' => [
'uiColor' => '#c0ffee',
],
])
->add('dueDate', DateType::class)
->add('save', SubmitType::class, ['label' => 'Create task'])
->getForm();
Let's see what we have in Twig:
{{ dump(form) }}
FormView {#398 ▼
+vars: array:24 [▶]
+parent: null
+children: array:3 [▼
"task" => FormView {#403 ▶}
"dueDate" => FormView {#405 ▶}
"save" => FormView {#327 ▶}
]
-rendered: false
-methodRendered: false
}
See where the configuration options are (the uiColor config comes from PHP as seen above whereas the toolbar config comes from YAML configuration):
FormView {#398 ▼
+children: array:3 [▼
"task" => FormView {#403 ▼
+vars: array:41 [▼
"config" => array:2 [▼
"toolbar" => array:1 [▶]
"uiColor" => "#c0ffee"
]
]
}
]
}
The FormView class doesn't seem to provide any methods for changing the vars: https://api.symfony.com/3.4/Symfony/Component/Form/FormView.html
You can neither use the merge filter because the FormView object will be converted into an array, and then you can't render the form:
{% set form = form|merge({}) %}
{{ dump(form) }}
array:3 [▼
"task" => FormView {#403 ▶}
"dueDate" => FormView {#405 ▶}
"save" => FormView {#327 ▶}
]
You could create a simple Twig extension, something like this:
<?php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction('set_ckeditor_config', [$this, 'SetCKEditorConfig']),
];
}
public function SetCKEditorConfig($formView, $name, $value)
{
$formView->vars['config'][$name] = $value;
}
}
And then in Twig:
{{ dump(form.task.vars.config) }}
{% do set_ckeditor_config(form.task, 'uiColor', '#bada55') %}
{% do set_ckeditor_config(form.task, 'height', 500) %}
{{ dump(form.task.vars.config) }}
{# You should render the form *after* calling the set_ckeditor_config function #}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
array:2 [▼
"toolbar" => array:1 [▶]
"uiColor" => "#c0ffee"
]
array:3 [▼
"toolbar" => array:1 [▶]
"uiColor" => "#bada55"
"height" => 500
]
As you can see, this way you can modify single configuration options. This is not exactly what you asked, but it's close, right?
You can modify SetCKEditorConfig so that you set multiple configs at once. Then you would need to call set_ckeditor_config in Twig only once. That might be better if there are many configs that you want to change.
You can also try to modify the code so that you can choose different config_name (i.e. something else than ckeditor_config_std in your case). It seemed to be a more complex thing to do, so I gave up.
I hope this leads you on the right track.
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'm running Symfony 3.4 LTS and I have an entity Attribute:
<?php
class Attribute
{
private $id;
private $code;
private $value;
private $active;
// My getters and setters
Below the database table:
I would like to get, in a form, all the rows with code == 'productstatus'. I tried:
<?php
$attributes = $this->getDoctrine()->getRepository(Attribute::class)->findBy(['code' => 'productstatus']);
// $attributes contains array with 3 objects 'Attribute'
$form = $this->createFormBuilder($attributes);
$form->add('value', TextType::class);
$output = $form->getForm()->createView();
If I dump() the $output var in Twig:
... I'm unable to make a loop to display the 3 fields with values.
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Result :
My goal is to allow the user to edit all the values of a specific attributes in the same form (or multiple forms, but in the same page). I already tried with CollectionType without success.
I found a solution: create 2 nested forms and use CollectionType. Hope it helps.
<?php
// Controller
$attributes = $this->getDoctrine()->getRepository(Attribute::class)->findBy(['code' => 'productstatus']);
$form = $this->createForm(AttributeForm::class, ['attributes' => $attributes]);
// AttributeForm
$builder
->add('attributes', CollectionType::class, [
'entry_type' => AttributeValueForm::class,
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
]);
// AttributeValueForm
$builder
->add('value', TextType::class, ['label' => false, 'required' => false])
->add('active', CheckboxType::class, ['label' => false, 'required' => false])
// Twig view
{{ form_start(form) }}
{% for attribute in form.attributes.children %}
<tr>
<td>{{ form_widget(attribute.value) }}</td>
<td>{{ form_widget(attribute.active) }}</td>
</tr>
{% endfor %}
{{ form_end(form) }}
You an use an if statement in the your AttributeType like in the example below:
$builder
->add('entree', EntityType::class, array('class'=>'cantineBundle:plat',
'choice_label'=>function($plat){
if($plat->getType()=='Entree'&&$plat->getStatus()=="non reserve"){
return $plat->getNomPlat();
}
},
'multiple'=>false)
);
In my project (symfony/flex 1.1) i had a method "displayMenuAndSubMenu([...])" (as a service) to build menu and submenus on a multidimensionnal array.
NB: Some menus may not have submenus
My problem is that when I want to render my menu board in a html.twig template,I can not reproduce the incremental variables $i and $a. Indeed, with my actually twig code I get a simple string output :-(
I have read somewhere that a custom twig extension will allow me to work around this difficulty.
But I find it very complicated to implement a simple counter.
So my questions are:
Is there a simpler way to do this in a html.twig template ?
Do I have to go through in a php.twig template (so disgustin in my sense) ?
Am I doomed to define a twig extension ? (And if so, do you have an example ?)
Thanks
ContentService::displayMenuAndSubmenu
public function displayMenuAndSubMenu(CategoryService $categoryService, CategoryTypeService $categoryTypeService)
{
$categoryId = $categoryService->getCategoryId('Primary');
$categoryTypeId = $categoryTypeService->getCategoryTypeId('Menu');
// Getting primary menus
$primaryMenu = $this->em->getRepository('App\Entity\Content')->findByCategoryTypeAndCategory($categoryTypeId, $categoryId);
// Constructing MainMenu with menus and submenus
$i = 0;
$menu = [];
$submenu = [];
$mainMenu = [];
foreach ($primaryMenu as $keyMenu => $menuItem) {
$menu[$i] = [ 'menu_' . $i => [
'id' => $menuItem->getId(),
'name' => $menuItem->getName(),
'body' => $menuItem->getBody(),
'slug' => $menuItem->getSlug(),
]
];
// Getting secondary menu
$secondaryMenu = $this->em->getRepository('App\Entity\Content')->findByParent($menuItem->getId());
$a = 0;
foreach ($secondaryMenu as $keySubmenu => $subMenuItem) {
$submenu[$i][$a] = [ 'submenu_' . $i . '_' . $a => [
'id' => $subMenuItem->getId(),
'name' => $subMenuItem->getName(),
'body' => $subMenuItem->getBody(),
'slug' => $subMenuItem->getSlug(),
]
];
$menu[$i] += $submenu[$i][$a];
$a++;
}
$mainMenu += $menu;
$i++;
}
return $mainMenu;
}
_menu.html.twig
{% set i = 0 %}
{% for menu in menu_items %}
{% set a = 0 %}
<div class="center">
<section class="primary-menu">
{% if 'menu_'~i~'.slug' is not empty %}
<header class="enter">{{ 'menu_'~i~'.name' }}</header>
{% for submenu in 'menu_'~i~'.submenu' %}
<article>
{{ 'submenu_'~i~'_'~a~'.name' }}
</article>
{% endfor %}
{% else %}
<header class="enter">{{ 'menu_'~i~'.name' }}</header>
{% endif %}
</section>
{% set i = i + 1 %}
{% endfor %}
dump($mainMenu) on ContentService class output
array:5 [▼
0 => array:4 [▼
"menu_0" => array:4 [▼
"id" => "a420742f-124a-11e9-a1fd-805e4fe8b43b"
"name" => "Menu 0"
"body" => "Primary menu0"
"slug" => null
]
"submenu_0_0" => array:4 [▼
"id" => "a4208ec3-124a-11e9-a1fd-805e4fe8b43b"
"name" => "Submenu 0"
"body" => "Secondary menu0"
"slug" => "menu-0-submenu-0"
]
"submenu_0_1" => array:4 [▼
"id" => "a420a70d-124a-11e9-a1fd-805e4fe8b43b"
"name" => "Submenu 1"
"body" => "Secondary menu1"
"slug" => "menu-0-submenu-1"
]
"submenu_0_2" => array:4 [▼
"id" => "a420ba8f-124a-11e9-a1fd-805e4fe8b43b"
"name" => "Submenu 2"
"body" => "Secondary menu2"
"slug" => "menu-0-submenu-2"
]
]
1 => array:1 [▼
"menu_1" => array:4 [▼
"id" => "a4205c9f-124a-11e9-a1fd-805e4fe8b43b"
"name" => "Home"
"body" => "Home menu"
"slug" => "home"
]
]
2 => array:4 [▶]
3 => array:4 [▶]
4 => array:4 [▶]
]
I am trying to pull in a repository into a form in Symfony 3.4 and then use the chosen option when the form is submitted.
Here's the form code:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('text', TextareaType::class, [
'label' => 'Text'
])
->add('category', EntityType::class, [
'class' => Category::class,
'choice_label' => 'name',
'query_builder' => function(CategoryRepository $repo) {
return $repo->createQueryBuilder('c')
->groupBy('c.name');
}
])
->add('subcategory', EntityType::class, [
'class' => Category::class,
'choice_label' => 'subcategory',
'query_builder' => function(CategoryRepository $repo) {
return $repo->createQueryBuilder('c')
->groupBy('c.subcategory');
}
]);
}
With this I can render the form and it looks good. I can choose the various options in the CategoryRepository.
{% block body %}
{{ form_start(form) }}
{{ form_label(form.name) }}
{{ form_errors(form.name) }}
{{ form_widget(form.name) }}
{{ form_label(form.subcategory) }}
{{ form_errors(form.subcategory) }}
{{ form_widget(form.subcategory) }}
{{ form_end(form) }}
On submit, when checking with Xdebug, the category is the object Category. I can see the correct values present (those chosen in the drop down of the form) but I want just the string, e.g. category.name. How do I do that?
Also, it might need a different question, but when I select one of the categories, I'd like the subcategory to be updated to exclude those that don't belong to that chosen category. I realise this may require jquery.
I'm certain there are better ways, but one solution would be to
$category = $data->getCategory();
$data->setCategory($category->getName());
$data->setSubcategory($category->getSubcategory());
And to exclude the subcategories I use jQuery / JavaScript to retrieve the filtered results from a controller / repository, then remove or add those options in the HTML.
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.