Статьи

CRUD (Создать чтение, удалить, удалить) в приложении Laravel

Если вы хотите узнать больше о Laravel, посмотрите наш живой урок Laravel с Исааком Кастильо , преподавателем нашего недавнего курса Laravel.

В предыдущей части мы загрузили наше приложение Laravel CRUD, создав базу данных, некоторые контроллеры, базовые маршруты и простые представления. В этой части мы завершим все и осуществим правильный CRUD.

Laravel Logo

Если вы хотите пройти эту интерактивную прогулку по документам Laravel, пожалуйста, ознакомьтесь с первой частью.

Создание записи

Продолжая прямо там, где мы остановились, давайте создадим страницу, на которой мы фактически выполним это действие. В нашем TasksController , давайте вернем представление как это:

 public function create() { return view('tasks.create'); } 

А теперь, в нашем каталоге views, давайте создадим tasks/create.blade.php и введем некоторый tasks/create.blade.php контент:

 @extends('layouts.master') @section('content') <h1>Add a New Task</h1> <p class="lead">Add to your task list below.</p> <hr> @stop 

На этом этапе мы могли бы вручную создать форму, но Laravel предлагает пакет для облегчения этой нагрузки для нас — Illuminate / Html . Давайте быстро это выполним, выполнив следующую команду:

 composer require illuminate/html 

Теперь, внутри нашего файла config/app.php , давайте добавим поставщика услуг в список:

 'Illuminate\Html\HtmlServiceProvider', 

Давайте добавим псевдонимы:

 'Form' => 'Illuminate\Html\FormFacade', 'Html' => 'Illuminate\Html\HtmlFacade', 

Теперь мы можем легко создать форму в нашем файле create.blade.php . Давайте продолжим и сделаем это с использованием форм-фасада и синтаксиса блейдов:

 {!! Form::open([ 'route' => 'tasks.store' ]) !!} <div class="form-group"> {!! Form::label('title', 'Title:', ['class' => 'control-label']) !!} {!! Form::text('title', null, ['class' => 'form-control']) !!} </div> <div class="form-group"> {!! Form::label('description', 'Description:', ['class' => 'control-label']) !!} {!! Form::textarea('description', null, ['class' => 'form-control']) !!} </div> {!! Form::submit('Create New Task', ['class' => 'btn btn-primary']) !!} {!! Form::close() !!} 

Проверьте этот скриншот нашего представления create до сих пор.

Create view default

Здесь важно отметить, что я указал маршрут, по которому мы будем отправлять сообщения, в соответствии с нашим списком маршрутов. Мы будем использовать метод store для обработки данных, поэтому давайте вернемся к нашему TasksController и начнем обработку этих данных.

Давайте обратим наши головы к запросам в Laravel.

Фасад запроса предоставит вам доступ к текущему запросу, связанному в контейнере.

