garrettmassey's avatar

Dynamically generating Nova Select's 'dependsOn()' method for create form

I'm working on building a support / ticket system where users can submit tickets to the helpdesk and based on the ticket types and system settings, the ticket will be automatically assigned to a specific team. But first, I need to get the creation form for the ticket to work.

A ticket model belongs to a TicketType model. The TicketType is a recursive / self referential model where it can belong to another TicketType, or it can have many child TicketTypes.

TicketType model relationships:

    public function children(): HasMany
    {
        return $this->hasMany(
					related: TicketType::class, 
					foreignKey: 'parent_id'
		);
    }

    public function parent(): BelongsTo
    {
        return $this->belongsTo(
					related: TicketType::class, 
					foreignKey: 'parent_id'
        );
    }

    public function tickets(): HasMany
    {
        return $this->hasMany(Ticket::class);
    }

Now, because the ticket type can reference itself, what I want to do is have a Select field for the 'root' types, that is, the types that do not have a parent type. When a user selects one of the root type options, a new select field should appear with the children of the currently selected root type as the options.

That is, if root types are [A, B, C] and A has children [1, 2, 3], then when the user selects "A" from the Select field, the new Select field that appears under it will show the options [1,2,3], and this would continue recursively until the user selects a final (nth) subtype.

The way I was handling this was by creating a method that returns an array of Select fields and calls on itself to generate the fields using the parent field's information. But when I go to test it, and I select an option I know has children, the child selects never appear, and I cannot figure out why.

Any help would be greatly appreciated!

protected function getTypes(?array $fields = null, ?string $parentFieldName = null, ?int $parentId = null)
    {
        if (!isset($fields)) {
            $fields = [];
        }

        if (is_null($parentFieldName)) {
            // Get root ticket types with their nested children
            $rootTypes = TicketTypeModel::selectGroups();

            // Convert to array for the Select field options
            $rootOptions = $rootTypes->pluck('name', 'id')->toArray();

            // Create the root Select field
            $rootFieldName = 'root_ticket_type_id';

            $fields[] = Select::make('Ticket Type', $rootFieldName)
                ->options($rootOptions)
                ->displayUsingLabels()
                ->rules('required')
                ->fillUsing(function (
							$request, 
							$model, 
							$attribute, 
							$requestAttribute) {
                    		    $model->{$attribute} = $request->input($requestAttribute);
                })->size('w-1/2');

            // Recursively create fields for children
            $rootTypes->each(function ($type) use (&$fields, $rootFieldName) {
                $this->getTypes($fields, $rootFieldName, $type->id);
            });
        } else {
            // Fetch children based on parent ID
            $children = TicketTypeModel::where('parent_id', $parentId)->get();
            if ($children->isNotEmpty()) {
                $childOptions = $children->pluck('name', 'id')->toArray();
                $childFieldName = 'ticket_type_' . $parentId;

                $fields[] = Select::make('Ticket Subtype', $childFieldName)
                    ->options($childOptions)
                    ->displayUsingLabels()
                    ->hide()
                    ->dependsOn([$parentFieldName], function (
							Select $field, 
							NovaRequest $request, 
							FormData $formData ) use ($parentFieldName, $parentId) 
						{
                        if ($formData->select($parentFieldName) == $parentId) {
                            $field->show()->rules('required');
                        }
                    })
                    ->size('w-1/2');

                // Recursively create fields for children
                $children->each(function ($child) use (&$fields, $childFieldName) {
                    $this->getTypes($fields, $childFieldName, $child->id);
                });
            }
        }

        return $fields;
    }
0 likes
0 replies

Please or to participate in this conversation.