Статьи

Доктрина Ларавела — лучшее из обоих миров?

Laravel Doctrine — это встроенная реализация знаменитого ORM для Laravel 5.X Framework и действительно интересная альтернатива стандартному выбору Eloquent . В этой статье мы узнаем, как его использовать и когда.

Почему Доктрина, а не Красноречивый?

Те из нас, кто хотя бы раз использовал Laravel, наверняка использовали Eloquent. Нам всем нравится его свободный и простой синтаксис, и мы все знаем такие фрагменты, как следующие:

$user = new App\User; $user->name = 'Francesco'; $user->email = '[email protected]'; $user->save(); 

То, что мы используем здесь, это так называемый шаблон «Активная запись». Часто используемый архитектурный шаблон, названный Мартином Фаулером в его ультра-известной книге «Шаблоны архитектуры корпоративных приложений». Чтобы упростить и объяснить вещи, этот шаблон предполагает, что одна строка в базе данных обрабатывается как объект в нашем программном обеспечении. Однако со временем эта картина встретила много критических замечаний:

  • Active Record — это сильная связь между операциями с базами данных и классами в нашем программном обеспечении. Для многих небольших проектов этот подход более чем хорош, но что, если наше программное обеспечение усложняется? Мы могли бы наткнуться на необходимость большего количества классов, не всегда связанных с конкретной таблицей в нашей базе данных. Там Active Record не помогает нам достичь хорошего уровня абстракции от источника данных.

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

Теперь, в той же книге, о которой мы упоминали ранее, Мартин Фаулер объяснил другой архитектурный паттерн: Data Mapper . Data Mapper состоит из наличия промежуточного уровня, который, работая в обоих направлениях, обеспечивает доступ к источнику данных с одной стороны, и хорошей абстракции от него с другой . Это означает, что объекты в программном обеспечении не тесно связаны с источником данных, что значительно улучшает изоляцию ответственности. Результат? Теперь разработчик может сосредоточиться на создании объекта, который ближе к реальной ситуации, а не к системе базы данных, выбранной для работы.

Eloquent — это реализация шаблона Active Record, а Doctrine — реализация Data Mapper. Давайте посмотрим, как мы можем установить Doctrine для Laravel и как его настроить и использовать.

Установка Доктрины для Laravel

Как обычно, мы будем использовать Homestead Improved в качестве стандартной среды разработки для наших тестов.

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

 composer create-project laravel/laravel Project 

Затем мы входим в папку нашего проекта и добавляем Laravel Doctrine как зависимость от Composer.

 composer require "laravel-doctrine/orm:1.1.*" 

Успешная установка доктрины Ларавела

Нам также необходимо добавить следующий класс в наш список поставщиков услуг в файле config/app.php :

 LaravelDoctrine\ORM\DoctrineServiceProvider::class, 

Также мы можем зарегистрировать три фасада для EntityManager, Registry и Doctrine в одном файле:

 'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class, 'Registry' => LaravelDoctrine\ORM\Facades\Registry::class, 'Doctrine' => LaravelDoctrine\ORM\Facades\Doctrine::class, 

Наконец, мы можем опубликовать специальный файл конфигурации:

 artisan vendor:publish --tag="config" 

Мы сделали! Laravel Doctrine полностью установлен и настроен.

Пример приложения — список дел!

Чтобы узнать больше о Doctrine для Laravel, мы собираемся воспроизвести существующий пример приложения, в котором используется Eloquent. А как насчет промежуточного списка задач на официальном сайте Laravel?

Мы создадим очень простой многопользовательский список задач. В этом приложении наш пользователь сможет:

  • войти в свою область.
  • список существующих задач.
  • добавить новую задачу в список.
  • пометить задачу как выполненную / не выполненную.
  • обновить существующую задачу в списке.
  • удалить существующую задачу из списка.

Очевидно, что каждый пользователь сможет видеть только свои задачи.

Давайте сначала TodoList пространство имен приложения на TodoList .

 artisan app:name TodoList 

Хорошо, теперь мы можем начать с самых основ доктрины. В Eloquent мы использовали модели. Как мы начнем сейчас?

Сущность!

В Doctrine мы используем Entities для представления объектов нашего приложения. В отличие от Eloquent, где модель расширяет базовый класс Model , сущность Doctrine — это простой PHP-класс, который ничего не расширяет.

