Объектно-ориентированное программирование (ООП) является одним из столпов любого языка программирования и одним из самых больших его преимуществ является наследование, а мы можем это использовать для реализации кастомного Blueprint.
Для начала давайте разберем, что такое Blueprint?
Класс Blueprint
В реальных задачах мы часто используем миграции. Когда мы создаем какую-либо миграцию, то в папке миграций автоматически создается файл, имеющий два метода: up и down. Они содержат логику в соответствии с требованиями конкретной миграции.
Чтобы определить новую таблицу и добавить в неё столбцы, мы можем использовать экземпляр Blueprint. Вот как он используется в миграции:
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
}
Проблема
Давайте рассмотрим пример системы управления сотрудниками. У каждого сотрудника (системный администратор, менеджер по персоналу или менеджер проекта) будут разные доступы к разным модулям в системе. Смотрите на рисунки ниже.


Как правило, сотрудник отдела кадров обрабатывает заявления на отпуск, а проект-менеджер отвечает за выставление счетов в рамках проекта. Теперь, чтобы отслеживать изменения, у нас должны быть в каждой модели следующие поля: created_at, created_by, updated_at, updated_by, deleted_at и deleted_by.
Проблема 1. Метод Timestamps() предоставляет поля created_at и updated_at, а метод SoftDeletes() предоставляет поле deleted_at. Нам нужно еще в каждую модель вручную добавить поля created_by, updated_by и deleted_by.
Проблема 2. В случае неблагоприятных обстоятельствах, когда нужно изменить название какого-либо поля, то это будет утомительной задачей, ведь придется сделать это в каждой модели.
Решение
Чтобы решить обе проблемы, мы должны переместить все эти поля в одно место. Это делается путем создания нового класса CustomBlueprint.
namespace App\common;
use Illuminate\Database\Schema\Blueprint;
class CustomBlueprint extends Blueprint
{
public function commonFields()
{
$this->timestamp('created_at')->nullable();;
$this->unsignedBigInteger('created_by')->nullable();
$this->timestamp('updated_at')->nullable();;
$this->unsignedBigInteger('updated_by')->nullable();
$this->timestamp('deleted_at')->nullable();;
$this->unsignedBigInteger('deleted_by')->nullable();
$this->boolean('is_deleted')->default(0);
}
}
Так как мы расширяем дефолтный класс Blueprint, то по умолчанию мы можем использовать все его поля и методы, такие как string, char, decimal, timestamps() и т.д. Теперь, наш первый шаг — создать метод commonFields(), и объявить все общие поля, необходимые для моделей, в нем. Настало время изменить файл миграции.
public function up()
{
$schema = DB::connection()->getSchemaBuilder();
}
Нам нужен экземпляр Schema Builder для соединения, и это делается с помощью метода getSchemaBuilder() в методе up.
$schema->blueprintResolver(function ($table, $callback) {
return new CustomBlueprint($table, $callback);
});
BlueprintResolver используется для установки преобразователя. Возвращая наш новый объект CustomBlueprint, мы можем изменить дефолтное поведение объекта Blueprint.
$schema->create('roles', function (CustomBlueprint $table) {
$table->bigIncrements('id');
$table->string('name')->unique();
$table->commonFields();
});
Теперь мы можем использовать объект класса CustomBlueprint для добавления столбцов вместо дефолтного класса Blueprint. Благодаря методу commonFields() модель «role» получит created_at, updated_by и все другие поля, определенные в нём. После запуска этой мы сможем увидеть следующую структуру.

Окончательный файл миграции выглядит так:
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use App\Common\CustomBlueprint;
class CreateRolesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$schema = DB::connection()->getSchemaBuilder();
$schema->blueprintResolver(function ($table, $callback) {
return new CustomBlueprint($table, $callback);
});
$schema->create('roles', function (CustomBlueprint $table) {
$table->bigIncrements('id');
$table->string('name')->unique();
$table->commonFields();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('roles');
}
}
Если мы будем следовать этой процедуре для каждой модели, то эти поля автоматически добавятся и в них.
Автор: Harsh Shah
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.
