Урок 03

CRUD: создание и чтение

Создание

new + save

Классический способ:

$post = new Post();
$post->title = 'Заголовок';
$post->body = 'Текст';
$post->save();

save() возвращает true или false.

create

Массовое присваивание:

$post = Post::create([
    'title' => 'Заголовок',
    'body' => 'Текст',
]);

Требует $fillable. Поля, которых нет в $fillable, будут проигнорированы.

firstOrCreate

Найти или создать:

$post = Post::firstOrCreate(
    ['slug' => 'hello-world'],              // по этим полям ищем
    ['title' => 'Hello World', 'body' => '…'], // эти ставим при создании
);

Если найдено — возвращает существующее. Если нет — создаёт с обоими массивами.

Пример:

$tag = Tag::firstOrCreate(['name' => 'Laravel']);
// если тега нет — создаст только с name

firstOrNew

Как firstOrCreate, но не сохраняет:

$post = Post::firstOrNew(['slug' => 'hello']);
// $post->exists === false
$post->title = 'Hello';
$post->save();

updateOrCreate

Обновить или создать:

$post = Post::updateOrCreate(
    ['slug' => 'hello-world'],
    ['title' => 'Новый заголовок'],
);

Если найдено — обновит. Если нет — создаст.

Частое использование — сохранение формы, sync данных.

insert

Массовая вставка без моделей:

Post::insert([
    ['title' => 'A', 'body' => 'Text A'],
    ['title' => 'B', 'body' => 'Text B'],
]);

Что НЕ работает:

  • Не запускает события (creating, created).
  • Не проставляет timestamps.
  • Не возвращает ID.
  • Не проходит через касты.

Быстро, но осторожно.

createMany

Массово через модель:

$user->posts()->createMany([
    ['title' => 'A', 'body' => 'Text A'],
    ['title' => 'B', 'body' => 'Text B'],
]);

Возвращает коллекцию моделей.

upsert

INSERT ... ON CONFLICT UPDATE:

Post::upsert([
    ['id' => 1, 'title' => 'A', 'slug' => 'a'],
    ['id' => 2, 'title' => 'B', 'slug' => 'b'],
], ['id'], ['title']);

Аргументы:

  1. Данные.
  2. Уникальные поля (по ним определяется конфликт).
  3. Поля для обновления.

Один запрос вместо N. Быстро.

Чтение

find

По ID:

$post = Post::find(1);
// null, если нет

Массив ID:

$posts = Post::find([1, 2, 3]);
// Collection

findOrFail

404, если не найдено:

$post = Post::findOrFail(1);
// ModelNotFoundException → 404

В контроллере:

public function show(int $id)
{
    $post = Post::findOrFail($id);
    return view('posts.show', compact('post'));
}

findMany

Как find, но с массивом:

$posts = Post::findMany([1, 2, 3]);

first

Первая запись:

$post = Post::where('status', 'published')->first();
// null, если нет

firstOrFail

$post = Post::where('slug', 'hello')->firstOrFail();
// 404, если нет

all

Все записи:

$posts = Post::all();

⚠️ Осторожно — если записей миллион, всё в память.

get

После условий:

$posts = Post::where('status', 'published')->get();

value

Одно значение:

$title = Post::where('id', 1)->value('title');

Быстрее, чем find(1)->title.

pluck

Колонка значений:

$titles = Post::pluck('title');
// Collection: ['A', 'B', 'C']

$titles = Post::pluck('title', 'id');
// Collection: [1 => 'A', 2 => 'B']

⚠️ pluck возвращает Collection, не модель.

count

$count = Post::where('status', 'published')->count();

exists

if (Post::where('slug', 'hello')->exists()) {
    // есть
}

if (Post::where('slug', 'hello')->doesntExist()) {
    // нет
}

Агрегаты

Post::max('views');
Post::min('views');
Post::sum('views');
Post::avg('rating');

Обновление

Через атрибут

$post = Post::find(1);
$post->title = 'Новый';
$post->save();

update

$post->update(['title' => 'Новый']);

Требует $fillable.

Массовое

Post::where('status', 'draft')->update(['status' => 'archived']);

⚠️ НЕ запускает события модели (updating, updated). ⚠️ НЕ обновляет updated_at.

С updated_at:

Post::where('status', 'draft')->update([
    'status' => 'archived',
    'updated_at' => now(),
]);

increment / decrement

$post->increment('views');       // +1
$post->increment('views', 5);    // +5
$post->decrement('likes');       // -1

Быстро — один UPDATE без чтения.

Удаление

delete

$post->delete();

destroy

По ID:

Post::destroy(1);
Post::destroy([1, 2, 3]);

С SoftDeletes — помечает как удалённые.

Массовое

Post::where('status', 'draft')->delete();

⚠️ Не запускает события.

truncate

Post::truncate();

Удаляет всё. Сбрасывает auto-increment. ⚠️ Нельзя откатить.

Практика

// Создание
$user = User::find(1);

$post = $user->posts()->create([
    'title' => 'Первый пост',
    'body' => 'Текст',
    'status' => 'draft',
]);

// Обновление
$post->update(['status' => 'published', 'published_at' => now()]);

// Массовое обновление
Post::where('user_id', $user->id)
    ->where('status', 'draft')
    ->update(['status' => 'archived', 'updated_at' => now()]);

// Чтение
$published = Post::where('status', 'published')
    ->orderBy('published_at', 'desc')
    ->limit(10)
    ->get();

// Удаление
$post->delete();

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

1. create без $fillable:

protected $fillable = [];
Post::create(['title' => 'X']);   // создастся пустая запись

2. insert вместо create:

Post::insert(['title' => 'X']);
// timestamps = NULL, events не сработали

3. update без updated_at:

Post::where('x', 1)->update(['y' => 2]);
// updated_at остался старым

4. firstOrCreate с логикой в первом массиве:

// Плохо: если найдено, второй массив игнорируется
Post::firstOrCreate(['slug' => 'x', 'title' => 'Y']);

Итог

  • Создание: create, firstOrCreate, updateOrCreate, insert, upsert.
  • Чтение: find, findOrFail, first, all, get, pluck, value.
  • Обновление: update, increment, массовое.
  • Удаление: delete, destroy, truncate.
  • Правило: для моделей — методы модели, для массовых — Query Builder.

Дальше — массовые операции и upsert.