Вот первая заглушка сущности Task :

 <?php namespace TodoList\Entities; class Task { private $id; private $name; private $description; private $isDone = false; public function __construct($name, $description) { $this->name = $name; $this->description = $description; } public function getId() { return $this->id; } public function getName() { return $this->name; } public function getDescription() { return $this->description; } public function setName($name) { $this->name = $name; } public function setDescription($description) { $this->description = $description; } public function isDone() { return $this->isDone; } public function toggleStatus() { if(!$this->isDone) { $this->isDone = true; } else { $this->isDone = false; } } } 

Неплохо как начало. Да, это немного более «многословно», чем модель Eloquent. Однако для этого есть конкретная причина. Прежде всего, если мы думаем об этом, это простой класс PHP. Он ничего не говорит о его реализации, не имеет никаких дополнительных обязанностей. Все, что он делает, строго связано с самой задачей. Это хорошо, потому что этот подход более «совместим» с принципом единой ответственности .

Но…

Где находится база данных?

В Eloquent модели являются лишь интерфейсом к базе данных. С некоторой «магией», иногда даже без написания ни одной строчки кода в модели, мы можем просто начать работать с ними. Единственное, что нам нужно сделать, — это соответственно разработать нашу базу данных. С Doctrine ключевая концепция немного отличается.

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

