chore: Record the 1.2.0 changelog - #24
Conversation
Releases 1.2.0.
Code Review Summary✨ Release 1.2.0 syncs 🚀 Key Improvements
💡 Minor Suggestions
|
| * A role assignment is written through the user's own morph relation, so | ||
| * this is what was stored, which is not always the configured class. | ||
| */ | ||
| private function userMorphClass(): string |
There was a problem hiding this comment.
The new helper is private, yet this release is explicitly about making subclasses work (see the RenamedWorkspace/subclass tests). A host that subclasses Workspace and needs to customise how the related user morph name is resolved (e.g. a subclassed or morph-mapped user model) cannot override a private method. Prefer protected so it is consistent with the subclass-friendly intent of the change and with HasWorkspaces::workspaceMorphClass() being overridable.
| private function userMorphClass(): string | |
| protected function userMorphClass(): string |
| * A role assignment is written through the user's own morph relation, so | ||
| * this is what was stored, which is not always the configured class. | ||
| */ | ||
| private function userMorphClass(): string |
There was a problem hiding this comment.
HasWorkspaces::workspaceMorphClass() is public static and overridable, but the symmetric userMorphClass() here is private. Because the release is explicitly about subclassing/morph-map support, a Workspace subclass should be able to override how the user-side morph name is resolved (e.g. when the user model is subclassed and the configured class differs from the concrete type). Raising visibility to protected restores that symmetry without changing behaviour.
| private function userMorphClass(): string | |
| + protected function userMorphClass(): string |
|
|
||
| if (method_exists($this, 'assignRole')) { | ||
| $this->assignRole($role, static::workspaceModel(), $workspace->id); | ||
| $this->assignRole($role, static::workspaceMorphClass(), $workspace->id); |
There was a problem hiding this comment.
The write path stores the role assignment's context_type from static::workspaceMorphClass() (a fresh instance of the configured model), whereas the read paths that must find it — Workspace::members(), Workspace::roleAssignments() and WorkspaceController::hasRole() — all compare context_type against the actual $workspace instance's getMorphClass(). Storing the name of the instance being joined keeps the write and read sides of the contract on the same value, mirroring the fix already applied on the read side. In the common case (config == concrete type) the values are identical, so this is a robustness alignment rather than a behaviour change.
| $this->assignRole($role, static::workspaceMorphClass(), $workspace->id); | |
| + $this->assignRole($role, $workspace->getMorphClass(), $workspace->id); |
| if (method_exists($this, 'removeRole')) { | ||
| foreach (Role::values() as $role) { | ||
| $this->removeRole($role, static::workspaceModel(), $workspace->id); | ||
| $this->removeRole($role, static::workspaceMorphClass(), $workspace->id); |
There was a problem hiding this comment.
Same alignment as joinWorkspace: removal should target the morph name the assignment was stored under. Using the $workspace instance here matches the context_type value used by members() and the controller's hasRole() checks, so assignments created and removed through the same workspace instance resolve consistently.
| $this->removeRole($role, static::workspaceMorphClass(), $workspace->id); | |
| + $this->removeRole($role, $workspace->getMorphClass(), $workspace->id); |
| return $this; | ||
| } | ||
|
|
||
| /** |
There was a problem hiding this comment.
The added helper resolves the user's polymorphic name from config('workspaces.user_model'), but the reverse relation HasWorkspaces::workspaces() matches the same column with the runtime instance's $this->getMorphClass(). The two directions of the same membership therefore agree only while workspaces.user_model names the exact runtime user class; a host whose runtime user is a subclass (or morph-mapped model) that config does not name will still get an empty members() result. members() cannot see the runtime user, so the assumption is inherent — but it should be stated so the coupling is discoverable rather than re-discovered as a bug. Documenting the required config value is a safe, behavior-preserving clarification.
| /** | |
| + /** | |
| + * The name a user is stored under in a polymorphic column. | |
| + * | |
| + * A role assignment is written through the user's own morph relation, so | |
| + * this is what was stored, which is not always the configured class. | |
| + * | |
| + * This derives the value from `workspaces.user_model`, whereas | |
| + * `HasWorkspaces::workspaces()` derives it from the runtime user instance. | |
| + * The two agree only when `workspaces.user_model` names the runtime user | |
| + * class (morph map aliases included); otherwise `members()` can miss rows | |
| + * that `$user->workspaces()` still returns. | |
| + */ | |
| + private function userMorphClass(): string | |
| + { | |
| + $model = config('workspaces.user_model', 'App\\Models\\User'); | |
| + | |
| + return (new $model)->getMorphClass(); | |
| + } |
Adds the 1.2.0 entry. The release itself is the dev to main sync that follows.