[2.x] chore(flags): dispatch model events when flags are deleted - #4818
[2.x] chore(flags): dispatch model events when flags are deleted#4818DavideIadeluca wants to merge 4 commits into
Conversation
imorland
left a comment
There was a problem hiding this comment.
The direction here is good — replacing the global HasMany::macro('delete') with an explicit Dismissed event is a real improvement and takes some fragile complexity out of AuditIntegration. A few things need addressing before it can go in, though.
Flags are deleted in two places, and only one is covered. DeleteFlagsHandler dismisses via the UI, but Listener/DeleteFlags also deletes a post's flags in response to Post\Event\Deleted (when a flagged post is deleted). The old macro hooked HasMany::delete globally, so it caught both paths; the new Dismissed event is only dispatched from the command handler. As it stands, deleting a flagged post no longer logs post.dismissed_flags — that's a regression from the current behaviour.
The stated goal isn't fully met for the same reason. The point of the change is to fire model events on flag deletion so observers can hook in, but Listener/DeleteFlags still uses a bulk ->delete(), which fires no model events. So an observer would see UI dismissals but silently miss the post-deletion case.
Needs test coverage for the post-deletion path. AuditTest::delete() only exercises the command path (which still works), so this gap passes CI unnoticed. Please add a test that deletes a flagged post and asserts the dismissal is handled (audit log entry / events fired), so the second path is actually covered.
So: apply the same treatment to Listener/DeleteFlags so both deletion paths behave consistently, and add the post-deletion test. Happy to look again once that's in.
|
@imorland Thanks for the review — I dug into the post-deletion path and the regression turns out not to exist.
That's also why the macro was semantically questionable: it hooked For model events: hooking Deleted can never fire them (the cascade already removed the rows), so I moved Listener/DeleteFlags to One trade-off with listening to Deleting: the flags are removed before the post delete is final, so if the deletion were to abort after the event, the flags would already be gone. Previously the cascade removed them atomically with the post row. The window is a single delete statement on the API path, so I think it's acceptable — but if we want to avoid it entirely, we could snapshot the flags on Deleting (loadMissing('flags')) and run their Eloquent deletes on Deleted: the rows are already cascade-deleted by then, but Model::delete() still fires the model events. Happy to go that route if you prefer. Post-deletion path is now covered: DeleteFlagsOnPostDeletionTest asserts model events fire per flag, and |
|
Thanks Dave — really appreciate you digging into the cascade timing, and the "acted upon vs dismissed" framing is a genuinely good point that I want to come back to. But before that, I went and tested the post-deletion path directly, and it turns out the old code does log I think the cascade reasoning has one gap: at
|
control |
logsDismissal_onPostDeletion |
report (count) |
|
|---|---|---|---|
| 2.x | pass | pass — entry written | fail, prints 1 |
| this PR | pass | fail — no entry | pass, count 0 |
control passing on both sides rules out a setup artefact, so the flip is purely the post-deletion path. So the PR does remove the dismissal log on that path — that's the behaviour change I flagged originally.
Now, the more interesting bit: I actually think your instinct might be right — deleting a flagged post arguably isn't a "dismissal", and it's worth questioning whether the old log was ever the correct thing to record. But that makes this a deliberate change to audit semantics rather than a no-op, and I'd want us to decide it on purpose:
- If we agree the post-deletion path shouldn't log a dismissal, let's say so explicitly and keep a test that pins the new behaviour (no entry) as intended — and it's worth a thought as to whether anything else should be recorded when a moderator makes flagged content disappear, so the audit trail doesn't just go quiet.
- If we'd rather preserve the existing audit trail, the listener needs to log on the post-deletion path too.
Either's fine by me — I just don't want it changing silently. What's your read on whether that post-deletion log is worth keeping? The model-events rework itself (moving to Deleting, dropping the HasMany macro) is a solid improvement regardless of which way we go.
Yes It's a behaviour change when compared to the default branch right now, however this change was only introduced with the 2.x rewrite of
This is interesting, I'm observing the complete opposite on my local machine. There must be some environment differences or I understand you incorrectly Can you try running the following SQL on your local machine and share the output (prerequisit being that your local dev has at least one flag)? SET @p = (SELECT post_id FROM flags LIMIT 1);
SELECT @p AS probe_post_id;
START TRANSACTION;
SELECT COUNT(*) AS before_delete FROM flags WHERE post_id = @p;
DELETE FROM posts WHERE id = @p;
SELECT COUNT(*) AS after_delete FROM flags WHERE post_id = @p;
ROLLBACK;
SELECT COUNT(*) AS after_rollback FROM flags WHERE post_id = @p;This is what I'm getting: if I understand you correctly on your machine Output of my php flarum info |
Fixes #0000
No eloquent model events being dispatched when a flag gets deleted
Changes proposed in this pull request:
Reviewers should focus on:
Screenshot
Necessity
Confirmed
composer test).Required changes: