Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG Eager loading with multiple objects pointing to the same has_one trigger extra db fetch #11170

Closed
2 tasks done
beerbohmdo opened this issue Mar 5, 2024 · 1 comment
Closed
2 tasks done

Comments

@beerbohmdo
Copy link
Contributor

beerbohmdo commented Mar 5, 2024

Module version(s) affected

5.1.18

Description

The current implementation of eagerLoad does not allow that multiple objects have the same has_one relation.

If I have a relation like in the yaml file below, the eager query will load all required EagerLoadBars but will only pass the object into the last EagerLoadFoo object, all objects before will trigger an additional query to the database.

Is this wanted?

How to reproduce

<?php

namespace tests;

use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBVarchar;
use SilverStripe\ORM\HasManyList;

/**
 * @property string|null $Dummy
 * @method HasManyList<EagerLoadFoo> Foos()
 */
class EagerLoadBar extends DataObject implements TestOnly
{
    /**
     * @var array
     * @config
     */
    private static array $db = [
        'Dummy' => DBVarchar::class . '(255)',
    ];

    /**
     * @var array
     * @config
     */
    private static array $has_many = [
        'Foos'  => EagerLoadFoo::class,
    ];
}
<?php

namespace tests;

use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBVarchar;

/**
 * @property string|null $Dummy
 * @property int $BarID
 * @method EagerLoadBar Bar()
 */
class EagerLoadFoo extends DataObject implements TestOnly
{
    /**
     * @var array
     * @config
     */
    private static array $db = [
        'Dummy' => DBVarchar::class . '(255)',
    ];

    /**
     * @var array
     * @config
     */
    private static array $has_one = [
        'Bar'   => EagerLoadBar::class,
    ];
}
tests\EagerLoadBar:
  bar1:
    Dummy: 1
  bar2:
    Dummy: 2
  bar3:
    Dummy: 3
  bar4:
    Dummy: 4

tests\EagerLoadFoo:
  foo1:
    Bar: =>tests\EagerLoadBar.bar1
  foo2:
    Bar: =>tests\EagerLoadBar.bar2
  foo3:
    Bar: =>tests\EagerLoadBar.bar1
  foo4:
    Bar: =>tests\EagerLoadBar.bar3
  foo5:
    Bar: =>tests\EagerLoadBar.bar2
  foo6:
    Bar: =>tests\EagerLoadBar.bar4
<?php

namespace tests;

use ReflectionProperty;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\DataObject;

class EagerLoadTest extends SapphireTest
{
    protected static $fixture_file = [
        'eager.yml',
    ];

    protected static $extra_dataobjects = [
        EagerLoadBar::class,
        EagerLoadFoo::class,
    ];

    public function testEagerLoading(): void
    {
        $fooNames = array_flip($this->allFixtureIDs(EagerLoadFoo::class));

        $list = EagerLoadFoo::get()->eagerLoad('Bar');

        $property = new ReflectionProperty(DataObject::class, 'eagerLoadedData');

        /** @var EagerLoadFoo $foo */
        foreach ($list as $foo) {
            $eagerLoadedData = $property->getValue($foo);

            $this->assertArrayHasKey('Bar', $eagerLoadedData, 'asserting that ' . ($fooNames[$foo->ID] ?? $foo->ID) . ' has loaded Bar');
            $this->assertEquals($foo->BarID, $eagerLoadedData['Bar']?->ID, 'asserting that ' . ($fooNames[$foo->ID] ?? $foo->ID) . ' has loaded Bar');
        }
    }
}

Possible Solution

Modify the DataList::fetchEagerLoadHasOne to store multiple IDs per Relation.

Additional Context

No response

Validations

  • Check that there isn't already an issue that reports the same bug
  • Double check that your reproduction steps work in a fresh installation of silverstripe/installer (with any code examples you've provided)

PRs

@beerbohmdo beerbohmdo changed the title Eager loading with multiple objects pointing to the same has_one trigger extra db fetch BUG Eager loading with multiple objects pointing to the same has_one trigger extra db fetch Mar 5, 2024
@maxime-rainville maxime-rainville added this to the Silverstripe CMS 5.3 milestone Mar 19, 2024
beerbohmdo added a commit to beerbohmdo/silverstripe-framework that referenced this issue Mar 26, 2024
beerbohmdo added a commit to beerbohmdo/silverstripe-framework that referenced this issue Mar 26, 2024
beerbohmdo added a commit to beerbohmdo/silverstripe-framework that referenced this issue Mar 26, 2024
@GuySartorelli GuySartorelli self-assigned this Apr 17, 2024
GuySartorelli pushed a commit to beerbohmdo/silverstripe-framework that referenced this issue Apr 18, 2024
GuySartorelli pushed a commit to beerbohmdo/silverstripe-framework that referenced this issue Apr 18, 2024
GuySartorelli added a commit that referenced this issue Apr 18, 2024
FIX Ensure eagerLoading don't load has_one twice (#11170)
@GuySartorelli
Copy link
Member

PR merged. This will be automatically tagged by GitHub Actions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants