Search code examples
laravelformslaravel-livewire

Save fields from form that dont belong to the Model


Im working with Laravel (11 with livewire). I have a SERVICE and i need to add some SERVICE_EXTEND fields (like wp_postmeta). SERVICE_EXTEND are generic fields with a meta_key and meta_value

Model\Service.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
 * Class Service
 *
 * @property $id
 * @property $father_id
 * @property $category_id
 * @property $customer_id
 * @property $name
 * @property $date
 * @property $description
 * @property $created_at
 * @property $updated_at
 *
 * @property Category $category
 * @property Customer $customer
 * @property Service $service
 * @property Service[] $services
 * @property ServiceExtend[] $serviceExtends
 * @property ServiceItem[] $serviceItems
 * @package App
 * @mixin \Illuminate\Database\Eloquent\Builder
 */
class Service extends Model
{
    
    protected $perPage = 20;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = ['father_id', 'category_id', 'customer_id', 'name', 'date', 'description'];


    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function category()
    {
        return $this->belongsTo(\App\Models\Category::class, 'category_id', 'id');
    }
    
    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function customer()
    {
        return $this->belongsTo(\App\Models\Customer::class, 'customer_id', 'id');
    }
    
    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function service()
    {
        return $this->belongsTo(\App\Models\Service::class, 'father_id', 'id');
    }
    
    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function services()
    {
        return $this->hasMany(\App\Models\Service::class, 'id', 'father_id');
    }
    
    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function serviceExtends()
    {
        return $this->hasMany(\App\Models\ServiceExtend::class, 'id', 'service_id');
    }
    
    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function serviceItems()
    {
        return $this->hasMany(\App\Models\ServiceItem::class, 'id', 'service_id');
    }
    
}

Forms\ServiceForm.php

<?php

namespace App\Livewire\Forms;

use App\Models\Service;
use Livewire\Form;

class ServiceForm extends Form
{
    public ?Service $serviceModel;
    
    public $father_id = '';
    public $category_id = '';
    public $customer_id = '';
    public $name = '';
    public $date = '';
    public $description = '';

    public function rules(): array
    {
        return [
            'category_id' => 'required',
            'customer_id' => 'required',
            'name' => 'required|string',
            'date' => 'required',
            'description' => 'string',
        ];
    }

    public function setServiceModel(Service $serviceModel): void
    {
        $this->serviceModel = $serviceModel;
        
        $this->father_id = $this->serviceModel->father_id;
        $this->category_id = $this->serviceModel->category_id;
        $this->customer_id = $this->serviceModel->customer_id;
        $this->name = $this->serviceModel->name;
        $this->date = $this->serviceModel->date;
        $this->description = $this->serviceModel->description;
    }

    public function store(): void
    {
        $this->serviceModel->create($this->validate());

        $this->reset();
    }

    public function update(): void
    {
        $this->serviceModel->update($this->validate());

        $this->reset();
    }
}

on service\form.blade.php ive added

    <div>
        <x-label for="matricola" :value="__('Matricola')"/>
        <input type="text" name="matricola" id="matricola" wire:model.defer='matricola' class="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
        @error('matricola')
            <x-label class="mt-2" :messages="$message"/>
        @enderror
    </div>

on service\Create.php

<?php

namespace App\Livewire\Services;

use App\Livewire\Forms\ServiceForm;
use App\Models\Service;
use App\Models\Category;
use App\Models\Customer;
use App\Models\Item;
use Livewire\Attributes\Layout;
use Livewire\Component;

class Create extends Component
{
    public ServiceForm $form;

    public function mount(Service $service)
    {
        $this->form->setServiceModel($service);
    }

    public function save()
    {
        
        $this->form->store();

        if ($this->form['matricola']) {
            ServiceExtend::updateOrCreate(
                ['service_id' => $this->form->id, 'meta_key' => 'matricola'],
                ['meta_value' => $this->form['matricola'], 'meta_type' => null]
            );
        }       

        session()->flash('message', 'Service saved successfully.');
        return $this->redirectRoute('services.index', navigate: true);
    }

    #[Layout('layouts.app')]
    public function render()
    {
        $categories = Category::all()->where('type','service')->pluck('name', 'id');
        $customers = Customer::all()->mapWithKeys(function ($customer) {
            return [$customer->id => $customer->surname . ' ' . $customer->name];
        });
        $items = Item::all()->pluck('name','id');
        return view('livewire.service.create', compact('categories','customers','items'));
    }
}

I need to save MATRICOLA on table SERVICE_EXTEND and I dont know how.


Solution

  • Your syntax and setup isn't quite correct:

    • Add matricola to your form:
    public $matricola = '';
    
    • Update syntax of your input:
    <input type="text" id="matricola" wire:model='form.matricola' class="block w-full mt-1 border-gray-300 rounded-md shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
    

    Note: name attribute isn't necessary for Livewire, and .defer has been removed in LW3.

    That's all, actually. You need to specifically route your wire:model to the form property using {formName}.{propertyName}.