user14187085
user14187085

Reputation:

Laravel 7 - Array to Database (checkboxes)

I am creating a dashboard for a medical charity.

I have a multistep form setup in Laravel 7. It logs an incident in three steps (hence the session() usage). On step 2, it asks the user to select drugs administered. The drugs come from a database table (drugs) and is displayed to the user using inline checkboxes which they check. This then is returned to the controller as an array.

My problem is as follows:

BLADE VIEW (addincident2.blade.php) [... Replaces some code removed to make easier to read]

<form method="POST" action="{{ route('incident.doadd.2') }}">
    @csrf
    ...                         
    <div class="form-group row">
    <label for="incident_drugs" class="col-md-4 col-form-label text-md-right">{{ __('Drugs Used') }}</label>
        <div class="col-md-6">
            <div class="form-check">
            @foreach ($drugs as $drug)
            <label class="form-check-label">
                <input class="form-check-input @error('incident_drugs') is-invalid @enderror" name="drugs_used[]" type="checkbox" id="{{ $drug->drug_name }}" value="{{ $drug->drug_name }}">{{ $drug->drug_name }}
            </label></br >
            @endforeach
        </div>
            @error('incident_drugs')
            <span class="invalid-feedback" role="alert">
                <strong>{{ $message }}</strong>
            </span>
            @enderror
        </div>
    </div>                      
    ...
</form>

IncidentController

    // NEW INCIDENT ADD METHOD
public function AddIncidentPage1 (Request $request) 
{
    $request->session()->forget('incident');
    $request->session()->forget('drugused');
    $incident = $request->session()->get('incident');
    $drugused = $request->session()->get('drugused');
    $pagetitle = "Add an Incident"; 
    $incidenttypes = DB::table('incident_types')->pluck('incident_type');
            
    return view('incidents.addincident1', compact('pagetitle', 'incidenttypes', 'incident', 'drugused'));
}

public function DoAddIncident1 (Request $request)
{
    $validatedData = $request->validate([
    'incident_date' => 'required|date',
    'incident_number' => 'required|numeric|digits:8',
    'incident_location' => 'required|alpha_num|min:2|max:4',
    'incident_type' => 'required',
    'incident_desc' => 'required',
    'incident_duration' => 'required',
    'incident_work' => 'boolean',
    'incident_mileage' => 'numeric',
    'incident_case_review' => 'boolean',
    'incident_sd' => 'boolean',
    ]);
    
    $incident = new Incident();
    $incident->fill($validatedData);
    $request->session()->put('incident', $incident);
    
    if($validatedData['incident_sd'] == 0)
    {
        return redirect('/incidents/add/2');
    }
    elseif($validatedData['incident_case_review'] == 1)
    {
        return redirect('/incidents/add/3');
    }
    else
    {
        $incident = $request->session()->get('incident');
        $incident->fill($validatedData);
        $incident->incident_userid = Auth::user()->esr_number;
        $incident->save();
        return redirect()->intended('/incidents')->with('success','Incident created successfully!');
    }
}

public function AddIncidentPage2 (Request $request) 
{
    $incident = $request->session()->get('incident');
    $drugused = $request->session()->get('drugused');
    $drugs = Drug::all();
    
    $pagetitle = "Add an Incident"; 
            
    return view('incidents.addincident2', compact('pagetitle', 'incident', 'drugs', 'drugused'));
}

public function DoAddIncident2 (Request $request)
{
    $validatedData = $request->validate([
    'incident_fos' => 'boolean',
    ]);
    $incident = $request->session()->get('incident');
    $incident->fill($validatedData);
    $request->session()->put('incident', $incident);

    
    if($incident['incident_case_review'] == 1)
    {
        return redirect('/incidents/add/3');
    }
    else
    {
        $incident->incident_userid = Auth::user()->esr_number;
        $incident->save();
        return redirect()->intended('/incidents')->with('success','Incident created successfully!');
    }
}

public function AddIncidentPage3 (Request $request) 
{
    $incident = $request->session()->get('incident');
    $drugused = $request->session()->get('drugused');
    
    $pagetitle = "Add an Incident"; 
            
    return view('incidents.addincident3', compact('pagetitle', 'incident', 'drugused'));
}

public function DoAddIncident3 (Request $request)
{
    $validatedData = $request->validate([
    'incident_review_notes' => 'required_if:incident_case_review,1',
    ]);

        $incident = $request->session()->get('incident');
        $incident->fill($validatedData);
        $incident->incident_userid = Auth::user()->esr_number;
        $incident->save();
        $request->session()->forget('incident');


    return redirect()->intended('/incidents')->with('success','Incident created successfully!');
}

Upvotes: 2

Views: 174

Answers (1)

chugadie
chugadie

Reputation: 2873

Don't use the drug name as the ID (or value="" field) of the checkbox. Use a unique database ID.

When you name an html field ending with [], then PHP will make it an array.

    $user = $reqest->user();
    //is user related to drugs via drugs used?

    $drugsSubmitted = $request->input('drugs-used');
    $drugsSubmitted->each(function($item) use ($user) {
        $loadedDrug = \App\Models\Drug::find($item); //because it is a list of IDs from HTML POST
        $user->drugs()->associate($loadedDrug);
    });
    $user->drugs()->save();

But this doesn't remove any previous relationships. We want what was submitted to be the definitive list of drugs used. So we would have to clear out existing relationship from that user before calling $model->relation()->associate( $modelB )

You can use the $model->relation()->sync( $arrayOfIds ) sync method to save the absolute list of related IDs in 1 call.

The relation between a user and all possible drugs via a DrugsUsed table can be achieved like this:


class User {
    public function drugs() {
        return $this->belongsToMany('App\Models\Drug');
        //optionally you can specify the table which holds the many-to-many ids
        // return $this->belongsToMany('App\Models\Drug', 'user_drug_rel');
    }
}

(You will need to create the many-to-many table yourself with a migration)

php artisan make:migration create_user_drug_relation_table
public function up()
{
    Schema::create('user_drug_rel', function($table) {
        $table->increments('id');
        $table->integer('user_id');
        $table->integer('drug_id');
        $table->timestamps();
    });
}

Having described the "standard" way to save a many-to-many relationship (https://laravel.com/docs/6.x/eloquent-relationships) I would not limit my saving of the data to only 1 method.

When someone is dealing with clinical data, I usually jump to EAV modeling, because the data will likely change during the lifetime of the study. Which drugs are available, which may or may-not be selected may change. They might ask the same question again after 6 months.

I would load all of the drugs from the DB based on their ID (like the first each loop) and save the Name of the drug, the ID, the date, and a lot of other things into a json blob and store it in a table like user_form_submissions. This way, if someone hits back and changes the checkbox and submits again, you would have an ever increasing (append-only) log of what was submitted when.

Now that I've read your Incident controllers. I would definitely just add a json blob column to that table, and loop over submitted drug IDs and create an array of arrays.

Also, you can create an Incident and put the ID into the forms as hidden input types instead of trying to pass around objects/IDs in the session.


//Incident controller

public function doSavePage1(Request $req) {
    //validate
    $i = Incident::create([
         'attribute' => $request->input('attribute'),
         // etc...
    ]);

    return view('step.2.template', [
        'incident_id' => $i->id,
    ]);
}


public function doSavePage2(Request $req) {
    $i = Incident::find($request->input('incident_id'));
    if($i ==null) {
        return response()->back();
    }
}

Upvotes: 2

Related Questions