4

I'm trying to provide a Select list with only records that are related to the model via a pivot table.

While building a time tracker/budgeting software for a client I have two models I'm working with called Budgets and Projects that are joined together with a pivot table. (So budgets, projects, and budget_project)

I'm trying to display all projects that are related to a selected Budget (from the Budget resource when calling an action) on a Select field. I can't figure out how to pass the model->id into the fields function. I will then be running some code to analyze the Projects associated with the given Budget and creating a bunch of records that extend across the date range and other relationships.

Please help!

I'm looking for something like this...

class CreateSchedule extends Action
{
    use InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Perform the action on the given models.
     *
     * @param  \Laravel\Nova\Fields\ActionFields  $fields
     * @param  \Illuminate\Support\Collection  $models
     * @return mixed
     */
    public function handle(ActionFields $fields, Collection $models)
    {
        return Action::message('all good');
    }

    /**
     * Get the fields available on the action.
     *
     * @return array
     */
    public function fields()
    {
        $budgetProject = BudgetProject::where('budget_id',$this->id)->get();

        foreach($budgetProject as $bp){
            $projectArray[$bp->project_id] = $bp->project->name;
        }

        return [
            Select::make('Project','project_id')->options($projectArray),
        ];
    }
}
Nate S
  • 163
  • 3
  • 13

4 Answers4

3

For me it it works like this

pass the id in the Resource class

 (new LogsDiffAction($this->id))

Create a constructor in Action class to receive this parameter

 protected $model;

public function __construct($model)
{
    $this->model = $model;
}

and in fields you can do

if ($this->model) {
        $entity = Activity::find($this->model);
        $model = json_decode($entity->properties, true);
        $partial = view('admin.nova.log-diff-partial', ['model' => $model])->toHtml();

        return [
            Heading::make($partial)->asHtml(),
        ];
    }
1

I face a similar issue when I was working in the newsletter section where I have Template and Campaign models

You can add your model by doing this if you want to get recorded data Notice onlyOneTabeRow function is an inline action and it's Mandatory to pass model

public function actions(Request $request)
{
    return [
        (new CreateCampaign($this))
            ->onlyOnTableRow()
    ];
}

Now you can receive them in CreateCampaign action contractor

public function __construct($model)
{
    $this->template = $model;
}

And without making any request to the database you can get current record data like this

public function fields()
{
    return [
        Text::make('Name', 'name')
            ->default(function ($request) {
                return $this->template->name;
            })
            ->rules('required'),
        Text::make('Subject', 'subject')
            ->default(function ($request) {
                return $this->template->subject;
            })->rules('required'),
        Textarea::make('Content', 'content')
            ->default(function ($request) {
                return $this->template->content;
            })->rules('required')->showOnIndex(),
        Hidden::make('Template Id', 'template_id')
            ->default(function ($request) {
                return $this->template->id;
            })
    ];
}

Here is a photo of what I want once I clicked on inline action Create Campaign in the first record I get a form with the specific record I want displaying in action form

laravel nova inline action

0

For anyone who will struggle with Nova, I had to do something very similar. Just as other have suggested, You need to do the following steps.

  1. Pass whatever you need in the Action class as parameter while registering action. ( new MyAction($this->myParam) )
  2. Make a constructor in your action class and accept the parameter.
protected $receivedParam;

public function __construct($param)
{
   $this->receivedParam = $param;
}

You can then use your parameter in your fields() to do whatever you need to do with it.

Note For Actions initiated From Lens

However, While initiating Action from Lens, this will not work as you won't simply get the parameter while doing ( new MyAction($this->someParam) ). In the context of Lens, You need to first beat the Std Object that pops up and then you need to dig down the resource and then its attributes from $this.

// pass entire $this here and filter down in the Action Class
return [(new GiveMediaAccountAccessViaLens($this))]; // registering action from Lens
// Action Class
    protected $receivedParam;

    public function __construct($receivedParam)
    {
        $this->receivedParam = $receivedParam; // $receivedParam is $this from MediaAccountFilterByClient Lens
    }


    public function fields()
    {
        if(!$this->receivedParam->resource instanceof Model)
        {
            // If you are trying to generate a dropdown/select then you should set your options to
            // empty array. $options = [];
            $options = [];
        }
        else
        {
            // Here, $this->receivedParam->resource contains the Model Object, by calling
            // getRawOriginal() on it, You will get the Original Attributes array which contains
            // key value pair, like "some Attribute => "value"
            // $this->receivedParam->resource->getRawOriginal()['someAttributeOnModel']
            
            // Finally in case you are making a dropdown, you can your data to the array that
            // will be passed to Select field.
            $options = Arr::add($options, "value", "label");
            
        }

        // finally return the fields with custom filled Dropdown
        return [
            // key will be used as key to get the field value in handle()
            Select::make('Label', 'key')->options($options),
        ];
    }

For my use case, I ended up using two Action Classes (one to use while registering action from Lens, another one for Action registration from Resource Class because I was generating a custom dropdown. Nova does some weird stuff and I was getting an invalid argument for foreach while executing action from Resource.

MacTavish
  • 59
  • 8
-1
public function fields(Request $request)
{
    $budgetProject = BudgetProject::where('budget_id',$request->budget_id)->get();

    dd($budgetProject);

    foreach($budgetProject as $bp){
        $projectArray[$bp->project_id] = $bp->project->name;
    }

    return [
        Select::make('Project','project_id')->options($projectArray),
    ];
}
Lakhwinder Singh
  • 5,536
  • 5
  • 27
  • 52
iAmGroot
  • 950
  • 1
  • 6
  • 22
  • ErrorException`"Declaration of App\Nova\Actions\CreateSchedule::fields(App\Nova\Actions\Request $request) should be compatible with Laravel\Nova\Actions\Action::fields()"` – Nate S Jun 11 '19 at 18:08