The bigint→UUID conversion already preserved data via in-place column
swaps, but operators reasonably wanted stronger guarantees. Added:
- Pre-conversion snapshot per affected table:
CREATE TABLE <name>_bak_bigint_uuid_2026_04_29 LIKE <name>;
INSERT INTO <name>_bak_bigint_uuid_2026_04_29 SELECT * FROM <name>;
Idempotent — a retry after a crash never overwrites the original
snapshot. Snapshots are intentionally not auto-dropped.
- Post-conversion verification:
- row count must match the snapshot exactly per table; mismatch
throws and instructs recovery from the bak table
- FK integrity check: zero orphans in permission_members.permission_id
/ permission_usages.permission_id / role_members.role_id /
roles.parent_id (allowing NULL on the self-FK)
- Early-return if every affected table is already UUID, so reruns
on hosts that already migrated don't even create snapshots.
Plus roles:drop-uuid-migration-backup-tables to clean up the
snapshots once the operator is confident — with --dry-run and
explicit confirmation. Verified end-to-end on a disposable bigint
fixture: 3 rows preserved, 0 orphans, snapshot intact, schema
correctly converted to char(36).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The package's models (Permission, PermissionMember, Role, RoleMember,
Access, RequiredAccess) all use HasUuids but the published create
migrations created bigint columns. Every insert blew up in production
with 'Incorrect integer value: <uuid> for column id'.
Migrations
- create_blax_role_tables: uuid PK + uuidMorphs throughout
- create_blax_access_table: uuid PK + uuidMorphs/nullableUuidMorphs
- create_required_accesses_table: uuid PK + uuidMorphs
- add_source_to_accesses_table: nullableUuidMorphs
Two upgrade migrations convert in-place for hosts with existing data:
- 2026_04_29_000001 fixes required_accesses (idempotent, drops empty
table or leaves correct schema alone)
- 2026_04_29_000002 fixes the rest (permissions, permission_members,
permission_usages, roles, role_members, accesses) by adding staging
uuid columns, generating UUIDs per row, propagating into FK columns,
swapping in place, and rebuilding FK constraints. MySQL-only;
SQLite hosts get the correct schema directly from the create
migration. Idempotent (no-op on already-uuid schemas).
Models / traits
- Permission/PermissionMember restored to HasUuids (the schema fix
removes the conflict with the bigint id columns)
- RoleMember constructor was looking up the wrong config key
(role_members instead of role_member) and falling through to a
non-pluralised parent::getTable()
- HasRoles/HasPermissions now treat UUID strings as ids; previously
they were misinterpreted as role/permission names, so passing
$role->id to assignRole created a new role keyed by the UUID
- extendOrAddRoleByOrigin no longer json_encodes the context array;
the RoleMember 'context' cast handles it (it was double-encoding)
Reusable infrastructure
- MorphAliasRegistry: central alias <-> FQCN map with custom
per-class alias and name resolvers. Auto-bound as a singleton in
RolesServiceProvider; hosts register their own (alias, FQCN) pairs
- HasRequiredAccess gained addRequiredAccessByAlias /
removeRequiredAccessByAlias / requiredAccessAdminPayload helpers
- RequiredAccess::toAdminArray serializes a link via the registry
Test fixtures
- Manual DB::table()->insert() pivot rows now pass an explicit id
since pivot inserts don't go through HasUuids
- All 162 package tests passing
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces a generic "Required Access" mechanism: any model using
HasRequiredAccess can list other entities as required-access targets;
if the requesting entity has access to ANY of them — direct, role,
or permission — the holder is considered unlocked. Sits alongside
Required Roles / Permissions and is OR-combined with them.
The unlock check resolves in a single EXISTS query that joins
required_accesses with accesses, so cost stays O(1) regardless of
target count.
20 new unit tests cover relations, sync semantics, expiry handling,
isolation between holders, and the constant-cost query property.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Added source_id and source_type fields to the Access model to track the origin of access grants.
- Implemented source relationship in the Access model for better access management.
- Introduced revokeBySource method to delete access entries based on their source.
- Updated grantAccess and revokeAccess methods to handle source parameters for more granular control.
- Added RevokesAccessOnDelete trait to automatically revoke access when the source model is deleted.
- Created SourceAccessesRevoked event to notify when access grants are revoked due to source deletion.
- Enhanced tests to cover new source-related functionality and ensure proper behavior during access management.
- Updated RolesServiceProvider to support auto-loading migrations based on configuration.
- Added migration files for creating roles and access tables, including source columns for existing installations.