Diferencia entre revisiones de «PHP Livewire»

De MediaWiki
Ir a la navegación Ir a la búsqueda
Línea 518: Línea 518:
  
 
         @csrf
 
         @csrf
−
         @include('marcas.campos_marca')
+
         <div>
 +
          Descripción:<input type="text" maxlength="45" size="45" required
 +
              wire:model.live="descripcion" name="descripcion" />
 +
          @error('descripcion') <span class="error">{{ $message }}</span> @enderror
 +
        </div>
 
         <div>
 
         <div>
 
             <input type="submit" name="btnAlta" value="Alta" onClick="return confirm('Estás seguro de dar de alta a nova marca ');"/>
 
             <input type="submit" name="btnAlta" value="Alta" onClick="return confirm('Estás seguro de dar de alta a nova marca ');"/>
 
             <input type="reset" name="btnBorrar" value="Limpar" />
 
             <input type="reset" name="btnBorrar" value="Limpar" />
−
 
 
         </div>
 
         </div>
  

Revisión del 07:05 11 abr 2025

Introducción

  • Livewire permite o uso de compoñentes integrados en Laravel nos que se establece unha comunicación bidireccional (podemos enviar información desde o cliente (HTML) ao servidor (controlador) e viceversa sen necesidade de recargar completamente a páxina).
É o mesmo concepto que se empregaba en AJAX e que agora se usa con FETCH, pero integrado no framework de Laravel.



  • IMPORTANTE: Cando vexades a documentación mirade que version estades a consultar xa que dende a versión 2 á versión 3 houbo cambios.
Unha vez instalado en Laravel podedes consultar a versión instalada buscando a entrada livewire/livewire no arquivo composor.json no raíz do proxecto.
Polo de agora (versión 10-12) estamos a traballar coa versión 3.x de Livewire.


Instalación

  • No directorio raíz do proxecto Laravel escribe: composer require livewire/livewire
Unha vez instalado en Laravel podedes consultar a versión instalada buscando a entrada livewire/livewire no arquivo composor.json no raíz do proxecto.
Polo de agora (versión 10-12) estamos a traballar coa versión 3.x de Livewire.



Validación de campos

  • Imos crear a táboa marcas e imos crear unha relación entre perfumes e marcas.
