{"id":13396155,"url":"https://github.com/kirkbushell/eloquence","last_synced_at":"2025-12-24T16:30:57.027Z","repository":{"id":16329499,"uuid":"19079086","full_name":"kirkbushell/eloquence","owner":"kirkbushell","description":"A drop-in library for certain database functionality in Laravel, that allows for extra features that may never make it into the main project.","archived":false,"fork":false,"pushed_at":"2024-06-27T04:58:52.000Z","size":308,"stargazers_count":542,"open_issues_count":1,"forks_count":58,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-09-01T20:02:48.457Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/kirkbushell.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":"2014-04-23T17:34:09.000Z","updated_at":"2024-08-28T10:03:44.000Z","dependencies_parsed_at":"2023-11-29T09:42:43.674Z","dependency_job_id":"63afef5b-1ff8-4f89-b71f-f97b5611f64a","html_url":"https://github.com/kirkbushell/eloquence","commit_stats":{"total_commits":215,"total_committers":26,"mean_commits":8.26923076923077,"dds":0.3627906976744186,"last_synced_commit":"63c63e41850bac0b3a9058d29f09e94bb73f1315"},"previous_names":[],"tags_count":45,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirkbushell%2Feloquence","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirkbushell%2Feloquence/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirkbushell%2Feloquence/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirkbushell%2Feloquence/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kirkbushell","download_url":"https://codeload.github.com/kirkbushell/eloquence/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243493908,"owners_count":20299738,"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","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":"2024-07-30T18:00:41.453Z","updated_at":"2025-12-24T16:30:57.021Z","avatar_url":"https://github.com/kirkbushell.png","language":"PHP","funding_links":[],"categories":["Popular Packages","Paquetes utiles","Packages"],"sub_categories":["Database/Eloquent/Models"],"readme":"# Eloquence\n\n![Version](https://img.shields.io/packagist/v/kirkbushell/eloquence.svg)\n![Downloads](https://img.shields.io/packagist/dt/kirkbushell/eloquence.svg)\n[![Test](https://github.com/kirkbushell/eloquence/actions/workflows/test.yml/badge.svg)](https://github.com/kirkbushell/eloquence/actions/workflows/test.yml)\n\nEloquence is a package to extend Laravel's base Eloquent models and functionality.\n\nIt provides a number of utilities and attributes to work with Eloquent in new and useful ways,\nsuch as camel cased attributes (such as for JSON apis and code style cohesion), data aggregation and more.\n\n## Installation\n\nInstall the package via composer:\n\n    composer require kirkbushell/eloquence\n\n## Usage\n\nEloquence is automatically discoverable by Laravel, and shouldn't require any further steps. For those on earlier \nversions of Laravel, you can add the package as per normal in your config/app.php file:\n\n    'Eloquence\\EloquenceServiceProvider',\n\nThe service provider doesn't do much, other than enable the query log, if configured.\n\n## Readonly models\n\nEloquence supports the protection of models by ensuring that they can only be loaded from the database, and not\nwritten to, or have their values changed. This is useful for data you do not wish to be altered, or in cases\nwhere you may be sharing models across domain boundaries.\n\nTo use, simply add the HasReadOnly trait to your model:\n\n```php\nuse \\Eloquence\\Behaviours\\Readonly\\HasReadOnly;\n\nclass Log extends Model {\n    use HasReadOnly;\n}\n```\n\n## Camel case all the things!\n\nFor those of us who prefer to work with a single coding style right across our applications, using the CamelCased trait \nwill ensure you can do exactly that. It transforms all attribute access from camelCase to snake_case in real-time,\nproviding a unified coding style across your application. This means everything from attribute access to JSON API \nresponses will all be camelCased. To use, simply add the CamelCased trait to your model:\n\n    use \\Eloquence\\Behaviours\\HasCamelCasing;\n\n### Note!\n\nEloquence ***DOES NOT CHANGE*** how you write your schema migrations. You should still be using snake_case when setting \nup your columns and tables in your database schema migrations. This is a good thing - snake_case of columns names is the \ndefacto standard within the Laravel community and is widely-used across database schemas, as well.\n\n## Behaviours\n\nEloquence comes with a system for setting up behaviours, which are really just small libraries that you can use with your \nEloquent models. The first of these is the count cache.\n\n### Count cache\n\nCount caching is where you cache the result of a count on a related model's record. A simple example of this is where you \nhave posts that belong to authors. In this situation, you may want to count the number of posts an author has regularly,\nand perhaps even order by this count. In SQL, ordering by an aggregated value is unable to be indexed and therefore - slow.\nYou can get around this by caching the count of the posts the author has created on the author's model record.\n\nTo get this working, you need to do two steps:\n\n1. Use the HasCounts trait on the child model (in this, case Post) and\n2. Configure the count cache settings by using the CountedBy attribute.\n\n#### Configuring a count cache\n\nTo setup a count cache configuration, we add the HasCounts trait, and setup the CountedBy attribute:\n\n```php\nuse Eloquence\\Behaviours\\CountCache\\CountedBy;\nuse Eloquence\\Behaviours\\CountCache\\HasCounts;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Post extends Model {\n    use HasCounts;\n\n    #[CountedBy]\n    public function author(): BelongsTo\n    {\n        return $this-\u003ebelongsTo(Author::class);\n    }\n}\n```\n\nThis tells the count cache behaviour that the model has an aggregate count cache on the Author model. So, whenever a post \nis added, modified or deleted, the count cache behaviour will update the appropriate author's count cache for their \nposts. In this case, it would update `post_count` field on the author model.\n\nThe example above uses the following standard conventions:\n\n* `post_count` is a defined field on the User model table\n\nIt uses your own relationship to find the related record, so no other configuration is required!\n\nOf course, if you have a different setup, or different field names, you can alter the count cache behaviour by defining\nthe appropriate field to update:\n\n```php\nclass Post extends Model {\n    use HasCounts;\n\n    #[CountedBy(as: 'total_posts')]\n    public function author(): BelongsTo\n    {\n        return $this-\u003ebelongsTo(Author::class);\n    }\n}\n```\n\nWhen setting the as: value (using named parameters here from PHP 8.0 for illustrative and readability purposes), you're \ntelling the count cache that the aggregate field on the Author model is actually called `total_posts`.\n\nHasCounts is not limited to just one count cache configuration. You can define as many as you need for each BelongsTo\nrelationship, like so:\n\n```php\n#[CountedBy(as: 'total_posts')]\npublic function author(): BelongsTo\n{\n    return $this-\u003ebelongsTo(Author::class);\n}\n\n#[CountedBy(as: 'num_posts')]\npublic function category(): BelongsTo\n{\n    return $this-\u003ebelongsTo(Category::class);\n}\n```\n\n### Sum cache\n\nSum caching is similar to count caching, except that instead of caching a _count_ of the related model objects, you cache a _sum_\nof a particular field on the child model's object. A simple example of this is where you have an order that has many items.\nUsing sum caching, you can cache the sum of all the items' prices, and store that as a cached sum on the Order model.\n\nTo get this working -- just like count caching -- you need to do two steps:\n\n1. Add the HasSums to your child model and\n2. Add SummedBy attribute to each relationship method that requires it.\n\n#### Configure the sum cache\n\nTo setup the sum cache configuration, simply do the following:\n\n```php\nuse Eloquence\\Behaviours\\SumCache\\HasSums;\nuse Eloquence\\Behaviours\\SumCache\\SummedBy;\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Item extends Model {\n    use HasSums;\n\n    #[SummedBy(from: 'amount', as: 'total_amount')]\n    public function order(): BelongsTo\n    {\n        return $this-\u003ebelongsTo(Order::class);\n    }\n}\n```\n\nUnlike the count cache which can assume sensible defaults, the sum cache needs a bit more guidance. The example above \ntells the sum cache that there is an `amount` field on Item that needs to be summed to the `total_amount` field on Order.\n\n### Cache recommendations\n\nBecause the cache system works directly with other model objects and requires multiple writes to the database, it is\nstrongly recommended that you wrap your model saves that utilise caches in a transaction. In databases like Postgres,\nthis is automatic, but for databases like MySQL you need to make sure you're using a transactional database engine\nlike InnoDB.\n\nThe reason for needing transactions is that if any one of your queries fail, your caches will end up out of sync. It's \nbetter for the entire operation to fail, than to have this happen. Below is an example of using a database transaction\nusing Laravel's DB facade:\n\n```php\nDB::transaction(function() {\n    $post = new Post;\n    $post-\u003eauthorId = $author-\u003eid;\n    $post-\u003esave();\n});\n```\n\nIf we return to the example above with posts having authors - if this save was not wrapped in a transaction, and the post\nwas created but for some reason the database failed immediately after, you would never see the count cache update in the\nparent Author model, you'll end up with erroneous data that can be quite difficult to debug.\n\n### Sluggable\n\nSluggable is another behaviour that allows for the easy addition of model slugs. To use, implement the Sluggable trait:\n\n```php\nclass User extends Model {\n    use HasSlugs;\n\n    public function slugStrategy(): string\n    {\n        return 'username';\n    }\n}\n```\n\nIn the example above, a slug will be created based on the username field of the User model. There are two other\nslugs that are supported, as well:\n\n* id and\n* uuid\n\nThe only difference between the two above, is that if you're using UUIDs, the slug will be generated prior to the model\nbeing saved, based on the uuid field. With ids, which are generally auto-increase strategies - the slug has to be \ngenerated after the record has been saved - which results in a secondary save call to the database.\n\nThat's it! Easy huh?\n\n# Upgrading from v10\nVersion 11 of Eloquence is a complete rebuild and departure from the original codebase, utilising instead PHP 8.1 attributes\nand moving away from traits/class extensions where possible. This means that in some projects many updates will need to \nbe made to ensure that your use of Eloquence continues to work.\n\n## 1. Class renames\n\n* Camelcasing has been renamed to HasCamelCasing\n* Sluggable renamed to HasSlugs\n\n## 2. Updates to how caches work\nAll your cache implementations will need to be modified following the guide above. But in short, you'll need to import\nand apply the provided attributes to the relationship methods on your models that require aggregated cache values.\n\nThe best part about the new architecture with Eloquence, is that you can define your relationships however you want! If \nyou have custom where clauses or other conditions that restrict the relationship, Eloquence will respect that. This makes\nEloquence now considerably more powerful and supportive of individual domain requirements than ever before.\n\nLet's use a real case. This is the old approach, using Countable as an example:\n\n```php\nclass Post extends Model\n{\n    use Countable;\n    \n    public function countCaches() {\n        return [\n            'num_posts' =\u003e ['User', 'users_id', 'id']\n        ];\n    }\n}\n```\n\nTo migrate that to v11, we would do the following:\n\n```php\nuse Eloquence\\Behaviours\\CountCache\\CountedBy;\n\nclass Post extends Model\n{\n    use \\Eloquence\\Behaviours\\CountCache\\HasCounts;\n    \n    #[CountedBy(as: 'num_posts')]\n    public function user(): BelongsTo\n    {\n        return $this-\u003ebelongsTo(User::class);\n    }\n}\n```\n\nNote the distinct lack of required configuration. The same applies to the sum behaviour - simply migrate your configuration \naway from the cache functions, and into the attributes above the relationships you wish to have an aggregated cache \nvalue for.\n\n## Changelog\n\n#### 12.0.1\n\n* Added Readonly model support.\n\n#### 12.0.0\n\n* Added Laravel 12 support\n\n#### 11.0.4\n\n* Bug fix provided by #120 addressing the creation of new models without related model objects\n\n#### 11.0.3\n\n* Bug fix for count cache when relation is removed (#118)\n* Identified and applied a similar bugfix for the sum cache\n\n#### 11.0.2\n\n* Fixed a bug where relationships were not being returned\n\n#### 11.0.1\n\n* Fixed dependency error to support Laravel 11\n\n#### 11.0.0\n\n* Complete rework of the Eloquent library - version 11 is **_not_** backwards-compatible\n* UUID support removed - both UUIDs and ULIDs are now natively supported in Laravel and have been for some time\n* Cache system now works directly with models and their relationships, allowing for fine-grained control over the models it works with\n* Console commands removed - model caches can be rebuilt using Model::rebuildCache() if something goes awry\n* Fixed a number of bugs across both count and sum caches\n* CamelCasing renamed to CamelCased\n* Syntax, styling, and standards all modernised\n\n#### 10.0.0\n\n* Boost in version number to match Laravel\n* Support for Laravel 10.0+\n* Replace date casting with standard Laravel casting (https://laravel.com/docs/10.x/upgrade#model-dates-property)\n\n#### 9.0.0\n\n* Boost in version number to match Laravel\n* Support for Laravel 9.0+\n* Updated to require PHP 8.1+\n* Resolved method deprecation warnings\n\n#### 8.0.0\n\n* Boost in version number to match Laravel\n* Support for Laravel 7.3+\n\n* Fixes a bug that resulted with the new guarded attributes logic in eloquent\n\n#### 4.0.1\n\n* Fixes a bug that resulted with the new guarded attributes logic in eloquent\n\n#### 4.0.0\n\n* Laravel 7 support (thanks, @msiemens!)\n\n#### 3.0.0\n\n* Laravel 6 support\n* Better slug creation and handling\n\n#### 2.0.7\n\n* Slug uniqueness check upon slug creation for id-based slugs.\n\n#### 2.0.6\n\n* Bug fix when restoring models that was resulting in incorrect count cache values.\n\n#### 2.0.3\n\n* Slugs now implement Jsonable, making them easier to handle in API responses\n* New artisan command for rebuilding caches (beta, use at own risk)\n\n#### 2.0.2\n\n* Updated PHP dependency to 5.6+\n* CountCache and SumCache behaviours now supported via a service layer\n\n#### 2.0.0\n\n* Sum cache model behaviour added\n* Booting of behaviours now done via Laravel trait booting\n* Simplification of all behaviours and their uses\n* Updated readme/configuration guide\n\n#### 1.4.0\n\n* Slugs when retrieved from a model now return Slug value objects.\n\n#### 1.3.4\n\n* More random, less predictable slugs for id strategies\n\n#### 1.3.3\n\n* Fixed a bug with relationships not being accessible via model properties\n\n#### 1.3.2\n\n* Slugged behaviour\n* Fix for fillable attributes\n\n#### 1.3.1\n\n* Relationship fixes\n* Fillable attributes bug fix\n* Count cache update for changing relationships fix\n* Small update for implementing count cache observer\n\n#### 1.3.0\n\n* Count cache model behaviour added\n* Many-many relationship casing fix\n* Fixed an issue when using ::create\n\n#### 1.2.0\n\n* Laravel 5 support\n* Readme updates\n\n#### 1.1.5\n\n* UUID model trait now supports custom UUIDs (instead of only generating them for you)\n\n#### 1.1.4\n\n* UUID fix\n\n#### 1.1.3\n\n* Removed the schema binding on the service provider\n\n#### 1.1.2\n\n* Removed the uuid column creation via custom blueprint\n\n#### 1.1.1\n\n* Dependency bug fix\n\n#### 1.1.0\n\n* UUIDModel trait added\n* CamelCaseModel trait added\n* Model class updated to use CamelCaseModel trait - deprecated, backwards-compatibility support only\n* Eloquence now its own namespace (breaking change)\n* EloquenceServiceProvider added use this if you want to overload the base model automatically (required for pivot model camel casing).\n\n#### 1.0.2\n\n* Relationships now support camelCasing for retrieval (thanks @linxgws)\n\n#### 1.0.1\n\n* Fixed an issue with dependency resolution\n\n#### 1.0.0\n\n* Initial implementation\n* Camel casing of model attributes now available for both setters and getters\n\n## License\n\nThe Laravel framework is open-sourced software licensed under the MIT license.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkirkbushell%2Feloquence","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkirkbushell%2Feloquence","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkirkbushell%2Feloquence/lists"}