How to specify a default value for a field Laravel Nova - laravel

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';
});

Related

How can I get the data inside my notifications from within my Controller?

I'm trying to get the numbers of records from my notifications, where the candidate_user_id column from inside the data attribute is the same as the UserId (Authenticated User).
After I dd, I was able to get the data from all of the records in the table by using the pluck method (Line 1). I then tried to use the Where clause to get the items that I need
but this just didn't work, it was still returning all of the records in the table.
DashboardController.php:
public function index()
{
$notifications = Notification::all()->pluck('data');
$notifications->where('candidate_user_id', Auth::user()->id);
dd($notifications);
}
Here is a partial screenshot of the data that is being plucked.
How can I get the data from this, in a way like this ->where('candidate_user_id', Auth::user()->id);?
If data was a JSON field on the table you could try to use a where condition to search the JSON using the -> operator:
Notification::where('data->candidate_user_id', Auth::id())->pluck('data');
Assuming you only want this data field and not the rest of the fields, you can call pluck on the builder directly. There isn't much reason to hydrate Model instances with all the fields to then just pluck a single field from them if it is just a table field, so you can ask the database for just the field you want.
The data in the data field is a json string, so you can tell Laravel to automatically cast it as an array using the $casts property on each of the models that is notifiable.
For instance, if you have a User model which uses the trait (ie has use Notifiable), add this:
protected $casts = [
'data' => 'array',
];
If you want to access all notifications for the auth user.
$user = auth()->user();
dd($user->notifications->pluck('data'));
If you really want to do in your question way, here is how.
$notifications = Notification::all()->pluck('data');
$notifications = $notifications->where('candidate_user_id', Auth::user()->id)
->all();
This assumes you that you did not modify the default laravel notifications relationship and database migration setup. If you have modified some of the default ones, you need to provide how you modify it.

Laravel unique validation must include appended text when validating

I am using the Laravel tenancy package in which there is a domains table in my database and each value in the domain column within this table is appended with .test.co.uk.
When the user enters the URL in the form, they are presented with an input element (shown above) in which they enter a URL/Domain but the .test.co.uk is already appended so the only thing they need to enter is the text that goes before that, e.g. they would enter johnsmith and in the domain column it would store johnsmith.test.co.uk. The problem I have is that I need the validation on this column to be unique but also include the .test.co.uk when performing the validation so that it looks at the value stored in the table because if a user enters johnsmith and there is currently a record in the domains table where the value is johnsmith.test.co.uk then the validation would pass but I need the validation to fail in this scenario. I am currently using a Request class which is extending the FormRequest class and have this:
public function rules()
{
return [
'url' => 'required|string|unique:domains,domain',
];
}
I have also tried a rule object but I don't think a rule object is the correct solution to this problem. Is there a convenient "Laravel" way of doing this?
In your Request class use prepareForValidation()
Docs: https://laravel.com/docs/7.x/validation#prepare-input-for-validation
protected function prepareForValidation()
{
$this->merge([
'url' => $this->url . '.test.co.uk',
]);
}

How to filter options for Laravel Nova select field

I have a resource of type Process_type.
It has a BelongsToMany relationship with Process_event
It also stores a default Process_event as a BelongsTo relationship - I want the possible options for this to be limited to those in the BelongsToMany relationship, not the entire set of process events in the database.
I was expecting to be able to limit the options on a BelongsTo::make() field, but can't find a way to do this. So I'm falling back to using a Select field, and passing in the options via the options() method.
This is how my fields method looks currently
public function fields(Request $request)
{
$options = [];
$options['process_events'] = \App\Process_event::all()->pluck('title','id');
return [
ID::make()->sortable(),
Text::make('Title')->sortable(),
Select::make('Process Event')->options($options['process_events']),
BelongsToMany::make('Process Events', 'process_events', Process_event::class)
];
}
As it stands, I still have all process events as options. How can I access the current entity via the $request object passed in, so I can look up the ->process_events() on the model. I can't find any documentation on this.
Is this possible? If not, what is a better way to do this?

Yii2: How to avoid required fields in a view?

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.

How to combine Laravel validation required_if and required_without_all rules?

I have a situation with a subscription form, which must have different validation rules depending on user selection.
I almost complete this, but I'm stuck in a point which need a combination of rules that I think I can't get with predefined laravel rules.
As shown in the following chart, the point is when a user select invoicing preferences, with options Digital and Printed, if user option is Printed I need at least one physical address, so street address field group OR district address fields group must be mandatory.
Mandatory field unless other field is filled can be achieved by required_without_allrule, so I've trying with no success, a combination of required_if and required_without_allrules, like the following example:
public function rules()
{
return [
...
'invoicing_preferences' => 'required',
'invoicing_email' => 'email|required_if:invoicing_preferences,digital',
'invoicing_street_name' => 'string|required_if:invoicing_preferences,printed|required_without_all:invoicing_district,invoicing_parcel',
'invoicing_street_number' => 'number|required_if:invoicing_preferences,printed|required_without_all:invoicing_district,invoicing_parcel',
'invoicing_street_flat' => 'number|required_if:invoicing_preferences,printed|required_without_all:invoicing_district,invoicing_parcel',
'invoicing_street_dep' => 'alpha_num|required_if:invoicing_preferences,printed|required_without_all:invoicing_district,invoicing_parcel',
'invoicing_district' => 'alpha_num|required_if:invoicing_preferences,printed|required_without_all:invoicing_street_name, invoicing_street_number; invoicing_street_flat,invoicing_street_dep',
'invoicing_parcel' => 'alpha_num|required_if:invoicing_preferences,printed|required_without_all:invoicing_street_name, invoicing_street_number; invoicing_street_flat,invoicing_street_dep',
...
];
}
This combination doesn't work because always results in the required_with_allrule no matter if I've checked digital at the first point.
The rules() method is a method that is expected to return array of rules. Why would I write about such an obvious thing? Well, insert any kind of validation logic inside it, which means that it can also do some evaluation of posted data and gradually build up the returning array.
public function rules()
{
$this; // holds information about request itself with all the data POST-ed
if (something) {
return []; // something is true...
}
return []; // default behaviour (ehm, something is not true)
}
Another similar approach is to use multiple arrays and in the end merge them together (build them up). Which may result in nicer code. Also do not be afraid of using one or two private methods to clean up the code.

Resources