Compare commits

...

4 Commits

Author SHA1 Message Date
Hernold Koch 2007d16caf
Merge f8ba11e7f3 into f60e1bc236 2024-05-16 13:13:50 +02:00
github-actions f60e1bc236 Merge branch '5.2' into 5 2024-05-16 01:13:12 +00:00
Guy Sartorelli 50a0018363
FIX many_many through should allow subclasses (#11230)
```php
class HomePage extends Page
{
    private static $many_many = [
        'HeroImages' => [
            'through' => PageImageLink::class,
            'from' => 'Page',
            'to' => 'Image',
        ]
    ];

}
```

```php
class PageImageLink extends DataObject
{
    private static $has_one = [
        'Page' => SiteTree::class,
        'Image' => Image::class,
    ];
}

This fails because the linking object's relation class doesn't exactly match the owner. Sharing the linking objects across various entries in the ancestry should be a supported use case.

Co-authored-by: Aaron Carlino <unclecheese@leftandmain.com>
2024-05-13 14:15:37 +12:00
Hernold Koch f8ba11e7f3 FIX 'which' check in 'sake' now works for aliases
Improves on the current implementations since
'command' already only returns executable commands.
Additionally, the success output of 'command' is not standardized
and may contain additional information.
Therefore, a 'test' if the result of 'command'
is a valid executable file, may fail incorrectly.

Now just relying on the standardized exit status of 'command -v'.
2024-05-10 17:59:15 +02:00
6 changed files with 138 additions and 21 deletions

5
sake
View File

@ -9,10 +9,7 @@ Executes a SilverStripe command"
exit 1
fi
if ! [ -x "$(command -v which)" ]; then
echo "Error: sake requires the 'which' command to operate." >&2
exit 1
fi
command -v which >/dev/null 2>&1 || { echo >&2 "Error: sake requires the 'which' command to operate."; exit 1; }
# find the silverstripe installation, looking first at sake
# bin location, but falling back to current directory

View File

@ -1239,7 +1239,7 @@ class DataObjectSchema
}
// Validate bad types on parent relation
if ($key === 'from' && $relationClass !== $parentClass) {
if ($key === 'from' && $relationClass !== $parentClass && !is_subclass_of($parentClass, $relationClass)) {
throw new InvalidArgumentException(
"many_many through relation {$parentClass}.{$component} {$key} references a field name "
. "{$joinClass}::{$relation} of type {$relationClass}; {$parentClass} expected"

View File

@ -25,10 +25,12 @@ class ManyManyThroughListTest extends SapphireTest
ManyManyThroughListTest\Item::class,
ManyManyThroughListTest\JoinObject::class,
ManyManyThroughListTest\TestObject::class,
ManyManyThroughListTest\TestObjectSubclass::class,
ManyManyThroughListTest\PolyItem::class,
ManyManyThroughListTest\PolyJoinObject::class,
ManyManyThroughListTest\PolyObjectA::class,
ManyManyThroughListTest\PolyObjectB::class,
ManyManyThroughListTest\PseudoPolyJoinObject::class,
ManyManyThroughListTest\Locale::class,
ManyManyThroughListTest\FallbackLocale::class,
];
@ -161,46 +163,82 @@ class ManyManyThroughListTest extends SapphireTest
];
}
public function testAdd()
public function provideAdd(): array
{
/** @var ManyManyThroughListTest\TestObject $parent */
$parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
return [
[
'parentClass' => ManyManyThroughListTest\TestObject::class,
'joinClass' => ManyManyThroughListTest\JoinObject::class,
'joinProperty' => 'ManyManyThroughListTest_JoinObject',
'relation' => 'Items',
],
[
'parentClass' => ManyManyThroughListTest\TestObjectSubclass::class,
'joinClass' => ManyManyThroughListTest\PseudoPolyJoinObject::class,
'joinProperty' => 'ManyManyThroughListTest_PseudoPolyJoinObject',
'relation' => 'MoreItems',
],
];
}
/**
* @dataProvider provideAdd
*/
public function testAdd(string $parentClass, string $joinClass, string $joinProperty, string $relation)
{
$parent = $this->objFromFixture($parentClass, 'parent1');
$newItem = new ManyManyThroughListTest\Item();
$newItem->Title = 'my new item';
$newItem->write();
$parent->Items()->add($newItem, ['Title' => 'new join record']);
$parent->$relation()->add($newItem, ['Title' => 'new join record']);
// Check select
$newItem = $parent->Items()->filter(['Title' => 'my new item'])->first();
$newItem = $parent->$relation()->filter(['Title' => 'my new item'])->first();
$this->assertNotNull($newItem);
$this->assertEquals('my new item', $newItem->Title);
$this->assertInstanceOf(
ManyManyThroughListTest\JoinObject::class,
$joinClass,
$newItem->getJoin()
);
$this->assertInstanceOf(
ManyManyThroughListTest\JoinObject::class,
$newItem->ManyManyThroughListTest_JoinObject
$joinClass,
$newItem->$joinProperty
);
$this->assertEquals('new join record', $newItem->ManyManyThroughListTest_JoinObject->Title);
$this->assertEquals('new join record', $newItem->$joinProperty->Title);
}
public function testRemove()
public function provideRemove(): array
{
/** @var ManyManyThroughListTest\TestObject $parent */
$parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
return [
[
'parentClass' => ManyManyThroughListTest\TestObject::class,
'relation' => 'Items',
],
[
'parentClass' => ManyManyThroughListTest\TestObjectSubclass::class,
'relation' => 'MoreItems',
],
];
}
/**
* @dataProvider provideRemove
*/
public function testRemove(string $parentClass, string $relation)
{
$parent = $this->objFromFixture($parentClass, 'parent1');
$this->assertListEquals(
[
['Title' => 'item 1'],
['Title' => 'item 2']
],
$parent->Items()
$parent->$relation()
);
$item1 = $parent->Items()->filter(['Title' => 'item 1'])->first();
$parent->Items()->remove($item1);
$item1 = $parent->$relation()->filter(['Title' => 'item 1'])->first();
$parent->$relation()->remove($item1);
$this->assertListEquals(
[['Title' => 'item 2']],
$parent->Items()
$parent->$relation()
);
}

View File

@ -3,6 +3,11 @@ SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject:
Title: 'my object'
parent2:
Title: 'my object2'
SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObjectSubclass:
parent1:
Title: 'my object'
parent2:
Title: 'my object2'
SilverStripe\ORM\Tests\ManyManyThroughListTest\Item:
# Having this one first means the IDs of records aren't the same as the IDs of the join objects.
child0:
@ -30,6 +35,25 @@ SilverStripe\ORM\Tests\ManyManyThroughListTest\JoinObject:
Title: 'join 4'
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject.parent2
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child2
SilverStripe\ORM\Tests\ManyManyThroughListTest\PseudoPolyJoinObject:
join1:
Title: 'join 1'
Sort: 4
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObjectSubclass.parent1
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child1
join2:
Title: 'join 2'
Sort: 2
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObjectSubclass.parent1
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child2
join3:
Title: 'join 3'
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObjectSubclass.parent2
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child1
join4:
Title: 'join 4'
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObjectSubclass.parent2
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child2
SilverStripe\ORM\Tests\ManyManyThroughListTest\PolyObjectA:
obja1:
Title: 'object A1'

View File

@ -0,0 +1,28 @@
<?php
namespace SilverStripe\ORM\Tests\ManyManyThroughListTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
/**
* @property string $Title
* @method TestObject Parent()
* @method Item Child()
*/
class PseudoPolyJoinObject extends DataObject implements TestOnly
{
private static $table_name = 'ManyManyThroughListTest_PseudoPolyJoinObject';
private static $db = [
'Title' => 'Varchar',
'Sort' => 'Int',
];
private static $has_one = [
'Parent' => TestObject::class,
'Child' => Item::class,
];
private static $default_sort = '"Sort" ASC';
}

View File

@ -0,0 +1,30 @@
<?php
namespace SilverStripe\ORM\Tests\ManyManyThroughListTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\ManyManyThroughList;
/**
* Basic parent object
*
* @property string $Title
* @method ManyManyThroughList Items()
*/
class TestObjectSubclass extends TestObject implements TestOnly
{
private static $table_name = 'ManyManyThroughListTest_TestObjectSubclass';
private static $db = [
'Title' => 'Varchar'
];
private static $many_many = [
'MoreItems' => [
'through' => PseudoPolyJoinObject::class,
'from' => 'Parent',
'to' => 'Child',
]
];
}