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

lara28580's avatar

Repopulate form data on back button browser

I am trying to repopulate my packages properly but that does not work very well. If i click the browser back button it always takes the default value 'small' but that should not be the case. The problem is if the packages are not properly repopulated the user books the wrong package.

I think this does not work x-data="{ package: '{{ old('package_type', 'small') }}' }

My code

<fieldset x-data="{ package:  '{{ old('package_type', 'small') }}' }">
                        <legend class="text-base font-medium text-gray-900">Wähle Dein Paket</legend>

                        <div class="mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-3 sm:gap-x-4">
                            <!--
                            Checked: "border-transparent", Not Checked: "border-gray-300"
                            Active: "border-lime-500 ring-2 ring-lime-500"
                            -->
                            <label
                                :class="package == 'small' ? 'border-lime-500 ring-2 ring-lime-500' : 'border-gray-300'"
                                class="relative bg-white border rounded-lg shadow-sm p-4 flex cursor-pointer focus:outline-none">
                                <input type="radio" x-model="package" name="package_type" value="small" 
                                    class="sr-only" aria-labelledby="package-type-0-label"
                                    :aria-checked="package == 'small'"
                                    aria-describedby="package-type-0-description-0 package-type-0-description-1">
                                <span class="flex-1 flex">
                                    <span class="flex flex-col">
                                        <span id="package-type-0-label"
                                            class="block text-sm font-medium text-gray-900">
                                            Small </span>
                                        <span id="package-type-0-description-0"
                                            class="mt-1 flex items-center text-sm text-gray-500">Normale
                                            Stellenauschreibung</span>
                                        <span id="package-type-0-description-1"
                                            class="mt-6 text-sm font-medium text-gray-900">{{ App\Service\Job\JobService::getEuroAmount(App\Service\Job\JobService::PACKAGE_TYPE_SMALL_PRICE) }}
                                            €</span>
                                    </span>
                                </span>
                                <!--
                                Not Checked: "invisible"

                                Heroicon name: solid/check-circle
                            -->
                                <svg class="h-5 w-5 text-lime-600"
                                    :class="package == 'small' ? 'visible' : 'invisible'"
                                    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
                                    aria-hidden="true">
                                    <path fill-rule="evenodd"
                                        d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
                                        clip-rule="evenodd" />
                                </svg>
                                <!--
                                Active: "border", Not Active: "border-2"
                                Checked: "border-lime-500", Not Checked: "border-transparent"
                            -->
                                <span
                                    class="package == 'small' ? 'border border-lime-500' : 'border-2 border-transparent'"
                                    class="absolute -inset-px rounded-lg border-2 pointer-events-none"
                                    aria-hidden="true"></span>
                            </label>

                            <!--
                        Checked: "border-transparent", Not Checked: "border-gray-300"
                        Active: "border-lime-500 ring-2 ring-lime-500"
                        -->
                            <label
                                :class="package == 'medium' ? 'border-lime-500 ring-2 ring-lime-500' : 'border-gray-300'"
                                class="relative bg-white border rounded-lg shadow-sm p-4 flex cursor-pointer focus:outline-none">
                                <input type="radio" x-model="package" name="package_type" value="medium"
                                    class="sr-only" aria-labelledby="package-type-1-label"
                                    :aria-checked="package == 'medium'"
                                    aria-describedby="package-type-1-description-0 package-type-1-description-1">
                                <span class="flex-1 flex">
                                    <span class="flex flex-col">
                                        <span id="package-type-1-label"
                                            class="block text-sm font-medium text-gray-900">
                                            Medium </span>
                                        <span id="package-type-1-description-0"
                                            class="mt-1 flex items-center text-sm text-gray-500">Stellenauschreibung
                                            mit farblicher Hervorhebung</span>
                                        <span id="package-type-1-description-1"
                                            class="mt-6 text-sm font-medium text-gray-900">{{ App\Service\Job\JobService::getEuroAmount(App\Service\Job\JobService::PACKAGE_TYPE_MEDIUM_PRICE) }}
                                            €</span>
                                    </span>
                                </span>
                                <!--
                                Not Checked: "invisible"

                                Heroicon name: solid/check-circle
                            -->
                                <svg class="h-5 w-5 text-lime-600"
                                    :class="package == 'medium' ? 'visible' : 'invisible'"
                                    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
                                    aria-hidden="true">
                                    <path fill-rule="evenodd"
                                        d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
                                        clip-rule="evenodd" />
                                </svg>
                                <!--
                                Active: "border", Not Active: "border-2"
                                Checked: "border-lime-500", Not Checked: "border-transparent"
                            -->
                                <span
                                    class="package == 'medium' ? 'border border-lime-500' : 'border-2 border-transparent'"
                                    class="absolute -inset-px rounded-lg border-2 pointer-events-none"
                                    aria-hidden="true"></span>
                            </label>

                            <!--
                        Checked: "border-transparent", Not Checked: "border-gray-300"
                        Active: "border-lime-500 ring-2 ring-lime-500"
                        -->
                            <label
                                :class="package == 'premium' ? 'border-lime-500 ring-2 ring-lime-500' : 'border-gray-300'"
                                class="relative bg-white border rounded-lg shadow-sm p-4 flex cursor-pointer focus:outline-none : relative bg-white border rounded-lg shadow-sm p-4 flex cursor-pointer focus:outline-none">
                                <input type="radio" x-model="package" name="package_type" value="premium"
                                    class="sr-only" aria-labelledby="package-type-2-label"
                                    :aria-checked="package == 'premium'"
                                    aria-describedby="package-type-2-description-0 package-type-2-description-1">
                                <span class="flex-1 flex">
                                    <span class="flex flex-col">
                                        <span id="package-type-2-label"
                                            class="block text-sm font-medium text-gray-900">
                                            Premium </span>
                                        <span id="package-type-2-description-0"
                                            class="mt-1 flex items-center text-sm text-gray-500">Stellenauschreibung
                                            mit farblicher Hervorhebung und besserem Ranking</span>
                                        <span id="package-type-2-description-1"
                                            class="mt-6 text-sm font-medium text-gray-900">{{ App\Service\Job\JobService::getEuroAmount(App\Service\Job\JobService::PACKAGE_TYPE_PREMIUM_PRICE) }}
                                            €</span>
                                    </span>
                                </span>
                                <!--
                                    Not Checked: "invisible"

                                    Heroicon name: solid/check-circle
                                -->
                                <svg class="h-5 w-5 text-lime-600"
                                    :class="package == 'premium' ? 'visible' : 'invisible'"
                                    xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
                                    aria-hidden="true">
                                    <path fill-rule="evenodd"
                                        d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
                                        clip-rule="evenodd" />
                                </svg>
                                <!--
                                    Active: "border", Not Active: "border-2"
                                    Checked: "border-lime-500", Not Checked: "border-transparent"
                                -->
                                <span
                                    class="package == 'premium' ? 'border border-lime-500' : 'border-2 border-transparent'"
                                    class="absolute -inset-px rounded-lg border pointer-events-none"
                                    aria-hidden="true"></span>
                            </label>
                        </div>
                    </fieldset>
