{"id":20012306,"url":"https://github.com/archtechx/laravel-hasmanywithinverse","last_synced_at":"2025-10-15T14:14:47.883Z","repository":{"id":44643039,"uuid":"252307484","full_name":"archtechx/laravel-hasmanywithinverse","owner":"archtechx","description":"Define HasMany while also setting the inverse relationship in Laravel.","archived":false,"fork":false,"pushed_at":"2023-02-16T10:35:39.000Z","size":30,"stargazers_count":69,"open_issues_count":1,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-04T13:13:39.284Z","etag":null,"topics":["eloquent","laravel"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/archtechx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-01T23:16:31.000Z","updated_at":"2025-04-10T12:25:03.000Z","dependencies_parsed_at":"2024-12-19T16:11:18.300Z","dependency_job_id":"5d84056a-1bc0-446c-b840-99d88a095589","html_url":"https://github.com/archtechx/laravel-hasmanywithinverse","commit_stats":{"total_commits":28,"total_committers":6,"mean_commits":4.666666666666667,"dds":0.5357142857142857,"last_synced_commit":"af040d9823e89726b4e0f22528190f93f923170c"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/archtechx/laravel-hasmanywithinverse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archtechx%2Flaravel-hasmanywithinverse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archtechx%2Flaravel-hasmanywithinverse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archtechx%2Flaravel-hasmanywithinverse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archtechx%2Flaravel-hasmanywithinverse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/archtechx","download_url":"https://codeload.github.com/archtechx/laravel-hasmanywithinverse/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/archtechx%2Flaravel-hasmanywithinverse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279085197,"owners_count":26100015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-15T02:00:07.814Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["eloquent","laravel"],"created_at":"2024-11-13T07:29:41.308Z","updated_at":"2025-10-15T14:14:47.844Z","avatar_url":"https://github.com/archtechx.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stancl/laravel-hasmanywithinverse\n\n## Why?\n\n[Jonathan Reinink](https://github.com/reinink) wrote a great blog post about [Optimizing circular relationships in Laravel](https://reinink.ca/articles/optimizing-circular-relationships-in-laravel)\n\nBy manually setting the (`belongsTo`) relationship to a parent model on related (`hasMany`) child models, you can save unnecessary queries for the parent model -- when the child needs an instance of the parent model.\n\nThis probably sounds confusing, so just read the blog post. It's very good.\n\nJonathan's approach suggests using something like this:\n\n```php\n$category-\u003eproducts-\u003eeach-\u003esetRelation('category', $category);\n```\n\nThis works, but it's not very clean and there are cases when it doesn't work. For example, on model creation.\n\nIf you're accessing the parent model in `creating` and `saving` events on the children, the `-\u003eeach-\u003esetRelation()` approach won't help you at all. (And if you're building a complex app with [Laravel Nova](https://nova.laravel.com), there's a high chance you're using lots of such events.)\n\n## Practical Example \u0026 Benchmarks\n\nI have an e-commerce application where an `Order` has child models: `OrderProduct`, `OrderStatus` and `OrderFee` (think shipping costs, payment fees, etc).\n\nWhen some of those models are **being created** (`creating` Eloquent event), they are accessing the parent model.\n\nFor example, `OrderProduct`s convert their prices to `$this-\u003eorder-\u003ecurrency`. `OrderFee`s check for other order fees, and they prevent creating themselves if a fee with the same code already exists (so that you can't have, say, the shipping cost counted twice). Etc.\n\nThis results in order creation being expensive, resulting in a large amount of n+1 queries.\n\n### Benchmark\n\nI haven't run a huge amount of tests, so I won't present the time differences here. I will only talk about database query count.\n\nI have created an order with 6 products.\n\n#### This is the amount of queries made with regular `hasMany()`\n\n![Query count with hasMany()](https://i.imgur.com/Yss7aVl.png)\n\nAnd now I just replace all of these calls:\n\n```php\nreturn $this-\u003ehasMany(...);\n```\nwith these calls\n```php\nreturn $this-\u003ehasManyWithInverse(..., 'order');\n```\n\ninside the `Order` model.\n\n#### And this is the amount of queries made with `hasManyWithInverse()`\n\n![Query count with hasManyWithInverse()](https://i.imgur.com/XimW6T7.png)\n\nSee the query count reduction.\n\nThe duration was also decreased from 114ms to 45ms on my machine, though note that I did not run this test a million times to calculate an average duration, so that benchmark might not be very accurate.\n\nThis is pretty impressive for **a free improvement that only requires changing a few simple calls to a similar method**.\n\nBut note that this is not a silver bullet for solving all n+1 queries. As you can see, even with this implemented, my app still has many duplicated queries. (Although not all are unintentional n+1s as there are a few `$this-\u003erefresh()` calls to keep the order up-to-date after state transitions).\n\n## Installation\n\nLaravel 9.x and 10.x are supported.\n\n```\ncomposer require stancl/laravel-hasmanywithinverse\n```\n\n## Usage\n\n```php\nnamespace App;\n\nuse Stancl\\HasManyWithInverse\\HasManyWithInverse;\n\nclass Order extends Model\n{\n    use HasManyWithInverse;\n\n    public function products()\n    {\n        // 'order' is the name of the relationship in the other model, see below\n        return $this-\u003ehasManyWithInverse(OrderProduct::class, 'order');\n    }\n}\n\nclass OrderProduct extends Model\n{\n    public function order()\n    {\n        return $this-\u003ebelongsTo(Order::class);\n    }\n}\n```\n\nYou may also want to use the trait in a base Eloquent model and then use `$this-\u003ehasManyWithInverse()` without thinking about traits in the specific models.\n\n## Details\n\nThe (simple) internals of the package are just methods copied from Eloquent source code, with a few lines added to them. The `hasManyWithInverse()` method signature is the same as `hasMany()` (you can set `$foreignKey` and `$localKey`), except the second argument (`$inverse`) was added to let you define the name of the relationship on the child model, and the last argument (`$config`) was added to let you configure the relation setting's behavior.\n\n**This package sets the parent relation on children both when creating children (`$child = $parent-\u003echildren()-\u003ecreate()`) and when resolving parent's children (`$children = $parent-\u003echildren`).** You can customize this behavior for every relationship.\n\nTo disable setting the relationship during child **creation**, do this:\n```php\nclass Parent extends Model\n{\n    public function children()\n    {\n        return $this-\u003ehasManyWithInverse(Child::class, 'parent', null, null, ['setRelationOnCreation' =\u003e false]);\n    }\n}\n```\n\nTo disable setting the relationship during child **resolution**, do this:\n```php\nclass Parent extends Model\n{\n    public function children()\n    {\n        return $this-\u003ehasManyWithInverse(Child::class, 'parent', null, null, ['setRelationOnResolution' =\u003e false]);\n    }\n}\n```\n\nYou may also pass a callable as the config value. This is useful if you want to disable this behavior on some requests. See example below.\n\n## Laravel Nova\n\nIt's a good idea to disable setting the relationship on resolution for Nova requests. They tend to make a lot of queries and this can slow the page down (or result in 502 errors).\n\nHere's an example implementation using a base model and adding config to filter out Nova requests.\n\n```php\nabstract class Model extends EloquentModel\n{\n    use HasManyWithInverse {\n        hasManyWithInverse as originalHasManyWithInverse;\n    }\n\n    public function hasManyWithInverse($related, $inverse, $foreignKey = null, $localKey = null, $config = [])\n    {\n        $config = array_merge(['setRelationOnResolution' =\u003e function () {\n            if (request()-\u003eroute() \u0026\u0026 in_array('nova', request()-\u003eroute()-\u003emiddleware())) {\n                return false;\n            }\n        }], $config);\n\n        return $this-\u003eoriginalHasManyWithInverse($related, $inverse, $foreignKey, $localKey, $config);\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farchtechx%2Flaravel-hasmanywithinverse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farchtechx%2Flaravel-hasmanywithinverse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farchtechx%2Flaravel-hasmanywithinverse/lists"}