I am using Symfony 6 and created an entity for global options and so the sql table has only 3 columns : name, label and value
In order to store global info about my app. Like this.
id
name
label
value
1
maintenance
Set the app in maintenance
0
2
allow_comments
Allow comments for products
1
How can I make a form which contains every elements of the table ?
I would like the result to be like this : Expected form result
Thanks
As you have a table (so it's an entity I guess) CollectionType is a way to go.
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
// ...
$builder->add('emails', CollectionType::class, [
// each entry in the array will be an "email" field
'entry_type' => EmailType::class,
// these options are passed to each "email" type
'entry_options' => [
'attr' => ['class' => 'email-box'],
],
]);
As the entry_type you can just pass EntityType.
Ref: https://symfony.com/doc/current/reference/forms/types/collection.html
Related
While I was trying to show pictures I made this pleasant discovery in backpack:
$this->crud->addFields([
[ // Upload
'name' => 'pictures', //<-- this is an Entity
'label' => 'Photos',
'type' => 'upload_multiple',
'upload' => true,
'disk' => 'uploads'
]
]);
This fragment of code gives me this:
My questions is:
What am I doing wrong?
How can get the value 'file' of this two vectors? I need this for display the images.
You probably forgot to add 'photos' to the cast array.
Based on the documentation:
Step 3. Since the filenames are stored in the database as a JSON array, we're going to use attribute casting on your model, so every time we get the filenames array from the database it's converted from a JSON array to a PHP array:
protected $casts = [
'photos' => 'array'
];
https://laravel-backpack.readme.io/docs/crud-fields#upload_multiple
In a Symfony 2.4+ project, what is the best way to register an array of values for a field that saves as an integer but needs to display human readable values in a template?
I have an entity with a property that is populated with integer values that represent different constant values:
/**
* The repetition frequency for billing cycle:
* #ORM\Column(type="smallint")
*/
protected $repetition = 0;
I would like to store the names of these values somewhere, so initially I put them in my entity with a getter:
protected $repetitionName = array(
0 => 'Setup',
1 => 'Second',
2 => 'Minute',
3 => 'Hour',
4 => 'Day',
5 => 'Week',
6 => 'Month',
7 => 'Year'
);
public function getRepetitionName() {
return $this->repetitionName;
}
This seems like a great central repository for the values.
Then in my twig template I don't want to display the integer, I want the corresponding name value. So I translate them like this:
<div class="billingCycle">{{ entity.repetitionName[entity.repetition] }}</div>
And in my form builder I make a field that references that array like this:
$builder->add('repetition', 'choice', array(
'label' => 'Billing Cycle',
'help' => 'The repetition frequency when this service is billed.',
'choices' => $builder->getData()->getRepetitionNamePer(),
// default to monthly (the most common)
'empty_data' => 6,
'required' => TRUE
));
The Problems With This Approach
1. Translation: If I want to translate this at any point, it's hard coded in one place.
2. Reusability: If I have other entities that have repetition (e.g. event calendar) it would be nice to reuse this.
3. Configurability: Ideally these would be editable in a config file instead of the entity code.
Alternative Solution: Custom Form Type as Global Service with Config Parameters
The better option seems to set some default parameters in the config file:
parameters:
gutensite_component.options.status:
0: Inactive
1: Active
gutensite_component.options.repetition:
0: Setup
1: Second
2: Minute
3: Hour
4: Day
5: Week
6: Month
7: Year
Then create a custom form type Gutensite\ComponentBundle\FormType\RepetitionType that loads the options from the config parameters. See the Documentation for a great example of this. Then just refer to that field type like this:
$builder->add('repetition', 'repetition', array(
'label' => 'Billing Cycle',
'help' => 'The repetition frequency when this service is billed.',
// default to monthly (the most common)
'empty_data' => 6,
'required' => TRUE
));
The inconvenient part of this solution, is that you have to either parameter to twig in your config (which is bloat for every template even if you don't need it), or always remember to manually pass the parameters to twig from your controller.
// I add to the standard object `$controller->view` which gets passed to Twig
$controller->view->options['repetition'] = $this->container->getParameter('gutensite_component.options.repetition');
To be accessed like:
<div class="priceValue label label-primary">${{ entity.price }}/<span class="billingCycle">{{ view.options.repetition[entity.repetition] }}</span></div>
This is more clunky than I would like but it is reusable. Maybe others have better solutions to pass the configuration to twig than what is represented here.
Other Suggestions?
Do you have any other suggestions, best practices or lessons learned? Please share.
What I would do is to save those values as objects of a new entity and set a manyToOne relation from your main entity (or any other entity as your calendar event entity) to the repetition entity
If so, you can easily add new repetitions every time you want or get all objects of the main entity using any specific repetition.
Also you can build a nice form for a new object of the main entity with an input (human readable) of repetition values:
$builder->add('repetition', 'entity', array(
'class' => 'AcmeDefaultBundle:Repetition', // This is your new 'repetition entity'
'property' => 'repetitionName', // The field of your 'repetition entity' that stores the name (the one that will show a human readable value instead of the id)
'expanded' => true, // True if you prefer checkbox instead of a dropdown list
'label' => 'Billing Cycle',
'help' => 'The repetition frequency when this service is billed.',
'required' => true,
));
Now when you want to show the name of the repetition in twig you will use:
<div class="billingCycle">{{ entity.Repetition.repetitionName }}</div> (you can also translate that value later)
A cleaner way (if you use it only for this entity) is to define a public method in your entity that returns you that array:
public function getRepetitionNames() {
return array(
0 => 'Setup',
1 => 'Second',
2 => 'Minute',
3 => 'Hour',
4 => 'Day',
5 => 'Week',
6 => 'Month',
7 => 'Year'
);
}
Another method to retrieve the label based by your "repetition" field value:
/**
* #param $key
* #return null
*/
function getRepetitionLabel(){
$repetitions = $this->getRepetitionNames();
return isset($repetitions[$this->repetition]) ? $repetitions[$this->repetition] : 0;
}
Will be much easier to access it in twig now:
<div class="billingCycle">{{ entity.repetitionLabel }}</div>
Last, in your form use your entity to retrieve the values:
$object = $builder->getData();
$repetitionChoices = $object->getRepetitionNames();
$builder->add('repetition', 'choice', array(
'label' => 'Billing Cycle',
'help' => 'The repetition frequency when this service is billed.',
'choices' => $repetitionChoices
'empty_data' => 6,
'required' => TRUE
));
If you plan to use it in more entities, you could use a interface.
This is an existing schema I'm working with and I'm trying to not make any changes for the time being. I have an entity property that represents a university semester, like "fall12", "spring11", etc.
When adding or editing this entity with a form, I want to split that property into two form fields: "Season" (fall or spring") and "Year" (2011, 2012, etc):
...
->add('formSemesterSeason', 'choice', array(
'label' => 'Season',
'mapped' => false,
'choices' => array('fall' => 'Fall', 'spring' => 'Spring'),
))
->add('formSemesterYear', 'choice', array(
'label' => 'Year',
'mapped' => false,
'choices' => $this->courseManager->getYearChoices(),
))
...
When submitting the form, the values need to be combined and saved to the "semester" property on the entity as a string
When viewing the edit form, the existing "semester" value needs to be split between the two.
I don't think data transformers help here, since they apply to transforming just one form item.
Right now I'm using a form event POST_SET_DATA to fill out the two form fields when editing an existing entity:
$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) use ($course_manager) {
$course = $event->getData();
$form = $event->getForm();
$semester = $course->getSemester();
$parsed_semester = $course_manager->parseSemesterMachineName($semester);
$form->get('formSemesterSeason')->setData($parsed_semester->season);
$form->get('formSemesterYear')->setData($parsed_semester->yearFull);
});
This works well, but how do I then combine the values back after the form has been submitted? I can do it easily in the controller, but I think I should be able to use form events, and have the data manipulation take place before form validation.
You can combine them back in a POST_SUBMIT listener.
The best way (reuseable) would be to create your own custom form type with a data transformer to split/combine the fields internally.
There are "recipes" in the cookbook but the best way that I found to create it was to rip apart the DateTime field type and associated transformers (DataTransformerChain, DateTimeToArrayTransformer & ArrayToPartsTransformer) for parts and build my own from that.
I have a problem, I want to create a simple search form with a filter assembly. These filters are attributes that belong to attribute groups.
group 1
[] Attribute 1
[] Attribute 2
[] Attribute 3
group 2
[] Attribute 1
[] Attribute 2
[] Attribute 3
But the problem is that I can not do (graphic aspect)
$builder->add('attribut', 'entity', array(
'class' => 'RestoFrontBundle:Attribut',
'group_by' => 'groupeAttribut.id',
'expanded' => true,
'multiple' => true,
'query_builder' => function(AttributRepository $er) {
return $er->createQueryBuilder('a')
->join("a.groupeAttribut", 'g')
->where("a.statut = 1");
}
))
->getForm();
Also I can not manage the game if the checkbox has been checked.
You note that the graphic aspect is the hard part. That is due to the way checkboxes are used in HTML. Unlike select inputs there is no notion of an optgroup. The closest analog for checkboxes would be a fieldset with a legend.
You may want explore using a Choice Field type rather than an entity type. Provide your choices via some provider function within which you format the options array(s) whether or not you retrieved them from a database. ( For the record, that's exactly how I populate the select at http://stayattache.com which has multiple places to retrieve options from. ) You may even want to explore creating your own form field type and template to format the output. Checkout the documentation on creating your own field type.
I hope that helps a bit. There are probably plenty of other ways to approach this as well.
This is such a trivial problem that I can't believe I couldn't find an answer.
Symfony 2, doctrine 2.1. I've got two entities and one intermediate entity (join table). User, Pref, and UsersPrefs. Pref table is dictionary table, so that I could change pref name in one place only. Ok, let's see the picture:
infographic http://dl.dropbox.com/u/22495762/infographic.png
As You can see, I want to have a checkbox group, with all the possible choices (prefs) and preferred choices checked. So, if there are 3 prefs, and only 2 selected by the user, there should be 3 checkboxes, 2 selected.
It's simple, if done plain PHP - query database twice to get list of all prefs and user prefs, render checkboxes depending on values, add some actions to handle form submit, done.
But for the life of God I can't get this to work using symfony & doctrine. I was able to get to the point where I can update relationships in doctrine and further in database, but I'm using raw query values for that:
$data = $request->request->get('some_form');
and this supposedly isn't the way it should be done?
Morevoer, I'm completely stuck as to how should I display checkbox list. I either get list of all options, none checked, or only user options, all checked. Or 'left joined' result set with checkboxes for all cases, useless anyway.
I've come to the point where I tried to overload twig checkbox template, but I couldn't pass variables to the form template, and that was the last straw...
EDIT:
This way I'm getting group of checkboxes, not connected to user choices:
->add('prefs', 'entity', array(
'class' => 'Some\TestBundle\Entity\Pref',
'expanded' => 'true',
'multiple' => 'true',
'property' => 'name'
))
And this way I'm getting only user choices, all checked:
->add('prefs', 'entity', array(
'class' => 'Some\TestBundle\Entity\UserPrefs',
'multiple' => 'false',
'expanded' => 'false',
'property' => 'pref.name',
'query_builder' => function(EntityRepository $er) use ($id) {
return $er->createQueryBuilder('u')
->where("u.user = :id")
->setParameter('id', $id)
;
},
))
And I tried left joins and other options, but at best I could get list of all possible options for all possible users, checked accordingly.
EDIT (POSSIBLE SOLUTION):
I'm displaying checkbox group:
->add('pref_ids', 'choice', array(
'choices' => array(
'1' => 'pref one',
'2' => 'pref two',
'3' => 'pref three',
),
'expanded' => 'true',
'multiple' => 'true'
))
I've added $pref_ids array in User entity. Now I just need to set values in array according to preferences chosen by user:
public function setPrefIds()
{
$prefs = $this->getPrefs();
$this->pref_ids = array();
foreach($prefs as $pref){
array_push($this->pref_ids, $pref->getPref()->getId());
}
return $this;
}
This way I get appropriate checkboxes checked.
Writing values to database is reversal of the process. I'm getting input values from request:
$data = $request->request->get('edit_form');
var_dump($data['pref_ids']);
Removing all user prefs:
foreach ($userPrefs as $pref){
$em->remove($pref);
}
And setting actual associations in doctrine from ids:
$entity->setPrefsById($em, $data['pref_ids']);
Here I'm passing entity manager to entity itself, but I need to refactor it, because it looks kinda messy this way.
Then $em->flush(); and that's it.
That's the best I could come up with. Probably it's overcomplicated and should be done entirely different way. Unfortunately couldn't figure out this "other way".
You need the choice field type: http://symfony.com/doc/current/reference/forms/types/choice.html
In your builder, it will be something like
$builder->add('prefs', 'choice', array('multiple' => true, 'expanded' => true, 'choices' => fetch the available prefs from the database here);
Edit: Sorry I'm mistaken, you need the "Entity" type, which fetches automatically the choices from the database: http://symfony.com/doc/current/reference/forms/types/entity.html
You must still put multiple => true expanded => true to get checkboxes.