├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── defaultable_field.php └── src ├── DefaultableField.php ├── DefaultableServiceProvider.php ├── HasDefaultableActionFields.php └── HasDefaultableFields.php /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | /node_modules 4 | package-lock.json 5 | composer.phar 6 | composer.lock 7 | phpunit.xml 8 | .phpunit.result.cache 9 | .DS_Store 10 | Thumbs.db 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Inspheric 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Nova Defaultable Fields 2 | Populate default values for Nova fields when creating resources and **now supports resource actions (since v1.2)!** 3 | 4 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/inspheric/nova-defaultable.svg?style=flat-square)](https://packagist.org/packages/inspheric/nova-defaultable) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/inspheric/nova-defaultable.svg?style=flat-square)](https://packagist.org/packages/inspheric/nova-defaultable) 6 | 7 | ## Installation 8 | 9 | Install the package into a Laravel app that uses [Nova](https://nova.laravel.com) with Composer: 10 | 11 | **Note: This package is currently incompatible with Nova version 3.4.0 and above.** 12 | 13 | ```bash 14 | composer require inspheric/nova-defaultable 15 | ``` 16 | 17 | (Optional) If you want to use the `defaultLast()` method when creating resources ([see below](#default-the-last-saved-value)), you need to add the trait `Inspheric\NovaDefaultable\HasDefaultableFields` to your base Resource class (located at `app\Nova\Resource.php`): 18 | 19 | ```php 20 | use Inspheric\NovaDefaultable\HasDefaultableFields; 21 | 22 | abstract class Resource extends NovaResource 23 | { 24 | use HasDefaultableFields; 25 | 26 | // ... 27 | } 28 | ``` 29 | 30 | (Optional) If you want to use the `defaultLast()` method on resource actions, you need to add the trait `Inspheric\NovaDefaultable\HasDefaultableActionFields` to **every** Action class (located at `app\Nova\Actions\`, one by one) that you wish to use defaultable fields on: 31 | 32 | ```php 33 | use Inspheric\NovaDefaultable\HasDefaultableActionFields; 34 | 35 | class YourAction extends Action 36 | { 37 | use HasDefaultableActionFields; 38 | 39 | // ... 40 | } 41 | ``` 42 | 43 | ## Basic Usage 44 | 45 | When creating resources or running resource actions, there may be values which can be defaulted to save the user time, rather than needing to be entered into a blank form every time. This could include populating the `user_id` on a resource that the current user owns, repeating the same 'parent' record for several new records in a row, starting with a checkbox in a checked state, or populating an incrementing value, e.g. an invoice number. 46 | 47 | This package plugs into existing fields and provides two simple methods to supply a default value. 48 | 49 | *Note:* The defaultable behaviour below is only applicable on the resource's 'create' or 'attach' form, or on action requests. Fields will not be defaulted on 'update' or 'update-attached' requests; however, the last used value will be stored on any successful save request, and will be defaulted on a later 'create'/'attach' request. 50 | 51 | ### Default any value 52 | 53 | Use the `default($value)` method on any standard Nova field to populate a simple value, e.g. 54 | 55 | ```php 56 | Text::make('Name') 57 | ->default('Default Name'), 58 | ``` 59 | 60 | or: 61 | ```php 62 | Boolean::make('Active') 63 | ->default(true), 64 | ``` 65 | The `default()` method can also take a callback as the first parameter, which will return the value to be populated: 66 | 67 | ```php 68 | Text::make('Name') 69 | ->default(function(NovaRequest $request) { 70 | return $request->user()->name.'\'s New Resource'; 71 | }), 72 | ``` 73 | 74 | ### Special cases 75 | #### Default a BelongsTo field 76 | 77 | To use the `default()` method on a Nova `BelongsTo` field, you can supply either: 78 | 79 | * An instance of an Eloquent model, e.g. 80 | 81 | ```php 82 | // $author = $request->user(); 83 | 84 | BelongsTo::make('Author') 85 | ->default($author), 86 | ``` 87 | 88 | * The primary key of the related record, e.g. 89 | 90 | ```php 91 | // $id = 1; 92 | 93 | BelongsTo::make('Author') 94 | ->default($id), 95 | ``` 96 | 97 | *Note:* This is a convenience only and should not be relied upon for enforcing that an author can only edit their own posts, etc. 98 | 99 | #### Default a MorphTo field 100 | 101 | To use the `default()` method on a Nova `MorphTo` field, you can supply either: 102 | 103 | * An instance of an Eloquent model (the simplest option), e.g. 104 | 105 | ```php 106 | // $post = App\Post::find(1); 107 | 108 | MorphTo::make('Commentable') 109 | ->types([ 110 | Post::class, 111 | Video::class, 112 | ]) 113 | ->default($post), 114 | ``` 115 | 116 | * An array containing the primary key and the morph type, e.g. 117 | 118 | ```php 119 | // $postId = 1; 120 | 121 | MorphTo::make('Commentable') 122 | ->types(...) 123 | ->default([$postId, App\Nova\Post::class]), // The Resource class or class name 124 | ``` 125 | or: 126 | ```php 127 | MorphTo::make('Commentable') 128 | ->types(...) 129 | ->default([$postId, App\Post::class]), // The Eloquent model class or class name 130 | ``` 131 | or: 132 | ```php 133 | MorphTo::make('Commentable') 134 | ->types(...) 135 | ->default([$postId, 'posts']), // The uriKey string 136 | ``` 137 | 138 | * An instance of a Nova Resource, e.g. 139 | 140 | ```php 141 | // $postResource = Nova::newResourceFromModel(App\Post::find(1)); 142 | 143 | MorphTo::make('Commentable') 144 | ->types(...) 145 | ->default($postResource), 146 | ``` 147 | 148 | ### Default the last saved value 149 | 150 | Use the `defaultLast()` method to cache the last value that was saved for this field and repopulate it the next time this field is resolved: 151 | 152 | ```php 153 | Text::make('Name') 154 | ->defaultLast(), 155 | ``` 156 | 157 | This will be useful in the following scenarios: 158 | * After saving (create or update) one resource, the last value will be repopulated when creating the next new resource. 159 | * After running an action on one resource, the last value will be repopulated when running the same action on another resource. 160 | 161 | The value is cached uniquely to the user, resource class, action name (if applicable), field, and attribute. The default cache duration is an hour, but this is customisable (see [Configuration](#configuration)). 162 | 163 | This can be used, for example, to speed up creating multiple resources one after another with the same parent resource, e.g. 164 | 165 | ```php 166 | BelongsTo::make('Author') 167 | ->defaultLast(), 168 | ``` 169 | 170 | *Note:* The `defaultLast()` method handles the morph type for `MorphTo` fields automatically. 171 | 172 | *Note:* Because the "Select Action" dropdown is not refreshed after an action is run on the index view, `defaultLast()` cannot repopulate each last value if you run the action several times while on the same index view. If you need the value to be repopulated every time on the index view, you can set the property `$refreshIndex = true` on the action class, e.g. 173 | 174 | ```php 175 | class YourAction extends Action 176 | { 177 | protected $refreshIndex = true; 178 | 179 | public function handle(ActionFields $fields, Collection $models) 180 | { 181 | // ... 182 | } 183 | } 184 | ``` 185 | 186 | When the action is run from the index view, it will return a redirect response to refresh the whole page. It has no effect if the action is run from the detail view, because Nova already refreshes the page after each action automatically. 187 | 188 | > :confounded: I don't really like this workaround but can't think of an alternative. I would be happy to hear other ideas. 189 | 190 | *Note:* If you set `$refreshIndex = true`, and you return your own [action response](https://nova.laravel.com/docs/2.0/actions/defining-actions.html#action-responses) from the action's `handle()` method, it will be ignored on the index view because it is overridden by the redirect response. It will behave as normal on the detail view. 191 | 192 | ### Display using a callback 193 | 194 | Both the `default($value, callable $callback = null)` and `defaultLast(callable $callback = null)` methods can take a callback as the final parameter which will transform the defaulted value (whether retrieved from cache or passed to the `default()` method) before it is populated, e.g. 195 | 196 | ```php 197 | $lastInvoiceNumber = auth()->user()->last_invoice_number; 198 | 199 | Number::make('Invoice Number') 200 | ->default($lastInvoiceNumber, function($value, NovaRequest $request) { 201 | return $value + 1; // Note: Here the $value came from $lastInvoiceNumber 202 | }), 203 | ``` 204 | 205 | ```php 206 | Number::make('Invoice Number') 207 | ->defaultLast(function($value, NovaRequest $request) { 208 | return $value + 1; // Note: Here the $value came from the cache 209 | }), 210 | ``` 211 | 212 | This can be used, for example, to increment a value each time a new resource is created. *Note:* This is a convenience only and should not be relied upon for uniqueness or strictly sequential incrementing. 213 | 214 | ### Default last value _or_ static value 215 | 216 | If the user does not yet have a 'last' value stored, or the cache has expired, the value for `defaultLast()` will be blank. If you want to fall back to another value if nothing is found in the cache, you can simply do this in the callback, e.g. 217 | 218 | ```php 219 | BelongsTo::make('Author') 220 | ->defaultLast(function($value, NovaRequest $request) { 221 | return $value ?: $request->user()->id; 222 | }), 223 | ``` 224 | 225 | In this example, the author of the first record created will default to the current user, but every subsequent record will default to the last value saved. 226 | 227 | ## Advanced Usage 228 | ### Extend 229 | 230 | Out of the box, the package supports all standard Nova fields which have a single value and can be edited on the 'create' form. There is specific behaviour for the `BelongsTo` and `MorphTo` fields, as described above. 231 | 232 | This package does not support any of the fields that implement `Laravel\Nova\Contracts\ListableField`, such as `HasMany`, `BelongsToMany` etc., or fields that extend `Laravel\Nova\Fields\File`, such as `File`, `Image` or `Avatar`. 233 | 234 | Any custom field with a single value which extends `Laravel\Nova\Fields\Field` *should* work without customisation. However, if required, you can extend the behaviour to support custom field types which need additional metadata to be populated. 235 | 236 | The `DefaultableField::extend()` method takes the class name of your custom field and a callback which receives `$field` (the resolved `Field` instance) and `$value` (the value that was passed to `default()` or retrieved from cache by `defaultLast()`). 237 | 238 | You **must** return the `$field` from your callback, and it is suggested that you use `$field->withMeta()` to send the appropriate metadata that will cause the field to be prepopulated, e.g. in your `App\Providers\NovaServiceProvider`: 239 | 240 | ```php 241 | use Inspheric\NovaDefault\DefaultableField; 242 | 243 | DefaultableField::extend(YourField::class, function($field, $value) { 244 | return $field->withMeta([ 245 | 'value' => $value, // This line is usually required to populate the value 246 | 'yourMeta' => 'yourValue', // Any additional meta depends on your custom field type 247 | ]); 248 | }); 249 | ``` 250 | 251 | *Note:* If you are using `extend()` as above, the basic defaulting functionality of this package is *completely* overridden, so you must ensure that your own callback sets the field's value correctly. This is usually done by setting the `'value'` meta key, but your field may differ, or may need additional meta keys to be set (such as `'belongsToId'` in the case of a `BelongsTo` field). 252 | 253 | You can pass an array of field types as the first argument to use the same callback on all of them, i.e. 254 | 255 | ```php 256 | DefaultableField::extend([YourField::class, YourOtherField::class], function($field, $value) { 257 | // ... 258 | }); 259 | ``` 260 | 261 | Alternatively, `Inspheric\NovaDefault\DefaultableField` is macroable, so you can add a macro and then use the macro's name as a string as the second argument for the `extend()` method, i.e. 262 | 263 | ```php 264 | DefaultableField::macro('handleYourField', function($field, $value) { 265 | // ... 266 | }); 267 | 268 | DefaultableField::extend(YourField::class, 'handleYourField'); 269 | DefaultableField::extend(YourOtherField::class, 'handleYourField'); 270 | ``` 271 | 272 | ## Configuration 273 | 274 | The configuration can be published using: 275 | ```bash 276 | php artisan vendor:publish --provider="Inspheric\NovaDefaultable\DefaultableServiceProvider" --tag="config" 277 | ``` 278 | 279 | The configuration file contains two keys: 280 | * `cache.key` - The key to use to store the "last" values in the cache. Defaults to 'default_last' and will be prepended to the authenticated user ID, resource class and field attribute for uniqueness. 281 | * `cache.ttl` - The time to store the last values in the cache, in seconds. Defaults to one hour (60 * 60). 282 | 283 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inspheric/nova-defaultable", 3 | "description": "Default values for Nova fields when creating resources and running resource actions.", 4 | "keywords": [ 5 | "laravel", 6 | "nova", 7 | "field", 8 | "default", 9 | "prepopulate", 10 | "default value", 11 | "cache", 12 | "action", 13 | "create", 14 | "update" 15 | ], 16 | "license": "MIT", 17 | "require": { 18 | "php": ">=7.1.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Inspheric\\NovaDefaultable\\": "src/" 23 | } 24 | }, 25 | "extra": { 26 | "laravel": { 27 | "providers": [ 28 | "Inspheric\\NovaDefaultable\\DefaultableServiceProvider" 29 | ] 30 | } 31 | }, 32 | "config": { 33 | "sort-packages": true 34 | }, 35 | "minimum-stability": "dev", 36 | "prefer-stable": true 37 | } 38 | -------------------------------------------------------------------------------- /config/defaultable_field.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'key' => 'default_last', // The key to use in the cache. It will be made unique with the authenticated user's ID. 6 | 'ttl' => 60 * 60, // The number of seconds to store each cached value. 7 | ], 8 | ]; 9 | -------------------------------------------------------------------------------- /src/DefaultableField.php: -------------------------------------------------------------------------------- 1 | 'handleMorphTo', 32 | BelongsTo::class => 'handleBelongsTo', 33 | ]; 34 | 35 | /** 36 | * Classes or interfaces which are not supported. 37 | * 38 | * @var array 39 | */ 40 | protected static $unsupported = [ 41 | ListableField::class, 42 | File::class, 43 | ]; 44 | 45 | /** 46 | * Enable defaulting behaviour on a custom field type. 47 | * 48 | * @param string|\Laravel\Nova\Fields\Field|string[]|\Laravel\Nova\Fields\Field[] $field 49 | * @param string|callable $macro 50 | * 51 | * @return void 52 | */ 53 | public static function extend($field, $macro) 54 | { 55 | if (is_array($field)) { 56 | foreach ($field as $fld) { 57 | $fld = is_object($fld) ? get_class($fld) : $fld; 58 | 59 | static::$fieldMacros[$fld] = $macro; 60 | } 61 | 62 | return; 63 | } 64 | 65 | $field = is_object($field) ? get_class($field) : $field; 66 | 67 | static::$fieldMacros[$field] = $macro; 68 | } 69 | 70 | /** 71 | * Set a default field value on a create request. 72 | * 73 | * @param \Laravel\Nova\Fields\Field $field 74 | * @param mixed|array|callable $value 75 | * @param callable|null $callback 76 | * 77 | * @return \Laravel\Nova\Fields\Field 78 | */ 79 | public static function default(Field $field, $value, callable $callback = null) 80 | { 81 | foreach (static::$unsupported as $unsupported) { 82 | if ($field instanceof $unsupported) { 83 | $class = get_class($field); 84 | 85 | throw new InvalidArgumentException("Field type `$class` does not support defaultable values"); 86 | } 87 | } 88 | 89 | $request = app(NovaRequest::class); 90 | 91 | if ($request->isCreateOrAttachRequest() || static::isActionRequest($request)) { 92 | if (!is_array($value) && !is_string($value) && is_callable($value)) { 93 | $value = call_user_func($value, $request); 94 | } 95 | 96 | if (is_callable($callback)) { 97 | $value = call_user_func($callback, $value, $request); 98 | } 99 | 100 | foreach (static::$fieldMacros as $class => $macro) { 101 | if ($field instanceof $class) { 102 | if (is_callable($macro)) { 103 | return call_user_func($macro, $field, $value); 104 | } elseif (is_string($macro) && method_exists(static::class, $macro)) { 105 | return call_user_func([static::class, $macro], $field, $value); 106 | } elseif (is_string($macro) && static::hasMacro($macro)) { 107 | return static::__callStatic($macro, [$field, $value]); 108 | } else { 109 | throw new BadMethodCallException("Invalid defaultable field handler for `$class`"); 110 | } 111 | } 112 | } 113 | 114 | return $field->withMeta([ 115 | 'value' => $value, 116 | ]); 117 | } 118 | 119 | return $field; 120 | } 121 | 122 | /** 123 | * Set the default to the last used value. 124 | * 125 | * @param \Laravel\Nova\Fields\Field $field 126 | * @param callable|null $callback 127 | * 128 | * @return \Laravel\Nova\Fields\Field 129 | */ 130 | public static function defaultLast(Field $field, callable $callback = null) 131 | { 132 | $request = app(NovaRequest::class); 133 | 134 | if ($request->isCreateOrAttachRequest() || static::isActionRequest($request)) { 135 | $action = static::getActionName($request); 136 | $cacheKey = static::cacheKey($request, $field, $action); 137 | 138 | $last = Cache::get($cacheKey); 139 | 140 | $field->default($last, $callback); 141 | } 142 | 143 | return $field->withMeta(['defaultLast' => 'true']); 144 | } 145 | 146 | /** 147 | * Determine the name of the action. 148 | * 149 | * @param NovaRequest $request 150 | * 151 | * @return string|null 152 | */ 153 | protected static function getActionName(NovaRequest $request) 154 | { 155 | $action = null; 156 | 157 | if (static::isActionRequest($request)) { 158 | $action = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 6)[5]['object'] ?? null; 159 | 160 | if ($action instanceof Action) { 161 | $action = $action->uriKey(); 162 | } else { 163 | $action = null; 164 | } 165 | } 166 | 167 | return $action; 168 | } 169 | 170 | /** 171 | * Determine whether the request is an Action request. 172 | * 173 | * @param \Laravel\Nova\Http\Requests\NovaRequest $request 174 | * 175 | * @return bool 176 | */ 177 | protected static function isActionRequest(NovaRequest $request) 178 | { 179 | return $request->route()->getController() instanceof ActionController; 180 | } 181 | 182 | /** 183 | * Store the last value for a field on a resource. 184 | * 185 | * @param \Laravel\Nova\Http\Requests\NovaRequest $request 186 | * @param \Laravel\Nova\Fields\Field $field 187 | * @param string|null $action 188 | * 189 | * @return string 190 | */ 191 | public static function cacheLastValue(NovaRequest $request, Field $field, $action = null) 192 | { 193 | if ($field instanceof MorphTo) { 194 | $cacheable = [ 195 | $request->{$field->attribute}, 196 | $request->{$field->attribute.'_type'}, 197 | ]; 198 | } else { 199 | $cacheable = $request->{$field->attribute}; 200 | } 201 | 202 | $cacheKey = self::cacheKey($request, $field, $action); 203 | 204 | Cache::put($cacheKey, $cacheable, config('defaultable_field.cache.ttl')); 205 | } 206 | 207 | /** 208 | * Return the cache key for the field on the resource. 209 | * 210 | * @param \Laravel\Nova\Http\Requests\NovaRequest $request 211 | * @param \Laravel\Nova\Fields\Field $field 212 | * @param string|null $action 213 | * 214 | * @return string 215 | */ 216 | public static function cacheKey(NovaRequest $request, Field $field, $action = null) 217 | { 218 | if (static::isActionRequest($request)) { 219 | $action = ($action ?: $request->query('action')).'::'; 220 | } 221 | 222 | return config('defaultable_field.cache.key').'.'.auth()->id().'.'.md5($request->resource().'::'.$action.get_class($field).'::'.$field->attribute); 223 | } 224 | 225 | /** 226 | * Default behaviour for MorphTo fields. 227 | * 228 | * @param \Laravel\Nova\Fields\MorphTo $field 229 | * @param mixed|array|\Illuminate\Database\Eloquent\Model|\Laravel\Nova\Resource $value 230 | * 231 | * @return \Laravel\Nova\Fields\MorphTo 232 | */ 233 | protected static function handleMorphTo(MorphTo $field, $value) 234 | { 235 | if (is_array($value)) { 236 | list($value, $type) = $value; 237 | 238 | if ($value instanceof Model) { 239 | $value = $value->getKey(); 240 | } 241 | 242 | if (is_a($type, Resource::class, true)) { 243 | $type = $type::uriKey(); 244 | } elseif (is_a($type, Model::class, true)) { 245 | $resource = Nova::resourceForModel($type); 246 | $type = $resource::uriKey(); 247 | } 248 | } elseif (is_a($value, Resource::class)) { 249 | $type = $value::uriKey(); 250 | $value = $value->model()->getKey(); 251 | } elseif (is_a($value, Model::class)) { 252 | $model = Nova::newResourceFromModel($value); 253 | $type = $model::uriKey(); 254 | $value = $value->getKey(); 255 | } else { 256 | $type = null; 257 | $value = null; 258 | } 259 | 260 | return $field->withMeta([ 261 | 'morphToType' => $type, 262 | 'morphToId' => $value, 263 | ]); 264 | } 265 | 266 | /** 267 | * Default behaviour for BelongsTo fields. 268 | * 269 | * @param \Laravel\Nova\Fields\BelongsTo $field 270 | * @param mixed|array $value 271 | * 272 | * @return \Laravel\Nova\Fields\BelongsTo 273 | */ 274 | protected static function handleBelongsTo(BelongsTo $field, $value) 275 | { 276 | if (is_a($value, Resource::class)) { 277 | $value = $value->model()->getKey(); 278 | } elseif (is_a($value, Model::class)) { 279 | $value = $value->getKey(); 280 | } 281 | 282 | return $field->withMeta([ 283 | 'belongsToId' => $value, 284 | ]); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/DefaultableServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 18 | __DIR__.'/../config/defaultable_field.php' => config_path('defaultable_field.php'), 19 | ]); 20 | 21 | $this->mergeConfigFrom( 22 | __DIR__.'/../config/defaultable_field.php', 'defaultable_field' 23 | ); 24 | 25 | Field::macro('default', function($value, callable $callback = null) { 26 | return DefaultableField::default($this, $value, $callback); 27 | }); 28 | 29 | Field::macro('defaultLast', function(callable $callback = null) { 30 | return DefaultableField::defaultLast($this, $callback); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/HasDefaultableActionFields.php: -------------------------------------------------------------------------------- 1 | fields())->filter(function($field) { 25 | return $field->meta['defaultLast'] ?? false; 26 | })->each(function($field) use ($fields) { 27 | $field->withMeta(['value' => $fields->{$field->attribute}]); 28 | $request = app(NovaRequest::class); 29 | 30 | DefaultableField::cacheLastValue($request, $field, $this->uriKey()); 31 | }); 32 | 33 | $results = parent::handleResult($fields, $results); 34 | 35 | return $this->refreshIndex($results); 36 | } 37 | 38 | /** 39 | * Provide the ability to refresh the index page when an action is run, 40 | * so that the action fields' default values can be repopulated. 41 | * 42 | * @param array|null $action 43 | * 44 | * @return array|null 45 | */ 46 | protected function refreshIndex($action = null) 47 | { 48 | if ($this->refreshIndex ?? false) { 49 | $request = app(ActionRequest::class); 50 | 51 | $referrer = $request->header('Referer'); 52 | $uriKey = Nova::newResourceFromModel($request->targetModel())->uriKey(); 53 | 54 | if (Str::endsWith(stristr($referrer.'?', '?', true), '/'.$uriKey)) { 55 | $action = Action::redirect($referrer); 56 | } 57 | } 58 | 59 | return $action; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/HasDefaultableFields.php: -------------------------------------------------------------------------------- 1 | filter(function($field) { 21 | return $field->meta['defaultLast'] ?? false; 22 | })->each(function($field) use ($request) { 23 | DefaultableField::cacheLastValue($request, $field); 24 | }); 25 | 26 | return parent::fillFields($request, $model, $fields); 27 | } 28 | } 29 | --------------------------------------------------------------------------------