mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
NEW: getByLink plugin allows arrays of links (#2666)
* NEW: getByLink plugin allows arrays of links * Allow configurable resolver * Remove unused exception tag * Refactor to preserve API backward compat * Unit test * Add plugin to readOne * Fix test * add new test
This commit is contained in:
parent
1794eee0ea
commit
f3a76ccf2d
@ -7,3 +7,7 @@ modelConfig:
|
|||||||
plugins:
|
plugins:
|
||||||
getByLink:
|
getByLink:
|
||||||
after: filter
|
after: filter
|
||||||
|
readOne:
|
||||||
|
plugins:
|
||||||
|
getByLink:
|
||||||
|
after: filter
|
||||||
|
@ -12,6 +12,7 @@ use SilverStripe\GraphQL\Schema\Exception\SchemaBuilderException;
|
|||||||
use SilverStripe\GraphQL\Schema\Field\ModelQuery;
|
use SilverStripe\GraphQL\Schema\Field\ModelQuery;
|
||||||
use SilverStripe\GraphQL\Schema\Interfaces\ModelQueryPlugin;
|
use SilverStripe\GraphQL\Schema\Interfaces\ModelQueryPlugin;
|
||||||
use SilverStripe\GraphQL\Schema\Schema;
|
use SilverStripe\GraphQL\Schema\Schema;
|
||||||
|
use SilverStripe\ORM\ArrayList;
|
||||||
use SilverStripe\ORM\DataList;
|
use SilverStripe\ORM\DataList;
|
||||||
|
|
||||||
if (!interface_exists(ModelQueryPlugin::class)) {
|
if (!interface_exists(ModelQueryPlugin::class)) {
|
||||||
@ -29,7 +30,18 @@ class LinkablePlugin implements ModelQueryPlugin
|
|||||||
* @var string
|
* @var string
|
||||||
* @config
|
* @config
|
||||||
*/
|
*/
|
||||||
private static $field_name = 'link';
|
private static $single_field_name = 'link';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @config
|
||||||
|
*/
|
||||||
|
private static $list_field_name = 'links';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $resolver = [__CLASS__, 'applyLinkFilter'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
@ -43,7 +55,6 @@ class LinkablePlugin implements ModelQueryPlugin
|
|||||||
* @param ModelQuery $query
|
* @param ModelQuery $query
|
||||||
* @param Schema $schema
|
* @param Schema $schema
|
||||||
* @param array $config
|
* @param array $config
|
||||||
* @throws SchemaBuilderException
|
|
||||||
*/
|
*/
|
||||||
public function apply(ModelQuery $query, Schema $schema, array $config = []): void
|
public function apply(ModelQuery $query, Schema $schema, array $config = []): void
|
||||||
{
|
{
|
||||||
@ -52,43 +63,42 @@ class LinkablePlugin implements ModelQueryPlugin
|
|||||||
if ($class !== SiteTree::class && !is_subclass_of($class, SiteTree::class)) {
|
if ($class !== SiteTree::class && !is_subclass_of($class, SiteTree::class)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Schema::invariant(
|
$singleFieldName = $this->config()->get('single_field_name');
|
||||||
!$query->isList(),
|
$listFieldName = $this->config()->get('list_field_name');
|
||||||
'Plugin %s cannot be applied to queries that return lists. Query "%s" is a list',
|
$fieldName = $query->isList() ? $listFieldName : $singleFieldName;
|
||||||
static::getIdentifier(),
|
$type = $query->isList() ? '[String]' : 'String';
|
||||||
$query->getName()
|
$query->addArg($fieldName, $type);
|
||||||
|
$query->addResolverAfterware(
|
||||||
|
$config['resolver'] ?? static::config()->get('resolver')
|
||||||
);
|
);
|
||||||
$fieldName = $this->config()->get('field_name');
|
|
||||||
|
|
||||||
$query->addArg($fieldName, 'String');
|
|
||||||
$query->addResolverAfterware([static::class, 'applyLinkFilter']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $obj
|
|
||||||
* @param array $args
|
|
||||||
* @param array $context
|
* @param array $context
|
||||||
* @param ResolveInfo $info
|
* @return callable
|
||||||
* @param callable $done
|
|
||||||
* @return SiteTree|DataList|null
|
|
||||||
*/
|
*/
|
||||||
public static function applyLinkFilter(
|
public static function applyLinkFilter($obj, array $args, array $context, ResolveInfo $info)
|
||||||
$obj,
|
{
|
||||||
array $args,
|
$singleFieldName = static::config()->get('single_field_name');
|
||||||
array $context,
|
$listFieldName = static::config()->get('list_field_name');
|
||||||
ResolveInfo $info,
|
$filterLink = $args['filter'][$singleFieldName] ?? ($args['filter'][$listFieldName] ?? null);
|
||||||
callable $done
|
$argLink = $args[$singleFieldName] ?? ($args[$listFieldName] ?? null);
|
||||||
) {
|
$linkData = $filterLink ?: $argLink;
|
||||||
$fieldName = static::config()->get('field_name');
|
if (!$linkData) {
|
||||||
$filterLink = $args['filter'][$fieldName] ?? null;
|
return $obj;
|
||||||
$argLink = $args[$fieldName] ?? null;
|
|
||||||
$filterLink = $filterLink ?: $argLink;
|
|
||||||
|
|
||||||
if ($filterLink) {
|
|
||||||
$done();
|
|
||||||
return SiteTree::get_by_link($filterLink);
|
|
||||||
}
|
}
|
||||||
|
// Normalise to an array for both cases. The readOne operation will get
|
||||||
|
// ->first() run on it by the firstResult plugin.
|
||||||
|
$links = is_array($linkData) ? $linkData : [$linkData];
|
||||||
|
|
||||||
return $obj;
|
$result = ArrayList::create();
|
||||||
|
|
||||||
|
foreach ($links as $link) {
|
||||||
|
$page = SiteTree::get_by_link($link);
|
||||||
|
if ($page) {
|
||||||
|
$result->push($page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
112
tests/php/GraphQL/LinkablePluginTest.php
Normal file
112
tests/php/GraphQL/LinkablePluginTest.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\CMS\Tests\GraphQL;
|
||||||
|
|
||||||
|
use SilverStripe\AssetAdmin\Tests\GraphQL\FakeResolveInfo;
|
||||||
|
use SilverStripe\CMS\GraphQL\LinkablePlugin;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\GraphQL\Schema\DataObject\DataObjectModel;
|
||||||
|
use SilverStripe\GraphQL\Schema\Field\ModelQuery;
|
||||||
|
use SilverStripe\GraphQL\Schema\Schema;
|
||||||
|
use SilverStripe\GraphQL\Schema\SchemaConfig;
|
||||||
|
|
||||||
|
class LinkablePluginTest extends SapphireTest
|
||||||
|
{
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
if (!class_exists(Schema::class)) {
|
||||||
|
$this->markTestSkipped('GraphQL 4 test ' . __CLASS__ . ' skipped');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $list
|
||||||
|
* @dataProvider provideApply
|
||||||
|
*/
|
||||||
|
public function testApply(bool $list)
|
||||||
|
{
|
||||||
|
$query = new ModelQuery(
|
||||||
|
new DataObjectModel(SiteTree::class, new SchemaConfig()),
|
||||||
|
'testQuery'
|
||||||
|
);
|
||||||
|
$query->setType($list ? '[SiteTree]' : 'SiteTree');
|
||||||
|
|
||||||
|
$plugin = new LinkablePlugin();
|
||||||
|
$plugin->apply($query, new Schema('test'));
|
||||||
|
$args = $query->getArgs();
|
||||||
|
$field = $list ? 'links' : 'link';
|
||||||
|
$this->assertArrayHasKey($field, $args);
|
||||||
|
$this->assertEquals($list ? '[String]' : 'String', $args[$field]->getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResolver()
|
||||||
|
{
|
||||||
|
$page = SiteTree::create([
|
||||||
|
'Title' => 'Test page',
|
||||||
|
'URLSegment' => 'test-page',
|
||||||
|
'ParentID' => 0,
|
||||||
|
]);
|
||||||
|
$page->write();
|
||||||
|
$page->publishRecursive();
|
||||||
|
|
||||||
|
$page = SiteTree::create([
|
||||||
|
'Title' => 'Other test page',
|
||||||
|
'URLSegment' => 'other-test-page',
|
||||||
|
'ParentID' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$page->write();
|
||||||
|
$page->publishRecursive();
|
||||||
|
|
||||||
|
$result = LinkablePlugin::applyLinkFilter('test', ['link' => 'test-page'], [], new FakeResolveInfo());
|
||||||
|
$this->assertTrue($result->exists());
|
||||||
|
$this->assertEquals('Test page', $result->first()->Title);
|
||||||
|
|
||||||
|
$result = LinkablePlugin::applyLinkFilter('test', ['links' => ['test-page']], [], new FakeResolveInfo());
|
||||||
|
$this->assertTrue($result->exists());
|
||||||
|
$this->assertEquals('Test page', $result->first()->Title);
|
||||||
|
|
||||||
|
$result = LinkablePlugin::applyLinkFilter(
|
||||||
|
'test',
|
||||||
|
['links' => ['test-page', 'other-test-page']],
|
||||||
|
[],
|
||||||
|
new FakeResolveInfo()
|
||||||
|
);
|
||||||
|
$this->assertTrue($result->exists());
|
||||||
|
$this->assertCount(2, $result);
|
||||||
|
$titles = $result->column('Title');
|
||||||
|
$this->assertTrue(in_array('Test page', $titles));
|
||||||
|
$this->assertTrue(in_array('Other test page', $titles));
|
||||||
|
|
||||||
|
$result = LinkablePlugin::applyLinkFilter(
|
||||||
|
'test',
|
||||||
|
['links' => ['test-page', 'fail-page']],
|
||||||
|
[],
|
||||||
|
new FakeResolveInfo()
|
||||||
|
);
|
||||||
|
$this->assertTrue($result->exists());
|
||||||
|
$this->assertCount(1, $result);
|
||||||
|
$this->assertEquals('Test page', $result->first()->Title);
|
||||||
|
|
||||||
|
$result = LinkablePlugin::applyLinkFilter('test', ['link' => 'fail-page'], [], new FakeResolveInfo());
|
||||||
|
$this->assertFalse($result->exists());
|
||||||
|
|
||||||
|
$result = LinkablePlugin::applyLinkFilter('test', ['links' => ['fail-page']], [], new FakeResolveInfo());
|
||||||
|
$this->assertFalse($result->exists());
|
||||||
|
|
||||||
|
$result = LinkablePlugin::applyLinkFilter('test', ['notAnArg' => 'fail'], [], new FakeResolveInfo());
|
||||||
|
$this->assertEquals('test', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function provideApply()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[true],
|
||||||
|
[false],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user