fix(page): 일괄 발행/미발행 시 활동 이력이 기록되도록 after_publish 훅 발화
페이지 목록 일괄 발행/미발행이 after_publish 훅을 발화하지 않아 활동로그에 발행/미발행이 기록되지 않던 문제를 수정한다. - bulkChangePublishStatus 를 DB::transaction 내부에서 페이지별로 before_publish -> update -> after_publish 발화하도록 재작성 (per-item 로그) - 존재하지 않는 id 가 섞이면 전체 롤백 (all-or-nothing) - 단일 mass UPDATE 전용 bulkUpdatePublished 제거 (dead code, 의존 확장 영향 없음) - 일괄 발행 성공 응답 메시지의 :count 미치환 정정 (success 의 4번째 인자 messageParams 로 치환 파라미터 전달) - 회귀 테스트 추가 (훅 발화 횟수/published 인자/롤백/메시지 치환)
This commit is contained in:
1 parent
0de58baf46
commit
bb654fb56c
7 files changed
+144
-32
No files matched your search
@@ -30,6 +30,7 @@
|
||||
- 새 페이지 등록 화면에서 슬러그를 입력하기 전 주소 미리보기가 "/page/{{slug}}" 처럼 표시되던 문제를 수정했습니다. 이제 입력 전에는 "/page/example" 로 보이고, 입력하면 입력한 주소로 즉시 바뀝니다. (#424 @gnuboard 님께서 제보해주셨습니다.)
|
||||
- 관리자 화면에서 페이지 첨부파일의 다운로드 / 미리보기가 관리자 전용 주소로 열리도록 수정했습니다. 이전에는 관리자 화면에서도 공개용 주소가 사용되어 비공개 페이지의 첨부파일을 관리자가 열지 못하는 경우가 있었습니다. (#424 @gnuboard 님께서 제보해주셨습니다.)
|
||||
- 페이지를 수정하거나 삭제한 직후 해당 페이지 상세 화면의 검색엔진용 캐시가 즉시 갱신되도록 수정했습니다. 이전에는 일부 캐시가 갱신되지 않아 변경 전 내용이 검색엔진에 남는 경우가 있었습니다. (#424 @gnuboard 님께서 제보해주셨습니다.)
|
||||
- 페이지 목록에서 여러 페이지를 한 번에 발행 / 미발행할 때 활동 이력에 발행 여부가 기록되지 않던 문제를 수정했습니다. 이제 일괄 처리에서도 페이지별로 발행 / 미발행 기록이 남습니다. (#424 @gnuboard 님께서 제보해주셨습니다.)
|
||||
|
||||
## [1.0.0-beta.3] - 2026-05-11
|
||||
|
||||
|
||||
@@ -212,9 +212,12 @@ class PageController extends AdminBaseController
|
||||
$validated = $request->validated();
|
||||
$count = $this->pageService->bulkChangePublishStatus($validated['ids'], $validated['published']);
|
||||
|
||||
return $this->success('sirsoft-page::messages.page.bulk_publish_success', [
|
||||
'count' => $count,
|
||||
]);
|
||||
return $this->success(
|
||||
'sirsoft-page::messages.page.bulk_publish_success',
|
||||
['count' => $count],
|
||||
200,
|
||||
['count' => $count],
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
return $this->error('sirsoft-page::messages.page.bulk_publish_failed', 500, $e->getMessage());
|
||||
}
|
||||
|
||||
@@ -97,13 +97,4 @@ interface PageRepositoryInterface
|
||||
* @return int 일치하는 페이지 수
|
||||
*/
|
||||
public function countByKeyword(string $keyword): int;
|
||||
|
||||
/**
|
||||
* 여러 페이지의 발행 상태를 일괄 변경합니다.
|
||||
*
|
||||
* @param array $ids 페이지 ID 목록
|
||||
* @param array $data 업데이트할 데이터 (published, updated_by 등)
|
||||
* @return int 변경된 페이지 수
|
||||
*/
|
||||
public function bulkUpdatePublished(array $ids, array $data): int;
|
||||
}
|
||||
@@ -289,18 +289,6 @@ class PageRepository implements PageRepositoryInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 여러 페이지의 발행 상태를 일괄 변경합니다.
|
||||
*
|
||||
* @param array $ids 페이지 ID 목록
|
||||
* @param array $data 업데이트할 데이터 (published, updated_by 등)
|
||||
* @return int 변경된 페이지 수
|
||||
*/
|
||||
public function bulkUpdatePublished(array $ids, array $data): int
|
||||
{
|
||||
return Page::whereIn('id', $ids)->update($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* filters 배열을 search/search_field로 정규화합니다.
|
||||
*
|
||||
|
||||
@@ -211,16 +211,40 @@ class PageService
|
||||
*/
|
||||
public function bulkChangePublishStatus(array $ids, bool $published): int
|
||||
{
|
||||
$updateData = [
|
||||
'published' => $published,
|
||||
'updated_by' => Auth::id(),
|
||||
];
|
||||
|
||||
if ($published) {
|
||||
$updateData['published_at'] = now();
|
||||
if (empty($ids)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->pageRepository->bulkUpdatePublished($ids, $updateData);
|
||||
return DB::transaction(function () use ($ids, $published) {
|
||||
$userId = Auth::id();
|
||||
$count = 0;
|
||||
|
||||
foreach ($ids as $id) {
|
||||
// 존재하지 않는 페이지가 섞이면 예외 → 트랜잭션 전체 롤백 (all-or-nothing)
|
||||
$page = $this->pageRepository->findOrFail((int) $id);
|
||||
|
||||
HookManager::doAction('sirsoft-page.page.before_publish', $page, $published);
|
||||
|
||||
$updateData = [
|
||||
'published' => $published,
|
||||
'updated_by' => $userId,
|
||||
];
|
||||
|
||||
// 발행 시 published_at 갱신
|
||||
if ($published) {
|
||||
$updateData['published_at'] = now();
|
||||
}
|
||||
|
||||
$page = $this->pageRepository->update($page, $updateData);
|
||||
|
||||
// 페이지별 after_publish 발화 → 활동로그 per-item 기록 (이슈 #424-19)
|
||||
HookManager::doAction('sirsoft-page.page.after_publish', $page, $published);
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
return $count;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -882,6 +882,11 @@ class PageControllerTest extends FeatureTestCase
|
||||
->assertJsonPath('success', true)
|
||||
->assertJsonPath('data.count', 3);
|
||||
|
||||
// 성공 메시지의 :count 플레이스홀더가 실제 건수로 치환되는지 확인 (이슈 #424-19 회귀)
|
||||
$message = $response->json('message');
|
||||
$this->assertStringNotContainsString(':count', $message, '메시지에 :count 플레이스홀더가 치환되지 않고 남았습니다.');
|
||||
$this->assertStringContainsString('3', $message, '메시지에 변경 건수(3)가 포함되어야 합니다.');
|
||||
|
||||
// DB 확인
|
||||
foreach ($ids as $id) {
|
||||
$this->assertDatabaseHas('pages', ['id' => $id, 'published' => true]);
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Modules\Sirsoft\Page\Tests\Unit\Services;
|
||||
|
||||
use App\Enums\PermissionType;
|
||||
use App\Enums\ScopeType;
|
||||
use App\Extension\HookManager;
|
||||
use App\Helpers\PermissionHelper;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Role;
|
||||
@@ -206,6 +207,105 @@ class PageServiceTest extends ModuleTestCase
|
||||
);
|
||||
|
||||
$this->assertEquals(3, $count);
|
||||
|
||||
// 일괄 발행 후 published/published_at 가 실제 갱신되는지 확인
|
||||
foreach ($pages as $page) {
|
||||
$this->assertDatabaseHas('pages', [
|
||||
'id' => $page->id,
|
||||
'published' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 일괄 발행 시 after_publish 훅이 페이지 수만큼 발화되는지 확인 (이슈 #424-19 회귀)
|
||||
*
|
||||
* 일괄변경 경로가 after_publish 훅을 발화하지 않으면 활동로그가 기록되지 않는다.
|
||||
* 단일 발행(changePublishStatus)과 동일하게 페이지별(per-item) 발화를 보장한다.
|
||||
*/
|
||||
public function test_bulk_change_publish_status_fires_after_publish_hook_per_page(): void
|
||||
{
|
||||
$pages = Page::factory()->count(3)->create([
|
||||
'published' => false,
|
||||
'created_by' => $this->adminUser->id,
|
||||
'updated_by' => $this->adminUser->id,
|
||||
]);
|
||||
|
||||
$firedPageIds = [];
|
||||
$firedPublishedFlags = [];
|
||||
HookManager::addAction(
|
||||
'sirsoft-page.page.after_publish',
|
||||
function (Page $page, bool $published) use (&$firedPageIds, &$firedPublishedFlags) {
|
||||
$firedPageIds[] = $page->id;
|
||||
$firedPublishedFlags[] = $published;
|
||||
}
|
||||
);
|
||||
|
||||
$this->service->bulkChangePublishStatus($pages->pluck('id')->toArray(), true);
|
||||
|
||||
// 페이지 수만큼 발화 + 각 페이지 id 가 1회씩
|
||||
$this->assertCount(3, $firedPageIds);
|
||||
$this->assertEqualsCanonicalizing(
|
||||
$pages->pluck('id')->toArray(),
|
||||
$firedPageIds
|
||||
);
|
||||
// 발행 인자가 모두 true
|
||||
$this->assertSame([true, true, true], $firedPublishedFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일괄 미발행 시 after_publish 훅이 published=false 로 발화되는지 확인 (이슈 #424-19 회귀)
|
||||
*/
|
||||
public function test_bulk_change_publish_status_fires_unpublish_hook_per_page(): void
|
||||
{
|
||||
$pages = Page::factory()->published()->count(3)->create([
|
||||
'created_by' => $this->adminUser->id,
|
||||
'updated_by' => $this->adminUser->id,
|
||||
]);
|
||||
|
||||
$firedPublishedFlags = [];
|
||||
HookManager::addAction(
|
||||
'sirsoft-page.page.after_publish',
|
||||
function (Page $page, bool $published) use (&$firedPublishedFlags) {
|
||||
$firedPublishedFlags[] = $published;
|
||||
}
|
||||
);
|
||||
|
||||
$this->service->bulkChangePublishStatus($pages->pluck('id')->toArray(), false);
|
||||
|
||||
$this->assertSame([false, false, false], $firedPublishedFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일괄 처리 도중 한 건이라도 실패하면 전체 롤백되는지 확인 (이슈 #424-19 회귀)
|
||||
*
|
||||
* all-or-nothing: 존재하지 않는 id 가 섞이면 정상 페이지도 발행되지 않아야 한다.
|
||||
*/
|
||||
public function test_bulk_change_publish_status_rolls_back_on_failure(): void
|
||||
{
|
||||
$pages = Page::factory()->count(2)->create([
|
||||
'published' => false,
|
||||
'created_by' => $this->adminUser->id,
|
||||
'updated_by' => $this->adminUser->id,
|
||||
]);
|
||||
|
||||
$ids = $pages->pluck('id')->toArray();
|
||||
$ids[] = 999999; // 존재하지 않는 페이지 id
|
||||
|
||||
try {
|
||||
$this->service->bulkChangePublishStatus($ids, true);
|
||||
$this->fail('존재하지 않는 페이지 id 포함 시 예외가 발생해야 한다.');
|
||||
} catch (\Throwable $e) {
|
||||
// 예외 발생 기대
|
||||
}
|
||||
|
||||
// 정상 페이지 2건도 발행되지 않아야 함 (전체 롤백)
|
||||
foreach ($pages as $page) {
|
||||
$this->assertDatabaseHas('pages', [
|
||||
'id' => $page->id,
|
||||
'published' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── restoreVersion ────────────────────────────────
|
||||
|
||||
Reference in new issue
Block a user