{"id":51914404,"url":"https://github.com/whilesmartphp/eloquent-holdings","last_synced_at":"2026-07-27T09:30:29.299Z","repository":{"id":371616645,"uuid":"1282125328","full_name":"whilesmartphp/eloquent-holdings","owner":"whilesmartphp","description":"Polymorphic holdings register (crypto, stocks, property, any asset) for Laravel: track quantity and value, with pluggable live pricing.","archived":false,"fork":false,"pushed_at":"2026-07-15T21:53:14.000Z","size":24,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"dev","last_synced_at":"2026-07-15T23:25:50.032Z","etag":null,"topics":[],"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/whilesmartphp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-27T11:02:26.000Z","updated_at":"2026-07-15T21:53:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/whilesmartphp/eloquent-holdings","commit_stats":null,"previous_names":["whilesmartphp/eloquent-holdings"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/whilesmartphp/eloquent-holdings","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-holdings","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-holdings/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-holdings/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-holdings/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/whilesmartphp","download_url":"https://codeload.github.com/whilesmartphp/eloquent-holdings/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whilesmartphp%2Feloquent-holdings/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35947060,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-27T02:00:06.776Z","response_time":101,"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":[],"created_at":"2026-07-27T09:30:28.566Z","updated_at":"2026-07-27T09:30:29.293Z","avatar_url":"https://github.com/whilesmartphp.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# whilesmart/eloquent-holdings\n\nA polymorphic holdings register for Laravel: one model for anything you own and want to value, whether crypto, stocks, property, or collectibles. Each holding is scoped to an owner (a workspace, organization, user) through [`whilesmart/eloquent-owner-access`](https://github.com/whilesmartphp/eloquent-owner-access), carries a quantity and a unit price, and can be priced by hand or refreshed automatically from a price provider you bind.\n\nThe package never hard codes a price source. It stores an opaque `provider` and `external_ref`, and asks a host-bound `HoldingPriceProvider` to resolve prices, so a CoinGecko, stock, or any other feed lives in your app, not here.\n\n## Install\n\n```\ncomposer require whilesmart/eloquent-holdings\nphp artisan migrate\n```\n\nRoutes register automatically under the `api` prefix with `auth:sanctum`. Set `HOLDINGS_REGISTER_ROUTES=false` to mount them yourself.\n\n## Owning model\n\nAdd the trait to whatever owns holdings:\n\n```php\nuse Whilesmart\\Holdings\\Traits\\HasHoldings;\n\nclass Workspace extends Model\n{\n    use HasHoldings;\n}\n\n// Manual: you maintain the price\n$workspace-\u003eholdings()-\u003ecreate([\n    'name' =\u003e 'Rental flat',\n    'quantity' =\u003e 1,\n    'currency' =\u003e 'USD',\n    'unit_price' =\u003e 120000,\n]);\n\n// Auto: refreshed from a bound provider\n$workspace-\u003eholdings()-\u003ecreate([\n    'name' =\u003e 'Bitcoin',\n    'symbol' =\u003e 'BTC',\n    'quantity' =\u003e 0.5,\n    'currency' =\u003e 'USD',\n    'price_source' =\u003e 'auto',\n    'provider' =\u003e 'coingecko',\n    'external_ref' =\u003e 'bitcoin',\n]);\n```\n\n`$holding-\u003evalue` returns `quantity * unit_price` in the holding's currency.\n\n## Live pricing\n\nImplement `HoldingPriceProvider` in your app and bind it. The package groups auto-priced holdings by `(provider, currency)` and asks for current prices:\n\n```php\nuse Whilesmart\\Holdings\\Contracts\\HoldingPriceProvider;\n\nclass CoingeckoPriceProvider implements HoldingPriceProvider\n{\n    public function prices(string $provider, array $externalRefs, string $currency): array\n    {\n        if ($provider !== 'coingecko') {\n            return [];\n        }\n        // ...call CoinGecko, return [externalRef =\u003e price] in $currency\n    }\n}\n\n// AppServiceProvider::register()\n$this-\u003eapp-\u003ebind(HoldingPriceProvider::class, CoingeckoPriceProvider::class);\n```\n\nRefresh prices via the command (schedule it as you like) or the API:\n\n```\nphp artisan holdings:reprice\nPOST /api/holdings/reprice\n```\n\nA `HoldingsRepriced` event carries the ids of the holdings that changed, so the host can invalidate caches or recompute net worth.\n\n## Authorization\n\nRecords are owner-scoped. Bind an `OwnerAuthorizer` (see `whilesmart/eloquent-owner-access`) to decide who can see and modify each owner's holdings. Without one, the permissive default applies.\n\n## Endpoints\n\n| Method | Path | Action |\n|---|---|---|\n| GET | `/api/holdings` | List (owner-scoped; filter by `owner_type`/`owner_id`, `price_source`, `q`) |\n| POST | `/api/holdings` | Create |\n| GET | `/api/holdings/{holding}` | Show |\n| PUT/PATCH | `/api/holdings/{holding}` | Update |\n| DELETE | `/api/holdings/{holding}` | Delete (soft) |\n| POST | `/api/holdings/reprice` | Refresh auto-priced holdings |\n\n## Development\n\n```\nmake bootstrap   # one-time: pull the package make targets out of the image\nmake test        # run the test suite in a container\nmake lint        # pint --test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhilesmartphp%2Feloquent-holdings","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwhilesmartphp%2Feloquent-holdings","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhilesmartphp%2Feloquent-holdings/lists"}