0 likes
23 replies
jlrdw's avatar

Old data should come from failed validation, not a back button.

1 like
lara28580's avatar

@jlrdw Ok thanks! Maybe you know what I can do about it ? So the package gets re populated properly?

1 like
vincent15000's avatar

@SmokeTM You can for example store the datas in the session and when going back to the previous page you could check if any datas are in the session and populate the fields if needed.

1 like
vincent15000's avatar

@SmokeTM The request class ? You probably mean in the controller ?

And when you say packages, do you mean the form fields ?

Well I don't have enough information to help you.

What is the scenario ? The user is on the webpage, he opens the form and then what happens ? He clicks on a button to go to another page and then he comes back with the back button in the browser ?

lara28580's avatar

@vincent15000 The user opens the form fills in everything and choose a package (form fields). If the form was sent successfully it is possible for the user to click the browser back button. If he does so the default package "small" is chosen not matter what package the user has chosen before. If he then sents the form again not the "small" package is chosen but the package from before if we assume the user has chosen medium or premium before. I hope it is clear what I mean.

This is my request class

class StoreRequest extends FormRequest
{   
    public function response(array $errors){
        return redirect()->back()->withErrors($errors)->withInput();
    }

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {   
        return [
            'title' => 'required|string|max:150',
            'name' => 'required|string|max:30',
            'logo' => 'nullable|image|mimes:jpeg,png,jpg,svg|mimetypes:image/*|max:1024|dimensions:min_width=100,min_height=100',
            'email' => 'required|email|string|max:255',
            'activity' => 'required|string|max:35',
            'industry' => 'required|string|max:255|exists:industries,name',
            'region' => 'required|string|exists:regions,name',
            'city' => 'required|string|max:30',
            'zip_code' => 'required|string|max:7',
            'street' => 'string|max:60',
            'street_number' => 'string|max:5',
            'revenue' => 'required|string|max:6|regex:/^[0-9]+(\,[0-9][0-9]?)?$/',
            'revenue_type' => 'required|string|in:Stunde,Pauschal',
            //'contract' => 'required|string|max:255',
            'work_date' => 'required|date|after_or_equal:now',
            'time_of_use' => 'required|date|after_or_equal:now|before_or_equal:work_date',
            'phone_number' => 'phone:AT,DE|nullable|numeric|string',
            'phone_number_country' => 'required_with:phone_number|nullable|string|in:AT,DE',
            'google_maps' => 'nullable|url|string|max:255',
            'website' => 'url|nullable|string|max:255',
            'card_holder' => 'required|string|max:255',
            'card_holder_email' => 'required|email|string|max:255',
            'card_holder_address' => 'required|string|max:255',
            'card_holder_zip_code'  => 'required|numeric|string',
            'card_holder_city'  => 'required|string|max:255',
            'card_holder_country' => 'required|string|max:255|in:AT,DE,CH',
            'package_type' => 'required|in:' . JobService::PACKAGE_TYPE_SMALL . "," . JobService::PACKAGE_TYPE_MEDIUM . "," . JobService::PACKAGE_TYPE_PREMIUM,
            'agb' => 'accepted',
        ];
    }
}
1 like
vincent15000's avatar

