diff --git a/database/migrations/2023_10_11_192125_create_connectives_table.php b/database/migrations/2023_10_11_192125_create_connectives_table.php index e064f2d..7f69687 100644 --- a/database/migrations/2023_10_11_192125_create_connectives_table.php +++ b/database/migrations/2023_10_11_192125_create_connectives_table.php @@ -15,8 +15,8 @@ public function up(): void $table->string('to_model_type')->index(); $table->integer('to_model_id')->index(); $table->string('connection_type')->index(); - //- from datetime - - //- through datetime (nullable) + // - from datetime - + // - through datetime (nullable) $table->timestamps(); }); } diff --git a/resources/boost/guidelines/core.blade.php b/resources/boost/guidelines/core.blade.php new file mode 100644 index 0000000..6c7f661 --- /dev/null +++ b/resources/boost/guidelines/core.blade.php @@ -0,0 +1,194 @@ +## Laravel Connective + +This package provides a flexible system for creating typed connections between Eloquent models with support for nested relationships and inverse lookups. + +### Setup + +Models must implement `ConnectiveContract` and use the `Connective` trait: + +@verbatim + +use AuroraWebSoftware\Connective\Contracts\ConnectiveContract; +use AuroraWebSoftware\Connective\Traits\Connective; + +class User extends Model implements ConnectiveContract +{ + use Connective; + + public static function supportedConnectionTypes(): array + { + return ['friendship', 'family', 'colleague']; + } +} + +@endverbatim + +### Creating Connections + +Connections are unidirectional. Create both directions if needed: + +@verbatim + +// One-way connection +$user1->connectTo($user2, 'friendship'); + +// Two-way friendship +$user1->connectTo($user2, 'friendship'); +$user2->connectTo($user1, 'friendship'); + +@endverbatim + +@verbatim + +// User can have multiple addresses with different types +$user->connectTo($homeAddress, 'home'); +$user->connectTo($officeAddress1, 'office'); +$user->connectTo($officeAddress2, 'office'); + +@endverbatim + +### Retrieving Connections + +Get `Connection` model instances: + +@verbatim + +// All connections +$connections = $user->connections(); + +// Filter by type +$friendConnections = $user->connections('friendship'); + +// Filter by type and model +$officeAddresses = $user->connections('office', Address::class); + +// Multiple types +$homeAndOffice = $user->connections(['home', 'office'], Address::class); + +@endverbatim + +### Retrieving Connected Models + +Get actual model instances (not Connection models): + +@verbatim + +// Get all friends +$friends = $user->connectives('friendship'); + +// Get addresses of specific types +$addresses = $user->connectives(['home', 'office'], Address::class); + +// Ignore global scopes +$allFriends = $user->connectives('friendship', User::class, []); +$specificFriends = $user->connectives('friendship', User::class, [ActiveScope::class]); + +@endverbatim + +### Nested Connections + +Chain `connectives()` for unlimited depth: + +@verbatim + +// Friends of friends +$friendsOfFriends = $user->connectives('friendship')->connectives('friendship'); + +// Office addresses of friends +$friendOffices = $user->connectives('friendship')->connectives('office', Address::class); + +// Complex nesting +$result = $user + ->connectives('friendship') + ->connectives('family') + ->connectives('home', Address::class); + +@endverbatim + +### Inverse Connections + +Find who connected TO this model: + +@verbatim + +// Get Connection models where this user is the target +$incomingConnections = $user->inverseConnections('friendship'); + +// Get User models who connected to this user +$followers = $user->inverseConnectives('friendship', User::class); + +// With scope control +$allFollowers = $user->inverseConnectives('friendship', User::class, []); + +@endverbatim + +### Configuration + +Define global connection types in `config/connective.php`: + +@verbatim + +return [ + 'connection_types' => [ + 'friendship', + 'family', + 'colleague', + 'home', + 'office', + 'ownership', + ], +]; + +@endverbatim + +### Common Patterns + +@verbatim + +// Mutual friendship check +$user1Friends = $user1->connectives('friendship')->pluck('id'); +$user2Friends = $user2->connectives('friendship')->pluck('id'); +$areMutualFriends = $user1Friends->contains($user2->id) + && $user2Friends->contains($user1->id); + +// Friend suggestions (friends of friends who aren't already friends) +$friendsOfFriends = $user->connectives('friendship') + ->connectives('friendship') + ->reject(fn($u) => $u->id === $user->id) + ->reject(fn($u) => $user1Friends->contains($u->id)); + +@endverbatim + +@verbatim + +class Organization extends Model implements ConnectiveContract +{ + use Connective; + + public static function supportedConnectionTypes(): array + { + return ['employee', 'partner', 'vendor']; + } +} + +// Organization connections +$org->connectTo($user, 'employee'); +$org->connectTo($vendor, 'vendor'); + +// Get all employees +$employees = $org->connectives('employee', User::class); + +// Get all users who work at this org +$workers = $org->inverseConnectives('employee', User::class); + +@endverbatim + +### Best Practices + +- Always define `supportedConnectionTypes()` to validate connection types +- Use two-way connections for symmetric relationships (friendship) +- Use one-way connections for asymmetric relationships (following) +- Use `connections()` when you need Connection model metadata +- Use `connectives()` when you need the actual connected models +- Use `inverseConnectives()` for reverse lookups (who follows me, who owns this) +- Pass `ignoreScopes` parameter when you need soft-deleted or filtered models diff --git a/src/ConnectiveService.php b/src/ConnectiveService.php index d4e8c95..f13e42a 100755 --- a/src/ConnectiveService.php +++ b/src/ConnectiveService.php @@ -16,6 +16,6 @@ public function connectionTypes(): array { return is_array(Config::get('connective.connection_types')) ? Config::get('connective.connection_types') : - throw new ConfigValueException(); + throw new ConfigValueException; } } diff --git a/src/ConnectiveServiceProvider.php b/src/ConnectiveServiceProvider.php index bb98daf..502fe26 100644 --- a/src/ConnectiveServiceProvider.php +++ b/src/ConnectiveServiceProvider.php @@ -27,7 +27,7 @@ public function configurePackage(Package $package): void ->name('connective') ->hasConfigFile('connective') ->hasViews() - //->hasMigration('create_connective_table') + // ->hasMigration('create_connective_table') ->hasCommand(ConnectiveCommand::class); } } diff --git a/src/Exceptions/ConfigValueException.php b/src/Exceptions/ConfigValueException.php index a972a88..43cda2c 100644 --- a/src/Exceptions/ConfigValueException.php +++ b/src/Exceptions/ConfigValueException.php @@ -4,6 +4,4 @@ use Exception; -class ConfigValueException extends Exception -{ -} +class ConfigValueException extends Exception {} diff --git a/src/Exceptions/ConnectionTypeException.php b/src/Exceptions/ConnectionTypeException.php index 669d320..23680c7 100644 --- a/src/Exceptions/ConnectionTypeException.php +++ b/src/Exceptions/ConnectionTypeException.php @@ -4,6 +4,4 @@ use Exception; -class ConnectionTypeException extends Exception -{ -} +class ConnectionTypeException extends Exception {} diff --git a/src/Exceptions/ConnectionTypeNotSupportedException.php b/src/Exceptions/ConnectionTypeNotSupportedException.php index c965e2b..3c4677f 100644 --- a/src/Exceptions/ConnectionTypeNotSupportedException.php +++ b/src/Exceptions/ConnectionTypeNotSupportedException.php @@ -4,6 +4,4 @@ use Exception; -class ConnectionTypeNotSupportedException extends Exception -{ -} +class ConnectionTypeNotSupportedException extends Exception {} diff --git a/src/Exceptions/ModelIsNotConnectiveException.php b/src/Exceptions/ModelIsNotConnectiveException.php index 7750933..2ecbeea 100644 --- a/src/Exceptions/ModelIsNotConnectiveException.php +++ b/src/Exceptions/ModelIsNotConnectiveException.php @@ -4,6 +4,4 @@ use Exception; -class ModelIsNotConnectiveException extends Exception -{ -} +class ModelIsNotConnectiveException extends Exception {} diff --git a/tests/PackageTest.php b/tests/PackageTest.php index 5d8a878..c290c98 100644 --- a/tests/PackageTest.php +++ b/tests/PackageTest.php @@ -14,8 +14,8 @@ beforeEach(function () { Artisan::call('migrate:fresh'); - //include_once __DIR__.'/../database/migrations/2023_10_11_192125_create_connectives_table.php'; - //(new create_connective_tables)->up(); + // include_once __DIR__.'/../database/migrations/2023_10_11_192125_create_connectives_table.php'; + // (new create_connective_tables)->up(); Config::set( [