Мы можем получить наш экземпляр запроса двумя способами, согласно документации. Давайте придерживаться метода внедрения зависимостей. Наш метод store теперь должен выглядеть так:

 public function store(Request $request) { // } 

Теперь мы можем выгрузить информацию, чтобы увидеть, что публикуется. Мы будем использовать вспомогательную функцию dd() , которая по умолчанию включена в Laravel. Он объединяет компонент SyDony VarDupmer и функцию PHP die . Добавьте следующее в метод store :

 dd($request->all()); 

Теперь отправьте пустую форму, и вы увидите данные. Вернитесь и заполните несколько фиктивных данных в форме, и вы увидите, что запрос обновлен. Если мы не заинтересованы в какой-либо проверке, то сохранить новое задание в базе данных легко. В документации по Eloquent мы заметим, что мы можем вызвать метод create, чтобы создать новую строку в нашей таблице. Давайте сделаем это, добавив следующее в наш метод store . Мы также перенаправим обратно туда, откуда пришли:

 public function store(Request $request) { $input = $request->all(); Task::create($input); return redirect()->back(); } 

Мы готовы создать новую задачу сейчас. Давайте введем некоторые фиктивные данные и отправим их. Э-э-э … есть MassAssignmentException . Laravel по умолчанию предотвращает массовое назначение, что хорошо. Это просто означает, что мы должны объявить, какие поля можно назначать по массе. Я предлагаю вам прочитать об этом , но вот как будет выглядеть наша обновленная модель Task :

 class Task extends Model { /** * Fillable fields * * @var array */ protected $fillable = [ 'title', 'description' ]; } 

Теперь давайте попробуем добавить нашу задачу снова. Если мы добились успеха, мы должны быть перенаправлены обратно туда, откуда мы пришли, то есть на страницу «создать задачу». Сейчас нет никаких указаний относительно того, была ли задача успешно добавлена, но давайте проверим базу данных через командную строку:

 sqlite3 storage/database.sqlite select * from tasks; 

Мы должны увидеть таблицу, возвращенную с нашей новой записью. Сладкий! Как насчет сообщений об успехе и проверки? Давайте сначала проверим наш ввод, чтобы убедиться, что все поля являются обязательными. Laravel поставляется с действительно простым в использовании классом Validator , и быстрое чтение должно заставить нас работать в кратчайшие сроки. Мы проверяем в нашем контроллере, поэтому давайте посмотрим на этот раздел . Давайте проверим наш вклад, добавив это в начало метода store :

 $this->validate($request, [ 'title' => 'required', 'description' => 'required' ]); 

Теперь обратите внимание, что если мы пропустим какой-либо из наших входных данных, остальная часть нашего метода не будет выполнена, и мы останемся на странице с тем, какой ввод уже был введен. Laravel автоматически выдаст ошибку, к которой мы можем обратиться в нашем шаблоне блейда. Давайте вставим следующий фрагмент над нашей формой:

 @if($errors->any()) <div class="alert alert-danger"> @foreach($errors->all() as $error) <p>{{ $error }}</p> @endforeach </div> @endif 

Теперь мы увидим аккуратно выписанные для нас ошибки.

Create task errors

Как насчет сообщения об успехе? Хорошо, если наш валидатор пройдет, остальная часть нашего кода будет выполнена, и мы можем перенаправить обратно с помощью флэш-сообщения Session . Обновите метод store чтобы создать новое флэш-сообщение:

 public function store(Request $request) { $this->validate($request, [ 'title' => 'required', 'description' => 'required' ]); $input = $request->all(); Task::create($input); Session::flash('flash_message', 'Task successfully added!'); return redirect()->back(); } 

Теперь мы можем добавить это в наш шаблон лезвия:

 @if(Session::has('flash_message')) <div class="alert alert-success"> {{ Session::get('flash_message') }} </div> @endif 

Вот что мы должны увидеть.

Create success message

Сейчас мы проверяем и добавляем задачи, а также передаем данные обратно в представление для вывода. Отлично. Нам все еще нужен способ на самом деле увидеть наши записи.

Чтение записей

Вернувшись к нашему методу index , мы теперь можем вывести все задачи, которые мы создали до сих пор. Добавьте это к методу index :

 public function index() { $tasks = Task::all(); return view('tasks.index')->withTasks($tasks); } 

Мы можем получить доступ и вывести задачи следующим образом:

 @foreach($tasks as $task) <h3>{{ $task->title }}</h3> <p>{{ $task->description}}</p> <p> <a href="{{ route('tasks.show', $task->id) }}" class="btn btn-info">View Task</a> <a href="{{ route('tasks.edit', $task->id) }}" class="btn btn-primary">Edit Task</a> </p> <hr> @endforeach 

Вот скриншот для представления индекса.

Task listing

Давайте теперь выясним, как мы собираемся отображать одну запись. В этом текущем приложении это, вероятно, не нужно, потому что мы уже выводим всю информацию, но мы все равно это сделаем. Если мы посмотрим на наш список маршрутов, то tasks.show ясно, что путь tasks.show — это путь. Он принимает подстановочный знак в URL, и для нашего приложения мы будем использовать идентификатор задачи. Как и прежде, мы создадим файл show.blade.php и расширим наш основной макет:

 @extends('layouts.master') @section('content') <h1>{{ $task->title }}</h1> <p class="lead">{{ $task->description }}</p> <hr> <a href="{{ route('tasks.index') }}" class="btn btn-info">Back to all tasks</a> <a href="{{ route('tasks.edit', $task->id) }}" class="btn btn-primary">Edit Task</a> <div class="pull-right"> <a href="#" class="btn btn-danger">Delete this task</a> </div> @stop 

Теперь давайте обновим наш метод show :

 public function show($id) { return view('tasks.show'); } 

Если мы перейдем к URL со случайным подстановочным знаком — /tasks/320918 — мы должны увидеть наш фиктивный шаблон. Давайте на самом деле выбрать правильную задачу. Используя Eloquent, мы можем искать запись с соответствующим идентификатором, и если ничего не найдено, мы сгенерируем ModelNotFoundException которое мы можем перехватить. Если запись найдена, мы можем получить к ней доступ по нашему мнению. Вот обновленный метод:

 public function show($id) { $task = Task::findOrFail($id); return view('tasks.show')->withTask($task); } 

Теперь, на наш взгляд, мы можем правильно вывести запись следующим образом:

 <h1>{{ $task->title }}</h1> <p class="lead">{{ $task->description }}</p> 

Перейдите к tasks/1 , и вы должны увидеть результат.

Single task view

Вернувшись в наше index представление, мы теперь можем вывести ссылки на каждую отдельную задачу:

 <a href="{{ route('tasks.show', $task->id) }}">view</a> 

Обратите внимание, что мы указали правильный подстановочный знак на основе идентификатора задачи. Теперь вы должны быть в состоянии приятно кликнуть! Давайте перейдем к редактированию.

Обновление записи с помощью привязки модели формы

Теперь вы, вероятно, понимаете, как легко подготовить новый вид для нашего приложения RESTful. На этот раз он не отличается, поэтому мы создадим файл edit.blade.php , edit.blade.php макет мастера и edit.blade.php с ним соответствующий метод контроллера. Вот метод edit :

 public function edit($id) { return view('tasks.edit'); } 

И вот мнение, чтобы соответствовать:

 @extends('layouts.master') @section('content') <h1>Edit Task - Task Name </h1> <p class="lead">Edit this task below. <a href="{{ route('tasks.index') }}">Go back to all tasks.</a></p> <hr> @stop 

Если вы посмотрите на список маршрутов, вы заметите, что маршрут редактирования также принимает подстановочный знак. Мы будем последовательны и будем использовать ID. Переход к /tasks/gibberish/edit отобразит фиктивную страницу, но давайте добавим нужный контент. Прежде всего, мы можем обновить все наши ссылки «изменить» в index и show представления следующим образом:

 <a href="{{ route('tasks.edit', $task->id) }}">edit</a> 

Еще раз обратите внимание, как мы вызываем правильный маршрут и передаем соответствующий шаблон. В нашем шаблоне редактирования нам понадобится форма, аналогичная той, которую мы использовали для создания задачи, но было бы очень полезно, если бы форма уже была заполнена существующими полями. Благодаря привязке модели формы Laravel, это кусок пирога. Мы скопируем форму создания, хотя лучшей практикой было бы извлечь ее в какую-то часть. В любом случае мы скопируем его и свяжем с ним нашу модель:

 {!! Form::model($task, [ 'method' => 'PATCH', 'route' => ['tasks.update', $task->id] ]) !!} <div class="form-group"> {!! Form::label('title', 'Title:', ['class' => 'control-label']) !!} {!! Form::text('title', null, ['class' => 'form-control']) !!} </div> <div class="form-group"> {!! Form::label('description', 'Description:', ['class' => 'control-label']) !!} {!! Form::textarea('description', null, ['class' => 'form-control']) !!} </div> {!! Form::submit('Update Task', ['class' => 'btn btn-primary']) !!} {!! Form::close() !!} 

Обратите внимание, как мы используем запрос PATCH в форме, чтобы соответствовать нашему ресурсу RESTful. Обратите также внимание на то, как мы вызываем переменную $task , привязываем ее к модели и ссылаемся на идентификатор, который будет использоваться для просмотра таблицы. Это означает, что мы должны передать правильное задание. В TasksController мы можем обновить метод edit следующим образом:

 public function edit($id) { $task = Task::findOrFail($id); return view('tasks.edit')->withTask($task); } 

Как и раньше, если идентификатор не найден, мы получим ModelNotFoundException . В этот момент мы можем снова скопировать наш фрагмент ошибок, но это совсем не СУХОЙ. Не волнуйтесь, мы можем легко это исправить, используя партиалы. Blade позволяет нам ссылаться на любой файл с помощью директивы @include() . Во-первых, давайте создадим папку в нашем каталоге views, которая называется partials. Там я создам подкаталог с alerts , а затем файл с именем errors.blade.php . Давайте скопируем наш фрагмент ошибок в этот новый файл:

 @if($errors->any()) <div class="alert alert-danger"> @foreach($errors->all() as $error) <p>{{ $error }}</p> @endforeach </div> @endif 

Теперь мы можем ссылаться на него в любом из наших файлов, например так:

 @include('partials.alerts.errors') 

Теперь мы можем заменить исходный фрагмент в нашем шаблоне create.blade.php этой частичной ссылкой, а также ссылаться на него в нашем шаблоне редактирования. Теперь весь вид редактирования должен выглядеть так:

 @extends('layouts.master') @section('content') <h1>Editing "{{ $task->title }}"</h1> <p class="lead">Edit and save this task below, or <a href="{{ route('tasks.index') }}">go back to all tasks.</a></p> <hr> @include('partials.alerts.errors') @if(Session::has('flash_message')) <div class="alert alert-success"> {{ Session::get('flash_message') }} </div> @endif {!! Form::model($task, [ 'method' => 'PATCH', 'route' => ['tasks.update', $task->id] ]) !!} <div class="form-group"> {!! Form::label('title', 'Title:', ['class' => 'control-label']) !!} {!! Form::text('title', null, ['class' => 'form-control']) !!} </div> <div class="form-group"> {!! Form::label('description', 'Description:', ['class' => 'control-label']) !!} {!! Form::textarea('description', null, ['class' => 'form-control']) !!} </div> {!! Form::submit('Update Task', ['class' => 'btn btn-primary']) !!} {!! Form::close() !!} @stop 

Вот скриншот вида, когда мы редактируем задачу.

Editing a task

Давайте теперь перейдем к нашему методу update , который получит данные из отправки формы, и попробуем обновить нашу запись. Как и прежде, мы проверим наш ввод и зарегистрируем ошибки в нашем представлении, если таковые имеются. Если проверка пройдена, мы возьмем ввод, обновим задачу, сохраним ее и перенаправим обратно с сообщением об успешном завершении. Вот как выглядит метод:

 public function update($id, Request $request) { $task = Task::findOrFail($id); $this->validate($request, [ 'title' => 'required', 'description' => 'required' ]); $input = $request->all(); $task->fill($input)->save(); Session::flash('flash_message', 'Task successfully added!'); return redirect()->back(); } 

Попробуйте и убедитесь сами, это работает! Теперь вы можете создавать, читать и обновлять записи, но давайте преодолеем последнее препятствие.

Удаление записи

Удаление записи RESTful действительно требует запроса DELETE. Вы можете обойти это вне контроллера с помощью JavaScript, но это немного выходит за рамки этой статьи. Если мы просмотрим одну задачу, вы заметите, что я оставил там кнопку-заполнитель, чтобы удалить ее. Нам действительно нужно изменить это в форму, которая отправляет запрос DELETE в метод destroy и обрабатывает удаление записи там. Вот наш обновленный шаблон шоу, включающий форму удаления:

 @extends('layouts.master') @section('content') <h1>{{ $task->title }}</h1> <p class="lead">{{ $task->description }}</p> <hr> <div class="row"> <div class="col-md-6"> <a href="{{ route('tasks.index') }}" class="btn btn-info">Back to all tasks</a> <a href="{{ route('tasks.edit', $task->id) }}" class="btn btn-primary">Edit Task</a> </div> <div class="col-md-6 text-right"> {!! Form::open([ 'method' => 'DELETE', 'route' => ['tasks.destroy', $task->id] ]) !!} {!! Form::submit('Delete this task?', ['class' => 'btn btn-danger']) !!} {!! Form::close() !!} </div> </div> @stop 

Внутри нашего TaskController мы можем обработать запрос в методе destroy , округляя наш контроллер RESTful. Еще раз, Eloquent делает это бризом. Мы извлечем связанную запись в таблице, удалим ее и перенаправим обратно в список задач:

 public function destroy($id) { $task = Task::findOrFail($id); $task->delete(); Session::flash('flash_message', 'Task successfully deleted!'); return redirect()->route('tasks.index'); } 

На этом этапе давайте перенесем наше флэш-сообщение в основной файл макета, чтобы оно отображалось в каждом шаблоне при каждом размещении флэш-сообщения. Мы можем удалить его из шаблонов создания и редактирования и сохранить его только в мастер-макете, например так:

 <main> <div class="container"> @if(Session::has('flash_message')) <div class="alert alert-success"> {{ Session::get('flash_message') }} </div> @endif @yield('content') </div> </main> 

Теперь перейдите к задаче и удалите ее. Вы будете перенаправлены обратно в список задач с сообщением, информирующим вас о том, что задача была успешно удалена. Конечно, ваш список задач будет обновлен, чтобы соответствовать.

Завершение

В этом уроке мы рассмотрели множество основных концепций и затронули множество полезных функций Laravel. Создавая это приложение CRUD с нуля, мы смочили наши вкусовые рецепторы рабочим процессом Laravel.

У вас должна быть прочная основа и понимание, чтобы продолжать и строить что-то самостоятельно. Я настоятельно рекомендую проверить различные части документации, чтобы увидеть, что доступно, и просто поэкспериментировать. Используйте это как строительный блок, иди туда и покажи свой кустарный дух. Обратная связь? Комментарии? Оставь их ниже!

Продолжайте изучать Laravel с нашим живым уроком Laravel . Один учитель, один урок, за которым следуют ваши вопросы Ларавела.