I have dynamic number of questions and answers. I use DynamicModel() for collecting these data.
$attributes = [
"answer_1" => "Some Value 1",
"answer_2" => "Some Value 2",
"answer_3" => "Some Value 3",
.....
]
$customFormModel = new DynamicModel($attributes);
I also have
$labels = [
"answer_1" => "Label 1",
"answer_2" => "Label 2",
"answer_3" => "Label 3",
.....
]
I just can't get the $label array to be set as Label Attributes for my Dynamic Model.
My $customFormModel->attributeLabels() is always empty.
These results with the following issue.
I get error messages like these
Answer 1 can not be blank
Answer 2 can not be blank
Answer 3 can not be blank
What I want instead
Label 1 can not be blank
Label 2 can not be blank
Label 3 can not be blank
Try this
$customFormAttributes = array_map(function ($key) use ($attributes) {
return $attributes[$key];
}, array_flip($labels));
$customFormModel = new DynamicModel($customFormAttributes);
It looks like you misuse the DynamicModel concept here. This should be used for ad hoc validation and not to handle the form presentation as well. If you need to add labels use regular Model instead.
You can extend DynamicModel and add attributeLabels() there and use your new method instead. But what is the point of using DynamicModel when you can just create regular model with everything ready to use?
For custom attribute label you can add ActiveField widget method label()
<?= $form->field($customFormModel, 'attribute')->label('...') ?>
or static equivalent from Html class Html::label(), Html::activeLabel().
you can simply extend DynamicModel like this :
class MyDynamicModel extends DynamicModel
{
public $attributeLabels = [];
public function attributeLabels(){
return $this->attributeLabels;
}
}
now you can set $attributeLabels as array
Related
I am trying to add a new key-value pair to the already loaded JSON Array. I am adding the new key-value pair to customize the header column cells in react bootstrap table but getting the below errors. Can any one please help?
'Columns' in the below state is where I wanted to add new key-value pair
state = {
data: MYResult.Products || [],
actualData: MYResult.Products || [],
columns: MYResult.ParametricList_Attributes || [],
isCompareClicked: false,
isDisabled: true,
selected: []
};
This is how I am adding the key-value pair -
componentDidMount(){
checkbox = (column, colIndex) => {
return (
<h5>{ column.text }<checkbox/></h5>
);
}
console.log(this.state.columns) ;
newColumn = this.state.columns.map((column) => {
return {...column, headerFormatter: checkbox};
});
this.setState({columns: newColumn });
}
Full code here - https://codesandbox.io/s/o1r988qkz Please uncomment the componentDidMount() to see the issue
Firstly, there's a typo in dcolumn and column.
And regarding the not defined error, you need to define it using const. Use like:
const checkbox = (column, colIndex) => {
return (
<h5>{column.text}<checkbox /></h5>
);
}
JavaScript variables need to be declared when they are used. Public class syntax can not be used everywhere. The error you're getting is self-evident - 'checkbox is not defined'.
Refer this on how to use it: https://tylermcginnis.com/javascript-private-and-public-class-fields/
I simply declared the undeclared variables in your example and the code worked.
I have a Model who has a column (attribute) that stored a comma separated value of IDs.
For Example,
Movie has a column "Genre" that includes more than one genre, e.g.: 40,20,1,3
How can I use Select2 widget to show these values separated when 'multiple' => true
And how can I save them back into comma-separated value as a string. I want a solution that will allow for quick flexibility. I know you can implode and explode the string but seems too much.
Any help appreciated
If I remember correctly pass the default option as part of the $options configuration for the widget:
echo $form->field($model, 'model_attribute_name')->widget(Select2::className(), [
'data' => $data
'options' => [
'class' => 'form-control',
'placeholder' => 'Choose Option...',
'selected' => 40
],
'pluginOptions' => [
'allowClear' => true,
],
])->label('Select2 Form Field');
This is from memory for grain fo salt here. The documentation at http://demos.krajee.com/widget-details/select2 is not very specific about how to do this.
I don't believe you can do that. Select2 sends the data in post as an array, so you would still need to use implode before saving. What i would do instead is in your model class:
class MyModel extends \yii\db\ActiveRecord {
$public myArrayAttribute;
...
public function beforeSave($insert) {
if (parent::beforeSave($insert)) {
$this->myAttribute = implode(',', $this->myArrayAttribute);
return true;
}
return false;
}
public function afterFind() {
parent::afterFind();
$this->myArrayAttribute = explode(',', $this->myAttribute);
}
}
This way myArrayAttribute will hold the values from the comma separated field as an array. Of course you will need to add validation rules for it and use it instead of your other attribute in create and update forms.
if you're displaying a form with already populated fields, maybe you want to update an already existing object, and you want to display the already saved value for the Select2 field, use 'data' => [ 1 => 'Some value' ], where 1 is the value, associated to the value displayed in the form. You can retrieve stuff to put in data from DB beforehand.
Source: https://github.com/kartik-v/yii2-widget-select2/issues/37
I have a model "Product" that I would like to modify or "mutate" one of its attributes for, but only in specific instances.
I store attribute, price as an integer. So $1.99 gets stored as 199.
I would like to incorporate this with the activeForm in such a way that when getting the price it converts to "1.99" in the field (visually). But when I submit the form, before validation, it modifies the price from "1.99" to "199".
I'm assuming this will require Behaviors, and specifically attaching a behavior to the model before creating the active form. However, I'm still confused on how to set this up. I see there is an AttributeBehavior class or I can make my own Behavior class, but I've been having trouble figuring out implementation in this case.
The situation:
foreach ($store_item->storeProducts as $i=>$product) {
?>
<tr>
<td>
<?= $form->field($product, '['.$i.']price')->label(false); ?>
</td>
</tr>
<?php
$i++;
}
?>
Here is a scenario where I check for empty attribute and assign value before saving. Note owner returns the Model so that you can access model attributes and functions that are public. Let me know if I can explain anything further
public function behaviors()
{
return [
[
'class' => AttributeBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => 'yourAttrib',
],
'value' => function ($event) {
$code = "N/A";
if(!empty($this->owner->yourAttrib))
{
$code = $this->owner->yourAttrib; //here change your attribute accordingly
}
return $code;
},
],
//other behaviors
];
}
You could simply use a getter/setter, e.g. :
public function getRealPrice()
{
return $this->price/100;
}
public function setRealPrice($value)
{
$this->price = $value*100;
}
And don't forget to :
add realPrice in your model's rules,
use realPrice in your form (instead of price).
Here is a basic add action:
public function add()
{
$article = $this->Articles->newEntity();
if ($this->request->is('post')) {
$article = $this->Articles->patchEntity($article, $this->request->data);
if ($this->Articles->save($article)) {
$this->Flash->success('Success.');
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('Fail.');
}
}
$this->set(compact('article'));
}
If a malicious user injects at form a field with name id and set the value of this field to 2. Since the user do that the id value will be in $this->request->data so at $this->Articles->patchEntity($article, $this->request->data) this id will be patched and at $this->Articles->save($article) the record 2 will be updated instead of create a new record??
Depends.
Entity::$_accessible
If you baked your models, then this shouldn't happen, as the primary key field will not be included in the entities _accessible property, which defines the fields that can be mass assigned when creating/patching entities. (this behavior changed lately)
If you baked your models, then this shouldn't happen, as the primary key field(s) will be set to be non-assignable in the entities _accessible property, which means that these the fields cannot be set via mass assignment when creating/patching entities.
If you didn't baked your models and haven't defined the _accessible property, or added the primary key field to it, then yes, in case the posted data makes it to the patching mechanism, then that is what will happen, you'll be left with an UPDATE instead of an INSERT.
The Security component
The Security component will prevent form tampering, and reject requests with modified forms. If you'd use it, then the form data wouldn't make it to the add() method in the first place.
There's also the fieldList option
The fieldList option can be used when creating/patching entities in order to specifiy the fields that are allowed to be set on the entity. Sparse out the id field, and it cannot be injected anymore.
$article = $this->Articles->patchEntity($article, $this->request->data, [
'fieldList' => [
'title',
'body',
//...
]
]);
And finally, validation
Validation can prevent injections too, however that might be considered a little wonky. A custom rule that simply returns false would for example do it, you could create an additional validator, something like
public function validationAdd(Validator $validator) {
return
$this->validationDefault($validator)
->add('id', 'mustNotBePresent', ['rule' => function() {
return false;
}]);
}
which could then be used when patching the entity like
$article = $this->Articles->patchEntity($article, $this->request->data, [
'validate' => 'add'
]);
I am trying to assign a placeholder value to a drop down menu that holds state abbreviations from an entity model.
This is the example code I have tried, but that is not working, because
#Html.DropDownListFor(m => m.StateID, new SelectList(Model.States, "ID", "Abbreviations", Model.StateID), new { id = "StateID" + Model.ID, data_placeholder = "State" })
When I open the page, the first value that shows is AL for Alabama instead that the word "State"
You can use this overloaded version of DropDownListFor to specify default option :-
#Html.DropDownListFor(m => m.StateID, new SelectList(Model.States,"ID","Abbreviations"),
"State", new { id = "StateID" + Model.Id })
Also, Please note that there is no need to specify Model.StateID again in SelectList constructor because automatic selection of dropdown (selected value) will be taken care by first param i.e. m => m.StateID.