Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default values for fields? #58

Closed
Jam0r85 opened this issue Aug 23, 2018 · 51 comments
Closed

Default values for fields? #58

Jam0r85 opened this issue Aug 23, 2018 · 51 comments
Labels
implemented Feature Request implemented

Comments

@Jam0r85
Copy link

Jam0r85 commented Aug 23, 2018

Not sure if i've missed this from the docs but is there anyway to populate a field in a create form with a default value?

eg.

Date::make('Date')->defaultValue(today())

This would be helpful with relationship fields also, especially with BelongsTo (example of using Spark with it's current_team_id column in the users table.

BelongsTo::make('Team')->defaultValue(Auth::user()->current_team_id)

Would help reduce bits and pieces being added to model observers checking for missing values and populating them etc

@chinleung
Copy link

You can do this by setting a default attribute to your model, but yeah I think there should be a way to pass a value to the fields.

@mink
Copy link

mink commented Aug 24, 2018

This would be especially useful for the code field. Being able to provide placeholder JSON data into a field by default would allow for more context on how that field can be filled appropriately, rather than being constricted to having it filled with "null" as it currently does.

@Jam0r85
Copy link
Author

Jam0r85 commented Aug 24, 2018

@mink Currently I hide my code field's when creating a record and use a model observer to input the default json values. Not the best solution but it does the job.

@mink
Copy link

mink commented Aug 25, 2018

I've found that if you set the default attribute on a model (as @chinleung mentioned) as an array, it will fill a JSON code field on the new resource view. If you set the default value as a string, any code field that is not declared as JSON will be filled in with the specified value too.

Other fields that I've tested such as the Text field will accept various types as a default value to fill in the field.


This would be great to have in the documentation. It is personally enough of a solution for my usage, however, being able to set default values within the resource class would be useful as well.

Edit: If you override a model's constructor you can programmatically declare the default attribute values, which you could use to pull in config data for instance. If you require different default values for the model specifically in Nova you could assign the values this way as well.

@crnkovic
Copy link

Is there a way to do this for an action field? I have an action field asking me to input something, it would be great if it would fill a field for me with a default data.

Something like:

Text::make('Field')->defaultValue('a-value');

@MadMikeyB
Copy link

MadMikeyB commented Aug 26, 2018

Would be great to be able to default let's say a BelongsTo::make('User') with the currently authenticated user.

@s-solodovnikov
Copy link

Something is known, how can I set a default value?

@stevelacey
Copy link

stevelacey commented Aug 30, 2018

As per what @mink suggested, default values for json (code) fields can be achieved by setting the value to the attribute in the model's constructor

  • if you add the value in attributes static it'd have to be a json string given laravel/nova tries to json decode it because of the cast
  • if you add it in creating, that's too late

This works for me:

    protected $casts = [
        'fields' => 'array',
    ];

    public function __construct(array $attributes = [])
    {
        $this->fields = config('app.default_fields');

        parent::__construct($attributes);
    }

I do however also have this field on a pivot... and haven't found a way to specify a default for that... the best way would probably indeed by a defaultValue method on Nova fields. I am toying with pivot classes but as of yet have had no success, I've also tried using resolveUsing but so far no luck

@chinleung
Copy link

@MadMikeyB You can achieve set the authenticated user by default for the BelongsTo::make('User') field like this:

public function __construct(array $attributes = [])
{
    if (! isset($attributes['user_id']) && auth()->check()) {
        $attributes['user_id'] = auth()->user()->id;
    }

    parent::__construct($attributes);
}

@crnkovic
Copy link

crnkovic commented Aug 30, 2018

Yeah but this seems like a hacky way to do it. Taylor explicitly said "Nova doesn't interact with your code, it doesn't even touch your code at all". I don't want to make adjustments to my code, especially overriding a constructor of a model, just so I can set default value in Nova. IMHO, this should be resolved, especially since this doesn't feel like a hard bug to fix, since there is a value property on the Field (or Element, not sure) class.

Until then, I'm fine with overriding certain methods in Nova resources, or extending the base Resource class or something, but I'm not fine with modifying my models, especially setting default attributes with some hardcoded values.

@stevelacey
Copy link

stevelacey commented Aug 30, 2018 via email

@davidhemphill davidhemphill added the request Feature Request label Aug 30, 2018
@taylorotwell
Copy link
Member

This repository is for tracking bugs. Feature requests may be emailed to Nova customer support.

@idragon81
Copy link

idragon81 commented Sep 9, 2018

Add the following to your resource file

    /**
     * Get a fresh instance of the model represented by the resource.
     *
     * @return mixed
     */
    public static function newModel()
    {
        $model = static::$model;
        $var = new $model;
        $var->attributeForDefaultValue = defaultValue;
        return $var;
    }

@roshangautam
Copy link

This is how you set default values. Will work most of the times except when your field is a number and you want to set 0 as default then it wouldn't work.

Text::make("Your Field")
->withMeta(["value" => "Some Value"])

@sonalmahajan01
Copy link

How to specify meta tag only while creating a new value and not for all. Right now it ignores its own values and displays this for all the records.

@mattivie14
Copy link

mattivie14 commented Oct 1, 2018

@sonalmahajan01 You can do this to have it only populate the default value if the item doesn't have a value for that field already:
Text::make("Name")->withMeta($this->name ? [] : ["value" => "John Smith"])

@tdondich
Copy link

tdondich commented Oct 5, 2018

Because of new PHP syntax, it can be even easier.

Text::make("Name")->withMeta(['value' => $this->name ?? 'Default']);

Makes use of the Null Coalescing Operator.

@peterhaluska
Copy link

peterhaluska commented Oct 8, 2018

@Jam0r85 I have managed to set a default value (current user's ID) on a BelongsTo field this way:

BelongsTo::make('User')
    ->withMeta([
        'value' => $this->user_id ?? auth()->user()->id, 
        'belongsToId' => $this->user_id ?? auth()->user()->id
    ])
    ->rules('required'),

@nickpoulos
Copy link

@peterhaluska small note on your solution. It works great for Create/Edit. However on Index/Details pages it causes the BelongTo value to be the ID instead.

I got around by adding two BelongsTo. The original BelongTo with ->hideWhenCreating()->hideWhenUpdating() attached. And then adding ->hideFromIndex()->hideFromDetail() to your BelongsTo with default value.

BelongsTo::make('Author', 'user', 'App\Nova\User')->withMeta([
'value' => $this->user_id ?? auth()->user()->id,
'belongsToId' => $this->user_id ?? auth()->user()->id
])->hideFromIndex()->hideFromDetail()

BelongsTo::make('Author', 'user', 'App\Nova\User')->hideWhenCreating()->hideWhenUpdating()

@MohmmedAshraf
Copy link

You should use this package: Nova Hidden Fields also you can set default value for your hidden fields such as current user_id and much more..

@dillingham
Copy link
Contributor

If you’re using the auth user for the id, don’t pass it via hidden fields. Use observers. Creating and updating events can access the auth user

@josemanuelsh
Copy link

@peterhaluska great solution, you saved me lots of time!!

@nickpoulos there is no need to make two BelongsTo. I am only using the 'belongsToId' and removed the 'value', it works great like that.

BelongsTo::make('User')
    ->withMeta([
        'belongsToId' => $this->user_id ?? auth()->user()->id
    ]);

@devonmather
Copy link

devonmather commented Jan 5, 2019

Unfortunately using the withMeta function doesn't appear to work in combination displayUsingLabels() when using a select field. Does anyone have a solution for this situation?

Select::make('Rate')
    ->options('work_rates' => [
        '1' => 'Standard',
        '2' => 'Priority',
    ])
    ->withMeta(['value' => '1'])
    ->displayUsingLabels()

@chinleung
Copy link

chinleung commented Jan 7, 2019

@devonmather You are not passing the options correctly. It should be:

Select::make('Rate')
    ->options([
        '1' => 'Standard',
        '2' => 'Priority',
    ])
    ->withMeta(['value' => '1'])
    ->displayUsingLabels()

Then the value Standard is selected by default.

@devonmather
Copy link

devonmather commented Jan 7, 2019

@chinleung Thanks for pointing out the syntax error I had in the arguments. However, that wasn't the issue. The issue is on the detail and index where the number '1' shows in place of the text 'Standard'.

Please see screenshots.

screen shot 2019-01-07 at 3 36 04 pm

screenshot

I have since solved the issue by using the model's attributes.

It seems the withMeta functionality for setting defaults could be flawed in that it will always set that value for any page, not just the create form. It also does not respect the displayUsingLabels() function.

@zareismail
Copy link

zareismail commented Jan 27, 2019

you can use fillUsing callback and in relation use filled callback like this:

public function fields(Request $request)
    {
        return [
            ID::make()->sortable(),
            ColorField::make('hex')->sortable()->fillUsing(function(NovaRequest $request, $model) {
                if(! isset($model->hex)){
                    $model->hex = '#000000';
                } 
            }), 
            BelongsTo::make('user', 'owner')->nullable()->filled(
                function(NovaRequest $request, $model) { 
                    if(is_null($model->owner_id)) {
                        $model->owner_id = $request->user()->id;
                    }
                    
                }
            ),
        ];
    }

@kitbs
Copy link

kitbs commented May 4, 2019

I've just published a package which wraps this default behaviour in some convenience methods, including caching previous values. Hope this is useful! https://novapackages.com/packages/inspheric/nova-defaultable

@tylernathanreed
Copy link

@LukasRothZeitraum, @devonmather

I've come up with a solution that works for me, and it seems to solve this problem. It also accounts for the issue that @crnkovic pointed out, and thus my solution doesn't require changing anything outside of Nova.

The problem that you guys were seeing is that using the withMeta approach replaces the value returned to the client, even if a value was already present (which is certainly the case for existing models).

Additionally, I'm dealing with a field that uses a resolve callback, which the value provided in withMeta has already handled, so if you override that component, any effect that your callback would have isn't performed (in my case, I'm working with a "percent" field, which is a number field that shows "35" to represent "0.35" in the database. I'd have to use "100" for the withMeta approach, instead of the desired value of "1").

The solution lies with provide default attributes to the model, but doing it within the Nova codebase. This is similar to solution @idragon81 posted, but more abstract.

In my base resource class, I've added the following methods:

/**
 * Creates and returns a fresh instance of the model represented by the resource.
 *
 * @return mixed
 */
public static function newModel()
{
    $model = static::$model;

    $instance = new $model;

    $instance->setRawAttributes(static::getDefaultAttributes());

    return $instance;
}

/**
 * Returns the default attributes for new model instances.
 *
 * @return array
 */
public static function getDefaultAttributes()
{
    return static::$defaultAttributes ?? [];
}

This allows me to provide either a defaultAttributes static attribute on the resource, or I can override the getDefaultAttributes method on the implementing resource (which is useful if I needed to make function calls instead of providing constants).

Now in my specific resource, I've added the following attribute:

/**
 * The default attributes for new model instances.
 *
 * @var array
 */
public static $defaultAttributes = [
    'percent' => 1
];

Doing this will send my "1" through my resolve callback, and converting it to "100" (similar to how "0.35" would have been changed to "35", had "0.35" been the value stored in the database). If the model is instead retrieved from the database, then it will replace my default value (even if the stored value is null, null would be used for the "percent" attribute rather than "1").

Hopefully this helps.

@ZebTheWizard
Copy link

I don't know if this is the Laravel way of doing things, but I just returned the default value within the resolve callback. However, I tried the same thing for relationships and it threw an error.

Boolean::make('My Boolean', 'working', function () {
    return true;
})
Date::make('My Default Date', 'date', function () {
    return now();
})

@rasmuscnielsen
Copy link

For anyone interested I made this macro which seems to do the trick:

In NovaServiceProvider:

public function boot()
{
    Field::macro('default', function ($default) {
        return $this->resolveUsing(function ($value) use ($default) {
            return $value ?? $default;
        });
    });
    
    parent::boot();
}

Use it like:

Text::make('Name')
    ->sortable()
    ->rules('required', 'max:255')
    ->default('John Doe'),

@intraordinaire
Copy link

@rasmuscnielsen Excellent thanks a lot !
I've juste updated a bit your solution.
In some cases, I needed a default value only when creating, but user can empty the field.

In NovaServiceProvider :

public function boot()
{
    Field::macro('default', function ($default) {
        return $this->resolveUsing(function ($value, $model) use ($default) {
            return $model->getKey() === null ? $default : $value;
        });
    });

    parent::boot();
}

And the usage stay the same when creating the field.

As i'm also using, NovaFlexibleContent package, i've added :

public function getKey()
{
    return $this->inUseKey();
}

In my layouts in order to have them working like expected.

@ajthinking
Copy link

@rasmuscnielsen and @intraordinaire when defining a macro like that I get

"Using $this when not in object context",…}

at $this->resolveUsing

Any ideas what I am missing?

@mpoma
Copy link

mpoma commented Jan 30, 2020

@intraordinaire Excelent, but i dont understand where you put and how to use in contenflexible
public function getKey()
{
return $this->inUseKey();
}

@intraordinaire
Copy link

@mpoma i have a BaseLayout class, all my flexibles extend this class.
Just an example, but you can put that on every class that extend the Layout class from the FlexibleContent package, or implementing your own solution if necessary.

use Whitecube\NovaFlexibleContent\Layouts\Layout;

class BaseLayout extends Layout
{
    /**
     * Return the layout identifier
     *
     * @return null|string
     */
    public function getKey()
    {
        return $this->inUseKey();
    }
}

@ajthinking Not sure what can be the problem here. Are you well in the NovaServiceProvider, is Field the good Laravel Nova class (use Laravel\Nova\Fields\Field;), can you dump $this ?

@florentpoujol
Copy link

florentpoujol commented Feb 17, 2020

Just for completeness, setting the default value via the 'value' attribute and withMeta() method not only override existing values (including in lists), but also prevent the value to be changed in the form, even if a different value was manually entered.

The correct answer is indeed from #58 (comment) (and #58 (comment)) : set the default values directly on the model attributes.

I personally do it in the fields() methods of the Nova resource, when the model exists property is false meaning that we are on the creation form.

@avonian
Copy link

avonian commented Apr 6, 2020

As per what @mink suggested, default values for json (code) fields can be achieved by setting the value to the attribute in the model's constructor

  • if you add the value in attributes static it'd have to be a json string given laravel/nova tries to json decode it because of the cast
  • if you add it in creating, that's too late

This works for me:

    protected $casts = [
        'fields' => 'array',
    ];

    public function __construct(array $attributes = [])
    {
        $this->fields = config('app.default_fields');

        parent::__construct($attributes);
    }

I do however also have this field on a pivot... and haven't found a way to specify a default for that... the best way would probably indeed by a defaultValue method on Nova fields. I am toying with pivot classes but as of yet have had no success, I've also tried using resolveUsing but so far no luck

did you ever figure out a way to set default values on pivot fields? i've looked up and down with no luck

@ziming
Copy link

ziming commented Apr 15, 2020

How would default value be handled for hasMany?

For example on User Detail page, I click 'add book', but on the book creation form i want school_id of Book to be prefilled with that User's school_id

@Theohorsmeier
Copy link

Theohorsmeier commented Apr 16, 2020

@ziming

How would default value be handled for hasMany?

For example on User Detail page, I click 'add book', but on the book creation form i want school_id of Book to be prefilled with that User's school_id

I have tried something similar to your situation, in the default callback function I want to use another relation of the current model to find the ID to use as default value, but seems not to work, the callback is not even called?

What I tried was something like this (in the Book fields):

BelongsTo::make('School')->default(function () {
  return $this->user->school->id;
})

So that when I create a new book from the User page, where the user ID is already fixed, it should be able to also assign the default school ID.

But since it doesn't seem to work, what would be the correct way of using default to achieve this?

@Theohorsmeier
Copy link

It seems like the callback is simply not executed for BelongsTo fields?
This works:

BelongsTo::make('School')->default(1)

This works (like in the examples):

Text::make('School')->default(function () {
  return 'Saturn';
}),

But then this does nothing:

BelongsTo::make('School')->default(function () {
  return 1;
}),

Another problem seems to be that at the moment of assigning the default value, the user relation is not known yet (even though we create the Book from the User resource page, and the dropdown on the Book create page is greyed out with the correct value).

This means that when I try to do it like this:

BelongsTo::make('School')->default($this->user->school->id)

I get Trying to get property 'school' of non-object

So maybe using default for this is not even possible?

@oleksandr-roskovynskyi
Copy link

Unfortunately using the withMeta function doesn't appear to work in combination displayUsingLabels() when using a select field. Does anyone have a solution for this situation?

Select::make('Rate')
    ->options('work_rates' => [
        '1' => 'Standard',
        '2' => 'Priority',
    ])
    ->withMeta(['value' => '1'])
    ->displayUsingLabels()

Have you solved this problem? Right now, I also can't solve the problem of displaying labels when default values are set for a field

Select::make('Тип', 'type')
                ->options(\App\Cost::TYPE)
                ->sortable()
                ->withMeta([
                    'value' => $this->type ?? 'expense' // default value for the select
                ])

@lopandpe
Copy link

lopandpe commented Jun 24, 2020

Easier, for the date field asked:

Date::make('Upload date', 'uploadDate', function ($value) {
                if ($value){
                    return $value;
                }else{
                    return now();
                }
            })

@eugenefvdm
Copy link

Wow this super cool soap opera is like the longest ever explanation of how to set defaults for Nova! For those still struggling with Select and the initial default value and displayUsingLabels because you're using ENUMs and you have nice friendly names for example Lead Statuses, that point to sensible lower case defaults in your database, e.g.:

'proposal_sent' => 'Proposal Sent'

The key is don't use withMeta but rather resolveUsing as in the example below:

Select::make('Status')->options(
           Leads::ui_options()
            )
            ->sortable()
            ->resolveUsing(function () {
                return $this->status ?? 'new';
            })
           ->displayUsingLabels(),

Compliments of this Stack post.

@cja-github
Copy link

cja-github commented Sep 10, 2020

This is how to set a default value on create only for a DateTime field:

            DateTime::make('When')
                ->withMeta(['value' => $this->when ?? Carbon::now()->toDateTimeString()])
                ->rules('required')
                ->hideFromIndex()
                ->hideFromDetail(),

            DateTime::make('When')
                ->sortable()
                ->hideWhenCreating()
                ->hideWhenUpdating()

Inspired by https://vander.host/knowledgebase/software-development/how-to-set-the-default-date-using-laravel-nova/

@namnh06
Copy link

namnh06 commented Sep 29, 2020

I met this issue today, should use this instead of the default or withMeta, from: #493 (comment)

use Illuminate\Database\Eloquent\Model;

class Booking extends Model
{
    protected $attributes = [
        'status' => 'pending',
        'price' => 0,
        'commission' => 0,
    ];
}

@n1k-crimea
Copy link

Wow this super cool soap opera is like the longest ever explanation of how to set defaults for Nova! For those still struggling with Select and the initial default value and displayUsingLabels because you're using ENUMs and you have nice friendly names for example Lead Statuses, that point to sensible lower case defaults in your database, e.g.:

'proposal_sent' => 'Proposal Sent'

The key is don't use withMeta but rather resolveUsing as in the example below:

Select::make('Status')->options(
           Leads::ui_options()
            )
            ->sortable()
            ->resolveUsing(function () {
                return $this->status ?? 'new';
            })
           ->displayUsingLabels(),

Compliments of this Stack post.

it's work, tnx

@crynobone crynobone removed the request Feature Request label Nov 30, 2020
@AshutoshJha15
Copy link

Text::make( 'purchase_count')
->default(function (){
return 0;
})
->sortable()
,

@crynobone crynobone added the implemented Feature Request implemented label Jan 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
implemented Feature Request implemented
Projects
None yet
Development

No branches or pull requests