Урок 03

Модификаторы колонок

Что это

Модификаторы — методы после колонки. Меняют поведение.

$table->string('email')->unique()->nullable();

unique и nullable — модификаторы.

Nullable

По умолчанию — NOT NULL. nullable() разрешает NULL.

$table->string('bio')->nullable();
$table->timestamp('published_at')->nullable();
$table->foreignId('category_id')->nullable()->constrained();

Когда: необязательные поля.

Default

Значение по умолчанию:

$table->integer('views')->default(0);
$table->boolean('active')->default(true);
$table->string('status')->default('draft');
$table->json('meta')->default('{}');
$table->timestamp('created_at')->useCurrent();

useCurrent — текущее время.

Unique

Уникальный индекс:

$table->string('email')->unique();
$table->string('slug')->unique();

Составной:

$table->unique(['user_id', 'slug']);

Своё имя:

$table->unique('email', 'users_email_unique');

Index

Обычный индекс:

$table->string('status')->index();
$table->index('created_at');
$table->index(['status', 'created_at']);

Своё имя:

$table->index('status', 'posts_status_idx');

Primary

Первичный ключ:

$table->primary('id');
$table->primary(['user_id', 'role_id']);   // составной

Обычно — через $table->id().

Auto-increment

$table->integer('id')->autoIncrement();

Или через $table->id() / increments().

Unsigned

Числа без отрицательных:

$table->integer('count')->unsigned();
$table->bigInteger('views')->unsigned();

В Laravel 11 — все числа по умолчанию unsigned при foreignId.

Comment

Комментарий к колонке:

$table->string('status')->comment('draft|published|archived');

Полезно для документации схемы.

After / Before

Позиция колонки (MySQL):

$table->string('status')->after('title');
$table->string('slug')->before('body');

⚠️ Работает только в MySQL. В PostgreSQL и SQLite — игнорируется.

Collation

Кодировка:

$table->string('title')->collation('utf8mb4_unicode_ci');

Charset

MySQL:

$table->string('title')->charset('utf8mb4');

Virtual / Stored

Виртуальные колонки (генерируемые):

$table->string('full_name')->virtualAs('CONCAT(first_name, " ", last_name)');
$table->integer('total')->storedAs('price * quantity');

virtualAs — вычисляется на лету. storedAs — сохраняется в БД.

Использование:

$user->full_name;   // вычисленное значение

Fulltext

Полнотекстовый поиск (MySQL, PostgreSQL):

$table->fullText('body');

Запрос:

Post::whereFullText('body', 'laravel')->get();

Spatial

Геоданные:

$table->geometry('location')->spatialIndex();
$table->point('location');

Invisible (MySQL 8+)**

Скрытая колонка:

$table->string('internal')->invisible();

Не показывается в SELECT *.

Chained

Модификаторы цепочкой:

$table->string('email')
    ->unique()
    ->nullable()
    ->comment('Email пользователя');

Порядок — не важен, но читаемость лучше соблюдать.

Полный пример

Schema::create('posts', function (Blueprint $table) {
    $table->id();

    $table->foreignId('user_id')
        ->constrained()
        ->cascadeOnDelete();

    $table->string('title');
    $table->string('slug')
        ->unique()
        ->comment('URL-slug для поста');

    $table->text('body');

    $table->string('status')
        ->default('draft')
        ->index();

    $table->integer('views')
        ->default(0)
        ->unsigned();

    $table->boolean('featured')
        ->default(false);

    $table->json('meta')
        ->nullable();

    $table->timestamp('published_at')
        ->nullable()
        ->index();

    $table->timestamps();
    $table->softDeletes();

    $table->unique(['user_id', 'slug']);
    $table->index(['status', 'published_at']);
});

Практика

Создай таблицу products:

  • id — автоинкремент.
  • name — строка, обязательно.
  • slug — строка, уникальная.
  • price — decimal(10,2), обязательно.
  • stock — integer, default 0.
  • is_active — boolean, default true.
  • description — text, nullable.
  • meta — json, nullable.
  • created_at, updated_at.
  • deleted_at — soft delete.

Проверь через php artisan db:show --table=products.

Частые ошибки

1. Модификатор без колонки:

$table->unique();   // ошибка

// Правильно:
$table->unique('email');

2. after на PostgreSQL:

$table->string('x')->after('y');   // игнорируется

3. Забыли nullable для foreignId:

$table->foreignId('category_id');   // NOT NULL

// Если категория может быть null:
$table->foreignId('category_id')->nullable();

4. default('') для NULL:

$table->string('bio')->default('');   // пустая строка

// Для NULL:
$table->string('bio')->nullable();

5. unique на больших текстовых:

$table->text('body')->unique();   // MySQL: индекс слишком длинный

Решение: уникальность на короткое поле (slug).

Итог

  • nullable() — может быть NULL.
  • default() — значение по умолчанию.
  • unique(), index() — индексы.
  • primary() — PK.
  • unsigned() — без отрицательных.
  • comment() — комментарий.
  • after(), before() — позиция (MySQL).
  • virtualAs, storedAs — вычисляемые.
  • fullText, spatialIndex — спец-индексы.

Дальше — внешние ключи.