Изучаем Eloquent: API Resources в Laravel 5.5

Добавлено: 27/01/2018 08:26 |  Обновлено: 27/01/2018 08:30 |  Добавил: nick |  Просмотры: 13328 Комментарии: 1
Вводная часть
В данном материале поработаем с классами ресурсов в Laravel 5.5 (Eloquent: API Resources). И также для примера используем два других нововведения из Laravel 5.5: функцию (helper) optional() и возможность создания отдельного файла для фабрики (factory). Пример работает в версии Laravel 5.5.
Что же такое API Resources в Laravel 5.5? Вот что по этому поводу говорит нам официальная документация:
Создавая свой API, вам может понадобиться промежуточный слой между вашими моделями и JSON-ответами, которые возвращаются пользователям. API Resources в Laravel 5.5 позволяют быстро и легко трансформировать ваши модели и коллекции моделей в желаемое JSON-представление. Перевод автора
Это теория, а на практике мы с вами сегодня используем api-ресурсы для вывода списка пользователей воображаемого веб-проекта. Пример не сложный, но позволяет наглядно понять как работают api-ресурсы. Также, как было заявлено в вводной части, в примере используем два других нововведения из Laravel 5.5: функцию (helper) optional() и возможность создания отдельного файла для фабрики (factory).

В данном примере я использую БД SQLite, но вы можете использовать любую другую.

Для того, чтобы фреймворк «понял», что мы хотим работать с SQLite, нужно изменить настройки соединения с БД в файле .env. Откроем его, и в строке DB_CONNECTION пропишите значение sqlite. Остальные строки данной секции (DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD) нужно удалить. Получится так:
DB_CONNECTION=sqlite
Не забудьте создать файл БД database.sqlite в папке database проекта.

Далее маршруты. Откроем файл web.php (папка routes) и добавим в него следующий маршрут:
Route::resource('api/users', 'Api\UserController')
    ->only(['index', 'show']);
Как видим, нам нужно, чтобы контроллер реагировал только на действия index и show.

Создадим сам контроллер, я назвал его UserController, потому как в данном проекте мы работаем с пользователями. Для создания контроллера понадобится команда:
php artisan make:controller Api/UserController —resource
Будет создана заготовка контроллера с кучей методов, из которых нам потребуется только два: index() и show($id). Остальные можно удалить.

Измените код контроллера на следующий:
<?php
namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use App\User;

class UserController extends Controller
{
    public function index()
    {
        $users = User::with('profile')->get();
        return UserResource::collection($users);
    }

    public function show(User $user)
    {
        return new UserResource($user);
    }
}
В методе index() мы используем статический метод collection() класса UserResource для вывода коллекции (списка) моделей пользователей в формате JSON.

В методе show() мы создаем новый объект класса UserResource из модели пользователя User и трансформируем его в JSON-представление.

Из кода контроллера следует, что далее нам следует поработать с моделью пользователя User, а затем создать класс UserResource, который требуется в контроллере.

Модель User существует по-умолчанию, поэтому нам не придется ее создавать, но для примера мы туда кое-что добавим. Чтобы немного усложнить проект создадим дополнительную модель Profile (профиль). В данной модели у нас будет поле location, которое будет содержать местоположение пользователя. Это поле будем выводить вместе с остальными полями из модели User (id, name, email). Как это будет реально выглядеть смотрите на рисунке в конце материала. Модель User и модель Profile должны быть соединены отношением hasOne. Для этого в файл модели User, внутри тела класса, после массивов $fillable и $hidden добавим код:
public function profile() {
    return $this->hasOne(Profile::class);
}
Теперь нужно создать саму модель Profile. Для этого используем команду:
php artisan make:model Profile -m
Этой командой мы также создали файл миграции create_profiles_table.php (опция -m в команде make:model). Откроем его (папка database/migrations). В метод up() добавим следующее содержимое:
Schema::create('profiles', function (Blueprint $table) {
    $table->increments('id');
    $table->unsignedInteger('user_id');
    $table->string('location');            
    $table->timestamps();
});
В модели Profile, в нашем примере, ничего менять или добавлять не требуется.

Запустим миграцию, чтобы создать таблицы в БД:
php artisan migrate
Наполним таблицу профилей случайными данными, используя Faker. Для этого запустим команду:
php artisan make:factory ProfileFactory
После этого в папке database/factories у нас будут 2 файла: UserFactory.php (был изначально) и ProfileFactory.php. Как видите, в Laravel 5.5 каждая фабрика теперь имеет отдельный файл. В файле UserFactory.php ничего менять не будем. Откроем для изменения файл ProfileFactory.php. Внутри метода define() в возвращаемый массив добавим элемент:
'location' => $faker->unique()->address
Создадим файл UsersTableSeeder.php, в котором укажем сколько связных записей нам нужно создать в таблицах users и profiles. Для этого запустим команду:
php artisan make:seeder UsersTableSeeder
Откроем созданный файл (UsersTableSeeder.php). В файле метод run() изменим следующим образом:
public function run()
{
    factory(App\User::class, 10)->create()->each(function($u) {
        $u->profile()->save(factory(App\Profile::class)->make());
    });
}
Здесь мы указываем, что нужно создать 10 связных записей в таблицах users и profiles.

Чтобы данный файл запустился его нужно добавить в очередь запуска через файл DatabaseSeeder.php. Откроем его и в методе run() расскоментируем строчку:
$this->call(UsersTableSeeder::class);
Ну а теперь самое приятное, запустим все это на исполнение командой:
php artisan db:seed
С моделью пользователя User и ее связной моделью Profile мы разобрались, теперь пришел черед создать класс UserResource (наконец-то!), который мы используем в контроллере. Данный класс создадим следующей командой:
php artisan make:resource UserResource
Откроем файл класса (папка app/Http/Resources). Заменим содержимое метода toArray() на следующее:
return [
    'id' => $this->id,
    'name' => $this->name,
    'email' => $this->email,
    'location' => optional($this->profile)->location
];
Заметьте, что здесь мы используем функцию (helper) optional(), которая также появилась в Laravel 5.5. Мы бы могли ее здесь и не использовать, а написать так:
'location' => $this->profile->location
Но тогда, если бы у нас не было создано связной записи в таблице profiles, для данного пользователя, то мы бы получили ошибку, а с функцией optional() поле location будет просто заполнено значением null. Попробуйте создать отдельную запись пользователя (без профиля) и удалите функцию optional() из кода.

Отдельную запись пользователя можно создать, запустив команду:
php artisan tinker
А затем команду:
factory(App\User::class, 1)->create();
На этом все. Результат работы приложения по адресу: /api/users (действие index) будет следующим: А по адресу, например, для вывода первого пользователя: /api/users/1 (действие show) будет следующим:

Оставьте свой комментарий

Комментарии