I don't get one thing with forms and I guess with HTML5. I do build a form-text like this:
->add(
'subject', 'text', array(
'constraints' => array(new MaxLength(array('limit' => 5, 'message' => 'subject.maxlength'))),
'required' => true,
'attr'=>array('oninvalid'=> "setCustomValidity('". $this->get('translator')->trans('subject.oninvalid',array(), 'validators') ."')")
))
1.) Problem - form:
If I don't give any input into subject, the HTML-subject.oninvalid is coming up. When I reach the limit 5 because of MaxLength I send this form and I render an extra field error_messages, what doesn't look like subject.oninvalid. Why does symfony do pop up two different ways of errors? Why doesn't subject.maxlength is not popping up like subject.oninvalid?
2.) Problem - translation:
I do have a validators.en.xliff file with plural entries, what is working. Symfony is expecting by subject.maxlength plural translation. Why? How can I pass the %count% like http://symfony.com/doc/2.0/book/translation.html#explicit-interval-pluralization in 'message' => 'subject.maxlength'?
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
Let's say I've got a FooType with BarType (collection; cascade validation setted to true) which itself has a FooBarType (collection; cascade validation setted to true)
So
//FooType
$builder
[...]
->add('bar', 'collection', [
//...
'type' => new BarType()
'error_bubbling' => false,
'allow_add' => true,
'allow_delete' => true,
'required' => false,
'by_reference' => false,
]
);
//BarType
$builder
[...]
->add('fooBar', 'collection', [
//...
'type' => new FooBarType()
'error_bubbling' => false,
'allow_add' => true,
'allow_delete' => true,
'required' => false,
'by_reference' => false,
]
);
Now the problem is, if I add from GUI one element to fooBar collection that's invalid due to Valid constraints, the error is attached to WHOLE collection (and not to collection's element field) and the element claim to be valid (saw from profiler). If I add from GUI more than one element to fooBar collection is still invalid, invalid status (error) is still attached to collection, first element still claims to be valid but other ones has error attached (that is what I desire).
Question
Why such behavior? Any ideas?
Additional info
This is the invalid path shown in profiler
Object(Symfony\Component\Form\Form).data.bar[0].foobar[0].fieldName =
null
that got the actual value, but is "tied" to collection and not to field element.
Symfony version: 2.7
Solution was pretty "easy" but hard to find.
I asked to myself if it was better to delete the question or to answer here directly. As I'm pretty sure that this error could happen also to other people and "investigation" isn't so easy, I've decided to answer indeed.
Mistake
Mistake was onto collection prototype javascript and its indexes. I'm talking about
http://symfony.com/doc/current/cookbook/form/form_collections.html
addTagForm() javascript function, that in my case was customized in order to reach what I need. The problem was I made a mistake onto index calculation so every new collection was starting from 1 instead of 0.
Thus, when form validation component tried to attach error to right field, producing this
Object(Symfony\Component\Form\Form).data.bar[0].foobar[0].fieldName =
null
no field (at foobar level) with index = 0 was find and error was attached to its parent (the collection).
So when I added more then one element to collection, every error I saw was relative to "other" field (in a n-1 fashion).
How i spot this error
Using profiler > form , onto left column you can spot all form fields with error attached. Observing indexes you can find what I'm talking about.
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.
I googled a lot trying to find an answer for my problem, but no luck.
I have two tables in two different db db1.pages and db2.layouts I have defined the two entity managers and I can successfully manage the two as unrelated entities.
My problem is on table db1.pages, I have a layout_id column which contains a reference to the db2.layouts.id column, I didn't created an association between the two because, from my understanding, is not supported by doctrine. I successfully managed to display a selection list of possible layouts on the PageType using the following code:
$builder
->add('name', null, array('label' => 'Name'))
->add('label', null, array('label' => 'Title'))
->add('home', null, array('label' => 'Homepage'))
->add('layoutId', 'entity', array(
'class' => 'USGMCMSBundle:Layouts',
'property' => 'label',
'label' => 'Layout',
))
but it doesn't select the correct layout matched by the value stored into layoutId. Is there a way to select the current selected item and keep it in sync when the user choose a different one?
Maybe using an EventSubscriber? but on which stage? Or is there a better solution?
Hope I made myself clear (sorry for my english)
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.