Где воздух гор - там тишина снегов, молчание камней и дремлет сила

Программирование Web

Laravel8 пример CRUD с помощью Jetstream

2021-06-21 22:48:45






В версии Laravel 8.xx появилась новая возможность - новый скаффолдинг
Jetstream, который позволяет быстро генерировать интерфейсы. Попробуем сделать простой CRUD c помощью него.
Далее все расписано пошагово:


1. Создадим новый проект Laravel


sudo composer create-project --prefer-dist laravel/laravel crd


Создадим новую модель Item и таблицу базы данных для нашего CRUDa


2./var/www/crd$ sudo php artisan make:model Item -m

Model created successfully.
Created Migration: 2021_06_09_125123_create_items_table
/var/www/crd$

Затем отредактируем файл миграции, добавим пару новых полей title и description.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateItemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('items', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('items');
}
}


Затем добавим новые поля в класс модели.


<?php

namespace App\Models;

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

class Item extends Model
{
use HasFactory;

protected $fillable = [
'title', 'description',
];

}


3. Теперь нам надо установить с помощью composera непосредственно Jetstream

/var/www/crd$ sudo composer require laravel/jetstream

Потом с npm скомпилируем файлы Jetstream в проект:

/var/www/crd$ sudo npm run dev              


И после чего, запустим скрипт миграции баз данных:

alexander@MyLands:/var/www/crd$ sudo php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (3,288.81ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (1,858.30ms)
Migrating: 2014_10_12_200000_add_two_factor_columns_to_users_table
Migrated:  2014_10_12_200000_add_two_factor_columns_to_users_table (2,284.34ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (1,963.98ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (2,747.60ms)
Migrating: 2021_06_09_125123_create_items_table
Migrated:  2021_06_09_125123_create_items_table (1,276.17ms)
Migrating: 2021_06_09_130105_create_sessions_table
Migrated:  2021_06_09_130105_create_sessions_table (5,392.04ms)
alexander@MyLands:/var/www/crd$


4. Теперь мы можем создать компонент items Liveware, который будет в дальнейшем генерировать весь спектр CRUD действий. Делаем мы это с помощью вновь доступной команды artisan liveware:



var/www/crd$ sudo php artisan make:livewire items

В итоге у нас получается такой код:



<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Models\Item;

class Items extends Component
{
public $items, $title, $description, $item_id;
public $isOpen = 0;

/**
* The attributes that are mass assignable.
*
* @var array
*/
public function render()
{
$this->items = Item::all();
return view('livewire.items');
}

/**
* The attributes that are mass assignable.
*
* @var array
*/
public function create()
{
$this->resetInputFields();
$this->openModal();
}

/**
* The attributes that are mass assignable.
*
* @var array
*/
public function openModal()
{
$this->isOpen = true;
}

/**
* The attributes that are mass assignable.
*
* @var array
*/
public function closeModal()
{
$this->isOpen = false;
}

/**
* The attributes that are mass assignable.
*
* @var array
*/
private function resetInputFields(){
$this->title = '';
$this->description = '';
$this->item_id = '';
}

/**
* The attributes that are mass assignable.
*
* @var array
*/
public function store()
{
$this->validate([
'title' => 'required',
'description' => 'required',
]);

Item::updateOrCreate(['id' => $this->item_id], [
'title' => $this->title,
'description' => $this->description
]);

session()->flash('message',
$this->item_id ? 'Item Updated Successfully.' : 'Item created Successfully.');

$this->closeModal();
$this->resetInputFields();
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
public function edit($id)
{
$item = Item::findOrFail($id);
$this->item_id = $id;
$this->title = $item->title;
$this->description = $item->description;

$this->openModal();
}

/**
* The attributes that are mass assignable.
*
* @var array
*/
public function delete($id)
{
Item::find($id)->delete();
session()->flash('message', 'Item Deleted Successfully.');
}
}



Первый метод render() служит для вывода данных в виде списка. Следующий метод create() вызывает 2 служебных метода:
resetInputFields() и openModal(). Первый из них очищает поля ввода веб-формы, а второй метод вызывает новое модальное окно веб формы.
Есть и другой метод closeModal(), который закрывает это окно.Для записи мы используем метод store(), для редактирования edit($id) с параметром $id записи, удаления delete($id), с тем же параметром.





5. Затем мы редактируем файл роутера web.php, куда помещаем ссылку на наш компонент:




<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
return view('welcome');
});

Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');


use App\Http\Livewire\Items;

Route::get('items', Items::class);





6. Далее исходный код 2 файлов типа blade:первый из них выводит данные на страницу, называется items.blade.php resources/views/livewire








<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Items Liveware CRUD
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg px-4 py-4">
@if (session()->has('message'))
<div class="bg-teal-100 border-t-4 border-teal-500 rounded-b text-teal-900 px-4 py-3 shadow-md my-3" role="alert">
<div class="flex">
<div>
<p class="text-sm">{{ session('message') }}</p>
</div>
</div>
</div>
@endif
<button wire:click="create()" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded my-3">Create Todo</button>
@if($isOpen)
@include('livewire.create')
@endif
<table class="table-fixed w-full">
<thead>
<tr class="bg-gray-100">
<th class="px-4 py-2 w-20">No.</th>
<th class="px-4 py-2">Title</th>
<th class="px-4 py-2">Desc</th>
<th class="px-4 py-2">Action</th>
</tr>
</thead>
<tbody>
@foreach($items as $item)
<tr>
<td class="border px-4 py-2">{{ $item->id }}</td>
<td class="border px-4 py-2">{{ $item->title }}</td>
<td class="border px-4 py-2">{{ $item->description}}</td>
<td class="border px-4 py-2">
<button wire:click="edit({{ $item->id }})" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Edit</button>
<button wire:click="delete({{ $item->id }})" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">Delete</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>

Второй под названием create.blade.php в той же папке resources/views/livewire, служит для создание новой записи и редактирования:

<div class="fixed z-10 inset-0 overflow-y-auto ease-out duration-400">
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<!-- This element is to trick the browser into centering the modal contents. -->
<span class="hidden sm:inline-block sm:align-middle sm:h-screen"></span>?
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" role="dialog" aria-modal="true" aria-labelledby="modal-headline">
<form>
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="">
<div class="mb-4">
<label for="exampleFormControlInput1" class="block text-gray-700 text-sm font-bold mb-2">Title:</label>
<input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="exampleFormControlInput1" placeholder="Enter Title" wire:model="title">
@error('title') <span class="text-red-500">{{ $message }}</span>@enderror
</div>
<div class="mb-4">
<label for="exampleFormControlInput2" class="block text-gray-700 text-sm font-bold mb-2">Description:</label>
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="exampleFormControlInput2" wire:model="description" placeholder="Enter Description"></textarea>
@error('description') <span class="text-red-500">{{ $message }}</span>@enderror
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<span class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
<button wire:click.prevent="store()" type="button" class="inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-green-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green transition ease-in-out duration-150 sm:text-sm sm:leading-5">
Save
</button>
</span>
<span class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
<button wire:click="closeModal()" type="button" class="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-base leading-6 font-medium text-gray-700 shadow-sm hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue transition ease-in-out duration-150 sm:text-sm sm:leading-5">
Cancel
</button>
</span>
</div>
</form>
</div>
</div>
</div>

На этом все, приложение работает.

Здесь нет комментариев


Новый комментарий:
























Яндекс.Метрика