Pet Relationship System
This document describes the flexible pet-user relationship system that replaces the simple ownership model with a comprehensive relationship framework supporting multiple relationship types and temporal tracking.
Overview
The pet relationship system allows pets to have multiple concurrent relationships with different users, each with specific access levels and temporal boundaries. This enables complex scenarios like co-ownership, fostering, and delegated management while maintaining a complete historical record.
Relationship Types
The system supports four distinct relationship types:
Owner Relationship
- Access Level: Full access including ownership transfer, relationship management, and pet deletion
- Use Cases: Primary pet owners, co-owners
- Capabilities:
- Edit all pet information
- Manage health records
- Transfer ownership
- Add/remove other relationships
- Delete the pet profile
Foster Relationship
- Access Level: Edit access for temporary caretaking
- Use Cases: Temporary fostering, animal shelter caretakers
- Capabilities:
- Edit pet information
- Update health records
- Manage placement requests
- Cannot transfer ownership
- Cannot manage other relationships
Editor Relationship
- Access Level: Edit access for pet management assistance
- Use Cases: Veterinarians, pet sitters, family members helping with pet care
- Capabilities:
- Edit pet information
- Update health records
- Manage placement requests
- Cannot transfer ownership
- Cannot manage other relationships
Viewer Relationship
- Access Level: Read-only access
- Use Cases: Family members, friends, interested adopters
- Capabilities:
- View pet information
- View health records
- Cannot make any changes
Data Model
PetRelationship Model
class PetRelationship extends Model
{
protected $fillable = [
'user_id',
'pet_id',
'relationship_type', // PetRelationshipType enum
'start_date',
'end_date', // null = active relationship
'created_by', // user who created this relationship
];
}Database Schema
pet_relationships table:
id: Primary keyuser_id: Foreign key to users tablepet_id: Foreign key to pets tablerelationship_type: Enum ('owner', 'foster', 'editor', 'viewer')start_date: Date when relationship beganend_date: Date when relationship ended (nullable)created_by: Foreign key to users table (who created relationship)created_at,updated_at: Timestamps
Relationship Lifecycle
Creating Relationships
Relationships are created with a start date and are active until an end date is set:
// Create a new owner relationship
$relationship = PetRelationshipService::createRelationship(
$user,
$pet,
PetRelationshipType::OWNER,
$createdByUser,
$startDate // optional, defaults to today
);Ending Relationships
Active relationships (where end_date is null) can be ended by setting an end date:
PetRelationshipService::endRelationship($relationship, $endDate);Transferring Ownership
Ownership transfers create a new owner relationship while ending the previous one:
PetRelationshipService::transferOwnership($pet, $fromUser, $toUser, $createdBy);Relationship Invitations
Owners can invite others to become co-owners, editors, or viewers via a shareable link or QR code. This replaces the older direct user-ID-based relationship assignment for external users.
Invitation Flow
- Owner creates invitation — selects a role (owner/editor/viewer) and receives a unique link + QR code
- Link is shared — via QR scan, copy-paste, or messaging
- Recipient opens link — sees pet info, role, inviter name, and a countdown timer
- Recipient accepts or declines — accepting creates the relationship; declining records the decision
Unauthenticated Recipient Handling
When a recipient opens an invite link without being logged in (common when scanning a QR code in the browser), two complementary mechanisms preserve the invitation through the auth flow:
- Redirect param chain: The invite page redirects to
/login?redirect=/pets/invite/{token}. If the user navigates from login to registration, the?redirect=param is carried along. After successful login or registration, the user is redirected back to the invite page. - localStorage fallback (
pendingInviteToken): Before redirecting to login, the invite token is saved tolocalStorage. When the user becomes authenticated (via any method — email login, registration, Google OAuth),App.tsxchecks for a pending token and redirects to the invite page. The token is cleared fromlocalStorageonce consumed. This covers cases where the redirect param is lost (e.g., OAuth flows, browser restarts).
Invitation Model
relationship_invitations table:
id, pet_id, invited_by_user_id, token (unique, 64 chars),
relationship_type (owner/editor/viewer), status (pending/accepted/declined/revoked/expired),
expires_at, accepted_by_user_id, accepted_at, declined_at, revoked_at- Invitations expire after 1 hour (enforced by
expires_at) - The
tokenis a 64-character random string used in the URL:/pets/invite/{token} - Auto-expiration: checking
isValid()marks overdue invitations as expired
Role Upgrade Logic
When a user accepts an invitation for a role higher than their current role, lower-privilege relationships are automatically ended:
- Hierarchy: viewer (1) < editor (2) < owner (3)
- Example: a viewer accepting an editor invitation ends the viewer relationship
- Same-level or lower invitations create the relationship without affecting existing ones
Invitation Management
- List pending: Owners see all pending invitations with countdown timers and a share button to re-open the QR/link dialog
- Revoke: Owners can cancel a pending invitation before it's accepted
- Self-invite guard: Users cannot accept their own invitations (422)
- Expired guard: Attempting to accept an expired invitation returns 410
API Endpoints
POST /api/pets/{pet}/relationship-invitations # Create invitation (owner only)
GET /api/pets/{pet}/relationship-invitations # List pending (owner only)
DELETE /api/pets/{pet}/relationship-invitations/{id} # Revoke (owner only)
GET /api/relationship-invitations/{token} # Preview (public, optional auth)
POST /api/relationship-invitations/{token}/accept # Accept (authenticated)
POST /api/relationship-invitations/{token}/decline # Decline (authenticated)Frontend
- PetRelationshipsSection — "Add person" button opens a dialog with role selection, then shows QR code + copyable link. Pending invitations are listed with countdown timers and share/revoke buttons.
- RelationshipInvitationPage (
/pets/invite/:token) — standalone page for recipients; saves the token tolocalStorageand redirects unauthenticated users to login with a return URL. Clears the stored token once the authenticated user lands on the page.
Leaving and Removing Relationships
Leave
Any user with an active relationship can leave a pet voluntarily, except the last remaining owner (a pet must always have at least one owner).
POST /api/pets/{pet}/leave- Ends all active relationships for the requesting user
- Returns 409 if the user is the sole owner
- Available from both the pet profile page (editors/co-owners) and the public profile page (viewers)
Remove
Owners can remove editors and viewers from a pet. Owners cannot be removed via this endpoint — ownership changes go through invitation or transfer flows.
DELETE /api/pets/{pet}/users/{user}- Only accessible to pet owners
- Returns 422 if attempting to remove an owner
Access Control
Permission Checking
The system provides methods to check user permissions for pets:
// Check if user has specific relationship type
$pet->hasRelationshipWith($user, PetRelationshipType::OWNER);
// Check if user can edit the pet
$user->can('update', $pet); // Uses PetPolicy with relationship logicViewer Permissions
API responses include viewer_permissions object indicating what the current user can do:
{
"viewer_permissions": {
"can_edit": true,
"can_manage_relationships": true,
"can_transfer_ownership": true,
"can_view_contact": false
}
}Migration from Old System
Previous System
- Simple
user_idforeign key on pets table - Pivot tables for editors and viewers (
pet_usertable) - OwnershipHistory table for historical tracking
- Limited to one owner per pet
New System Benefits
- Multiple Relationships: Pets can have multiple owners, fosters, editors, and viewers simultaneously
- Temporal Tracking: Complete history with start/end dates for all relationships
- Flexible Access: Support for complex scenarios like fostering and co-ownership
- Audit Trail: Track who created each relationship and when
- Future-Proof: Easy to add new relationship types as needed
Migration Process
- Data Migration: Existing ownership converted to owner relationships
- Editors/Viewers: Converted from pivot tables to relationship records
- Historical Data: OwnershipHistory migrated to relationship records with proper dates
- Backwards Compatibility: API maintains similar interface while using new backend
API Integration
Relationship Management Endpoints
// Add editor access
POST /api/pets/{pet}/relationships
{
"user_id": 123,
"relationship_type": "editor"
}
// Remove user access
DELETE /api/pets/{pet}/relationships/{relationship}
// Transfer ownership
POST /api/pets/{pet}/transfer-ownership
{
"to_user_id": 456
}Querying Relationships
// Get all active relationships for a pet
$relationships = $pet->activeRelationships;
// Get all pets for a user by relationship type
$ownedPets = PetRelationshipService::getPetsByRelationshipType($user, PetRelationshipType::OWNER);
$fosteredPets = PetRelationshipService::getPetsByRelationshipType($user, PetRelationshipType::FOSTER);Business Logic Examples
Fostering Scenario
- Owner creates placement request for fostering
- Helper accepts and confirms handover
- System creates foster relationship for helper
- Owner retains owner relationship
- When fostering ends, foster relationship is ended
Ownership Transfer
- Owner initiates transfer (permanent placement)
- New owner confirms handover
- System creates new owner relationship for new owner
- Previous owner relationship is ended
- Historical record maintained
Co-Ownership
- Multiple users can have active owner relationships
- All owners have full access and transfer capabilities
- Relationship history tracks all ownership changes
Benefits
- Flexibility: Support for complex pet care arrangements
- Auditability: Complete historical record of all relationships
- Scalability: Easy to add new relationship types
- Consistency: Unified system for all pet-user interactions
- Future-Proof: Extensible for additional relationship types and features
Related Documentation
- Pet Profiles - How relationships affect profile access
- Placement Request Lifecycle - How relationships work in placement scenarios
- User Invitations - Platform-level user invitations (separate from pet relationship invitations)
- Architecture - Technical implementation details