Livewire Comment System

김윤수·2024년 12월 12일
0

laravel

목록 보기
15/15

이번 블랙프라이데이에서 livewire 관련 강좌가 많은 코드코스 1년 강좌 포로그램이 50% 할인($120 > $60)이라 구매하였습니다.

가장 익숙한 내용인 댓글(comment) 강좌를 3일간 다 들고 난 뒤 내용 정리를 해둡니다.

늘 아는 내용이라고 생각했지만, 역시 강좌를 듣다가 보니 새로운 내용이 좀 있었네요.

마이그레이션

회원 탈퇴시 댓글은 남아 있게 마이그레이션
다형성(morph)도 nullable 하게

$table->foreignId('user_id')->nullable()->constrained()
    ->nullOnDelete(); # not cascadeOnDelete()
$table->nullableMorphs('commentable');

아니면 회원 탈퇴시 댓글 모두 삭제 처리하려면 모델 이벤트 처리

# User.php
public static function booted()
{
    static::deleting(static function (User $user) {
        $user->comments()->delete();
    });
}

다형성 모델 매핑

다형성 모델을 통해 댓글(comment) 모델을 저장하고 있는데 특이한 점은

commentable_type: App\Models\Article
commentable_id: 1

원래 이렇게 저장되어 있는데

# AppServiceProvider 
    public function boot(): void
    {
        Relation::enforceMorphMap([
            'article' => Article::class,
        ]);
    }
}

다형성 모델을 매핑을 이렇게 해두면

commentable_type: article
commentable_id: 1

이렇게 저장됩니다.
데이타베이스 용량도 줄이고 검색 쿼리도 더 빨라 질 것 같습니다. ^^

Policy를 권한 처리

# CommentItem.php
public function replyComment()
{
	$this->authorize('reply', $this->comment);
	....
}

# CommentPolicy.php
class CommentPolicy
{
    public function reply(User $user, Comment $comment)
    {
        return is_null($comment->parent_id);
    }
}

Apline Directive

livewire와 찰떡 궁합인 alpinjs를 이용한 directive(지침?) 처리
datetime 값을 기준으로 작성시간이 계속 변경됨
예: 몇초 전 > 1분 전 > 2분 전 ...

/** app.js */

import { Livewire, Alpine } from '../../vendor/livewire/livewire/dist/livewire.esm.js';

import humanDate from './directives/humanDate.js';

Alpine.directive('human-date', humanDate);
/** humanDate.js */
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
import ko from 'dayjs/locale/ko'

dayjs.extend(relativeTime)
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.locale(ko)

dayjs.tz.setDefault('Asia/Seoul')

export default (el) => {
    let datetime = el.getAttribute('datetime')

    if (!datetime) {
        return
    }

    const setHumanTime = () => {
        el.innerText = dayjs().tz().to(dayjs.tz(datetime))
    }

    setHumanTime()
    setInterval(setHumanTime, 30000)
}
# comment-item.blade.php
<div class="text-sm"
     x-human-date
     datetime="{{ $comment->created_at->toDateTimeString() }}">
    {{ $comment->created_at->diffForHumans() }}
</div>

댓글 삭제후 전체 목록 refresh

원래 제 스타일은 삭제가 발생하면 페이지 전체를 refresh를 했었습니다만, 여기는 그냥 해당 댓글을 감춰버리는 방식을 사용하네요.

# CommentItem
<?php

namespace App\Livewire;

use App\Livewire\Forms\CreateComment;
use App\Livewire\Forms\EditComment;
use App\Models\Comment;
use Livewire\Component;

class CommentItem extends Component
{
    public Comment $comment;
    public bool    $deleted = false;
    ....

    public function deleteComment()
    {
        ...
        $this->deleted = true;
    }
}


# comment-item.blade.php
<div>
    @if (! $deleted)
        <div ...>
    @endif
</div>

다음은 Build a Multi-room Realtime Chat with Livewire and Reverb (Reverb + livwire 채팅방 구현)을 들어볼 예정입니다.

profile
안녕하세요

2개의 댓글

comment-user-thumbnail
2024년 12월 30일

In the reply method, the condition return is_null($comment->parent_id); ensures that only comments without a parent can be replied to. Would this logic need to be adjusted if you plan to support nested or threaded Buckshot Roulette comments where replies can have multiple levels (i.e., replies to replies)? How would you handle permission for replying to a comment in such a case?

1개의 답글