В Доктрине мы можем сделать это несколькими способами. Для этого руководства мы будем использовать подход «аннотации»:

 <?php namespace TodoList\Entities; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="tasks") */ class Task { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string") */ private $name; /** * @ORM\Column(type="string") */ private $description; /** * @ORM\Column(type="boolean") */ private $isDone = false; 

С помощью этих аннотаций (которые мы можем найти в классе Doctrine\ORM\Mapping ) мы связали класс и его свойства с определенной структурой в базе данных. В этом случае к таблице tasks которая имеет поле id , name и description .

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

 artisan doctrine:schema:create # Creating database schema for default entity manager... # Database schema created successfully! 

Давайте проверим, что произошло в базе данных с помощью инструмента по нашему выбору (или из CLI).

Изменения базы данных успешно применены - screencap из MySQL Workbench

Мы поняли!

Вот что случилось:

  • с помощью аннотаций мы добавили некоторые метаданные в наш класс, не внося существенных изменений. В конце концов, аннотации — это комментарии.
  • выполняя команду artitan doctrine:schema:create , Doctrine автоматически сканирует сущности для аннотаций ORM. При обнаружении он создает и изменяет схему базы данных соответственно;

Звучит отлично, правда? Теперь мы можем начать работать с ним. Давайте сделаем несколько тестов, прежде чем погрузиться глубже.

Entity Manager

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

Давайте добавим новый маршрут в наш файл маршрутов:

 Route::get('test-add', function () { $task = new \TodoList\Entities\Task('Make test app', 'Create the test application for the Sitepoint article.'); \EntityManager::persist($task); \EntityManager::flush(); return 'added!'; }); 

… И выполните его, перейдя по URL test-add адресу test-add . После этого мы проверяем данные.

Первая запись успешно вставлена

Это сработало!

EntityManager — это специальный класс, который принимает экземпляры сущностей и «переводит» их в записи базы данных. Как мы можем ясно видеть, ответственность за постоянство больше не связана с моделью / сущностью. Но почему два отдельных метода? Как насчет старого доброго save ? Ну, все немного по-другому.

  • Метод persist используется для предоставления конкретной инструкции классу EntityManager . Там написано: «Эй, EntityManager, когда я дам вам сигнал, сохраните сущность».
  • метод flush используется для выполнения инструкции, которую мы ранее дали EntityManager — сигнал, который ему нужен.

Это более гибко, потому что мы также можем сделать множество модификаций для многих объектов, а затем, наконец, вызвать один метод flush для определенного «снимка». Конечно, мы также можем использовать Laravel Service Container для использования EntityManager:

 Route::get('test-add', function (\Doctrine\ORM\EntityManagerInterface $em) { $task = new \TodoList\Entities\Task('Make test app', 'Create the test application for the Sitepoint article.'); $em->persist($task); $em->flush(); return 'added!'; }); 

EntityManager — это не только добавление сущностей. Мы можем легко использовать его, чтобы найти экземпляры существующих. Давайте попробуем найти тот, который мы только что добавили, в другом маршруте find-test .

 Route::get('test-find', function (\Doctrine\ORM\EntityManagerInterface $em) { /* @var \TodoList\Entities\Task $task */ $task = $em->find(TodoList\Entities\Task::class, 1); return $task->getName() . ' - ' . $task->getDescription(); }); 

Если мы проверим URL, это наш ранее вставленный элемент!

Приложение

Хорошо, пришло время перейти от основ и начать веселиться с нашим предполагаемым реальным проектом.

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

Область задач

Давайте начнем с создания выделенного контроллера для задач.

 artisan make:controller TaskController 

Мы можем оставить это пустым пока. В папке resources/views создадим очень простой основной макет, который будет использовать каждый вид. Мы назовем это master.blade.php .

 <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>@yield('title') :: ToDo List!</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous"> </head> <body> <div class="container"> <h1>ToDo List!</h1> <hr> @if(session('success_message')) <div class="alert alert-success alert-dismissible" role="alert"> <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button> {{ session('success_message') }} </div> @endif @yield('content') </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script> </body> </html> 

Следующим шагом является создание формы. Давайте создадим новый файл с именем add.blade.php .

 @extends('master') @section('title') Add a Task @endsection @section('content') <div class="row"> <div class="col-md-12"> <h3>Add Task</h3> <p>Use the following form to add a new task to the system.</p> <hr> <form action="{{ url('add') }}" method="post"> {{ csrf_field() }} <p><input autofocus type="text" placeholder="Name..." name="name" class="form-control" /></p> <p><input type="text" placeholder="Description..." name="description" class="form-control" /></p> <hr> <p><button class="form-control btn btn-success">Add Task</button></p> </form> </div> </div> @endsection 

Наконец, контроллер:

 <?php namespace TodoList\Http\Controllers; use TodoList\Entities\Task; use Illuminate\Http\Request; use Doctrine\ORM\EntityManagerInterface; class TaskController extends Controller { public function getAdd() { return view('add'); } public function postAdd(Request $request, EntityManagerInterface $em) { $task = new Task( $request->get('name'), $request->get('description') ); $em->persist($task); $em->flush(); return redirect('add')->with('success_message', 'Task added successfully!'); } } 

У метода postAdd есть логика, необходимая для добавления нового элемента. Как показано выше, мы создаем нашу сущность, а затем сохраняем ее с помощью persist а затем flush .

Чтобы прочитать их, давайте создадим новое представление с именем tasks.blade.php :

 @extends('master') @section('title') Tasks List @endsection @section('content') <div class="row"> <div class="col-md-12"> <h3>Tasks List</h3> <table class="table table-striped"> <tr> <th>ID</th> <th>Name</th> <th>Description</th> <th>Status</th> <th>Operations</th> </tr> @forelse($tasks as $task) <tr> <td>{{ $task->getId() }}</td> <td>{{ $task->getName() }}</td> <td>{{ $task->getDescription() }}</td> <td> @if($task->isDone()) Done @else Not Done @endif </td> <td></td> </tr> @empty <tr> <td colspan="5">No tasks in the list... for now!</td> </tr> @endforelse </table> </div> </div> @endsection 

$tasks будет содержать список задач. Каждый элемент этого списка является экземпляром сущности Task мы создали ранее. Никаких магических методов, никаких свойств, которые мы не помним … только методы, которые мы создали, сохраняя их в чистоте! Теперь к контроллеру!

 ... class TaskController extends Controller { public function getIndex(EntityManagerInterface $em) { $tasks = $em->getRepository(Task::class)->findAll(); return view('tasks', [ 'tasks' => $tasks ]); } ... 

Мы можем увидеть что-то новое в getIndex : метод getRepository . Проще говоря, репозиторий — это объект с очень конкретной ответственностью: иметь дело со слоем постоянства, начиная с (или доходя до) простых сущностей. Это означает, что мы можем продолжать обрабатывать наши объекты как обычный объект PHP, но с преимуществом простого управления уровнем персистентности. Для получения дополнительной информации о шаблоне хранилища, пожалуйста, смотрите здесь .

Мы можем создать репозиторий для каждой сущности, getRepository в нашем программном обеспечении, но Doctrine упрощает нашу жизнь, предоставляя EntityManager метод getRepository который создает действительно базовый репозиторий с наиболее часто используемыми методами. findAll является одним из этих методов.

Результат нашего поиска помещается в переменную $tasks и передается в представление.

Список задач

Изменить статус задачи

Давайте теперь посмотрим, как переключить статус задачи с «не выполнено» на «выполнено» и наоборот. Давайте создадим метод getToggle в TaskController который будет отвечать на URL toggle/STATUS_ID .

 public function getToggle(EntityManagerInterface $em, $taskId) { /* @var Task $task */ $task = $em->getRepository(Task::class)->find($taskId); $task->toggleStatus(); $newStatus = ($task->isDone()) ? 'done' : 'not done'; $em->flush(); return redirect('/')->with('success_message', 'Task successfully marked as ' . $newStatus); } 

Мы назовем этот конкретный URL из списка. Давайте отредактируем таблицу в виде:

 ... <td>{{ $task->getId() }}</td> <td>{{ $task->getName() }}</td> <td>{{ $task->getDescription() }}</td> <td> @if($task->isDone()) Done @else Not Done @endif - <a href="{{ url('toggle/' . $task->getId()) }}">Change</a> </td> ... 

Мы видим, что на этот раз мы не использовали persist метод. Мы toggleStatus метод toggleStatus нашей сущности, а затем использовали flush . Это потому, что мы уже сохранили эту сущность в нашей базе данных — нам просто нужно обновить одно существующее значение свойства на другое.

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

Удаление задачи

Давайте разрешить удаление элементов сейчас.

 public function getDelete(EntityManagerInterface $em, $taskId) { $task = $em->getRepository(Task::class)->find($taskId); $em->remove($task); $em->flush(); return redirect('/')->with('success_message', 'Task successfully removed.'); } 

EntityManager автоматически позаботится о процессе удаления задачи с помощью метода remove . Мы можем добавить ссылку в столбец таблицы «Операции» в нашем представлении, чтобы позволить пользователю удалить конкретную задачу.

 ... <tr> <td>{{ $task->getId() }}</td> <td>{{ $task->getName() }}</td> <td>{{ $task->getDescription() }}</td> <td> @if($task->isDone()) Done @else Not Done @endif - <a href="{{ url('toggle/' . $task->getId()) }}">Change</a> </td> <td> <a href="{{ url('delete/' . $task->getId()) }}">Delete</a> </td> </tr> ... 

Изменение задачи

Давайте создадим представление edit.blade.php .

 @extends('master') @section('title') Edit Task @endsection @section('content') <div class="row"> <div class="col-md-12"> <h3>Edit Task</h3> <p>Use the following form to edit che chosen task.</p> <hr> <form action="{{ url('edit/' . $task->getId()) }}" method="post"> {{ csrf_field() }} <p><input autofocus type="text" placeholder="Name..." name="name" class="form-control" value="{{ $task->getName() }}" /></p> <p><input type="text" placeholder="Description..." name="description" class="form-control" value="{{ $task->getDescription() }}" /></p> <hr> <p><button class="form-control btn btn-success">Save Task</button></p> </form> </div> </div> @endsection 

Теперь к контроллеру: давайте добавим getEdit и postEdit .

 public function getEdit(EntityManagerInterface $em, $taskId) { $task = $em->getRepository(Task::class)->find($taskId); return view('edit', [ 'task' => $task ]); } public function postEdit(Request $request, EntityManagerInterface $em, $taskId) { /* @var Task $task */ $task = $em->getRepository(Task::class)->find($taskId); $task->setName($request->get('name')); $task->setDescription($request->get('description')); $em->flush(); return redirect('edit/' . $task->getId())->with('success_message', 'Task modified successfully.'); } 

Наконец, давайте добавим операцию в список задач.

  <tr> <td>{{ $task->getId() }}</td> <td>{{ $task->getName() }}</td> <td>{{ $task->getDescription() }}</td> <td> @if($task->isDone()) Done @else Not Done @endif - <a href="{{ url('toggle/' . $task->getId()) }}">Change</a> </td> <td> <a href="{{ url('edit/' . $task->getId()) }}">Edit</a> | <a href="{{ url('delete/' . $task->getId()) }}">Delete</a> </td> </tr> 

Выполнено!

Добавление пользователей — отношения

Теперь, как мы можем определить конкретную связь между двумя объектами? Давайте продолжим наше исследование Доктрины с большим количеством примеров. Для этого нам понадобится новый объект: User .

 <?php namespace TodoList\Entities; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping AS ORM; /** * @ORM\Entity * @ORM\Table(name="users") */ class User { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string") */ private $name; /** * @ORM\Column(type="string") */ private $email; /** * @ORM\OneToMany(targetEntity="Task", mappedBy="user", cascade={"persist"}) * @var ArrayCollection|Task[] */ private $tasks; /** * User constructor. * @param $name * @param $email * @param $password */ public function __construct($name, $email) { $this->name = $name; $this->email = $email; $this->tasks = new ArrayCollection(); } /** * @return mixed */ public function getId() { return $this->id; } /** * @return mixed */ public function getName() { return $this->name; } /** * @return mixed */ public function getEmail() { return $this->email; } /** * @return mixed */ public function getTasks() { return $this->tasks; } /** * Assigns the $task task to the current user. * * @param Task $task */ public function addTask(Task $task) { if(!$this->tasks->contains($task)) { $task->setUser($this); $this->tasks->add($task); } } } 

Что здесь нового? В частности это:

 /** * @ORM\OneToMany(targetEntity="Task", mappedBy="user", cascade={"persist"}) * @var ArrayCollection|Task[] */ private $tasks; 

С OneToMany аннотации OneToMany мы определяем новую связь между множеством между сущностью User и Task . Мы также указываем, что это свойство будет ArrayCollection , структурой, которую Doctrine использует для этой цели. Мы также определили несколько методов для доступа к задачам текущего пользователя и добавления нового.

 /** * @return mixed */ public function getTasks() { return $this->tasks; } /** * Assigns the $task task to the current user. * * @param Task $task */ public function addTask(Task $task) { if(!$this->tasks->contains($task)) { $task->setUser($this); $this->tasks->add($task); } } 

Метод addTask вызывает метод setUser который еще не существует. Нам нужно будет отредактировать класс Task чтобы отразить эти изменения. Прежде всего, свойство user

 /** * @ORM\ManyToOne(targetEntity="User", inversedBy="tasks") * @var User */ private $user; 

… а затем несколько методов для установки и получения user экземпляра.

 /** * @return User */ public function getUser() { return $this->user; } /** * @param User $user */ public function setUser(User $user) { $this->user = $user; } 

Очевидно, что прежде чем идти вперед, нам понадобится «обновленная» версия нашей базы данных, чтобы быть уверенным, что все будет работать нормально.

 artisan doctrine:schema:update 

Приведенная выше команда обновит структуру схемы в соответствии с нашими сущностями. Хорошо, протестируйте все с помощью временного маршрута test-rel .

 Route::get('test-rel', function(\Doctrine\ORM\EntityManagerInterface $em) { $user = new \TodoList\Entities\User( 'Francesco', '[email protected]' ); $user->addTask(new \TodoList\Entities\Task( 'Buy milk', 'Because it is healthy' )); $user->addTask(new \TodoList\Entities\Task( 'Buy chocolate', 'Because it is good' )); $em->persist($user); $em->flush(); return 'Done!'; }); 

Вот что мы можем найти в наших таблицах:

Таблица пользователей

Таблица задач

Оно работает! Это всего лишь пример, но документация Doctrine охватывает все виды отношений, которые могут вам понадобиться для вашего приложения.

Аутентификация пользователей

С Eloquent аутентификация была очень простой. По умолчанию выбранный драйвер для системы аутентификации был «красноречив», и модель User уже была там. С небольшой настройкой подход Doctrine может быть таким же простым.

Чтобы быть более точным, вот что мы должны будем сделать:

  • быть уверенным, что наши User объекты реализуют Authenticatable интерфейс Laravel;
  • отредактируйте настройки config/auth.php файле config/auth.php ;

Laravel Doctrine дает нам отличную возможность обрабатывать все необходимые операции для интерфейса, который мы хотим реализовать. Итак, давайте отредактируем сущность User .

 ... <?php namespace TodoList\Entities; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping AS ORM; use Illuminate\Contracts\Auth\Authenticatable; /** * @ORM\Entity * @ORM\Table(name="users") */ class User implements Authenticatable { use \LaravelDoctrine\ORM\Auth\Authenticatable; ... 

Примечание: эта особенность также добавит два новых столбца в нашу таблицу users ( password и remember_token ).

Теперь нам просто нужно изменить значения config/auth.php файле config/auth.php . Более конкретно, те, в массиве providers .

 ... 'providers' => [ 'users' => [ 'driver' => 'doctrine', 'model' => TodoList\User::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ], ... 

Как только мы закончим, мы можем перейти к этапу входа. Мы создаем новый вид, login.blade.php .

 @extends('master') @section('title') Login @endsection @section('content') <div class="row"> <div class="col-md-12"> <h3>Login</h3> <p>Use the following form to login into the system.</p> <hr> <form action="{{ url('login') }}" method="post"> {{ csrf_field() }} <p><input type="text" name="email" placeholder="Email..." class="form-control" /></p> <p><input type="password" name="password" placeholder="Password..." class="form-control" /></p> <hr> <p><button class="form-control btn btn-success">Login</button></p> </form> </div> </div> @endsection 

Давайте «защитим» наш TaskController с помощью промежуточного программного обеспечения auth и создадим два маршрута (GET и POST) для процедуры входа в систему в файле routes.php .

 <?php Route::group(['middleware' => ['web']], function () { Route::get('test-user', function(\Doctrine\ORM\EntityManagerInterface $em) { $user = new \TodoList\Entities\User('Francesco', '[email protected]'); $user->setPassword(bcrypt('123456')); $em->persist($user); $em->flush(); }); Route::get('login', function() { return view('login'); }); Route::post('login', function(\Illuminate\Http\Request $request) { if(\Auth::attempt([ 'email' => $request->get('email'), 'password' => $request->get('password') ])) { return redirect('/'); } }); Route::get('logout', function() { \Auth::logout(); return redirect('login'); }); Route::group(['middleware' => ['auth']], function () { Route::controller('/', 'TaskController'); }); }); из <?php Route::group(['middleware' => ['web']], function () { Route::get('test-user', function(\Doctrine\ORM\EntityManagerInterface $em) { $user = new \TodoList\Entities\User('Francesco', '[email protected]'); $user->setPassword(bcrypt('123456')); $em->persist($user); $em->flush(); }); Route::get('login', function() { return view('login'); }); Route::post('login', function(\Illuminate\Http\Request $request) { if(\Auth::attempt([ 'email' => $request->get('email'), 'password' => $request->get('password') ])) { return redirect('/'); } }); Route::get('logout', function() { \Auth::logout(); return redirect('login'); }); Route::group(['middleware' => ['auth']], function () { Route::controller('/', 'TaskController'); }); }); 

Примечание: есть также маршрут test-user для добавления примера пользователя.

Задачи пользователя

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

Давайте добавим вызов метода User::setUser в TaskController::postAdd .

 public function postAdd(Request $request, EntityManagerInterface $em) { $task = new Task( $request->get('name'), $request->get('description') ); $task->setUser(\Auth::user()); $em->persist($task); $em->flush(); return redirect('add')->with('success_message', 'Task added successfully!'); } 

Последний шаг — реализация ограниченного списка, в котором пользователь может видеть только свои задачи. Для этого мы модифицируем метод TaskController::getIndex следующим образом:

 public function getIndex(EntityManagerInterface $em) { /* @var User $user */ $user = \Auth::user(); $tasks = $user->getTasks(); return view('tasks', [ 'tasks' => $tasks ]); } 

getTasks метод getTasks , мы получаем все задачи для данного пользователя. В этом случае, поскольку Auth::user возвращает экземпляр текущего пользователя, мы получаем только зарегистрированные пользовательские задачи.

Вывод

Наше первое тестовое приложение с Laravel Doctrine завершено! Это было не сложно, не так ли?

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

И последнее, но не менее важное: в этом тестовом приложении мы затронули репозитории . Однако мы не использовали их в полную силу. Фактически, мы можем расширить Doctrine EntityRepository когда нам нужен конкретный набор функций, более сфокусированных на рассматриваемой проблеме предметной области. Связанная страница в документации содержит много интересной информации.

Вы использовали Доктрину Laravel? Не могли бы вы? Почему, почему нет? Дайте нам знать об этом в комментариях!