Skip to content

Fix driver uuid broadcast channel; comment visibility and comment.created; chat participant roles and system message meta - #306

Merged
roncodes merged 3 commits into
release/v1.6.69from
fix/driver-channel-and-comment-visibility
Oct 13, 2026
Merged

roncodes merged 3 commits into
release/v1.6.69from
fix/driver-channel-and-comment-visibility

Conversation

@roncodes

@roncodes roncodes commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

What and why

Core pieces for the order operations work (order notes visibility, order chat with the store), plus a broadcast channel bug fix.

1. Fix: relationship uuid channels (ResourceLifecycleEvent)

addRelationshipSpecificChannels checked isset($model->driver_assigned_uuid) but built the channel from $model->driverAssigned_uuid, which never exists, so order lifecycle events went out on a dead driverAssigned. channel instead of driverAssigned.{driver uuid}. It now reads the same snake case key it checks. Channel prefixes are unchanged (driverAssigned., customer., facilitator., vendor.). The existing test hid the bug by setting both attributes; it now sets only the real column, and a regression test covers the case where only foreign keys are set.

2. Comment visibility (meta.visibility = internal | customer)

  • When no visibility is given: a reply takes its parent's visibility. A comment on an order (Comment::$visibilityAwareSubjectTypes = ['order']) is internal when written through an internal route (int/..., storefront/int/...) and customer from anywhere else. Comments on any other subject keep no visibility, and no visibility counts as visible, so existing comments and callers behave as before. Unknown values are dropped and the default applies.
  • visibility is accepted on create as a shorthand for meta.visibility (in Comment::publish, so it works for both the console and the public API).
  • New on Comment: the scopes scopeVisibleToCustomers() (everything but internal, including null meta and meta without a visibility) and scopeInternal(), plus getVisibility(), isInternal() and isVisibleToCustomers().
  • Public v1 comments API: the listing (CommentFilter::queryForPublic) and GET v1/comments/{id} exclude internal comments; an internal comment's find returns 404. POST v1/comments accepts an optional visibility (nullable|in:internal,customer).
  • Comment resource: adds a top-level visibility field (internal or customer). Outside internal requests, internal replies are left out of replies.

3. Chat participant roles and system messages

  • Migration 2026_10_13_000000_add_meta_to_chat_participants_and_chat_messages_tables adds a nullable json meta column to chat_participants and chat_messages. Each step checks whether the column exists first, and down() drops it.
  • ChatParticipant: meta is fillable with a json cast. Adds the ROLE_CUSTOMER|ROLE_DRIVER|ROLE_STORE|ROLE_SYSTEM constants, ROLES, and getRole(), which reads meta.role and returns null for unknown values. The resource adds role and meta, so sender.role reaches apps over HTTP and in the chat_message.* broadcasts.
  • ChatMessage: meta is fillable with a json cast. Adds isSystem() (meta.system) and getSystemKind() (meta.kind, e.g. order_updated, address_change_requested). The resource adds system (bool) and meta. A missing sender now renders as null instead of throwing. The resource also had a duplicate public sender key that the participant resource always overwrote; it is removed, so the output is unchanged.
  • The public send-message endpoint still builds its own attributes, so customers cannot post system messages. Existing chat behaviour is otherwise unchanged.

4. comment.created broadcast

A new Fleetbase\Events\CommentCreated (ShouldBroadcastNow) is dispatched from Comment::created. Its payload is { id, event: "comment.created", created_at, subject_id, data: <Comment resource> }.

  • Every comment is broadcast on company.{company_uuid}.
  • Customer-visible comments (anything not internal) are also broadcast on the subject's channels, {subject type}.{subject public_id} and {subject type}.{subject uuid} (e.g. order.order_x). Internal notes never go to subject channels.
  • A broadcast failure is logged and never fails the comment.

Migrations

  • migrations/2026_10_13_000000_add_meta_to_chat_participants_and_chat_messages_tables.php (nullable json meta on chat_participants and chat_messages, guarded)