@SmokeTM I think that the response in the form request is only to customize the error response. Can somebody confirm ?

If the form is sent successfully, Laravel empties the form values, so you don't have them anymore.

But with ->withInput() the values should be automatically put into the session. So you should be able to retrieve them as session values.

For example in the store method in your controller.

public function store(FormRequest $request)
{
	$product = new Product;
	$product->fill($request->all());
	$product->save();
	return redirect()->view(...)->withInput();
}

You can also save the needed value in the localStorage (JS) and check the value on each loading of the page.

lara28580's avatar

@vincent15000 Really strange no matter what I try always the small package gets re populated and the medium or premium package stored if chosen before...

1 like
lara28580's avatar

@Snapey

/**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(StoreRequest $request, JobService $jobService)
    {   
        $user = $request->user();
        
        $errorMessage = $jobService->chargeUser($request);

        if(isset($errorMessage)) {
            session()->flash('error', $errorMessage);
            return back();
        }

        if($request->file('logo')) {
            $filePath = $jobService->uploadFile($request->file('logo'));
        }

        $job = new Job();
        $job->user_id = $user ? $user->id : null;
        $job->uuid = $user ? null : (string) Str::uuid();
        $job->title = $request->get('title');
        $job->name = $request->get('name');
        $job->logo = $filePath ?? null;
        $job->email = $request->get('email');
        $job->revenue = $request->get('revenue');
        $job->revenue_type = $request->get('revenue_type');
        //$job->contract = $request->get('contract');
        $job->activity = $request->get('activity');
        $job->industry = $request->get('industry');
        $job->work_date = Carbon::parse($request->get('work_date'))->toDatetimeString();
        $job->time_of_use = Carbon::parse($request->get('time_of_use'))->toDatetimeString();
        $job->region = $request->get('region');
        $job->city = $request->get('city');
        $job->zip_code = $request->get('zip_code');
        $job->street = $request->get('street');
        $job->street_number = $request->get('street_number');
        $job->phone_number_country = $request->get('phone_number_country');
        $job->phone_number = $request->get('phone_number');
        $job->phone = (string) PhoneNumber::make($request->get('phone_number'), $request->get('phone_number_country')) ?? null;
        $job->google_maps = $request->get('google_maps');
        $job->website = $request->get('website');
        
        $package_type = $request->get('package_type');
        if($package_type == JobService::PACKAGE_TYPE_MEDIUM || 
           $package_type == JobService::PACKAGE_TYPE_PREMIUM) {
            $job->color = $jobService->getColorForJob();
        }
        
        if($package_type == JobService::PACKAGE_TYPE_PREMIUM) {
            $job->sticky = 1;
        }
        
        //package prices are stored in cents
        $job->package_price = $jobService->getPackageValue($package_type);
        $job->package_type = $package_type;
        $job->enabled = 1;
        $job->save();

        //send mails to users
        $jobService->jobCreatedEmails($user, $job);

        return redirect()->route('jobs.create')->with('success', 'Die Anzeige wurde erfolgreich erstellt.');
    }
Snapey's avatar

I'm still not sure what you expect to happen? After data is saved, the user is returned to the jobs.create page.

If they press back, then they should see the form with all the fields in the same state as when they first opened the form (ie, all fields empty, and with no old data).

Since you have x-data="{ package: '{{ old('package_type', 'small') }}' } then the package will be set to 'small'` ?

1 like
lara28580's avatar

@Snapey Since you have x-data="{ package: '{{ old('package_type', 'small') }}' } then the package will be set to 'small'` ?

Yes but premium gets sent if the user chose premium before and not small and thats the problem. So what I mean the small package is visually chosen with border color and so on but the premium package is sent.

1 like
lara28580's avatar

No one has an idea what to do here?

1 like
jlrdw's avatar

@SmokeTM use ajax and some javascript to maintain the fields. Also lookup some past replies on preventing back button.

1 like
vincent15000's avatar

@SmokeTM

Yes but premium gets sent if the user chose premium before and not small and thats the problem.

Why is it a problem if premium is sent if the user has chosen premium. That's what you are saying, no ? Can you explain ?

So what I mean the small package is visually chosen with border color and so on but the premium package is sent.

If the premium package is sent, then you have stored the response in the database. Why don't you simply retrieve the chosen package from the database and display the previous page according to this choice ?

lara28580's avatar

@vincent15000

Why is it a problem if premium is sent if the user has chosen premium. That's what you are saying, no ? Can you explain ?

Its a problem because the small package is visually chosen but not really.

Here you can see small chosen but premium sent because premium was chosen by the user before. So the user things he buys the small package but buys then the premium without knowing it.

https://imgur.com/a/QqM4Pmp

If the premium package is sent, then you have stored the response in the database. Why don't you simply retrieve the chosen package from the database and display the previous page according to this choice ?

How does that work on when the user clicks the browser back button?

1 like
vincent15000's avatar

@SmokeTM When the user clicks the back button, it loads the previous page, you can act as for any route.

1 like
lara28580's avatar

@vincent15000 I dont know what you mean. I don't know how I should pass the just created job to the create page on back button click again?

1 like
vincent15000's avatar

@SmokeTM I don't thought about passing it, but about retrieving it from the database. You can retrieve the last created job binded to the authenticated user for example.

1 like
lara28580's avatar

@vincent15000 Thats possible for authenticated users but it is also possible to create a job as non authenticated user in my app.

1 like
vincent15000's avatar
Level 63

@SmokeTM Then why don't you put the datas in the session as I suggested you.

I think that there is a big confusion between what help you need and what we understand to help you.

1 like

Please or to participate in this conversation.