Search code examples
laraveleloquentlaravel-bladelaravel-livewirelaravel-9

Pre-Populating Parent Child Form from DB


I have tried prepopulating the forms from DB records, not happening for child form, I have use all conventions for Laravel table, pivot table etc., still nothing. The array display with the dd('$allTariffs') but doesn't show on view file, please help am stuck with

ErrorException foreach() argument must be of type array|object, null given

Edit Component Code

<?php

namespace App\Http\Livewire\Admin;

use App\Models\Products;
use App\Models\Tariffs;
use Livewire\Component;

class AdminEditTariffsComponent extends Component
{

    // public $productId;
    public $tariffId;

    public $allTariffs = [];
    public $rowProducts = [];

    public $tariffName;
    

    public function mount ($tariffId)
    {
        $this->rowProducts = Products::all();
 
        $tariff = Tariffs::where('id', $tariffId)->first();
        $this->tariffName = $tariff->tariff_name;
       
       
        // $allTariffs = $tariff->products()->where('tariffs_id', $tariffId)->get()->toArray();
        $allTariffs = $tariff->products->find($tariffId);
    

        // var_dump($allTariffs) ;
        // dd( $allTariffs); 
        
        foreach ($allTariffs->products as $product)
        {
            ['productId' => $product->pivot->products_id, 'basicCharge' => $product->pivot->basic_charge, 'additionalCharge' => $product->pivot->additional_charge];
        }
     
        

    }

    public function updateStatus()
    {
        $tariff = Tariffs::find($this->tariffId);
        $tariff->tariff_name = $this->tariffName;
        $tariff->created_by = auth()->user()->id;
        $tariff->save();
       
        foreach ($this->allTariffs as $product) {
            $tariff->products()->sync($product['productId'],
                ['basic_charge' => $product['basicCharge'], 
                'additional_charge' => $product['additionalCharge']]);
        }

        session()->flash('message', 'Tariff added successfully!');
        return redirect('/admin/tariffs/');

        
    }

    public function addProduct()
    {
        $this->allTariffs[] = ['productId' => '', 'basicCharge' => '', 'additionalCharge' => ''];
    }

    public function removeProduct($index)
    {
        unset($this->allTariffs[$index]);
        $this->allTariffs = array_values($this->allTariffs);
    }


    public function render()
    {
        $rowProducts = Products::all();
        return view('livewire.admin.admin-edit-tariffs-component',['rowProducts'=>$rowProducts])->layout('layouts.admin.base');
    }
}

Edit View

<div class="container-fluid">
    <!-- Begin Page Content -->
    <!-- Page Heading -->
    <div class="row">
        <div class="col-lg-8">
            <div class="card shadow mb-4">
                <div class="card-header py-3">
                    <h6 class="m-0 font-weight-bold text-primary">Edit Tariff</h6>
                </div>
                <div class="card-body">
                    <form wire:submit.prevent="editTariff">
                        @csrf
                        <div class="form-row">
                            <!-- Default input -->
                            <div class="form-group col-md-8">
                                <input type="text" class="form-control" placeholder="Enter Tariff Name"
                                    wire:model="tariffName" required>
                            </div>
                        </div>
                        <hr>

                        <div class="card">
                            <div class="card-header">
                                <h6 class="text-primary">Products, Basic and Weight Charges</h6>
                            </div>
                            <div class="card-body">
                                <table class="table" id="products_table">
                                    <thead>
                                        <tr>
                                            <th>Product</th>
                                            <th>Basic Charge</th>
                                            <th>Weight Charge</th>
                                            <th></th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        @foreach ($allTariffs as $index => $allTariff)
                                        <tr wire:key="tariff-{{ $value->wireKey }}">
                                            <td>
                                                <select
                                                    wire:model="allTariffs.{{$index}}.productId"
                                                    class="custom-select custom-select-sm form-control form-control-sm">
                                                    <option value="">Select Product</option>
                                                    @foreach ($rowProducts as $product)
                                                    <option value="{{ $product->id }}">
                                                        {{ $product->product_name }}
                                                    </option>
                                                    @endforeach
                                                </select>
                                            </td>
                                            <td>
                                                <input type="text"
                                                    class="form-control custom-select-sm form-control form-control-sm"
                                                    placeholder="Basic Charge"
                                                    wire:model="allTariffs.{{$index}}.basicCharge" required>
                                            </td>
                                            <td>
                                                <input type="text"
                                                    class="form-control custom-select-sm form-control form-control-sm"
                                                    placeholder="Weight Charge"
                                                    wire:model="allTariffs.{{$index}}.additionalCharge" required>
                                            </td>
                                            <td>
                                                <a href="#" wire:click.prevent="removeProduct({{$index}})" class="btn btn-danger btn-circle btn-sm">
                                                    <i class="fas fa-trash"></i>
                                                </a>
                                            </td>
                                        </tr>
                                        @endforeach
                                    </tbody>
                                </table>
                                <div class="row">
                                    <div class="col-md-12">
                                        <a href="#" wire:click.prevent="addProduct" class="btn btn-success btn-circle btn-sm">
                                            <i class="fas fa-plus"></i>
                                        </a>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <hr>

                        <div class="form-row">
                        <div class="form-group col-md-3"> 
                        <button type="submit" class="form-control btn btn-small btn-primary">Edit
                            Tariff</button>
                        </div>
                    </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <!-- /.container-fluid -->
</div>

Model File

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Tariffs extends Model
{
    use HasFactory;

    protected $table = "tariffs";


    public function products()
    {
        return $this->belongsToMany(Products::class, 'products_tariffs', 
        'products_id', 'tariffs_id')
            ->withPivot('basic_charge', 'additional_charge');

    }

    public function productstariffs()
    {
        return $this->hasMany(ProductsTariffs::class); 
    }
}

I have tried prepopulating the forms from DB records, not happening for child form, I have use all conventions for Laravel table, pivot table etc., still nothing. The array display with the dd('$allTariffs') but doesn't show on view file, please help am stuck with

ErrorException foreach() argument must be of type array|object, null given .thankyou


Solution

  • In your foreach in your mount function, you are creating an array but you aren't assigning it to anywhere. Therefore, $allTariffs is empty.

    Also, you're casting your variables by default as an array. You are setting $rowProducts as a Collection, however. But then in your render you are also getting the $rowProducts again. I'd suggest removing the = [] and removing the fetching of the $rowProducts each render.

    public $allTariffs = [];
    public $rowProducts;
    public $tariffName;
    
    public function mount ($tariffId)
    {
        $this->rowProducts = Products::all();
     
        // This assumes the $tarrifId always exists. If this isn't the case, perhaps 
        // wrap the related code in an "if ($tariff instanceof Tariff)"
        $tariff = Tariffs::find($tariffId);
        $this->tariffName = $tariff->tariff_name;
    
        foreach ($tariff->products as $product) {
            $this->allTariffs[] = ['productId' => $product->id, 'basicCharge' => $product->pivot->basic_charge, 'additionalCharge' => $product->pivot->additional_charge];
        }
    }
    
    public function render()
    {
        return view('livewire.admin.admin-edit-tariffs-component')->layout('layouts.admin.base');
    }