Validation

  • vendor/bin/pest (full suite, PHP 8.4): 2113 passed, 1 failed. The same single failure happens on the base branch (UtilsTest > utils reads composer package keyword metadata from the lock file, because there is no composer.lock in a worktree). Base had 2104 passed, so there are no regressions, and the 9 new tests pass.
  • The EventsAndExceptionsTest > unauthorized request exception... test fails only when CommentControllerTest runs before it in a partial run, because of a getController macro. This also happens on the base branch, and the test passes in the full suite.
  • php-cs-fixer fix --dry-run: 0 files to fix.
  • scripts/check-test-date-drift.php: passed.
  • phpstan on the touched files: only the same categories the codebase already reports at level max (undefined Eloquent magic properties, mixed resource calls). There are no new kinds of error.

New tests:

  • tests/Unit/EventsAndExceptionsTest.php: uuid channel regression, and chat resource role / meta / system / senderless message
  • tests/Unit/Http/CommentControllerTest.php: visibility defaults (console, storefront int, public, explicit, meta, invalid), replies inheriting from their parent, scopes, public listing/find/replies hiding internal comments, visibility on public create, comment.created channels and payload, dispatch, and broadcast failure tolerance
  • tests/Unit/Models/ChatModelsTest.php: migration up/down/idempotent, role and system meta round trip

Docs / API impact

  • API behaviour change (postman): the public GET v1/comments and GET v1/comments/{id} no longer return comments with meta.visibility = internal. POST v1/comments accepts visibility (internal|customer). Comment responses gain visibility. Chat participant responses gain role and meta, and chat message responses gain system and meta. These need updating in fleetbase/postman (Comments and Chat collections). (needs-api-spec)
  • Realtime (docs): the new comment.created event and its channels, plus the fixed driverAssigned.{uuid} channel, should be documented in fleetbase/fleetbase.io under the realtime/socket events docs. (needs-docs)
  • Existing data is untouched: nothing is backfilled, and comments with no visibility stay visible.

Part of the order operations program (notes visibility, order chat with the store).

…annels

addRelationshipSpecificChannels checked isset($model->driver_assigned_uuid) but then built the
channel from $model->driverAssigned_uuid, an attribute that never exists, so every order lifecycle
event broadcast on a dead 'driverAssigned.' channel instead of 'driverAssigned.{driver uuid}'.
Read the same snake case key it checks.
…st comment.created

Comments carry meta.visibility = internal | customer (a 'visibility' shorthand is accepted on
create). When none is given: replies follow their parent, comments on orders are internal when
written through an internal (console) route and customer from anywhere else, and comments on other
subjects keep no visibility, which stays visible, so existing data and callers are unaffected.

- Comment::scopeVisibleToCustomers() (everything but internal) and scopeInternal(),
  getVisibility(), isInternal(), isVisibleToCustomers()
- the public v1 comments API excludes internal comments from the listing (CommentFilter
  queryForPublic) and find, accepts an optional visibility on create, and the Comment resource
  hides internal replies outside internal requests and exposes a top level 'visibility'
- new CommentCreated event broadcasts 'comment.created' with the comment resource on
  company.{company_uuid} for every comment and, for customer visible comments only, on the
  subject's channels ({subject type}.{public_id} and {subject type}.{uuid}, e.g. order.order_x);
  a broadcast failure is logged and never fails the comment
Adds a nullable json meta column to chat_participants and chat_messages (guarded migration).

- ChatParticipant: meta (fillable, json cast), ROLE_* constants (customer, driver, store, system)
  and getRole() reading meta.role; the resource adds 'role' and 'meta', so a message's sender.role
  reaches the apps over HTTP and the chat_message broadcasts
- ChatMessage: meta (fillable, json cast), isSystem() (meta.system) and getSystemKind()
  (meta.kind); the resource adds 'system' and 'meta' and renders a missing sender as null instead
  of failing (the duplicate public 'sender' key it dropped was always overwritten by the
  participant resource)

The public send-message endpoint still builds its own attributes, so customers cannot post system
messages or set roles; existing chat behaviour is unchanged.
@roncodes
roncodes merged commit bb8845a into release/v1.6.69 Oct 13, 2026
2 of 3 checks passed
@roncodes
roncodes deleted the fix/driver-channel-and-comment-visibility branch October 13, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant