├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── extended-relationships.php ├── pint.json └── src ├── HasExtendedRelationships.php └── Relations ├── BelongsToArrayColumn.php ├── BelongsToManyKeys.php ├── HasManyArrayColumn.php └── HasManyKeys.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-extended-relationships` will be documented in this file. 4 | 5 | ## [2.0.0] 6 | 7 | ### Breaking Changes 8 | - **BREAKING**: Dropped support for Laravel 10 9 | - **BREAKING**: Dropped support for PHP 8.1 10 | - Minimum requirements are now Laravel 11+ and PHP 8.2+ 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) mr-punyapal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # laravel-extended-relationships 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/mr-punyapal/laravel-extended-relationships.svg?style=flat-square)](https://packagist.org/packages/mr-punyapal/laravel-extended-relationships) 4 | [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/mr-punyapal/laravel-extended-relationships/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/mr-punyapal/laravel-extended-relationships/actions?query=workflow%3Arun-tests+branch%3Amain) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/mr-punyapal/laravel-extended-relationships.svg?style=flat-square)](https://packagist.org/packages/mr-punyapal/laravel-extended-relationships) 6 | 7 | ## Requirements 8 | 9 | - PHP: ^8.2|^8.3|^8.4 10 | - Laravel: ^11.0|^12.0 11 | 12 | ### What is a need of extended relationships? 13 | The laravel-extended-relationships package provides additional, more efficient relationship methods for Laravel Eloquent models. The package offers several useful features such as reducing the number of database queries, improving performance, and minimizing duplicate code. 14 | 15 | I faced issue and made my own relationships then realize if I can use packages from open source then I can make one too and made this package. 16 | 17 | ## Installation 18 | 19 | You can install the package via composer: 20 | 21 | ```bash 22 | composer require mrpunyapal/laravel-extended-relationships 23 | ``` 24 | 25 | ## Usage 26 | 27 | First, include the `HasExtendedRelationships` trait in your model: 28 | 29 | ```php 30 | 31 | use MrPunyapal\LaravelExtendedRelationships\HasExtendedRelationships; 32 | 33 | class Post extends Model { 34 | use HasExtendedRelationships; 35 | 36 | //... 37 | } 38 | 39 | ``` 40 | 41 | Next, define the `BelongsToManyKeys` relationship with the `belongsToManyKeys` method: 42 | 43 | ```php 44 | 45 | public function auditors() { 46 | return $this->belongsToManyKeys( 47 | related: User::class, 48 | foreignKey: 'id', 49 | relations: [ 50 | 'created_by' => 'creator', 51 | 'updated_by' => 'updater', 52 | 'deleted_by' => 'deleter', 53 | ] 54 | ); 55 | } 56 | 57 | ``` 58 | 59 | ### This method takes three arguments: 60 | 61 | * The related model (`User::class`) 62 | * The foreign key (`id`) 63 | * An array mapping the related table's foreign key names to the corresponding relation names on the model (`['created_by' => 'creator', ...]`) 64 | 65 | ### Then, you can fetch data from the auditors relationship like so: 66 | 67 | ```php 68 | 69 | $post = Post::with('auditors')->first(); 70 | 71 | // Get the creator 72 | $post->auditors->creator; 73 | 74 | // Get the updater 75 | $post->auditors->updater; 76 | 77 | // Get the deleter 78 | $post->auditors->deleter; 79 | 80 | 81 | // also works with lazy loading 82 | 83 | $post = Post::find(7); 84 | 85 | // Get the creator 86 | $post->auditors->creator; 87 | 88 | // Get the updater 89 | $post->auditors->updater; 90 | 91 | // Get the deleter 92 | $post->auditors->deleter; 93 | 94 | ``` 95 | 96 | This allows you to define multiple relationships with just one method, and only a single query is fired in the database for all the relationships. 97 | 98 | 99 | 100 | ### Inverse relationship. 101 | 102 | 103 | ```php 104 | 105 | use MrPunyapal\LaravelExtendedRelationships\HasExtendedRelationships; 106 | 107 | class User extends Model{ 108 | 109 | use HasExtendedRelationships; 110 | 111 | public function audited(){ 112 | return $this->hasManyKeys( 113 | related: Post::class, 114 | relations: [ 115 | 'created_by' => 'created', 116 | 'updated_by' => 'updated', 117 | 'deleted_by' => 'deleted', 118 | ], 119 | localKey: 'id' 120 | ); 121 | } 122 | } 123 | 124 | ``` 125 | 126 | To retrieve the audited posts of a user, you can use the audited relationship. Here's an example: 127 | 128 | ```php 129 | 130 | $user = User::with('audited')->first(); 131 | 132 | // Get posts created by the user 133 | $user->audited->created; 134 | 135 | // Get posts updated by the user 136 | $user->audited->updated; 137 | 138 | // Get posts deleted by the user 139 | $user->audited->deleted; 140 | 141 | // also works with lazy loading 142 | 143 | $user = User::find(71); 144 | 145 | // Get posts created by the user 146 | $user->audited->created; 147 | 148 | // Get posts updated by the user 149 | $user->audited->updated; 150 | 151 | // Get posts deleted by the user 152 | $user->audited->deleted; 153 | 154 | ``` 155 | 156 | This allows you to define multiple relationships between models with a single method call, simplifying your code and reducing the number of queries executed. 157 | 158 | ### HasManyArrayColumn 159 | 160 | If you have a column companies in your users table which stores an array of local keys like [7, 71] or ["7", "71"], you can use the following relationship: 161 | 162 | ```php 163 | 164 | use MrPunyapal\LaravelExtendedRelationships\HasExtendedRelationships; 165 | 166 | class User extends Model 167 | { 168 | use HasExtendedRelationships; 169 | 170 | protected $casts=[ 171 | 'companies' => 'array' 172 | ]; 173 | 174 | public function myCompanies() 175 | { 176 | return $this->hasManyArrayColumn( 177 | related: Company::class, 178 | foreignKey: 'id', 179 | localKey: 'companies' 180 | ); 181 | } 182 | } 183 | 184 | ``` 185 | 186 | When fetching data, you can retrieve the related companies with: 187 | 188 | ```php 189 | 190 | $user = User::with('myCompanies')->first(); 191 | 192 | // get companies with ids 7 and 71 193 | $user->myCompanies; 194 | 195 | ``` 196 | 197 | This allows you to easily retrieve related records with an array of local keys, which can be useful in certain scenarios. 198 | 199 | ### Inverse Relationship for `HasManyArrayColumn` 200 | 201 | The `BelongsToArrayColumn` method allows you to define a relationship between a model and an array column on another model. 202 | if you have ["7", "71"] in array column and int 7 or 71 at your foreign-key then pass `$isString` flag as true to get expected results. 203 | 204 | Here's an example: 205 | 206 | ```php 207 | 208 | use MrPunyapal\LaravelExtendedRelationships\HasExtendedRelationships; 209 | 210 | class Company extends Model 211 | { 212 | use HasExtendedRelationships; 213 | 214 | public function companyFounders() 215 | { 216 | return $this->belongsToArrayColumn( 217 | related: User::class, 218 | foreignKey: 'id', 219 | localKey: 'companies', 220 | // optional, default is false (if true then it treats all values as string) 221 | isString: true 222 | ); 223 | } 224 | } 225 | 226 | ``` 227 | 228 | With this relationship defined, you can fetch related company founders with the following code: 229 | 230 | ```php 231 | 232 | $company = Company::with('companyFounders')->find(71); 233 | 234 | // Founders for company with id 71 235 | 236 | $company->companyFounders; 237 | 238 | ``` 239 | 240 | This will provide you with data from the `users` table where the `companies` array column contains the value 71. 241 | 242 | ## Testing 243 | 244 | ```bash 245 | composer test 246 | ``` 247 | 248 | ## Changelog 249 | 250 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 251 | 252 | ## Contributing 253 | 254 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 255 | 256 | ## Security Vulnerabilities 257 | 258 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 259 | 260 | ## Credits 261 | 262 | - [MrPunyapal](https://github.com/MrPunyapal) 263 | - [All Contributors](../../contributors) 264 | 265 | ## License 266 | 267 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 268 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mrpunyapal/laravel-extended-relationships", 3 | "description": "Package provides additional, more efficient relationship methods for Laravel Eloquent models.", 4 | "keywords": [ 5 | "mrpunyapal", 6 | "laravel", 7 | "laravel-extended-relationships", 8 | "relationship", 9 | "has-many", 10 | "Eloquent", 11 | "eloquent-relationships", 12 | "Json" 13 | ], 14 | "homepage": "https://github.com/mrpunyapal/laravel-extended-relationships", 15 | "license": "MIT", 16 | "authors": [ 17 | { 18 | "name": "Punyapal Shah", 19 | "email": "mrpunyapal@gmail.com", 20 | "role": "Developer" 21 | } 22 | ], 23 | "require": { 24 | "php": "^8.2|^8.3|^8.4", 25 | "illuminate/contracts": "^11.0|^12.0" 26 | }, 27 | "require-dev": { 28 | "laravel/pint": "^1.0", 29 | "nunomaduro/collision": "^8.0", 30 | "orchestra/testbench": "^9.0|^10.0", 31 | "pestphp/pest": "^2.0|^3.0", 32 | "pestphp/pest-plugin-arch": "^2.0|^3.0", 33 | "pestphp/pest-plugin-laravel": "^2.2|^3.0" 34 | }, 35 | "autoload": { 36 | "psr-4": { 37 | "MrPunyapal\\LaravelExtendedRelationships\\": "src/" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "MrPunyapal\\LaravelExtendedRelationships\\Tests\\": "tests/" 43 | } 44 | }, 45 | "scripts": { 46 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 47 | "analyse": "vendor/bin/phpstan analyse", 48 | "test": "vendor/bin/pest", 49 | "test-coverage": "vendor/bin/pest --coverage", 50 | "format": "vendor/bin/pint" 51 | }, 52 | "config": { 53 | "sort-packages": true, 54 | "allow-plugins": { 55 | "pestphp/pest-plugin": true, 56 | "phpstan/extension-installer": true 57 | } 58 | }, 59 | "minimum-stability": "stable", 60 | "prefer-stable": true 61 | } 62 | -------------------------------------------------------------------------------- /config/extended-relationships.php: -------------------------------------------------------------------------------- 1 | relatedNewQuery($related), $this, $foreignKey, $relations); 18 | } 19 | 20 | public function hasManyKeys(string $related, ?array $relations, ?string $localKey): HasManyKeys 21 | { 22 | return new HasManyKeys($this->relatedNewQuery($related), $this, $relations, $localKey); 23 | } 24 | 25 | public function hasManyArrayColumn(string $related, ?string $foreignKey, ?string $localKey): HasManyArrayColumn 26 | { 27 | return new HasManyArrayColumn($this->relatedNewQuery($related), $this, $foreignKey, $localKey); 28 | } 29 | 30 | public function belongsToArrayColumn(string $related, ?string $foreignKey, ?string $localKey, bool $isString = false): BelongsToArrayColumn 31 | { 32 | return new BelongsToArrayColumn($this->relatedNewQuery($related), $this, $foreignKey, $localKey, null, $isString); 33 | } 34 | 35 | protected function relatedNewQuery($related): Builder 36 | { 37 | return (new $related)->newQuery(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Relations/BelongsToArrayColumn.php: -------------------------------------------------------------------------------- 1 | getBaseQuery(); 31 | 32 | $query->when($this->isString, function ($q) { 33 | $q->whereJsonContains($this->ownerKey, (string) $this->getParentKey()); 34 | }, function ($q) { 35 | $q->whereJsonContains($this->ownerKey, $this->getParentKey()); 36 | }); 37 | 38 | $query->whereNotNull($this->ownerKey); 39 | } 40 | 41 | /** 42 | * Set the constraints for an eager load of the relation. 43 | */ 44 | public function addEagerConstraints(array $models): void 45 | { 46 | $ids = $this->getEagerModelKeys($models); 47 | $this->query->where(function ($q) use ($ids) { 48 | foreach ($ids as $id) { 49 | $q->when($this->isString, function ($q) use ($id) { 50 | $q->orWhereJsonContains($this->ownerKey, (string) $id); 51 | }, function ($q) use ($id) { 52 | $q->orWhereJsonContains($this->ownerKey, $id); 53 | }); 54 | } 55 | }); 56 | } 57 | 58 | /** 59 | * Match the eagerly loaded results to their many parents. 60 | * 61 | * @param string $relation 62 | */ 63 | public function match(array $models, Collection $results, $relation): array 64 | { 65 | $owner = $this->getOwnerKeyName(); 66 | foreach ($models as $model) { 67 | $id = $model->getAttribute($this->foreignKey); 68 | $collection = collect(); 69 | foreach ($results as $data) { 70 | $ownerValue = $data->{$owner}; 71 | if (is_array($ownerValue) && in_array($id, $ownerValue, true)) { 72 | $collection->push($data); 73 | } 74 | } 75 | $model->setRelation($relation, $collection); 76 | } 77 | 78 | return $models; 79 | } 80 | 81 | /** 82 | * Get the results of the relationship. 83 | */ 84 | public function getResults(): mixed 85 | { 86 | return $this->query->get(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Relations/BelongsToManyKeys.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | protected array $localKeys; 20 | 21 | /** 22 | * The local keys of the parent model. 23 | * 24 | * @var array 25 | */ 26 | protected array $relations; 27 | 28 | /** 29 | * Create a new has one or many relationship instance. 30 | */ 31 | public function __construct(Builder $query, Model $parent, protected string $foreignKey, array $relations) 32 | { 33 | $this->localKeys = array_keys($relations); 34 | $this->relations = $relations; 35 | parent::__construct($query, $parent); 36 | } 37 | 38 | /** 39 | * Set the base constraints on the relation query. 40 | * Note: Used to load relations of one model. 41 | */ 42 | public function addConstraints(): void 43 | { 44 | if (! static::$constraints) { 45 | return; 46 | } 47 | $this->query->where(function ($query) { 48 | foreach ($this->localKeys as $localKey) { 49 | $query->orWhere(function ($query) use ($localKey) { 50 | $query->where($this->foreignKey, '=', $this->getParentKey($localKey)) 51 | ->whereNotNull($this->foreignKey); 52 | }); 53 | } 54 | }); 55 | } 56 | 57 | /** 58 | * Set the constraints for an eager load of the relation. 59 | * Note: Used to load relations of multiple models at once. 60 | */ 61 | public function addEagerConstraints(array $models): void 62 | { 63 | $localKeys = $this->localKeys; 64 | $foreignKey = $this->foreignKey; 65 | $desireValues = []; 66 | foreach ($localKeys as $localKey) { 67 | $desireValues = array_merge($desireValues, $this->getKeys($models, $localKey)); 68 | } 69 | $this->query->whereIn($foreignKey, array_filter(array_unique($desireValues))); 70 | } 71 | 72 | /** 73 | * Initialize the relation on a set of models. 74 | * 75 | * @param string $relation 76 | */ 77 | public function initRelation(array $models, $relation): array 78 | { 79 | foreach ($models as $model) { 80 | $model->setRelation($relation, $this->related->newCollection()); 81 | } 82 | 83 | return $models; 84 | } 85 | 86 | /** 87 | * Match the related models with the given models based on the local keys. 88 | */ 89 | public function match(array $models, Collection $results, $relation): array 90 | { 91 | $dictionary = $this->buildDictionary($results); 92 | 93 | foreach ($models as $model) { 94 | $desireRelations = json_decode('{}'); 95 | foreach ($this->localKeys as $localKey) { 96 | $key = $model->getAttribute($localKey); 97 | if (isset($dictionary[$key])) { 98 | $desireRelations->{$this->relations[$localKey]} = $dictionary[$key]; 99 | } 100 | } 101 | $model->setRelation($relation, $desireRelations); 102 | } 103 | 104 | return $models; 105 | } 106 | 107 | /** 108 | * Build a dictionary using the given models. 109 | */ 110 | public function buildDictionary(Collection $models): array 111 | { 112 | $dictionary = []; 113 | foreach ($models as $model) { 114 | $dictionary[$model->{$this->foreignKey}] = $model; 115 | } 116 | 117 | return $dictionary; 118 | } 119 | 120 | /** 121 | * Get the parent key value for the given local key. 122 | */ 123 | public function getParentKey(string $localKey): mixed 124 | { 125 | return $this->parent->getAttribute($localKey); 126 | } 127 | 128 | /** 129 | * Get the results of the relationship. 130 | */ 131 | public function getResults(): mixed 132 | { 133 | if (! static::$constraints) { 134 | return $this->query->get(); 135 | } 136 | $results = $this->query->get(); 137 | $desireResults = json_decode('{}'); 138 | foreach ($this->localKeys as $localKey) { 139 | $desireResults->{$this->relations[$localKey]} = $results->where($this->foreignKey, '=', $this->getParentKey($localKey))->first(); 140 | } 141 | 142 | return $desireResults; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Relations/HasManyArrayColumn.php: -------------------------------------------------------------------------------- 1 | getRelationQuery(); 22 | 23 | $parentKeys = $this->getParentKey(); 24 | if (! empty($parentKeys)) { 25 | $query->whereIn($this->foreignKey, $parentKeys); 26 | } 27 | 28 | $query->whereNotNull($this->foreignKey); 29 | } 30 | 31 | /** 32 | * Get the parent key(s) for the relationship. 33 | */ 34 | public function getParentKey(): array 35 | { 36 | $attribute = $this->parent->getAttribute($this->localKey); 37 | 38 | return is_array($attribute) ? $attribute : []; 39 | } 40 | 41 | /** 42 | * Set the constraints for an eager load of the relation. 43 | */ 44 | public function addEagerConstraints(array $models): void 45 | { 46 | $this->query->whereIn($this->foreignKey, $this->getKeys($models, $this->localKey)); 47 | } 48 | 49 | /** 50 | * Get the Keys for an eager load of the relation. 51 | * 52 | * @param string|null $key 53 | */ 54 | protected function getKeys(array $models, $key = null): array 55 | { 56 | $keys = []; 57 | 58 | collect($models)->each(function ($value) use ($key, &$keys) { 59 | $attribute = $value->getAttribute($key); 60 | if (is_array($attribute)) { 61 | $keys = array_merge($keys, $attribute); 62 | } 63 | }); 64 | 65 | return array_values(array_unique($keys)); 66 | } 67 | 68 | /** 69 | * Match the eagerly loaded results to their many parents. 70 | * 71 | * @param string $relation 72 | */ 73 | public function matchMany(array $models, Collection $results, $relation): array 74 | { 75 | $foreign = $this->getForeignKeyName(); 76 | 77 | $dictionary = $results->mapToDictionary(fn ($result) => [$result->{$foreign} => $result])->all(); 78 | 79 | foreach ($models as $model) { 80 | $ids = $model->getAttribute($this->localKey); 81 | $collection = collect(); 82 | foreach ($ids ?? [] as $id) { 83 | if (isset($dictionary[$id])) { 84 | $collection = $collection->merge($this->getRelationValue($dictionary, $id, 'many')); 85 | } 86 | } 87 | 88 | $model->setRelation($relation, $collection); 89 | } 90 | 91 | return $models; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Relations/HasManyKeys.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | protected array $foreignKeys; 20 | 21 | /** 22 | * The relations of the parent model. 23 | * 24 | * @var array 25 | */ 26 | protected array $relations; 27 | 28 | /** 29 | * Create a new has one or many relationship instance. 30 | */ 31 | public function __construct(Builder $query, Model $parent, array $relations, protected string $localKey) 32 | { 33 | $this->foreignKeys = array_keys($relations); 34 | $this->relations = $relations; 35 | 36 | parent::__construct($query, $parent); 37 | } 38 | 39 | /** 40 | * Set the base constraints on the relation query. 41 | * Note: Used to load relations of one model. 42 | */ 43 | public function addConstraints(): void 44 | { 45 | if (! static::$constraints) { 46 | return; 47 | } 48 | 49 | $foreignKeys = $this->foreignKeys; 50 | 51 | $this->query->where(function ($query) use ($foreignKeys): void { 52 | foreach ($foreignKeys as $foreignKey) { 53 | $query->orWhere(function ($query) use ($foreignKey): void { 54 | $query->where($foreignKey, '=', $this->getParentKey()) 55 | ->whereNotNull($foreignKey); 56 | }); 57 | } 58 | }); 59 | } 60 | 61 | /** 62 | * Set the constraints for an eager load of the relation. 63 | * Note: Used to load relations of multiple models at once. 64 | */ 65 | public function addEagerConstraints(array $models): void 66 | { 67 | $foreignKeys = $this->foreignKeys; 68 | $keys = $this->getKeys($models, $this->localKey); 69 | 70 | $this->query->where(function ($query) use ($foreignKeys, $keys): void { 71 | $first = true; 72 | foreach ($foreignKeys as $foreignKey) { 73 | if ($first) { 74 | $query->whereIn($foreignKey, $keys); 75 | $first = false; 76 | } else { 77 | $query->orWhereIn($foreignKey, $keys); 78 | } 79 | } 80 | }); 81 | } 82 | 83 | /** 84 | * Initialize the relation on a set of models. 85 | * 86 | * @param string $relation 87 | */ 88 | public function initRelation(array $models, $relation): array 89 | { 90 | foreach ($models as $model) { 91 | $model->setRelation($relation, $this->related->newCollection()); 92 | } 93 | 94 | return $models; 95 | } 96 | 97 | /** 98 | * Match the eagerly loaded results to their parents. 99 | * Info: From HasMany class. 100 | * 101 | * @param string $relation 102 | */ 103 | public function match(array $models, Collection $results, $relation): array 104 | { 105 | $dictionary = $this->buildDictionary($results); 106 | 107 | foreach ($models as $model) { 108 | $key = $model->getAttribute($this->localKey); 109 | $desireRelations = json_decode('{}'); 110 | foreach ($this->foreignKeys as $foreignKey) { 111 | if (isset($dictionary[$foreignKey][$key])) { 112 | $desireRelations->{$this->relations[$foreignKey]} = $dictionary[$foreignKey][$key]; 113 | } else { 114 | $desireRelations->{$this->relations[$foreignKey]} = $this->related->newCollection(); 115 | } 116 | } 117 | $model->setRelation($relation, $desireRelations); 118 | } 119 | 120 | return $models; 121 | } 122 | 123 | /** 124 | * Build model dictionary keyed by the relation's foreign key. 125 | * Note: Custom code. 126 | */ 127 | public function buildDictionary(Collection $models): array 128 | { 129 | $dictionary = []; 130 | foreach ($models as $model) { 131 | foreach ($this->foreignKeys as $foreignKey) { 132 | if (! isset($dictionary[$foreignKey][$model->{$foreignKey}])) { 133 | $dictionary[$foreignKey][$model->{$foreignKey}] = $this->related->newCollection(); 134 | } 135 | $dictionary[$foreignKey][$model->{$foreignKey}]->push($model); 136 | } 137 | } 138 | 139 | return $dictionary; 140 | } 141 | 142 | /** 143 | * Get the key value of the parent's local key. 144 | * Info: From HasOneOrMany class. 145 | */ 146 | public function getParentKey(): mixed 147 | { 148 | return $this->parent->getAttribute($this->localKey); 149 | } 150 | 151 | /** 152 | * Get the results of the relationship. 153 | */ 154 | public function getResults(): mixed 155 | { 156 | if (! static::$constraints) { 157 | return $this->get(); 158 | } 159 | $results = $this->get(); 160 | $desireResults = json_decode('{}'); 161 | foreach ($this->foreignKeys as $foreignKey) { 162 | $desireResults->{$this->relations[$foreignKey]} = $results->where($foreignKey, '=', $this->getParentKey()) 163 | ->whereNotNull($foreignKey); 164 | } 165 | 166 | return $desireResults; 167 | } 168 | } 169 | --------------------------------------------------------------------------------