Данная статья документации актуальна только для версий 5.2 и 5.1 и была удалена в версии 5.3.
Введение
Это руководство позволит вам быстро освоить фреймворк Laravel. Оно содержит информацию о миграциях баз данных, Eloquent ORM, маршрутизации, аутентификации, авторизации, валидации, представлениях и Blade-шаблонах. Это отличная отправная точка для новичков в фреймворке Laravel и PHP-фреймворках в целом. Если вы уже использовали Laravel или другие PHP-фреймворки, вы можете ознакомиться с нашими более продвинутыми руководствами.
Чтобы рассмотреть основной набор функций Laravel, мы создадим простой список задач и будем придерживаться его (типичный пример списка «to-do»). В отличие от базового данное руководство позволит пользователям создавать аккаунты и аутентифицироваться в приложении. Полный финальный вариант исходного кода для этого проекта доступен на GitHub.
Установка
Конечно, в первую очередь вам будет нужен свежий фреймворк Laravel. Чтобы запустить его, вы можете использовать виртуальную машину Homestead или локальную PHP-среду на ваш выбор. Как только ваше окружение будет готово, вы можете установить фреймворк Laravel, используя Composer:
shcomposer create-project laravel/laravel quickstart --prefer-dist
Установка проекта Quickstart (не обязательно)
Вы можете просто прочитать данное руководство. Однако, если вы хотите загрузить исходный код для этого руководства и выполнить его на локальной машине, то можете клонировать Git хранилище и установить зависимости:
shgit clone https://github.com/laravel/quickstart-intermediate quickstart cd quickstart composer install php artisan migrate
Более полную информацию о создании локальной среды разработки Laravel вы сможете найти в документации по Homestead и по установке.
Подготовка базы данных
Миграции БД
Во-первых, давайте используем миграцию для определения таблицы базы данных для хранения всех наших задач. Миграции БД в Laravel позволяют простым способом определить структуру таблицы базы данных и выполнять модификации с использованием простого и выразительного PHP кода. Вместо того чтобы вручную добавлять столбцы в свои локальные копии БД, ваши товарищи по команде могут просто запустить миграции, которые вы поместили в систему управления версиями.
Таблица users
Поскольку мы решили, что пользователь может создать свой аккаунт в приложении, нам нужна таблица для хранениях пользователей. К счастью, Laravel уже поставляется с миграцией, включающей в себя базовую таблицу users. Поэтому нам не нужно вручную её создавать. По умолчанию миграция для таблицы users находится в каталоге database/migrations.
Таблица tasks
Теперь давайте создадим таблицу, которая будет содержать все наши задачи. Для создания различных классов может быть использован интерфейс Artisan. Он избавит вас от ручной генерации кода при создании проектов Laravel. Поэтому давайте используем команду shmake:migration
для создания миграции новой базы данных для нашей таблицы tasks:
shphp artisan make:migration create_tasks_table --create=tasks
Миграция будет помещена в каталог database/migrations вашего проекта. Как вы могли заметить, команда shmake:migration
уже добавила автоинкрементный ID и метки времени к файлу миграции. Давайте отредактируем этот файл и добавим дополнительный столбец name для имён наших задач, а также столбец user_id, который свяжет таблицу tasks с таблицей users:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTasksTable extends Migration
{
/**
* Запуск миграций
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned()->index();
$table->string('name');
$table->timestamps();
});
}
/**
* Откатить миграции
*
* @return void
*/
public function down()
{
Schema::drop('tasks');
}
}
Чтобы запустить нашу миграцию, мы будем использовать команду Artisan shmigrate
. Если вы используете Homestead, вы должны выполнить эту команду на своей виртуальной машине, так как у вашей host-машины не будет прямого доступа к базе данных:
shphp artisan migrate
Эта команда создаст все наши таблицы БД. Если вы просмотрите таблицы, используя какой-либо клиент для БД, вы должны заметить новую таблицу tasks, которая содержит столбцы, определённые в нашей миграции. Теперь мы готовы определить модели Eloquent ORM для наших задач!
Модели Eloquent
Eloquent — это стандартное ORM для Laravel (объектно-реляционное отображение). Eloquent делает безболезненным получение и хранение данных в вашей базе данных, используя чётко определённые «модели». Обычно, каждая Eloquent модель однозначно соответствует одной таблице базы данных.
Модель User
В первую очередь нам нужна модель, соответствующая нашей таблице users. Однако, если вы зайдете в папку app вашего проекта, вы увидите, что Laravel уже поставляется в комплекте с моделью User, поэтому нам не нужно создавать её вручную.
Модель Task
Давайте определим модель Task, которая будет соответствовать только что созданной нами таблице tasks. Мы снова можем использовать команду Artisan, чтобы сгенерировать эту модель. В этом случае мы будем использовать команду shmake:model
:
shphp artisan make:model Task
Модель будет помещена в каталог app вашего приложения. По умолчанию класс модели пуст. Нам не надо явно указывать, какой таблице соответствует Eloquent модель, потому что подразумевается, что имя таблицы – это имя модели во множественном числе (s на конце). В этом случае предполагается, что модель Task соответствует таблице базы данных tasks.
Давайте добавим несколько вещей в эту модель. Для начала определим, что атрибут name этой модели должен быть массово присваиваемым. Это позволит нам заполнить атрибут name при использовании метода Eloquent PHPcreate()
:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
/**
* Массово присваиваемые атрибуты.
*
* @var array
*/
protected $fillable = ['name'];
}
Мы познакомимся с моделями Eloquent ближе, когда добавим маршруты к нашему приложению. Разумеется, вы можете заглянуть и в полную документацию по Eloquent для получения дополнительной информации.
Отношения Eloquent
Теперь, когда наши модели определены, нам нужно связать их. Например, наш User может иметь несколько Task, в то время как Task привязан к единственному User. Определение взаимосвязи позволит нам быстро проходить через наши отношения:
$user = App\User::find(1);
foreach ($user->tasks as $task) {
echo $task->name;
}
Отношение tasks
Во-первых, давайте определим отношение для нашей модели User. Отношения Eloquent определены как методы моделей. Eloquent поддерживает несколько различных типов отношений, с которыми можно ознакомиться в полной документации по Eloquent. Мы определим функцию PHPtasks
в модели User, которая вызывает Eloquent-метод PHPhasMany()
:
добавлено в 5.2 ()
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
// Другие Eloquent свойства...
/**
* Получить все задачи пользователя.
*/
public function tasks()
{
return $this->hasMany(Task::class);
}
}
добавлено в 5.1 ()
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
// Другие Eloquent свойства...
/**
* Получить все задачи пользователя.
*/
public function tasks()
{
return $this->hasMany(Task::class);
}
}
Отношение user
Теперь давайте определим отношение user для модели Tasks. И снова мы определим отношение как метод модели. В этом случае мы будем использовать Eloquent-метод PHPbelongsTo()
, определяющий отношение:
<?php
namespace App;
use App\User;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
/**
* Массово присваиваемые атрибуты.
*
* @var array
*/
protected $fillable = ['name'];
/**
* Получить пользователя - владельца данной задачи
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
Прекрасно! Теперь наши отношения определены, и мы можем начать создавать наши контроллеры!
Маршрутизация
В базовой версии нашего приложения мы определили всю нашу логику в файле routes.php, используя замыкания. В данном приложении мы будем использовать контроллеры для организации наших маршрутов. Контроллеры позволят нам лучше организовать логику HTTP-запросов в нескольких файлах.
Вывод представления
У нас будет один маршрут, использующий замыкание: наш маршрут /, представляющий из себя лендинг для гостей приложения. Давайте заполним наш маршрут /. По этому маршруту мы хотим отрисовывать HTML-шаблон, который содержит страницу «welcome».
В Laravel все HTML-шаблоны хранятся в каталоге resources/views, и мы можем использовать вспомогательную функцию PHPview()
, чтобы возвратить один из этих шаблонов по нашему маршруту:
Route::get('/', function () {
return view('welcome');
});
Конечно, нам необходимо создать это представление, давайте сделаем это!
Аутентификация
Помните, что мы также должны позволить пользователям создавать учётные записи и входить в наше приложение. Как правило, построение всего слоя аутентификации в веб-приложении является трудоёмкой задачей . Однако, так как это распространённая задача, Laravel попытался сделать эту процедуру абсолютно безболезненной.
Во-первых, обратите внимание, что app/Http/Controllers/Auth/AuthController уже включён в приложение Laravel. Этот контроллер использует специальный типаж (trait) AuthenticatesAndRegistersUsers со всей необходимой логикой для создания и аутентификации пользователей.
Маршруты и представления аутентификации
Итак, что нам осталось сделать? Нам всё ещё нужно создать шаблоны регистрации и входа в систему, а также определить маршруты, указывающие на контроллер аутентификации.
добавлено в 5.2 ()
Мы можем сделать это с помощью Artisan-команды shmake:auth
:
shphp artisan make:auth
Теперь нам осталось только добавить маршруты аутентификации в наш файл маршрутов. Это можно сделать методом PHPauth()
фасада Route, который зарегистрирует все необходимые нам маршруты для регистрации, входа и сброса пароля:
// Маршруты аутентификации...
Route::auth();
Когда маршруты auth зарегистрированы, проверьте, что свойство PHP$redirectTo
контроллера app/Http/Controllers/Auth/AuthController имеет значение PHP/tasks
:
protected $redirectTo = '/tasks';
А также необходимо изменить в файле app/Http/Middleware/RedirectIfAuthenticated.php путь переадресации:
return redirect('/tasks');
добавлено в 5.1 ()
Для начала давайте добавим нужные нам маршруты в файл app/Http/routes.php:
// Маршруты аутентификации...
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');
// Маршруты регистрации...
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');
Представления аутентификации
Для аутентификации необходимо создать login.blade.php и register.blade.php в папке resources/views/auth. Конечно, дизайн и стиль этих представлений не имеет значения. Тем не менее, они должны содержать по крайней мере несколько основных полей.
Файл register.blade.php должен содержать форму, включающую в себя поля name, email, password и password_confirmation, эта форма должна создавать POST запрос к маршруту /auth/register.
Файл login.blade.php должен содержать форму, включающую в себя поля email и password, эта форма должна создавать POST запрос к маршруту /auth/login.
Если вы хотите просмотреть полные примеры для этих представлений, помните, что весь исходный код приложения доступен на GitHub.
Контроллер задач
Поскольку мы знаем, что нам нужно получать и сохранять задачи, давайте создадим TaskController с помощью командной строки Artisan, при этом новый контроллер будет помещён в папку app/Http/Controllers:
Теперь, когда контроллер создан, давайте создадим стабы для некоторых маршрутов в нашем файле app/Http/routes.php, указывающих на контроллер:
Route::get('/tasks', 'TaskController@index');
Route::post('/task', 'TaskController@store');
Route::delete('/task/{task}', 'TaskController@destroy');
Аутентификация всех маршрутов задач
В данном приложении мы хотим, чтобы все наши маршруты задач требовали аутентификации пользователя. Другими словами, пользователь должен зайти в систему, чтобы создать задачу. Поэтому мы должны ограничить доступ к нашим маршрутам задач и открывать доступ только аутентифицированным пользователям. Laravel контролирует это, используя посредника.
Чтобы проверять аутентификацию пользователя в каждом действии, мы можем добавить вызов метода middleware в конструктор контроллера. Все доступные посредники маршрута определены в файле app/Http/Kernel.php. В данном случае мы хотим добавить посредник auth ко всем методам контроллера:
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class TaskController extends Controller
{
/**
* Создание нового экземпляра контроллера.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}
Создание макетов и представлений
Основная часть этого приложения содержит одно представление с формой добавления новых задач, а также список всех текущих задач. Чтобы помочь вам визуализировать представление, мы сделали скриншот законченного приложения со стандартными стилями Bootstrap CSS:
Определяем макет
Почти все веб-приложения используют один макет на всех своих страницах. Например, у нашего приложения есть верхняя панель навигации, которая присутствовала бы на каждой странице (если бы у нас их было больше одной). Laravel упрощает использование этих общих функций на всех страницах, используя макеты Blade.
Как мы выяснили ранее, все представления Laravel хранятся в resources/views. Давайте определим представление нового макета в resources/views/layouts/app.blade.php. Расширение .blade.php даёт фреймворку команду использовать механизм шаблонной обработки Blade, чтобы отрисовать это представление. Конечно, в Laravel вы можете использовать и простые PHP-шаблоны. Однако Blade позволяет быстро написать простой и небольшой шаблон.
Наше представление app.blade.php должно выглядеть примерно так:
xml<!-- resources/views/layouts/app.blade.php --> <!DOCTYPE html> <html lang="en"> <head> <title>Laravel Quickstart - Intermediate</title> <!-- CSS и JavaScript --> </head> <body> <div class="container"> <nav class="navbar navbar-default"> <!-- Содержимое Navbar --> </nav> </div> @yield('content') </body> </html>
Обратите внимание на строчку xml@yield('content')
в макете. Это специальная Blade-директива для указания всем дочерним страницам, наследующим этот шаблон, где они могут внедрить своё содержимое. Давайте определим дочернее представление, которое будет использовать этот макет и выводить его основной контент.
Определяем дочернее представление
Отлично. Макет нашего сайта завершён. Теперь мы должны определить представление, которое содержит форму создания новой задачи, а также таблицу со списком всех существующих задач. Давайте определим это представление в файле resources/views/tasks/index.blade.php, оно будет соответствовать методу index в нашем TaskController.
Мы пропустим небольшую часть Bootstrap CSS и сфокусируемся на важном. Помните, вы можете скачать весь исходный код этого приложения с GitHub:
<!-- resources/views/tasks/index.blade.php -->
@extends('layouts.app')
@section('content')
<!-- Bootstrap шаблон... -->
<div class="panel-body">
<!-- Отображение ошибок проверки ввода -->
@include('common.errors')
<!-- Форма новой задачи -->
<form action="{{ url('task') }}" method="POST" class="form-horizontal">
{{ csrf_field() }}
<!-- Имя задачи -->
<div class="form-group">
<label for="task" class="col-sm-3 control-label">Задача</label>
<div class="col-sm-6">
<input type="text" name="name" id="task-name" class="form-control">
</div>
</div>
<!-- Кнопка добавления задачи -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-default">
<i class="fa fa-plus"></i> Добавить задачу
</button>
</div>
</div>
</form>
</div>
<!-- TODO: Текущие задачи -->
@endsection
Несколько поясняющих замечаний
Прежде чем двигаться дальше, давайте немного поговорим об этом шаблоне. Во-первых, директива PHP@extends
сообщает Blade, что мы используем макет, который мы определили в resources/views/layouts/app.blade.php. Все содержимое между PHP@section('content')
и PHP@endsection
будет добавлено вместо строчки директивы PHP@yield('content')
в макете app.blade.php.
Директива PHP@include('common.errors')
загрузит шаблон, расположенный в resources/views/common/errors.blade.php. Он пока не определён, но скоро мы это сделаем!
Итак, мы определили основной макет и представление для нашего приложения. Давайте вернём это представление из метода index контроллера TaskController:
/**
* Отображение списка всех задач пользователя.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
return view('tasks.index');
}
Теперь мы готовы добавить код в наш метод контроллера маршрута POST /task, чтобы обработать входящие данные из формы и добавить новую задачу в БД.
Добавление задач
Проверка ввода
Теперь, когда у нас есть форма на нашем представлении, мы должны добавить код к нашему методу TaskController@store, чтобы проверить входящие данные из формы и создать новую задачу. Во-первых, давайте проверим ввод.
Для этой формы мы создадим обязательное поле name и зададим, что оно должно содержать не более 255 символов. Если проверка не пройдёт, то мы перенаправим пользователя назад к URL /tasks, а также возвратим ему в сессию его введённые данные с указанием на ошибки:
/**
* Создание новой задачи.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|max:255',
]);
// Создание задачи...
}
Если вы создавали приложение по краткому руководству, то могли заметить, что код валидации там другой. Так как мы находимся в контроллере, мы можем использовать удобный типаж ValidatesRequests, который включён в базовый контроллер Laravel. Этот типаж представляет простой метод validate, который принимает запрос и массив правил валидации.
Нам даже не нужно самим определять результат валидации и даже не нужно вручную делать перенаправление. Если валидация не пройдена для заданных правил, пользователь будет автоматически перенаправлен туда, откуда он пришёл, и ошибки будут автоматически высвечены в сессии. Отлично!
Переменная PHP$errors
Помните, что мы использовали директиву PHP@include('common.errors')
в нашем представлении, чтобы отобразить ошибки ввода формы. Представление PHPcommon.errors
позволяет нам легко показывать ошибки ввода в одинаковом формате на всех наших страницах. Давайте определим содержимое этого представления:
<!-- resources/views/common/errors.blade.php -->
@if (count($errors) > 0)
<!-- Список ошибок формы -->
<div class="alert alert-danger">
<strong>Упс! Что-то пошло не так!</strong>
<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
Переменная PHP$errors
доступна в любом представлении Laravel. Если не будет ошибок ввода, она просто будет пустым экземпляром PHPViewErrorBag
.
Создание задачи
Теперь, когда обрабатывается ввод данных, давайте создадим новую задачу, продолжая заполнять наш маршрут. Как только новая задача будет создана, мы перенаправим пользователя назад к URL /tasks. Чтобы создать задачу, мы будем использовать мощность Eloquent отношений.
Большинство Laravel отношений предоставляют метод create, который принимает массив атрибутов и автоматически устанавливает значение внешнего ключа на соответствующей модели перед сохранением в базе данных. В этом случае метод create автоматически установит свойство user_id данной задачи на ID текущего аутентифицированного пользователя, к которому мы обращаемся с помощью PHP$request->user()
:
/**
* Создание новой задачи.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|max:255',
]);
$request->user()->tasks()->create([
'name' => $request->name,
]);
return redirect('/tasks');
}
Отлично! Теперь мы можем создавать задачи. Давайте продолжим создание нашего представления, добавив список всех существующих задач.
Отображение существующих задач
Во-первых, мы должны отредактировать наш метод TaskController@index, чтобы передать все существующие задачи в представление. Функция PHPview()
принимает массив данных вторым параметром, который будет доступным для представления. Каждый ключ массива станет переменной в представлении. Например, мы можем сделать так:
/**
* Показать список всех задач пользователя.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
$tasks = $request->user()->tasks()->get();
//для версии 5.1
//$tasks = Task::where('user_id', $request->user()->id)->get();
return view('tasks.index', [
'tasks' => $tasks,
]);
}
Тем не менее, давайте рассмотрим некоторые возможности внедрения зависимостей от Laravel, чтобы внедрить TaskRepository в наш TaskController, который мы будем использовать для доступа ко всем нашим данным.
Внедрение зависимостей
Сервис-контейнер Laravel является одной из самых мощных возможностей всего фреймворка. После прочтения базового руководства, не забудьте прочитать всю документацию по контейнеру.
Создание репозитория
Как мы уже упоминали ранее, мы хотим определить TaskRepository, который содержит логику доступа ко всем данным для модели Task. Это будет особенно полезно, если приложение будет расти, и вам понадобится повсеместно использовать Eloquent запросы в приложении.
Итак, давайте создадим папку app/Repositories и добавим класс TaskRepository. Помните, что все app папки Laravel автоматически загружаются с помощью стандарта автоматической загрузки PSR-4, так что вы можете создать сколько угодно дополнительных каталогов:
добавлено в 5.2 ()
<?php
namespace App\Repositories;
use App\User;
class TaskRepository
{
/**
* Получить все задачи заданного пользователя.
*
* @param User $user
* @return Collection
*/
public function forUser(User $user)
{
return $user->tasks()
->orderBy('created_at', 'asc')
->get();
}
}
добавлено в 5.1 ()
<?php
namespace App\Repositories;
use App\User;
use App\Task;
class TaskRepository
{
/**
* Получить все задачи заданного пользователя.
*
* @param User $user
* @return Collection
*/
public function forUser(User $user)
{
return Task::where('user_id', $user->id)
->orderBy('created_at', 'asc')
->get();
}
}
Внедрение репозитория
Как только наш репозиторий определён, мы можем просто передать в конструктор наш TaskController и использовать его в нашем маршруте index. Так как Laravel использует контейнер, чтобы разрешить все контроллеры, наши зависимости будут автоматически внедрены в экземпляр контроллера:
<?php
namespace App\Http\Controllers;
use App\Task;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Repositories\TaskRepository;
class TaskController extends Controller
{
/**
* Экземпляр TaskRepository.
*
* @var TaskRepository
*/
protected $tasks;
/**
* Создание нового экземпляра контроллера.
*
* @param TaskRepository $tasks
* @return void
*/
public function __construct(TaskRepository $tasks)
{
$this->middleware('auth');
$this->tasks = $tasks;
}
/**
* Показать список всех задач пользователя.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
return view('tasks.index', [
'tasks' => $this->tasks->forUser($request->user()),
]);
}
}
Отображение задач
Когда данные переданы, мы можем обращаться к задачам в нашем представлении tasks/index.blade.php и выводить их на экран таблицей. Blade-конструкция PHP@foreach
позволяет нам кратко писать циклы, которые компилируются в молниеносный простой PHP-код:
@extends('layouts.app')
@section('content')
<!-- Форма создания задачи... -->
<!-- Текущие задачи -->
@if (count($tasks) > 0)
<div class="panel panel-default">
<div class="panel-heading">
Текущая задача
</div>
<div class="panel-body">
<table class="table table-striped task-table">
<!-- Заголовок таблицы -->
<thead>
<th>Task</th>
<th> </th>
</thead>
<!-- Тело таблицы -->
<tbody>
@foreach ($tasks as $task)
<tr>
<!-- Имя задачи -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<td>
<!-- TODO: Кнопка Удалить -->
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@endsection
Наше приложение почти готово. Но у нас нет способа удалять наши существующие задачи, когда они завершены. Давайте добавим и это!
Удаление задач
Добавление кнопки удаления задачи
Мы оставили отметку «TODO» в коде, где предположительно будет находиться наша кнопка. Давайте добавим кнопку удаления к каждой строке нашего списка задач в представлении tasks/index.blade.php. Мы создадим маленькую однокнопочную форму для каждой задачи в списке. После нажатия кнопки приложению будет отправляться запрос DELETE /task, который будет обращаться к методу TaskController@destroy:
добавлено в 5.2 ()
<tr>
<!-- Имя задачи -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<!-- Кнопка Удалить -->
<td>
<form action="{{ url('task/'.$task->id) }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button type="submit" id="delete-task-{{ $task->id }}" class="btn btn-danger">
<i class="fa fa-btn fa-trash"></i>Удалить
</button>
</form>
</td>
</tr>
добавлено в 5.1 ()
<tr>
<!-- Имя задачи -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<!-- Кнопка Удалить -->
<td>
<form action="/task/{{ $task->id }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button>Удалить задачу</button>
</form>
</td>
</tr>
Примечание по спуфингу метода
Обратите внимание на то, что method формы кнопки удаления объявлен как POST, несмотря на то, что мы отвечаем на запрос, используя маршрут PHPRoute::delete
. HTML-формы позволяют использовать только GET и POST методы HTTP. А нам нужен способ имитировать запрос DELETE от формы.
Мы можем имитировать запрос DELETE, выводя результаты функции PHPmethod_field('DELETE')
в нашей форме. Эта функция генерирует скрытый ввод формы, который распознается Laravel и используется, чтобы переопределить вызываемый метод HTTP. Сгенерированное поле будет похоже на это:
<input type="hidden" name="_method" value="DELETE">
Привязка модели маршрута
Теперь мы почти готовы определить метод PHPdestroy()
в нашем TaskController. Но для начала давайте пересмотрим наше объявление маршрута и метод контроллера для этого маршрута:
Route::delete('/task/{task}', 'TaskController@destroy');
добавлено в 5.1 ()
Если не добавлять никакого дополнительного кода, то Laravel внедрит ID заданной задачи в метод TaskController@destroy:
/**
* Уничтожить заданную задачу.
*
* @param Request $request
* @param string $taskId
* @return Response
*/
public function destroy(Request $request, $taskId)
{
//
}
Однако, в первую очередь в этом методе мы должны будем получить экземпляр Task из базы данных, используя пришедший ID. Было бы неплохо, если б Laravel мог просто внедрить экземпляр Task, соответствующий этому ID? Давайте сделаем это возможным!
В нашем файле app/Providers/RouteServiceProvider.php в методе PHPboot()
давайте добавим следующую строку:
$router->model('task', 'App\Task');
Эта небольшая строчка заставит Laravel извлечь модель Task, соответствующую заданному ID, каждый раз, когда он видит PHP{task}
в объявлении маршрута. Теперь мы можем определить наш метод PHPdestroy()
:
/**
* Уничтожить заданную задачу.
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task)
{
//
}
добавлено в 5.2 ()
Поскольку переменная PHP{task}
в нашем маршруте совпадает с переменной PHP$task
, определённой в методе нашего контроллера, неявная привязка модели Laravel автоматически внедрит экземпляр соответствующей модели Task.
Авторизация
Теперь у нас есть экземпляр Task, внедрённый в метод PHPdestroy()
. Тем не менее, нет никакой гарантии того, что аутентифицированный пользователь на самом деле «владеет» данной задачей. Например, злоумышленник может сделать запрос на удаление задачи другого пользователя, передавая случайный ID задачи по URL /tasks/{task}. Поэтому мы должны использовать возможности авторизации Laravel, чтобы быть уверенным, что аутентифицированный пользователь на самом деле является владельцем экземпляра Task, который был внедрён в маршрут.
Создание политики
Laravel использует «политики» для организации логики авторизации в простых небольших классах. Как правило, каждая политика соответствует модели. Давайте создадим TaskPolicy, используя команду Artisan, которая поместит сгенерированный файл в app/Policies/TaskPolicy.php:
shphp artisan make:policy TaskPolicy
Следующим шагом будет добавление метода PHPdestroy()
к политике. Этот метод получает экземпляр User и экземпляр Task. Метод должен просто проверить, соответствует ли ID пользователя user_id задачи. Фактически, все методы политики должны возвращать true или false:
<?php
namespace App\Policies;
use App\User;
use App\Task;
use Illuminate\Auth\Access\HandlesAuthorization;
class TaskPolicy
{
use HandlesAuthorization;
/**
* Определяем, может ли данный пользователь удалить данную задачу.
*
* @param User $user
* @param Task $task
* @return bool
*/
public function destroy(User $user, Task $task)
{
return $user->id === $task->user_id;
}
}
В конце нам надо связать нашу модель Task с TaskPolicy. Мы можем сделать это, добавив одну строчку к свойству PHP$policies
в файле app/Providers/AuthServiceProvider.php. Она проинформирует Laravel о том, какая политика должна быть использована каждый раз, когда мы пытаемся авторизовать действие в экземпляре Task:
/**
* Маппинг политики для приложения.
*
* @var array
*/
protected $policies = [
'App\Task' => 'App\Policies\TaskPolicy',
//для версии 5.1
//Task::class => TaskPolicy::class,
];
Авторизация действия
Теперь, когда наша политика написана, давайте использовать ее в нашем методе PHPdestroy()
. Все контроллеры Laravel могут вызвать метод PHPauthorize()
, который представлен типажом AuthorizesRequest:
/**
* Уничтожение заданной задачи.
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task)
{
$this->authorize('destroy', $task);
// Удаление задачи...
}
Давайте немного исследуем этот вызов метода. Первым параметром, переданным методу PHPauthorize()
, является имя метода политики, который мы хотим вызвать. Второй параметр — экземпляр модели, который нас сейчас интересует. Помните, мы недавно сообщили Laravel, что наша модель Task соответствует нашему TaskPolicy. Значит фреймворк знает, с какой политикой выполнить метод PHPdestroy()
. Текущий пользователь будет автоматически отправлен в метод политики. Таким образом, мы не должны будем вручную передавать его здесь.
Если действие авторизовано, наш код будет продолжать выполняться как и всегда. Однако, если действие не авторизовано (метод политики PHPdestroy()
вернул false), то будет выдано исключение 403, и на экран пользователя будет выведена страница ошибки.
Существует несколько других способов взаимодействия с сервисами авторизации от Laravel. Обязательно ознакомьтесь с ними в полной документации по авторизации.
Удаление задачи
Наконец, давайте добавим к нашему методу PHPdestroy()
логику удаления текущей задачи. Мы можем использовать Eloquent-метод PHPdelete()
, чтобы удалить заданный экземпляр модели в базе данных. Когда запись будет удалена, мы перенаправим пользователя назад к URL /tasks:
/**
* Уничтожение заданной задачи.
*
* @param Request $request
* @param Task $task
* @return Response
*/
public function destroy(Request $request, Task $task)
{
$this->authorize('destroy', $task);
$task->delete();
return redirect('/tasks');
}
Комментарии (14)
Спасибо!
маршруты аутентификации заданы неверно
редиректит на
/login
, а у нас маршрутыили у меня сервер неверно настроен, но если сделать так то норм
При попытке зайти на страницу для авторизованных пользователей выбрасывается исключение \Illuminate\Auth\AuthenticationException. Оно отлавливается автоматически (файл App\Exceptions\handler.php, метод unauthenticated) и, если не требуется JSON-ответ, производится редирект на страницу /login.
P.S.: Назовите как-нибудь свой маршрут с формой авторизации (например Route::get('/auth/login', 'LoginController@showLogin')->name('auth') и используйте функцию route (route('auth')) для генерации URl
В laravel 5.3.* немного по другому:
/**
* Маршруты аутентификации
*/
Route::get('auth/login', 'Auth\LoginController@showLoginForm')->name('auth');
Route::post('auth/login', 'Auth\LoginController@login');
Route::get('auth/logout', 'Auth\LoginController@logout');
/**
* Маршруты регистрации
*/
Route::get('auth/register', 'Auth\RegisterController@showRegistrationForm');
Route::post('auth/register', 'Auth\RegisterController@register');
Для правильного редиректа в LoginController и RegisterController:
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/tasks';
Разъясните про внедрения. Пишут, что в "app/Providers/RouteServiceProvider.php в методе boot()" надо добавить строку
$router->model('task', 'App\Task');
но там такой строки нет, тем не менее в метод destroy() контроллера TaskController передается экземпляр App/Task как он туда попадает?
То-же самое касается и внедрения TaskRepository в конструктор этого-же контроллера.
Ни в одном из провайдеров, зарегистрированных в config/app.php нет упоминаний об App/Task (только в AuthServiceProvider регистрация TaskPolicy), ни TaskRepository.
У меня laravel 5.3.*
Привязку параметра маршрута к модели
PHP$router->model('task', 'App\Task');
делать не нужно.
Достаточно указать в в методе контроллера
PHPpublic function destroy(Task $task)
и laravel сам привяжет параметр к модели.
Такое ощущение, что инструкция написана для одной из старых версий. Попробуйте сами пройти от начала до конца. Исправьте ошибки. Тоже самое в «Быстрый старт». Как вы предлагаете быстро стартовать новичкам, если вылазят одни ошибки? Им нужно со смыслом разбираться, а не с ошибками.
Вопросы к самому Laravel, который меняет структуру даже в минорных релизах. Перевод официальной документации, просто старой (5.1). Кто хочет помочь обновить — пишите.
Блин, для человека, который никогда не работал с фреймворками — много неясного.... А еще говорят, что для новичка этот фреймворк как раз. Боюсь даже узнать, что там с другими.
Изменить редирект на /login можно в файле /app/Exceptions/Handler.php
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
return redirect()->guest('login'); //тут меняем login на auth (например)
}
Нужно срочно обновлять гайд. Возможно я полный нуб и идиот, но я потратил день на решение некоторых проблем из-за разности версий.
А одну проблему я так и не решил, ругается вот на это:
/**
* Показать список всех задач пользователя.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
return view('tasks.index', [
'tasks' => $this->tasks->forUser($request->user()),
]);
}
Следующая ошибка:
Специально для самых внимательных в начале статьи выделено:
Данная статья документации актуальна только для версий 5.2 и 5.1 и была удалена в версии 5.3.
А вопросы лучше задавать на форуме.
Как я понимаю в разделе «Отношение user» написано что нужно прописывать для модели Tasks, но такой модели не существует и там опечатка так как у нас есть только модель Task
в 5.4 У меня все проблемы были из-за отсутствия в классе репозитория namespace App\Repositories; и в некоторых местах не хватало use App\Task; use App\User; use App\Repositories\TaskRepository; — в контроллере и в классе политик. В моделях Task и User должны быть соответственно методы user() и tasks() — множественное число только в последнем случае. И не забыть в модели Task разрешить запись protected $fillable = ['name', 'user_id']; и в AuthServiceProvider прописать 'App\Task' ⇒ 'App\Policies\TaskPolicy',