A nivel de mysql:
CREATE TABLE perfumes.marcas (

  `id` int NOT NULL,

  `descripcion` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_spanish2_ci NOT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish2_ci;



INSERT INTO `marcas` (`id`, `descripcion`) VALUES
(1, 'Descripción actualizada da marca con id=1'),
(2, 'ADIDAS'),
(3, 'ARAMIS');


Táboa perfumes
CREATE TABLE perfumes.perfumes (
  `id` int NOT NULL,
  `descripcion` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_spanish2_ci NOT NULL,
  `prezo` decimal(8,2) NOT NULL,
  `data_compra` date DEFAULT NULL,
  `marca_id` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish2_ci;

ALTER TABLE perfumes.perfumes
  ADD PRIMARY KEY (`id`),
  ADD KEY `fk_PERFUMES_1_idx` (`id`),
  ADD KEY `fk_PERFUMES_MARCA_idx` (`marca_id`);

INSERT INTO perfumes.perfumes (`id`, `descripcion`, `prezo`, `data_compra`, `marca_id`) VALUES
(18, 'Best Aramis', '54.32', '2024-01-18', 2),
(19, 'Novo perfume da marca con id 1', '100.22', '2024-01-22', 1);



  • Creamos o modelo, ruta, controlador e polo de agora imos facer a vista para crear unha nova marca.
Lembrar que esta parte ven ser a continuación do feito na UD6 - Modelo Vista Controlador de Laralve.
Dende o terminal escribimos: php artisan make:model Marca --resource


Arquivo: app/models/Marca.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\Perfume;

class Marca extends Model
{
    public $timestamps = false;

    protected $fillable =   [
        'descripcion',

    ];

    public function perfumes(){
        return $this->hasMany(Perfume::class, 'marca_id','id');     // Non necesitamos poñer xa que 'marca_id','id' cumpren as normas de nomeamento
    }

}


Arquivo: app/models/Perfume.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Carbon\Carbon;

class Perfume extends Model
{
    protected $table = 'perfumes';  // Indicamos a táboa relacionada con este modelo.
    protected $primaryKey = 'id';    // Indicamos a clave primaria da táboa

    public $timestamps = false;    // Por defecto laravel inclúe dous campos (created_at e updated_at) de tipo datetime cando se fai unha operación de inserción e actualización sobre unha fila. Con isto indicamos que a táboa non ten eses campos.

    protected $casts = [           // Cando accedamos a unha fila empregando este modelo, se poñemos $fila->data_compra teremos un string. Se poñemos o cast, obteremos unha instancia da [https://carbon.nesbot.com/docs/ clase Carbon] de Laravel que nos permite facer operacións e formatear a data.
        'data_compra' => 'date',   // Tamén se pode empregar datetime. Devolve un Carbon
    ];

    protected $fillable =   [                            // Isto xa o veremos. O que indica é que cando facemos unha operación masiva de inserción ou un insert da forma create(datos) soamente deixa enviar en datos (que é un array) as columnas indicadas nesta propiedade (as columnas indicadas son descripcion, prezo e data_compra). Faise por motivos de seguridade.
        'descripcion',
        'prezo',
        'data_compra'
    ];

    protected function DataCompra(): Attribute
    {
        return Attribute::make(
            get: fn($value) => $value ? Carbon::parse($value)->format('d/m/Y') : null,   // Cando empregamos a data da forma echo($perfume->data_compra); convirte o obxecto Carbon a unha cadea có formato correcto
            set: fn($value) => $value ? Carbon::createFromFormat('d/m/Y', $value)->format('Y-m-d') : null   // Cando empregamos a data da forma: $perfume->data_compra = "10/01/2025" o gardará
        );
    }

   protected function Prezo(): Attribute
    {
        return Attribute::make(
            // Cuando accedemos al valor del campo, convertimos el punto en coma
            get: fn ($value) => number_format($value, 2, ',', '.'),
            // Cuando se asigna el valor, reemplazamos la coma por un punto
            set: fn ($value) => str_replace(',', '.', $value),
        );
    }

}


Arquivo: routes/web.php
<?php

use Illuminate\Support\Facades\Route;

use App\Http\Controllers\PerfumeController;
use App\Http\Controllers\MarcaController;

Route::resource('perfumes', PerfumeController::class);
Route::resource('marcas', MarcaController::class);


O arquivo de MarcaController xa se crearia con todo o código necesario para a xestión.


Arquivo resources/views/marcas/form_alta_marcas.blade.php (creado o cartafol marcas dentro de view)
@extends('plantilla');

@section('pageTitle', 'Alta de marcas')

@section('content')
<h2>Alta de Marcas</h2>



@if (session()->has('idPerfume'))
    <h4>Creado un novo rexistro co id: {{session('idPerfume')}}</h4>
@endif

@if(count($errors) > 0)
	<div class="errors">
		<ul>
		@foreach($errors->all() as $error)
			<li>{{ $error }}</li>
		@endforeach
		</ul>
	</div>
@endif


@include('mensaxes.alertas_javascript')


@endsection




Creamos o compoñente Livewire con esta orde: php artisan make:livewire marca
Isto creará dous arquivos en Laravel:
  • CLASS: app/Livewire/Marca.php => Ven a ser o controlador que vimos ata o de agora en Laravel.
  • VIEW: resources/views/livewire/marca.blade.php => Ven a ser a vista que vai visualizar Laravel no cliente.


Arquivo: app/Livewire/Marca.php
<?php

namespace App\Livewire;

use Livewire\Component;

class Marca extends Component
{
    public function render()
    {
        return view('livewire.marca');
    }
}
Básicamente indicamos en render a vista que vai enviar ao cliente.


Arquivo resource/views/Livewire/marca.blade.php
<form method="post" action="{{route('marcas.store')}}" name="formulario_marcas">

    @csrf
    <div>
            Descripción:<input type="text" maxlength="45" size="45" required name="descripcion" />
    </div>
    <div>
        <input type="submit" name="btnAlta" value="Alta" onClick="return confirm('Estás seguro de dar de alta a nova marca ');"/>
        <input type="reset" name="btnBorrar" value="Limpar" />

    </div>


</form>
<u<Nota: Non separamos os campos nun arquivo diferente xa que en livewire as operacións de alta e modificación vanse facer sobre a mesma vista.


  • Agora para incluir o compoñente Livewire nunha view de Laravel temos que poñer:
Arquivo resouces/views/marcas/form_alta_marca.blade.php
@extends('plantilla');

@section('pageTitle', 'Alta de Marcas')

@section('content')
<h2>Alta de novos perfumes</h2>


<livewire:marca />


................


  • Se agora poñemosm na url: http://localhost:8080/marcas/create veremos que aparecerá unha vista tal cual aparecía cando fixemos o de perfume, pero a diferenza de perfumes, esta vista (os campos e form) se xestionan cun compoñente Livewire.


  • Agora imos ver os cambios que temos que facer para xestionar os campos do formulario.
Primero imos definir no controlador do compoñente livewire os campos que imos xestionar. No noso caso está a descripcion.
<?php

namespace App\Livewire;

use Livewire\Component;

class Marca extends Component
{
    public $descripcion = "Valor inicial";   // O valor inicial non é necesasrio. Soamente para que vexades como dito valor se carga na vista.

    public function render()
    {
        return view('livewire.marca');
    }
}


Modificamos o campo descripcion.
Arquivo: resources/views/livewire/marca.blade.php
<div>
    Descripción:<input type="text" maxlength="45" required size="45" wire:model="descripcion" name="descripcion" />
</div>
  • wire:model="descripcion": O que estamos a dicir con isto é que este input está vinculado á propiedade $descripcion definida no compoñente Livewire. Calquera modificación realizada polo usuario no cliente (na páxina web) actualizará automaticamente o valor da propiedade no servidor grazas á reactividade proporcionada por Livewire. Ademais, se a propiedade no servidor cambia, ese cambio tamén se reflectirá no cliente.
Se vos dades conta non temos a propiedade value como tiñamos en perfumes.
Se agora cargades a páxina veredes como o valor inicial se carga no input.



  • Agora imos a facer a validación no servidor. Para iso temos que definir unha propiedade $rules coas regras de validación que xa empregamos en Laravel.
Ademáis imos crear un método ao que vai chamar o formulario cando faga submit e que fará a validación dos campos. No exemplo imos chamar ao método gardarMarca.
Arquivo: app/Livewire/Marca.php
<?php

namespace App\Livewire;

use Illuminate\Support\Facades\Log;

use Livewire\Component;

class Marca extends Component
{
    public $descripcion = "Valor inicial";

    protected $rules = [
        'descripcion' => 'required|string|min:5|max:45',
    ];

    public function render()
    {
        return view('livewire.marca');
    }

    public function gardarMarca(){
        $this->validate();

        Log::info('Pasa a validación');
    }
}
Para depurar o funcionamento dos compoñente livewire podemos empregar o facade Log::info e ver as mensaxes no arquivo /storage/logs/laravel.log (podedes borrar o contido antes de executar para ter todo limpo ao principio).


  • Para chamar a ese método temos que modificar a vista do compoñente livewire.
Arquivo: resources/views/livewire/marca.blade.php
<form wire:submit.prevent="gardarMarca">

    @csrf
    <div>
       Descripción:<input type="text" maxlength="45" size="45" required
              wire:model="descripcion" name="descripcion" />
    </div>
    <div>
        <input type="submit" name="btnAlta" value="Alta" onClick="return confirm('Estás seguro de dar de alta a nova marca ');"/>
        <input type="reset" name="btnBorrar" value="Limpar" />

    </div>


</form>
Con isto wire:submit.prevent="gardarMarca" estamos a decir que cando faga o submit chame de forma asíncrona ao controlador do compoñente livewire ao método gardarMarca.
Nota: Podemos asociar calquera click de botón directamente ao un método no controlador do livewire, da forma: <button wire:click="gardarMarca">+</button>


  • Tamén imos visualizar os erros de validación que poida enviar o controlador Livewire se non pasa a validación. Para iso temos que empregar a directiva @error sobre cada un dos campos que teñen validación. No exemplo se coloca ao lado do input, pero poderían ir en calquera parte.
Arquivo: resources/views/livewire/marca.blade.php
<div>
    Descripción:<input type="text" maxlength="45" required size="45" wire:model="descripcion" name="descripcion" />  
    @error('descripcion') <span class="error">{{ $message }}</span> @enderror
</div>


  • Se queremos personalizar as mensaxes de erro:
Arquivo: app/Livewire/Marca.php
<?php
...........

    protected $messages = [
        'descripcion.required' => 'La descripción es obligatoria.',
        'descripcion.string' => 'La descripción debe ser una cadena de texto.',
        'descripcion.min' => 'La descripción debe tener al menos 5 caracteres.',
        'descripcion.max' => 'La descripción no puede tener más de 45 caracteres.',
    ];
   ..............



  • Fixerase que neste caso a validación faise cando o usuario preme o botón de submit.
Se queremos que se faga a validación cando o usuario cambio o contido da caixa de texto temos que:
  • Empregar o método update($propiedade) que será chamado de forma automáticamente cando o campo teña unha modificación. Dito método fará a validación da propiedade. Temos que chamar ao método validatyOnly.
  • Cambiar a propiedade wire:model da caixa de texto por wire:model.live
Lembrar que estas chamadas fanse ao servidor polo que pode ser que non nos interese para aforrar recursos e comunicacións entre cliente e o servidor.


Arquivo: resources/views/livewire/marca.blade.php
MOI IMPORTANTE: Tedes que cambiar a propiedade wire:model por wire:model.live
<div>
    Descripción:<input type="text" maxlength="45" size="45" required
        wire:model.live="descripcion" name="descripcion" />
    @error('descripcion') <span class="error">{{ $message }}</span> @enderror
</div>
Arquivo: app/Livewire/Marca.php
    ..............
    public function updated($propiedade){
        $this->validateOnly($propiedade);
    }
    .............


Ao facer isto, cada vez que se preme no imput fará a validación.
Lembrar que poñer isto non quita que teñades a chamada $this->validate() cando se vaia a dar de alta/modificar os datos



  • Mirade a documentación xa que a validación tamén se pode definir en cada un dos campos en vez de empregar a propiedade $rules: https://livewire.laravel.com/docs/validation (escoller versión 3 na parte esquerda da páxina)


Operacións de alta-baixa-modificación

Alta

* A alta faise como sempre pero agora está no controlador do compoñente livewire.
: Hai un pequeno detalle xa que como chamamos Alta ao compoñente Livewire, se empregamos o modelo Marca teríamos o mesmo nome. Por iso empregaremos un alias para o modelo.


Arquivo: app/Livewire/Marca.php
   use App\Models\Marca as MarcaModel;

    .............
    public function gardarMarca(){
        $this->validate();

        Log::info('Pasa a validación');

        $marcaCreada = Marca::create([
            'descripcion' => $this->descripcion
        ]);

        session()->flash('message', "Marca creada correctamente con id $marcaCreada->id");
        $this->reset(['descripcion']);   // Borra os datos da caixa de texto.

    }


Fixarse que para enviar unha información á vista empregamos session()->flash que vai gardar nunha variable de sesión (message) o texto a amosar.
Dita variable non pode ser empregada pola páxina, soamente polo compoñente livewire xa que non se recarga a páxina enteira.
A chamada a reset vai poñer en branco os valor da variable $descripcion do compoñente. Pero como temos un valor por defecto na definición poñerá dito valor. Lembrar que non é obrigatorio e podemos quitalo.


  • Neste caso quedaríamos na pantalla de alta pero dita pantalla non se refresca. Soamente refresca o compoñente livewire.


  • Para amosar as mensaxes de que todo foi correcto, imos crear un arquivo na vista.
Tamén imos poñer nese arquivo os posibles erros que enviemos dende o controlador.
Arquivo app/views/mensaxes/mensaxes.blade.php. Lembrar crear o cartafol mensaxes.
@if (session()->has('message'))
    <div class="alert alert-success">
        {{ session('message') }}
    </div>
@endif
Simplemente amosamos o contido da variable de sesión message.


  • Incluimos dito arquivo na vista do compoñente livewire:
Arquivo app/views/livewire/marca.blade.php.
<div>
    <form wire:submit.prevent="gardarMarca">

        @csrf
        <div>
          Descripción:<input type="text" maxlength="45" size="45" required
               wire:model.live="descripcion" name="descripcion" />
          @error('descripcion') <span class="error">{{ $message }}</span> @enderror
        </div>
        <div>
            <input type="submit" name="btnAlta" value="Alta" onClick="return confirm('Estás seguro de dar de alta a nova marca ');"/>
            <input type="reset" name="btnBorrar" value="Limpar" />
        </div>


    </form>

    @include('mensaxes.mensaxes')
</div>

: '''IMPORTANTE:''' O compoñente livewire soamente pode ter un elemento raíz, no exemplo posto <div>. Non podedes poñer o include fora do div.


  • E que pasa se queremos ir a outra pantalla ou que recargue toda a páxina e non soamente o compoñente livewire ? Debemos empregar o método redirectRoute() poñendo o nome da ruta á que queremos ir.
 : Arquivo: app/Livewire/Marca.php
    ...................
    public function gardarMarca()
    {
        $this->validate();

        Log::info('Pasa a validación');

        $marcaCreada = MarcaModel::create([
            'descripcion' => $this->descripcion
        ]);

        session()->flash('message', "Marca creada correctamente con id $marcaCreada->id");
        $this->reset(['descripcion']);

        return $this->redirectRoute('marcas.create');
    }



Modificar

  • Agora, ao engadir unha nova operación temos un problema.
Se non empregáramos livewire teríamos dúas vistas diferentes, unha para alta e outra para modificación, pero ao estar nun compoñente livewire temos que engadir a lóxica para que a vista faga unha cousa ou outra.


Polo tanto imos engadir ao controlador unha variable que vai indicar se estamos en modo edición ou en modo alta. En función desa variable a vista amosará un texto diferente no botón e fará un actión diferente.

Funcionamento


Volver á UD7


-- Ángel D. Fernández González -- (2025).