From 8536043cb98bb8fb22259a14a8de487cab6577d5 Mon Sep 17 00:00:00 2001 From: Sergey Shevchenko Date: Fri, 19 Feb 2021 20:49:06 +1300 Subject: [PATCH] added docs --- README.md | 36 ++++++++++++++++------------- docs/en/Boosting_CSV_Export.md | 29 ++++++++++++++++++++++++ docs/en/Boosting_GridField.md | 33 +++++++++++++++++++++++++++ docs/en/Using_With_HasMany.md | 32 ++++++++++++++++++++++++++ docs/en/Using_With_HasOne.md | 11 +++++++++ docs/en/index.md | 41 ++++++++++++++++++++++++++++++++++ 6 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 docs/en/Boosting_CSV_Export.md create mode 100644 docs/en/Boosting_GridField.md create mode 100644 docs/en/Using_With_HasMany.md create mode 100644 docs/en/Using_With_HasOne.md create mode 100644 docs/en/index.md diff --git a/README.md b/README.md index e98938e..f74f8b1 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,15 @@ composer require gurucomkz/eagerloading Every DataObject that has has_one/many_many/belongs_many_many which you wish to have eagerloaded must include `EagerLoaderMultiAccessor` (see below). ## Features -### `$has_one` +* [Using with $has_one / $belongs_to](docs/en/Using_With_HasOne.md) +* [Using with $has_many / $many_many / $belongs_many_many](docs/en/Using_With_HasMany.md) +* [Boosting GridField output](docs/en/Boosting_GridField.md) +* [Boosting CSV export](docs/en/Boosting_CSV_Export.md) -Out of the box - no changes needed. +Read the docs for full explanation. +## Quick start -### `$has_many`, `$many_many`, `$belongs_meny_many` - -Add the following trait to all your models to use `$has_many`, `$many_many`, `$belongs_meny_many`: +### 1. Add the following trait to all your models to use `$has_many`, `$many_many`, `$belongs_meny_many`: ```php class MyClass extends DataObject { use Gurucomkz\EagerLoading\EagerLoaderMultiAccessor; @@ -37,20 +39,22 @@ class MyClass extends DataObject { } ``` -This trait declares `__call()` method necessary for accessing the eager-loaded data. - -If you have your own `__call()`, please put the contents of `EagerLoaderMultiAccessor::__call()` into it (traits do not override already declared methods). - -If the trait is not included, an exception will be thrown on attempt to use `$has_many`, `$many_many` or `$belongs_meny_many`. -## Admin GridField Eager Loading - -You can declare `private static $eager_loading` in your model listing to leverage the feature in the ModelAdmin's GridField output. +If you have your own `__call()` read [Using with $has_many/$many_many](docs/en/Using_With_HasMany.md). -Additionally, it tries to detect the fact that you are doing the CSV Export and scans the `$export_fields` for suitable relations and loads them -in advance in attempt to speed up the export. +### 2. Declare `private static $eager_loading` to boost ModelAdmin's GridField output. +```php +class YourClass extends DataObject +{ + private static $eager_loading = [ + 'Relation1', + 'Relation1.Relation4', + 'Relation2', + 'Relation3', + ]; +} +``` ## TODO -* Detect 'LIMIT' constraints and load only relevant daya instead of all. * for `->with(['RelLevel1.RelLevel2'])` - do not query for `RelLevel1` IDs twice. * for `->with(['RelLevel1','RelLevel1.RelLevel2'])` - do not query for `RelLevel1` IDs thrice. diff --git a/docs/en/Boosting_CSV_Export.md b/docs/en/Boosting_CSV_Export.md new file mode 100644 index 0000000..2bb71cb --- /dev/null +++ b/docs/en/Boosting_CSV_Export.md @@ -0,0 +1,29 @@ +# Boosting CSV Export + +The module tries to detect the fact that you are doing the CSV Export and scans the `private static $export_fields` for suitable relations and loads them in advance in attempt to speed up the export. + +In the following example relations `Subscribers` and `Address` would be loaded as a whole for every exported rows and instead of being fetched one-by-one. + +```php +class YourClass extends DataObject +{ + private static $has_one = [ + 'Subscriber' => Subscriber::class, + ]; + + private static $has_many = [ + 'Address' => Address::class, + ]; + + private static $export_fields = [ + 'Created' => 'Date', + 'ID' => 'Order ID', + 'Subscriber.FirstName' => 'Recipient first name', + 'Subscriber.LastName' => 'Recipient last name', + 'Address.Address' => 'Recipient street address', + 'Address.Region' => 'Recipient state', + 'Address.Country' => 'Recipient country', + 'Address.PostalCode' => 'Recipient postcode', + ]; +} +``` diff --git a/docs/en/Boosting_GridField.md b/docs/en/Boosting_GridField.md new file mode 100644 index 0000000..4956dfa --- /dev/null +++ b/docs/en/Boosting_GridField.md @@ -0,0 +1,33 @@ +# Boosting GridField Output + +You can add a property to your DataObject class with a list of relations to eagerly load when browsing this class entities with a GridGield (in CMS area). + +```php + +class YourClass extends DataObject +{ + private static $eager_loading = [ + 'Relation1', + 'Relation1.Relation4', + 'Relation2', + 'Relation3', + ]; + + private static $has_one = [ + 'Relation1' => SomeClassOne::class, + ]; + private static $has_many = [ + 'Relation2' => SomeClassTwo::class, + ]; + private static $many_many = [ + 'Relation3' => SomeClassThree::class, + ]; +} + +class SomeClassOne extends DataObject +{ + private static $has_one = [ + 'Relation4' => SomeClassFour::class, + ]; +} +``` diff --git a/docs/en/Using_With_HasMany.md b/docs/en/Using_With_HasMany.md new file mode 100644 index 0000000..5e603d6 --- /dev/null +++ b/docs/en/Using_With_HasMany.md @@ -0,0 +1,32 @@ +# Using with `$has_many`, `$many_many` and `$belongs_many_many` + +Module is able to load such relations out of the bot, but accessing it is a problem because it has to be done through functions. + +To enable access to `$has_many`, `$many_many`, `$belongs_many_many` **add the following trait to all your models**: +```php +class MyClass extends DataObject { + use Gurucomkz\EagerLoading\EagerLoaderMultiAccessor; + + // ... +} +``` + +This trait declares `__call()` method necessary for accessing the eager-loaded data. + +## When you have your own `__call()` method + +If you have your own `__call()`, please put the contents of `EagerLoaderMultiAccessor::__call()` into it: + +```php +class MyClass extends DataObject { + + public function __call($fn, $params) + { + // Copy contents of EagerLoaderMultiAccessor::__call() here + + // your code goes here + } +} +``` + +If the trait is not included, an exception will be thrown on attempt to use `$has_many`, `$many_many` or `$belongs_meny_many`. diff --git a/docs/en/Using_With_HasOne.md b/docs/en/Using_With_HasOne.md new file mode 100644 index 0000000..00a7ab0 --- /dev/null +++ b/docs/en/Using_With_HasOne.md @@ -0,0 +1,11 @@ +# Using With `$has_one` + +This type of relations is supported out of the box without any structural modifications. + +Simply add `->with([...relations...])` to the ORM invocation chain when needed. + +```php +MyModelClass::get()->with(['Relation1','Relation2'])->filter(...); +``` + +Once your code starts accessing data from the query, module will load related entries from `Relation1` and `Relation2` with much less queries, than doing that one-by-one as SilverStripe usually does. diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 0000000..583f244 --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,41 @@ +# Silverstripe EagerLoading + +Module solves the [N+1 problem](https://stackoverflow.com/questions/97197/what-is-the-n1-selects-problem-in-orm-object-relational-mapping) in SilverStripe 4. + +Normally SilverStripe uses Lazy Loading and fetches the required information only when it is actually accessed. + +I.e. +```php +$items = MyModelClass::get(); # no query yet + +foreach ($items as $item) { # makes a DB query for all $items + echo $item->Feature1->Title; # makes DB query for ONE Feature1 + echo $item->Feature2->Title; # makes DB query for ONE Feature2 + foreach ($items->Feature3() as $subitem) { # makes a DB query for ALL Feature3 + echo $subitem->Title; # makes DB query for ONE Feature2 + } +} +``` +With `K` rows in `MyModelClass`, `N` number of relations that are directly accessed every `for` iterations: +`K * (1 + N)` DB queries. + +Using this module the code above can be reduced to `K + N * 3`. + +## Solution + +```php +MyModelClass::get()->with(['Relation1','Relation2'])->filter(...); +``` + +It does not require huge configuration - only one function to be added to the query builder chain : `->with([..relations...])`. + +This will result in the final DataList to be presented by the `EagerLoadedDataList` class that handles the eager loading. + +The module takes advantage of `DataList::getGenerator()` to query for and attach the related records only when needed. + +## Examples + +* [Using with $has_one / $belongs_to](Using_With_HasOne.md) +* [Using with $has_many / $many_many / $belongs_many_many](Using_With_HasMany.md) +* [Boosting GridField output](Boosting_GridField.md) +* [Boosting CSV export](Boosting_CSV_Export.md)