Uczymy się zapisywać notatki do autorów. Kontynuacja poprzednich lekcji w poprzednim projekcie. Do dzieła.

Ok, najpierw dodajemy z użyciem metody save, jak w relacji jeden do jednego:

Artisan::command('person-add-lorem {id}', function (int $id) {

    $person = Person::findOrFail($id);

    $note = new Note(["title" => "Lorem", "content" => "Lorem Ipsum"]);

    $person->notes()->save($note);
    
    $this->comment("note added");
    
});

Dla save nie robi różnicy, czy persom ma notes jako hasOne czy tak jak tutaj hasMany. Save zapisuje jedną i koniec.

Ok, teraz od drugiej strony z associate, tak samo jak robiliśmy w relacji jeden do jednego:

Artisan::command('person-assoc-lorem {id}', function (int $id) {

    $person = Person::findOrFail($id);

    $note = new Note(["title" => "Lorem", "content" => "Lorem Ipsum"]);

    $note->author()->associate($person)->save();
    
    $this->comment("note added");
    
});

Uwaga, nie zadziała. W kodzie jest pułapka. Laravel po nazwie metody (author) wnioskuje, że szukamy author_id, a takiej kolumny nie mamy, jest person_id zgodnie z Laravelowymi konwencjami nazewniczymi.

I co wtedy? Wystarczy podać nazwę kolumny w modelu jako opcjonalny argument:

class Note extends Model
{
    use HasFactory;
    use SoftDeletes;

    protected $fillable = ['title', 'content'];
    
    public function author(){
            return $this->belongsTo(Person::class, "person_id");
    }
}

Teraz wyraźnie zaznaczyliśmy, że choć metoda nazywa się author to kolumna, w której mamy zapisane numery id autorów nazywa się person_id. Teraz associate zadziała.

Ok, ale co nowego mamy w relacjach jeden do wielu? Po pierwsze saveMany:

Artisan::command('person-save-many {id}', function (int $id) {

    $person = Person::findOrFail($id);

    
    $person->notes()->saveMany([
        new Note(["title" => "Lorem", "content" => "Lorem Ipsum"]),
        new Note(["title" => "Lorem2", "content" => "Lorem Ipsum 2"]),
        new Note(["title" => "Lorem3", "content" => "Lorem Ipsum 3"]),
    ]);
    
    $this->comment("notes added");
    
});

Denerwujące jest to ciągłe zapisywanie new Note, ale taka metoda istnieje, bo w realnych przypadkach często możemy dostać tablicę pełną obiektów, które trzeba zapisać.

Natomiast mamy jeszcze metodę createMany, do której przekazujemy tablicę tablic z atrybutami określonymi w fillable:

Artisan::command('person-create-many {id}', function (int $id) {

    $person = Person::findOrFail($id);

    
    $person->notes()->createMany([
        ["title" => "Lorem 4", "content" => "Lorem Ipsum"],
        ["title" => "Lorem 5", "content" => "Lorem Ipsum"]
    ]);
    
    
    $this->comment("notes added");
    
});

Ok, czy mamy jeszcze coś ciekawego? Warto odwiedzić dokumentację Laravela. Po pierwsze, mamy te metody:

$user = User::find(1);
 
$user->posts()->createQuietly([
    'title' => 'Post title.',
]);
 
$user->posts()->createManyQuietly([
    ['title' => 'First post.'],
    ['title' => 'Second post.'],
]);

O eventach dopiero sobie powiemy, ale tak, mamy tutaj tworzenie relacji „po cichu”, bez triggerowania eventów i observerów. Co jeszcze?

Takie coś zwane protected $touches:

class Comment extends Model
{
    /**
     * All of the relationships to be touched.
     *
     * @var array
     */
    protected $touches = ['post'];
 
    /**
     * Get the post that the comment belongs to.
     */
    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class);
    }
}

Chodzi o to, aby update na modelu w relacji BelongsTo zrobił update na timestap updated_at modelu rodzica.