Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Wakanda's avatar
Level 10

Programmatic route model binding

Hi Devs,

I have a Question Model that belongs to Either or Practice Or Exam Model

The issue is I want to use one endpoint for creating both Practice and Exam Questions.

My current setup is currently returning a 404

Routes

Route::resource('{exam?}/{practise?}/question', QuestionController::class);

Controller

public function create(Exam $exam, Practise $practise)
    {
        dd($practise);
    }

The above is returning a 404 but for example, if I remove the exam variable in the routes it works

Route::resource('{practise?}/question', QuestionController::class);

How can I solve this, just bear in mind the Question model belongs to many other parent models Like Quizzes, Courses, Tests, and so on. So creating a Controller and an endpoint for each relationship wouldn't make sense. Thank you.

0 likes
11 replies
anilkumarthakur60's avatar

Route::get('{exam}/{practise}/{question}', [QuestionController::class,'create'])

public function create(Exam $exam, Practise $practise, Question $question)
    {
        dd($practise);
    }
Wakanda's avatar
Level 10

@anilkumarthakur60

This will through an error because a route can only have one available param at a given time, depending on the relationship that needs to be constructed.

error

Illuminate\Routing\Exceptions\UrlGenerationException Missing required parameter for [Route: question.create] [URI: admin/{exam}{practise}/question/create] [Missing parameter: practise]. (View: /Users/mac/Documents/Projects/Exam/resources/views/admin/exam/show.blade.php) https://exam.test/admin/exams/0427b820-485a-4d91-9c07-10ba15a6d4e7

anilkumarthakur60's avatar

@Wakanda did you modify the route as below

Route::get('{exam}/{practise}/{question}', [QuestionController::class,'create'])

in your case, you are passing only two parameters through route but your controller is expecting three-parameter

Wakanda's avatar
Level 10

@anilkumarthakur60

I am only passing one param at a time but programmatically meaning at one point a user can pass a quiz param, another time a user may pass an Exam param or a Practise param

the reason why I have 2 params ending with a ? means a param can or cannot be available.

My question reads

How can I solve this, just bear in mind the Question model belongs to many other parent models Like Quizzes, Courses, Tests, and so on. So creating a Controller and an endpoint for each relationship wouldn't make sense. Thank you.

webrobert's avatar

@wakanda

Pretty sure you don’t have to use route model binding. Just pass parameters


Route::get('/questions/{Id}/{model}', QuestionController::class);

Index($id, $model) { // model as string, quiz etc

Question::where(‘id’, $id) ….

}
Wakanda's avatar
Level 10

@shaungbhone

Exam Create

<form 
        action="
        {{ isset($question) ? 
        route('question.update', ['exam' => $exam->id, 'question' => $question->id]) : 
        route('question.store', $exam->id)}}" 
        method="post" enctype="multipart/form-data"
        >

        @csrf
        @if(isset($question))
            @method('PATCH')
        @endif

        <div
            class="px-4 py-3 mb-8 bg-white rounded-lg shadow-md dark:bg-gray-800"
        >

            <label for="body" class="block mt-4 text-sm">
                <span class="text-gray-700 dark:text-gray-400">Question Body</span>
                <input type="hidden" name="body" id="body"
                    class="block w-full mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 form-textarea focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray"
                    rows="3"
                    placeholder="Enter question body."
                    value="{{ isset($question) ? $question->body : old('body') }}"
                >
                <trix-editor input="body" class="dark:text-gray-300"></trix-editor>

                @error('body')
                    <span class="text-xs text-red-600 dark:text-red-400">
                        {{ $message }}
                    </span>
                @enderror
            </label>

            @if(isset($question) && $question->image != '')
                <div class="mt-3 form-group">
                    <img src="{{ asset($question->image) }}" class="w-1/3 h-44">
                </div>
            @endif



            <div class="mt-4">
                <button type="submit" 
                    class="flex items-center justify-between px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple"
                >
                    <span>{{ isset($question) ? 'Update question Details': 'Add question' }}</span>
                    <svg 
                        class="w-4 h-4 ml-2 -mr-1"
                        xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3" />
                    </svg>
                </button>
            </div>
        </form>

The Practise Create blade would be similar as well.

The show is yet to be developed

Wakanda's avatar
Level 10

@anilkumarthakur60 @shaungbhone @webrobert @SilenceBringer

I have found the real problem but still, seek the solution.

After inspecting my URL and if one of the 2 params is null it places a forward slash

https://exam.test/admin/5e16ea62-0ad1-4428-a46d-2b117f1e6d52//question/create
//  as you can see they are 2 // here //question/create 

I have to get rid of one / if any of the params are missing

SilenceBringer's avatar

@Wakanda how you think laravel should imagine which one param do you mean if they will looks the same?

for exam with id of 1: /1/questions

for practice with the id of 1: /1/question

martinbean's avatar
Level 80

@wakanda That’s not how optional parameters, nor resource routes, work. Optional parameters can also only be at the end of a route URI.

Route–model binding here isn’t going to work as Laravel is going to try and scope the question to the previous parameter. So if the question isn’t directly related to the previous parameter, the route binding’s going to fail.

You’ve not shown us what your actual models look like, but if a question can belong to either a practice or an exam, then that’s a clear candidate for a polymorphic relation. You would then need two resource routes:

Route::resource('exam.question', ExamQuestionController::class);
Route::resource('practice.question', PracticeQuestionController::class);
Wakanda's avatar
Level 10

@martinbean

Thanks for responding and yes one to many morph relationship is already in place, and I had already implemented your suggestion and was refactoring thought having many routes and controllers was not such a good idea.

Please or to participate in this conversation.