So I currently have a request sending to a function:
$request->date
and date, in this case, is a string "2019-10-15"
The function is expecting \DateTime $scheduled as the argument this date is going to
How can I properly (using Carbon/laravel) convert this date string to dateTime in this request portion where it's being sent?
In your controller where you need the date, use this:
$date = Carbon::parse($request->date);
You can simply instantiate a DateTime from that date
$scheduled = new \DateTime($request->date);
As Laravel have no casts support inside request object, I would use passedValidation inside the custom request object:
public function rules()
{
return [
'date' => 'required|date',
];
}
protected function prepareForValidation(): void
{
$this->merge([
'date' => Carbon::parse($this->get('date'))
]);
}
public function getDate(): \DateTime
{
return $this->get('date');
}
Getter is optional, I like it just because it defines a type.
In Laravel we have a Request layer where all this stuff should be handled, controllers might get cluttered if we put casting and parsing into them.
This has not been appropriately tested.
I've got an entity with a duration property, declared as time type:
/* #ORM\Column(type="time") */
private $duration;
In my Symfony form, this duration property maps to a time field:
$builder->add(
'duration',
'time',
[
'input' => 'datetime',
'widget' => 'text'
]
)
As the time type does not exist in PHP, both internally use a DateTime object, so they need to add (invent) a date.
My problem is they don't appear to use the same date:
15mn extracted from the database are treated as 1970-01-01 00:15:00
15mn submitted by the form are treated as 2015-06-06 00:15:00 (i.e. today's date)
Consequence: Doctrine always consider the time has changed and always performs an SQL update.
What is the most efficient way to work around this?
i guess you could set the datetime to 1970-01-01 00:00:00 in the constructor of your entity
public function __construct()
{
$this->duration = new \DateTime('1970-01-01 00:00:00');
}
The solution lies in the setDuration() setter, as both Doctrine and Symfony use it to set the duration. Just don't update the whole DateTime object, only its time part:
public function setDuration(\DateTime $duration)
{
$this->duration->setTime(
$duration->format('G'),
$duration->format('i'),
$duration->format('s')
);
return $this;
}
I have an app using symfony 2.2 that has a file upload form, and another 'reportbuilder' form.
The issue I'm having is that when any of my input field values begin with a 'c', that letter is dropped. So if the field is submitted with a value of 'cat', the value after binding will become 'at'.
This only appears to occur with strings that begin with 'c'
This issue happens on any strings on any of my forms
The issue happens during the $form->bind() method (or somewhere therein - I've verified that the post variables are correct up to that point)
This issue occurs on my PREPROD box, but not my DEV box (environments SHOULD be the same, haven't done an extensive comparison to find a needle in a haystack ... both Redhat installs)
Based on some searching I've done, I suspect it may have something to do with character encoding (which I have attempted to compare between environments), but I'm somewhat at a loss.
I can provide some code if it helps, though since the issue is only occurring on one server and not another, I'm not sure which (if any) of the symfony code will help.
Does any of this stand out as a rookie encoding oversight or something like that?
Edit: This happens with any number of leading 'c's, so 'cat' and 'ccat' and 'Ccccccat' will all be converted to 'at'
Edit2: I'm able to manually set the text field from the post variable after bind as a workaround( $document->setName($postvars['name']) ). It becomes more of an issue with the 'Reportbuilder' form, which has a variable number of nested forms (report has one or more tabs, tabs have one or more extra columns, etc) - so a similar workaround is clunky and not ideal
Edit3: Adding code
class DefaultController extends Controller
{
public function indexAction()
{
...
$document = new Document();
$form = $this->createForm(new DocumentType($em,$user), $document);
/// Here the name variable in the request is 'cat', as expected
$form->bind($this->getRequest());
/// Here the value of the 'name' parameter in the form is 'at'
...
}
}
document.orm.yml:
Finance\GstBundle\Entity\Document:
type: entity
manyToOne:
report:
targetEntity: Report
mappedBy: report
user:
targetEntity: Finance\UserBundle\Entity\User
mappedBy: user
oneToMany:
doctabs:
targetEntity: Doctab
mappedBy: document
tabgroups:
targetEntity: Tabgroup
mappedBy: document
table: document
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
name:
type: string
length: 255
nullable: true
path:
type: string
length: 255
... and the DocumentType definition:
namespace Finance\GstBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class DocumentType extends AbstractType
{
protected $em;
protected $user;
public function __construct($em=null,$user=null){
$this->em = $em;
$this->user = $user;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('report','entity',array(
'class' => 'FinanceGstBundle:Report',
'property'=>'name',
'empty_value' => '--Choose a Template--'
))
->add('name')
->add('file')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Finance\GstBundle\Entity\Document'
));
}
public function getName()
{
return 'finance_gstbundle_documenttype';
}
}
After patching RHEL on all 3 (dev/pre/prod) boxes, the issue has resolved itself. I'm doing a post-mortem to attempt to identify the specific package that caused the bug we were experiencing.
Our versions of RHEL were in different 'states' on the different servers. PRE/PROD were signifigantly further behind in system patches than Dev.
Problem: where 'cat' becomes 'at'?
1 Disable javascript to make sure it doesn't update your name value on submit.
2 Make sure the value submitted is 'cat'
$form->get('name')->getData(); //should return 'cat'
Note: it's mentioned you've done this in your post but we don't see the code.
3 You might have a listener somewhere that reacts to POST_BIND:
$builder->addEventListener(FormEvents::POST_BIND, $yourListener);
4 Maybe your using a data transformer, and there is something wrong with the way you transform() and reverseTransform(). A good way to debug would be to check your ViewModel in the NormModel and ModelData.
4 Maybe you have a lifecycle callback
/** #PrePersist */
public function doStuffOnPrePersist()
{
$this->name = 'changed from prePersist callback!';
}
Is there an easy way to set a default value for text form field?
you can set the default value with empty_data
$builder->add('myField', 'number', ['empty_data' => 'Default value'])
Can be use during the creation easily with :
->add('myfield', 'text', array(
'label' => 'Field',
'empty_data' => 'Default value'
))
I've contemplated this a few times in the past so thought I'd jot down the different ideas I've had / used. Something might be of use, but none are "perfect" Symfony2 solutions.
Constructor
In the Entity you can do $this->setBar('default value'); but this is called every time you load the entity (db or not) and is a bit messy. It does however work for every field type as you can create dates or whatever else you need.
If statements within get's
I wouldn't, but you could.
return ( ! $this->hasFoo() ) ? 'default' : $this->foo;
Factory / instance.
Call a static function / secondary class which provides you a default Entity pre-populated with data. E.g.
function getFactory() {
$obj = new static();
$obj->setBar('foo');
$obj->setFoo('bar');
return $obj;
}
Not really ideal given you'll have to maintain this function if you add extra fields, but it does mean you're separating the data setters / default and that which is generated from the db. Similarly you can have multiple getFactories should you want different defaulted data.
Extended / Reflection entities
Create a extending Entity (e.g. FooCreate extends Foo) which gives you the defaulted data at create time (through the constructor). Similar to the Factory / instance idea just a different approach - I prefer static methods personally.
Set Data before build form
In the constructors / service, you know if you have a new entity or if it was populated from the db. It's plausible therefore to call set data on the different fields when you grab a new entity. E.g.
if( ! $entity->isFromDB() ) {
$entity->setBar('default');
$entity->setDate( date('Y-m-d');
...
}
$form = $this->createForm(...)
Form Events
When you create the form you set default data when creating the fields. You override this use PreSetData event listener. The problem with this is that you're duplicating the form workload / duplicating code and making it harder to maintain / understand.
Extended forms
Similar to Form events, but you call the different type depending on if it's a db / new entity. By this I mean you have FooType which defines your edit form, BarType extends FooType this and sets all the data to the fields. In your controller you then simply choose which form type to instigate. This sucks if you have a custom theme though and like events, creates too much maintenance for my liking.
Twig
You can create your own theme and default the data using the value option too when you do it on a per-field basis. There is nothing stopping you wrapping this into a form theme either should you wish to keep your templates clean and the form reusable. e.g.
form_widget(form.foo, {attr: { value : default } });
JS
It'd be trivial to populate the form with a JS function if the fields are empty. You could do something with placeholders for example. This is a bad, bad idea though.
Forms as a service
For one of the big form based projects I did, I created a service which generated all the forms, did all the processing etc. This was because the forms were to be used across multiple controllers in multiple environments and whilst the forms were generated / handled in the same way, they were displayed / interacted with differently (e.g. error handling, redirections etc). The beauty of this approach was that you can default data, do everything you need, handle errors generically etc and it's all encapsulated in one place.
Conclusion
As I see it, you'll run into the same issue time and time again - where is the defaulted data to live?
If you store it at db/doctrine level what happens if you don't want to store the default every time?
If you store it at Entity level what happens if you want to re-use that entity elsewhere without any data in it?
If you store it at Entity Level and add a new field, do you want the previous versions to have that default value upon editing? Same goes for the default in the DB...
If you store it at the form level, is that obvious when you come to maintain the code later?
If it's in the constructor what happens if you use the form in multiple places?
If you push it to JS level then you've gone too far - the data shouldn't be in the view never mind JS (and we're ignoring compatibility, rendering errors etc)
The service is great if like me you're using it in multiple places, but it's overkill for a simple add / edit form on one site...
To that end, I've approached the problem differently each time. For example, a signup form "newsletter" option is easily (and logically) set in the constructor just before creating the form. When I was building forms collections which were linked together (e.g. which radio buttons in different form types linked together) then I've used Event Listeners. When I've built a more complicated entity (e.g. one which required children or lots of defaulted data) I've used a function (e.g. 'getFactory') to create it element as I need it.
I don't think there is one "right" approach as every time I've had this requirement it's been slightly different.
Good luck! I hope I've given you some food for thought at any rate and didn't ramble too much ;)
If you need to set default value and your form relates to the entity, then you should use following approach:
// buildForm() method
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
...
->add(
'myField',
'text',
array(
'data' => isset($options['data']) ? $options['data']->getMyField() : 'my default value'
)
);
}
Otherwise, myField always will be set to default value, instead of getting value from entity.
You can set the default for related field in your model class (in mapping definition or set the value yourself).
Furthermore, FormBuilder gives you a chance to set initial values with setData() method. Form builder is passed to the createForm() method of your form class.
Also, check this link: http://symfony.com/doc/current/book/forms.html#using-a-form-without-a-class
If your form is bound to an entity, just set the default value on the entity itself using the construct method:
public function __construct()
{
$this->field = 'default value';
}
Approach 1 (from http://www.cranespud.com/blog/dead-simple-default-values-on-symfony2-forms/)
Simply set the default value in your entity, either in the variable declaration or the constructor:
class Entity {
private $color = '#0000FF';
...
}
or
class Entity {
private $color;
public function __construct(){
$this->color = '#0000FF';
...
}
...
}
Approach 2 from a comment in the above link, and also Dmitriy's answer (not the accepted one) from How to set default value for form field in Symfony2?
Add the default value to the data attribute when adding the field with the FormBuilder, adapted from Dmitriy's answer.
Note that this assumes that the property will and will only have the value null when it's a new, and not an existing, entity.
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('color', 'text', array(
'label' => 'Color:',
'data' => (isset($options['data']) && $options['data']->getColor() !== null) ? $options['data']->getColor() : '#0000FF'
)
);
}
A general solution for any case/approach, mainly by using a form without a class or when we need access to any services to set the default value:
// src/Form/Extension/DefaultFormTypeExtension.php
class DefaultFormTypeExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (null !== $options['default']) {
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($options) {
if (null === $event->getData()) {
$event->setData($options['default']);
}
}
);
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('default', null);
}
public function getExtendedType()
{
return FormType::class;
}
}
and register the form extension:
app.form_type_extension:
class: App\Form\Extension\DefaultFormTypeExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
After that, we can use default option in any form field:
$formBuilder->add('user', null, array('default' => $this->getUser()));
$formBuilder->add('foo', null, array('default' => 'bar'));
Don't use:
'data' => 'Default value'
Read here: https://symfony.com/doc/current/reference/forms/types/form.html#data
"The data option always overrides the value taken from the domain data (object) when rendering. This means the object value is also overriden when the form edits an already persisted object, causing it to lose its persisted value when the form is submitted."
Use the following:
Lets say, for this example, you have an Entity Foo, and there is a field "active" (in this example is CheckBoxType, but process is the same to every other type), which you want to be checked by default
In your FooFormType class add:
...
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
...
public function buildForm( FormBuilderInterface $builder, array $options )
{
...
$builder->add('active', CheckboxType::class, array(
'label' => 'Active',
));
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event){
$foo = $event->getData();
// Set Active to true (checked) if form is "create new" ($foo->active = null)
if(is_null($foo->getActive())) $foo->setActive(true);
}
);
}
public function configureOptions( OptionsResolver $resolver )
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle:Foo',
));
}
You can set a default value, e.g. for the form message, like this:
$defaultData = array('message' => 'Type your message here');
$form = $this->createFormBuilder($defaultData)
->add('name', 'text')
->add('email', 'email')
->add('message', 'textarea')
->add('send', 'submit')
->getForm();
In case your form is mapped to an Entity, you can go like this (e.g. default username):
$user = new User();
$user->setUsername('John Doe');
$form = $this->createFormBuilder($user)
->add('username')
->getForm();
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
if ($data == null) {
$form->add('position', IntegerType::class, array('data' => 0));
}
});
My solution:
$defaultvalue = $options['data']->getMyField();
$builder->add('myField', 'number', array(
'data' => !empty($defaultvalue) ? $options['data']->getMyField() : 0
)) ;
Just so I understand the problem.
You want to adjust the way the form is built based on data in your entity. If the entity is being created then use some default value. If the entity is existing use the database value.
Personally, I think #MolecularMans's solution is the way to go. I would actually set the default values in the constructor or in the property statement. But you don't seem to like that approach.
Instead you can follow this: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html
You hang a listener on your form type and you can then examine your entity and adjust the builder->add statements accordingly based on havine a new or existing entity. You still need to specify your default values somewhere though you could just code them in your listener. Or pass them into the form type.
Seems like a lot of work though. Better to just pass the entity to the form with it's default values already set.
If you're using a FormBuilder in symfony 2.7 to generate the form, you can also pass the initial data to the createFormBuilder method of the Controler
$values = array(
'name' => "Bob"
);
$formBuilder = $this->createFormBuilder($values);
$formBuilder->add('name', 'text');
If you set 'data' in your creation form, this value will be not modified when edit your entity.
My solution is :
public function buildForm(FormBuilderInterface $builder, array $options) {
// In my example, data is an associated array
$data = $builder->getData();
$builder->add('myfield', 'text', array(
'label' => 'Field',
'data' => array_key_exits('myfield', $data) ? $data['myfield'] : 'Default value',
));
}
Bye.
Often, for init default values of form i use fixtures. Of cause this way is not easiest, but very comfortable.
Example:
class LoadSurgeonPlanData implements FixtureInterface
{
public function load(ObjectManager $manager)
{
$surgeonPlan = new SurgeonPlan();
$surgeonPlan->setName('Free trial');
$surgeonPlan->setPrice(0);
$surgeonPlan->setDelayWorkHours(0);
$surgeonPlan->setSlug('free');
$manager->persist($surgeonPlan);
$manager->flush();
}
}
Yet, symfony type field have the option data.
Example
$builder->add('token', 'hidden', array(
'data' => 'abcdef',
));
There is a very simple way, you can set defaults as here :
$defaults = array('sortby' => $sortby,'category' => $category,'page' => 1);
$form = $this->formfactory->createBuilder('form', $defaults)
->add('sortby','choice')
->add('category','choice')
->add('page','hidden')
->getForm();
Default values are set by configuring corresponding entity. Before binding the entity to form set its color field to "#0000FF":
// controller action
$project = new Project();
$project->setColor('#0000FF');
$form = $this->createForm(new ProjectType(), $project);
If that field is bound to an entity (is a property of that entity) you can just set a default value for it.
An example:
public function getMyField() {
if (is_null($this->MyField)) {
$this->setMyField('my default value');
}
return $this->MyField;
}
I usually just set the default value for specific field in my entity:
/**
* #var int
* #ORM\Column(type="integer", nullable=true)
*/
protected $development_time = 0;
This will work for new records or if just updating existing ones.
As Brian asked:
empty_data appears to only set the field to 1 when it is submitted with no value.
What about when you want the form to default to displaying 1 in the
input when no value is present?
you can set the default value with empty_value
$builder->add('myField', 'number', ['empty_value' => 'Default value'])
I solved this problem, by adding value in attr:
->add('projectDeliveringInDays', null, [
'attr' => [
'min'=>'1',
'value'=>'1'
]
])
As a longtime procedural programmer, I finally made the switch to OOP/MVC in combination with Yii. I don't regret it at all, but I have a question which may be obvious.
In my model, generated by GII, I define rules and aliasses replacing values. Now I want the 'currency' value to only allow input specified in the aliasses. Ofcourse I can do something like is_number, greater that 0 and smaller than 4, but in that case I will have to update my code all the time when a new currency is added. Is there an easier way to do the input validation based on the defined values?
<?PHP
class Affiliateprograms extends CActiveRecord
{
//define rules
public function rules()
{
return array(array('currency', 'required'));
}
//set aliases
public static function itemAlias($type,$code=NULL)
{
$_items = array('currency' => array('1' => 'US Dollar','2' => 'Euro','3' => 'Yen'));
if (isset($code))
return isset($_items[$type][$code]) ? $_items[$type][$code] : false;
else
return isset($_items[$type]) ? $_items[$type] : false;
}
?>
You can use the builtin in validator (actually a CRangeValidator).
array('currency','in','range'=>array_keys(self::itemAlias('???')));
You need to insert the right $type of course.
Side note: Please reconsider your indenation style - it's pretty unusual and makes your code hard to read ;)