-
Notifications
You must be signed in to change notification settings - Fork 61
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
API Scaffolders can now be more expressive when registering fields #188
API Scaffolders can now be more expressive when registering fields #188
Conversation
Also fixes an issue where it was still asserting that fields existed on the dataobject when you might be using a custom resolver
4daa1ae
to
ef1167b
Compare
ef1167b
to
e50694e
Compare
I feel like I'm pretty keen to work on making this more intuitive. I might be able to come up with a BC way to implement this before 4.3 but for now this is ready to go. Hopefully it's acceptable as is, although it feels slightly hacky with the |
I generally really like this. I think it's something we've badly needed for some time, but what's missing for me, and IMO blocking the merge, is a YAML counterpart to this, unless I'm missing it. Ultimately we want YAML to be the primary way of scaffolding, and the procedural approach as a backdoor. |
Sounds fair. I'll try and get some time to address this before Friday. |
The Key being that for getters, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I said some mean things about the code.
I haven't tried running it locally yet. Let me know if you would like me to run it before addressing the feedback.
src/FieldDefinition.php
Outdated
use GraphQL\Type\Definition\Type; | ||
use SilverStripe\GraphQL\Scaffolding\Extensions\TypeCreatorExtension; | ||
|
||
class FieldDefinition |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we include some docs about what the purpose of this class is?
I'm wondering if it would make sense to define a matching interface for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no unit test for this class.
src/FieldDefinition.php
Outdated
/** | ||
* @return string | ||
*/ | ||
public function getDescription() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want matching setters for description, type and resolver?
@@ -102,27 +104,38 @@ public function addFields(array $fieldData) | |||
foreach ($fieldData as $k => $data) { | |||
$assoc = !is_numeric($k); | |||
$name = $assoc ? $k : $data; | |||
$field = ArrayData::create( | |||
[ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to update the example in the function doc to reflect that we now accept a FieldDefinition as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use ArrayLib::is_assoc()
for consistency.
@@ -173,7 +186,9 @@ public function removeField($field) | |||
*/ | |||
public function removeFields(array $fields) | |||
{ | |||
$this->fields = $this->fields->exclude('Name', $fields); | |||
foreach ($fields as $field) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it only works off the key. Maybe update the doc to make it clear that it expect a list key names, otherwise some people might get confused and try to pass it a FieldDefinition
object.
@@ -211,7 +226,7 @@ public function getNestedQueries() | |||
*/ | |||
public function setFieldDescription($field, $description) | |||
{ | |||
$existing = $this->fields->find('Name', $field); | |||
$existing = isset($this->fields[$field]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a @throws
statement in the doc?
$this->getDataObjectClass() | ||
) | ||
); | ||
foreach ($this->fields as $fieldName => $data) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is getting kind of big and difficult to follow. Can we split it up into smaller private methods?
* @param ViewableData $instance | ||
* @param $fieldName | ||
*/ | ||
public function assertValidFieldName(ViewableData $instance, $fieldName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entire point of this method is to throw an exception. We should include a @throws
statement.
[ | ||
|
||
// If we're given a field definition we can accept that interface | ||
if ($data instanceof FieldDefinition) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we update the unit test in tests/Scaffolding/Scaffolders/DataObjectScaffolderTest.php
to test FieldDefinition
?
* @return $this | ||
*/ | ||
public function addField($field, $description = null) | ||
public function addField($field, $definition = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we update the unit test in tests/Scaffolding/Scaffolders/DataObjectScaffolderTest.php
to test FieldDefinition
?
@@ -645,6 +663,20 @@ protected function nestedConnections() | |||
return $queries; | |||
} | |||
|
|||
protected function assertValidFieldData($data, $fieldName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document this method?
'Description' => $description | ||
] | ||
) | ||
$this->fields[$field] = ArrayData::create( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be its own subclass of ArrayData
. That way you can do your own validation, getters/setters, etc, and everything is more expressive.
$target->addField($field->Name, $field->Description); | ||
$arg = $field->Description; | ||
// Work around `ArrayData` not supporting objects as values (unless they extend "ViewableData") | ||
if ($field->hasField('Definition')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to me you wouldn't have to do this hack if you created your own ArrayData
subclass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I don't even think it should be a subclass of ArrayData as it's quite opinionated around holding objects that extend ViewableData
.
Thanks for the reviews guys! I've blocked a couple of hours out tomorrow afternoon to run over this some more so I'll try to address everything then. |
…ield definitions - addField now accepts only a FieldDefinition - scaffoldField can be used to auto-scaffold a FieldDefinition from just a field name - addFields has only been augmented to support FieldDefinitions, and scaffolding a FieldDefinition from an array of data - Yaml config can now be used to cast fields differently from how a DataObject normally casts it This can be useful if you want to expose a method rather than a field as you can define casting (rather than using the default string casting) - Cleaned up some code in the scaffolder
I've added a commit - some details on the change are in the commit message. I refactored this PR so that the only way that fields are maintained within I still need to increase the test coverage which I'll try to do before next week but I appreciate any comments you guys may have in the meantime. |
Also this is definitely breaking changes now :( - And isn't actually a dependency for the CCs elemental work in retrospect - We can drop it from the 4.3 release milestone (although it'd be awesome to get this into the new 3.0 release). |
Does this pull request resolve #162 ? If so, can you connect the two? |
I guess it probably does. This has breaking changes though and it's not high on my list to address this right now. |
I don't have access to this repo for some reason so I can't link PR (nor edit the issue to mention a PR) |
Core team should, I've fixed that |
@ScopeyNZ Do you still have work to do on this? We've got it in the blocked column on the OS board. |
I can't remember where I left it. Looks like there's at least a failed test to resolve. I'll put aside some time this week to look over it. |
I hate to take this conversation in a totally different direction, but I'm wondering if, since it appears version 4 will use generated code from a dev task, maybe we should rely on statically analysed type hints for this? Option A: Merge this into 3 only, with a new static analysis implementation in 4 |
Considering that the API is changing so quickly and we're jumping to new majors so often I don't think it's worth increasing maintenance burden in the I still haven't found the time to run over this PR - it's not currently relevant for me. |
Let's close for now. We can reopen later if we find it's relevant. |
This adds the ability to register a new
FieldDefinition
when adding fields with a scaffolder. This means you can explicitly specify the type AND the resolver for the field rather than relying on it's resolution through the field type of the field on the dataobject.Part of the reasoning for this PR is it allows scaffolders to define types (ie. boolean, text, int) rather than using
->obj
which falls back to "text" for everything that has no casting (or$db
) defined. Eg:This PR also tidies up how the fields are retained within the scaffolder, rather than being a non-associative array and then making sure the array is distinct on the field name, I updated it to just be associative by field name. Slight performance gain I suspect.
Fixes #162