I have a view about holidays where a user uses a form to choose a place to travel and a hotel. It has two models: HolidaysPlaces and HolidaysHotels.
The user have to fill the form in this order using the view:
The user completes the fields called Place and City (related with the HolidaysPlaces model).
The user checked a checkbox if he/she wants to choose a hotel. It able a field called Hotel (related with HolidaysHotels model).
The user completes that field.
The user press a Create button.
The controller receives and saves both models.
But the problem is when the user doesn't select the checkbox (number 2 of the list): The Hotel fieldis still required (with the red asterisk as defined in its model file). So the Create button doesn't work in this case.
How can I disabled the required feature?
Add a scenario for this case in your HolidaysHotels model, and include only the fields that you want checked.
Example: If you have 3 fields name, date and age that are required, create a scenario for two only, and set the scenario in the controller. Only those two fields will be checked.
In model:
public function scenarios(){
$scenarios = parent::scenarios();
$scenarios['create'] = ['name', 'date'];
return $scenarios;
}
In controller:
$holiday = new HolidayHotels();
$holiday->scenario = 'create';
To know more about scenarios: http://www.yiiframework.com/doc-2.0/guide-structure-models.html#scenarios
You can add some condition based validation in your model rules. Here is the snippet for both client and server validation. You can many conditions inside the function block.
['field-1', 'required', 'when' => function ($model) {
return $model->check_box == '1';
}, 'whenClient' => "function (attribute, value) {
return $('#checkbox-id').is(':checked') ';
}"],
The easiest way to solve it is to send the model with empty strings. Then the controller checks if the strings are empty. If so, the model is not saved. Else, it is saved.
It was the only way that works for me.
Related
I am working with field MorphTo and I try to make conditions for the resources.
For example I have 4 resources:
Accounts
PaymentMethodCreditCard
PaymentMethodBankAccount
Transactions
Every Account can add as many Payment Methods as he wants.
And in the transaction I work with MorphTo to select the Payment Method that the account selected.
My problem starts when I try to create a transaction from Nova and get a list of all the Payment Methods in the db without any relation to the account.
My ideal idea was like that:
MorphTo::make('Transactions')
->withoutTrashed()
->dependsOn('Account', function (MorphTo $field, NovaRequest $request, FormData $formData) {
if(isset($formData->Account)){
$field->types([
PaymentMethodCreditCard::class => fn($q)=>$q->where('account_id', $formData->Account),
PaymentMethodBankAccount::class => fn($q)=>$q->where('account_id', $formData->Account),
]);
}
}),
But of course it will not work, Someone has any idea how I can add conditions to the resource?
I'll give you two answers. Since the question does not exactly clarify whether the Account value is changable during the edit process or it's predefined.
Account value does not change.
If the account value does not change after the resource is loaded, then the solution you are looking for is Relatable Filtering
public static function relatablePaymentMethods(NovaRequest $request, $query)
{
$resource = $request->findResourceOrFail();
return $query->where('account_id', $resource->Account);
}
Account value can change
If the account value can change, then you'll need to create your own "Morph To" behavior using Select and Hidden fields.
With Select field, you can utilize the dependsOn and options functions, to change the query results when the Account changes.
I want to set the default value of a resource field to the authenticated user's id. I have a model called Note which has a one to many relationship with Game and User.
User hasMany Note
Game hasMany Note
Note belongsTo User
Note belongsTo Game
In Laravel Nova my fields looks like this for the note
ID::make()->sortable(),
Text::make('Note', 'note')->onlyOnIndex(),
Textarea::make('Note', 'note')->alwaysShow(),
BelongsTo::make('Game', 'game')->hideWhenCreating()->hideWhenUpdating(),
BelongsTo::make('Created By', 'user', 'App\Nova\User')->hideWhenCreating()->hideWhenUpdating(),
DateTime::make('Created At', 'created_at')->hideWhenCreating(),
DateTime::make('Updated At', 'updated_at')->hideWhenCreating(),
Because I am referencing the Note on the Game Nova resource, when I create a Note, the game_id column is populated correctly. But, I want the user_id column to be the value of the authenticated user. It does not seem to work like this, how would I accomplish it?
If I understand correctly from the line BelongsTo::make('Created By', 'user', 'App\Nova\User')->hideWhenCreating()->hideWhenUpdating() you're trying to set a default value for the column without showing the field on the form?
I don't think this is possible in this way. As soon as you use the hide functions the fields aren't rendered and will never be passed along with the request. I tried this, and the user_id field was never sent with the request.
I think there are two ways to do this:
Show the field in the form and set the default value using the metadata (and perhaps making the field read-only for good measure).
BelongsTo::make('Created By', 'user', 'App\Nova\User')->withMeta([
"belongsToId" => auth()->user()->id,
])
See this part of the Nova docs
Or use the Eloquent creating event. The following will go in your Note model.
public static function boot()
{
parent::boot();
static::creating(function($note)
{
$note->user_id = auth()->user()->id;
});
}
Granted, the above method is a bit simple. You'd be better off using proper event listeners.
Sidenote: from an architectural point of view, I'd go with option 2. Setting a default value without getting the end-user involved sounds like a job for the Eloquent model, not for a Nova form.
You can use a method resolveUsing(). An example
<?php
//...
Select::make('My Select', 'my_custom_name')
->options(['a' => 'a', 'b' => 'b', 'c' => 'c'])
->resolveUsing(function ($value, $resource, $attribute) {
// $value = model attribute value
// $attribute = 'my_custom_name'
return 'b';
});
I'm making a create form for one of my resources using Nova. Some of the fields have conditional relationships to one another.
For example: if "is trial" is selected we must specify a value for "trial end date", but there's no point showing the "end date" field on the page if "is trial" isn't picked. Another example, fields A and B are mutually exclusive.
All of these can easily be enforced with conditional validators in the backend, and I know how to do that. I'm just trying to make an interface that's not confusing.
How can I customize the frontend JS forms for this resource to reflect such conditional relationships?
Its possible using this one
// put this inside **public function fields(Request $request)**
BelongsTo::make('Subcategoria', 'subcategory', 'App\Nova\SubCategory'),
// conditional display
$this->sellerFields(),
//used for conditional seller input
protected function sellerFields()
{
if(\Auth::user()->role == "admin"){
return $this->merge([
BelongsTo::make('Vendedor', 'user', 'App\Nova\User'),
]);
}else{
return $this->merge([]);
}
}
Is there any way to prompt for validation with the attribute's label assigned via ActiveForm?
For instance I have a model attribute amount and the label defined in its attributeLabels function is "Amount"
But while generating form I neeed the label "Fees" so:
$form->field($model, 'amount')->textInput(['maxlength' => true])->label('Fees')
After validation it prompts me "Amount cannot be blank" - it is known to me that we can write a rule to change message but according to my requirements, same attribute (from same model) is having different labels on different forms.
I know that back-end implementation of default message uses:
'message' => '{attribute} cannot be blank.' does anyone know if there is any {x} by which the assigned label in ActiveForm can be retrieved?
PS: I know that this problem can be resolved by scenarios. But it would be hectic to write rule for every field which has dual label.
There is no such way, sorry! What you are doing is overriding the label within the view. Actually within the form to be more precise. If you check out what the label()-method does, you will see that it calls activeLabel() from the Html-helper. This method in turn renders your label and returns the code.
As you can see, none of this information gets written back to the model. Therefore, during the validation process, you won't have your new label at hand, because it never makes its way into the model.
Your cleanest option is the one you mentioned already. Use scenarios to decide which validation rule (and therefore message) to use. OR you could create your own public property in which you write your temporary label like so:
class MyModel extends \yii\db\ActiveRecord
{
public $myTempLabel;
public function attributeLabels()
{
$myLabel = $this->myTempLabel === null ? Yii::t('app', 'defaultLabel') : $this->myTempLabel;
return [
//other labels...
'myField'=>$myLabel,
//other labels...
];
}
}
In your view you can then set the value back into the attribute within your model.
Sorry for not being able to help better, but that's the way it's implemented!
What you need to do is handle the label changing logic in your attributeLabels() function.
class MyModel extends \yii\base\Model {
public $amount_label = 'amount';
public function attributeLabels() {
return [
'amount' => $this->amount_label
];
}
}
Then in your controller.
$model = new MyModel;
$model->amount_label = 'fees';
...
Of course you may want to set the label in a different way. For example, if your model as a type attribute, and this is the attribute which determines the label, you could do a conditional based on that type attribute.
I am trying to accomplish the following:
I have a form that starts with a combo box, let's say that the user will have to pick either "Student" or "Teacher".
Both "Student" and "Teacher" will have the same fields displayed in the form, but if "Teacher" is checked, I will have more fields being displayed (that are hidden at first and that I will show with jQuery when the user select "Teacher").
The problem is that I want those fields to be mandatory only if "Teacher" is selected.
I have no idea to manage that, I don't think it's gonna be possible using annotations such as:
#NotBlank
private String teacherCourse;
since this field will always be blank when the user will have selected the "Student" radio button.
Any idea? Can I do a custom validation method and how?
I've taken two approaches with this in the past.
Use an enum field on the submission to determine which type of validation to perform. This is flexible and allows for any number of custom validation methods.
An alternative is to use a base command object which both student and teacher classes extend. This allows both types to extend and override validation and fields. This requires that separate methods are used to bind each type.
You could use validation groups to differentiate between constraints applying to both entities and those applying to only one of them:
public interface TeacherConstraints {}
#NotBlank(groups=TeacherConstraints.class)
private String teacherCourse;
When validating your object, specify the group to validate depending on the type selected in your combo box:
//teacher
Set<ConstraintViolation<Object>> violations = validator.validate(object, TeacherConstraints.class);
//student
Set<ConstraintViolation<Object>> violations = validator.validate(object, Default.class);
You can use javascript or JQuery for front side validation... depending upon your combo box value. If it's a teacher or student
function validate(){
var combox_value = document.getElementbyID("combo_box").value;
if(combox_value == "Teacher"){
//Validate for Teacher fields
var input_text1 = document.getElementbyID("input_text"2).value;
if(input_text1=="" || input_text1==null){
alert("Field cannot be empty");
return false;
}
return true;
}
else if(combox_value == "Student"){
//Validate for Student fields
var input_text2 = document.getElementbyID("input_text2").value;
if(input_text2=="" || input_text2==null){
alert("Field cannot be empty");
return false;
}
return true;
}
}
For JQuery try these links for live examples...
http://speckyboy.com/2009/12/17/10-useful-jquery-form-validation-techniques-and-tutorials-2/
http://www.jeasyui.com/tutorial/form/form3.php
http://www.camcloud.com/blog/jquery-form-validation-tutorial