Skip to content

Commit

Permalink
added docs
Browse files Browse the repository at this point in the history
  • Loading branch information
gurucomkz committed Feb 19, 2021
1 parent 5a014fc commit 8536043
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 16 deletions.
36 changes: 20 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.

Expand Down
29 changes: 29 additions & 0 deletions docs/en/Boosting_CSV_Export.md
Original file line number Diff line number Diff line change
@@ -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',
];
}
```
33 changes: 33 additions & 0 deletions docs/en/Boosting_GridField.md
Original file line number Diff line number Diff line change
@@ -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,
];
}
```
32 changes: 32 additions & 0 deletions docs/en/Using_With_HasMany.md
Original file line number Diff line number Diff line change
@@ -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`.
11 changes: 11 additions & 0 deletions docs/en/Using_With_HasOne.md
Original file line number Diff line number Diff line change
@@ -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.
41 changes: 41 additions & 0 deletions docs/en/index.md
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 8536043

Please sign in to comment.