mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge branch '5' into 6
This commit is contained in:
commit
c6aee6c5c7
@ -1239,7 +1239,7 @@ class DataObjectSchema
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate bad types on parent relation
|
// Validate bad types on parent relation
|
||||||
if ($key === 'from' && $relationClass !== $parentClass) {
|
if ($key === 'from' && $relationClass !== $parentClass && !is_subclass_of($parentClass, $relationClass)) {
|
||||||
throw new InvalidArgumentException(
|
throw new InvalidArgumentException(
|
||||||
"many_many through relation {$parentClass}.{$component} {$key} references a field name "
|
"many_many through relation {$parentClass}.{$component} {$key} references a field name "
|
||||||
. "{$joinClass}::{$relation} of type {$relationClass}; {$parentClass} expected"
|
. "{$joinClass}::{$relation} of type {$relationClass}; {$parentClass} expected"
|
||||||
|
@ -376,7 +376,7 @@ class SSViewer_DataPresenter extends SSViewer_Scope
|
|||||||
|
|
||||||
// Check if the method to-be-called exists on the target object - if so, don't check any further
|
// Check if the method to-be-called exists on the target object - if so, don't check any further
|
||||||
// injection locations
|
// injection locations
|
||||||
$on = $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
$on = $this->getItem();
|
||||||
if (is_object($on) && (isset($on->$property) || method_exists($on, $property ?? ''))) {
|
if (is_object($on) && (isset($on->$property) || method_exists($on, $property ?? ''))) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,11 @@ namespace SilverStripe\View;
|
|||||||
use ArrayIterator;
|
use ArrayIterator;
|
||||||
use Countable;
|
use Countable;
|
||||||
use Iterator;
|
use Iterator;
|
||||||
|
use SilverStripe\ORM\FieldType\DBBoolean;
|
||||||
|
use SilverStripe\ORM\FieldType\DBText;
|
||||||
|
use SilverStripe\ORM\FieldType\DBFloat;
|
||||||
|
use SilverStripe\ORM\FieldType\DBInt;
|
||||||
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This tracks the current scope for an SSViewer instance. It has three goals:
|
* This tracks the current scope for an SSViewer instance. It has three goals:
|
||||||
@ -121,7 +126,11 @@ class SSViewer_Scope
|
|||||||
*/
|
*/
|
||||||
public function getItem()
|
public function getItem()
|
||||||
{
|
{
|
||||||
return $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
$item = $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
||||||
|
if (is_scalar($item)) {
|
||||||
|
$item = $this->convertScalarToDBField($item);
|
||||||
|
}
|
||||||
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,7 +185,7 @@ class SSViewer_Scope
|
|||||||
*/
|
*/
|
||||||
public function getObj($name, $arguments = [], $cache = false, $cacheName = null)
|
public function getObj($name, $arguments = [], $cache = false, $cacheName = null)
|
||||||
{
|
{
|
||||||
$on = $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
$on = $this->getItem();
|
||||||
return $on->obj($name, $arguments, $cache, $cacheName);
|
return $on->obj($name, $arguments, $cache, $cacheName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +249,7 @@ class SSViewer_Scope
|
|||||||
*/
|
*/
|
||||||
public function self()
|
public function self()
|
||||||
{
|
{
|
||||||
$result = $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
$result = $this->getItem();
|
||||||
$this->resetLocalScope();
|
$this->resetLocalScope();
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@ -338,7 +347,7 @@ class SSViewer_Scope
|
|||||||
*/
|
*/
|
||||||
public function __call($name, $arguments)
|
public function __call($name, $arguments)
|
||||||
{
|
{
|
||||||
$on = $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
$on = $this->getItem();
|
||||||
$retval = $on ? $on->$name(...$arguments) : null;
|
$retval = $on ? $on->$name(...$arguments) : null;
|
||||||
|
|
||||||
$this->resetLocalScope();
|
$this->resetLocalScope();
|
||||||
@ -368,4 +377,14 @@ class SSViewer_Scope
|
|||||||
{
|
{
|
||||||
return $this->upIndex;
|
return $this->upIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function convertScalarToDBField(bool|string|float|int $value): DBField
|
||||||
|
{
|
||||||
|
return match (gettype($value)) {
|
||||||
|
'boolean' => DBBoolean::create()->setValue($value),
|
||||||
|
'string' => DBText::create()->setValue($value),
|
||||||
|
'double' => DBFloat::create()->setValue($value),
|
||||||
|
'integer' => DBInt::create()->setValue($value),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,12 @@ class ManyManyThroughListTest extends SapphireTest
|
|||||||
ManyManyThroughListTest\Item::class,
|
ManyManyThroughListTest\Item::class,
|
||||||
ManyManyThroughListTest\JoinObject::class,
|
ManyManyThroughListTest\JoinObject::class,
|
||||||
ManyManyThroughListTest\TestObject::class,
|
ManyManyThroughListTest\TestObject::class,
|
||||||
|
ManyManyThroughListTest\TestObjectSubclass::class,
|
||||||
ManyManyThroughListTest\PolyItem::class,
|
ManyManyThroughListTest\PolyItem::class,
|
||||||
ManyManyThroughListTest\PolyJoinObject::class,
|
ManyManyThroughListTest\PolyJoinObject::class,
|
||||||
ManyManyThroughListTest\PolyObjectA::class,
|
ManyManyThroughListTest\PolyObjectA::class,
|
||||||
ManyManyThroughListTest\PolyObjectB::class,
|
ManyManyThroughListTest\PolyObjectB::class,
|
||||||
|
ManyManyThroughListTest\PseudoPolyJoinObject::class,
|
||||||
ManyManyThroughListTest\Locale::class,
|
ManyManyThroughListTest\Locale::class,
|
||||||
ManyManyThroughListTest\FallbackLocale::class,
|
ManyManyThroughListTest\FallbackLocale::class,
|
||||||
];
|
];
|
||||||
@ -161,46 +163,82 @@ class ManyManyThroughListTest extends SapphireTest
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAdd()
|
public function provideAdd(): array
|
||||||
{
|
{
|
||||||
/** @var ManyManyThroughListTest\TestObject $parent */
|
return [
|
||||||
$parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
|
[
|
||||||
|
'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 = new ManyManyThroughListTest\Item();
|
||||||
$newItem->Title = 'my new item';
|
$newItem->Title = 'my new item';
|
||||||
$newItem->write();
|
$newItem->write();
|
||||||
$parent->Items()->add($newItem, ['Title' => 'new join record']);
|
$parent->$relation()->add($newItem, ['Title' => 'new join record']);
|
||||||
|
|
||||||
// Check select
|
// Check select
|
||||||
$newItem = $parent->Items()->filter(['Title' => 'my new item'])->first();
|
$newItem = $parent->$relation()->filter(['Title' => 'my new item'])->first();
|
||||||
$this->assertNotNull($newItem);
|
$this->assertNotNull($newItem);
|
||||||
$this->assertEquals('my new item', $newItem->Title);
|
$this->assertEquals('my new item', $newItem->Title);
|
||||||
$this->assertInstanceOf(
|
$this->assertInstanceOf(
|
||||||
ManyManyThroughListTest\JoinObject::class,
|
$joinClass,
|
||||||
$newItem->getJoin()
|
$newItem->getJoin()
|
||||||
);
|
);
|
||||||
$this->assertInstanceOf(
|
$this->assertInstanceOf(
|
||||||
ManyManyThroughListTest\JoinObject::class,
|
$joinClass,
|
||||||
$newItem->ManyManyThroughListTest_JoinObject
|
$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 */
|
return [
|
||||||
$parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
|
[
|
||||||
|
'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(
|
$this->assertListEquals(
|
||||||
[
|
[
|
||||||
['Title' => 'item 1'],
|
['Title' => 'item 1'],
|
||||||
['Title' => 'item 2']
|
['Title' => 'item 2']
|
||||||
],
|
],
|
||||||
$parent->Items()
|
$parent->$relation()
|
||||||
);
|
);
|
||||||
$item1 = $parent->Items()->filter(['Title' => 'item 1'])->first();
|
$item1 = $parent->$relation()->filter(['Title' => 'item 1'])->first();
|
||||||
$parent->Items()->remove($item1);
|
$parent->$relation()->remove($item1);
|
||||||
$this->assertListEquals(
|
$this->assertListEquals(
|
||||||
[['Title' => 'item 2']],
|
[['Title' => 'item 2']],
|
||||||
$parent->Items()
|
$parent->$relation()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,11 @@ SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject:
|
|||||||
Title: 'my object'
|
Title: 'my object'
|
||||||
parent2:
|
parent2:
|
||||||
Title: 'my object2'
|
Title: 'my object2'
|
||||||
|
SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObjectSubclass:
|
||||||
|
parent1:
|
||||||
|
Title: 'my object'
|
||||||
|
parent2:
|
||||||
|
Title: 'my object2'
|
||||||
SilverStripe\ORM\Tests\ManyManyThroughListTest\Item:
|
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.
|
# Having this one first means the IDs of records aren't the same as the IDs of the join objects.
|
||||||
child0:
|
child0:
|
||||||
@ -30,6 +35,25 @@ SilverStripe\ORM\Tests\ManyManyThroughListTest\JoinObject:
|
|||||||
Title: 'join 4'
|
Title: 'join 4'
|
||||||
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject.parent2
|
Parent: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject.parent2
|
||||||
Child: =>SilverStripe\ORM\Tests\ManyManyThroughListTest\Item.child2
|
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:
|
SilverStripe\ORM\Tests\ManyManyThroughListTest\PolyObjectA:
|
||||||
obja1:
|
obja1:
|
||||||
Title: 'object A1'
|
Title: 'object A1'
|
||||||
|
@ -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';
|
||||||
|
}
|
30
tests/php/ORM/ManyManyThroughListTest/TestObjectSubclass.php
Normal file
30
tests/php/ORM/ManyManyThroughListTest/TestObjectSubclass.php
Normal 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',
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
@ -2218,4 +2218,16 @@ EOC;
|
|||||||
$this->assertTrue(file_exists($cacheFile ?? ''), 'Cache file wasn\'t created when it was meant to');
|
$this->assertTrue(file_exists($cacheFile ?? ''), 'Cache file wasn\'t created when it was meant to');
|
||||||
unlink($cacheFile ?? '');
|
unlink($cacheFile ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPrimitivesConvertedToDBFields()
|
||||||
|
{
|
||||||
|
$data = new ArrayData([
|
||||||
|
// null value should not be rendered, though should also not throw exception
|
||||||
|
'Foo' => new ArrayList(['hello', true, 456, 7.89, null])
|
||||||
|
]);
|
||||||
|
$this->assertEqualIgnoringWhitespace(
|
||||||
|
'hello 1 456 7.89',
|
||||||
|
$this->render('<% loop $Foo %>$Me<% end_loop %>', $data)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user