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

FIX Provide alternatives to session for storing GridField_FormAction state #8627

Conversation

ScopeyNZ
Copy link
Contributor

@ScopeyNZ ScopeyNZ commented Nov 21, 2018

Fixes #8589

Currently the session is used to store GridField action state. This PR doesn't change that by default. I've abstracted the storage method into an interface and provided an alternate that will just pass down the state to the DOM and then route it back when a request is built to perform an action.

I tried to go down the path of invalidating this action state but it's hard to tell exactly when the user navigates away from the GridField - especially as multiple GridFields can exist on one page, and some are loaded through AJAX requests.

Relies on silverstripe/silverstripe-admin#757

public function load($id)
{
// Check the request
return json_decode($this->request->requestVar('ActionState'), true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth casting the requestVar as a string, since json_decode(true, true) would return int(1). Maybe also update the PHPdoc to be explicit about what it can return instead of "mixed"

/**
* @param HTTPRequest $request
*/
public function __construct(HTTPRequest $request);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in two minds about whether it's a good idea to have the constructor in this interface

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just read this and now believe we should remove the constructor: https://www.alainschlesser.com/including-constructor-interface/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah at the time I wasn't really happy with this code either. I need the request in both implementations of the interface and I don't think it's a good pattern to fetch the request from global state (Controller::curr or Injector). I'll just suck it up though; remove the constructor and use Controller::curr.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could always give it a setter and use injector calls to provide it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or put the constructor in an abstract parent class - separating the implementation from the interface entirely

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah having the constructor in an abstract is a possibility but then I'll have to do some introspection on the type of object configured for the interface which sounds pretty hard. I think I'll make an abstract but just use private static $dependencies. This would be much better with constructor DI 😉

* Load state for a given ID
*
* @param string $id
* @return mixed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be more specific than this - are we expecting an array?

@ScopeyNZ ScopeyNZ self-assigned this Nov 21, 2018
@robbieaverill
Copy link
Contributor

There are also some minimal docs that could use a nudge too: https://docs.silverstripe.org/en/4/developer_guides/forms/field_types/gridfield/#saving-the-gridfield-state

@ScopeyNZ
Copy link
Contributor Author

Thanks for the heads up. The GridField state is distinct form GridField_FormAction state. Something that took me a few moments to realise when I started this work 😅 .

I'll add a section about configuring it to use attributes/request params instead of session.

Otherwise I'm done with implementation now.

@ScopeyNZ
Copy link
Contributor Author

Docs pushed 🙂

Copy link
Contributor

@maxime-rainville maxime-rainville left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look to make sure Session wasn't being referenced by GrdiFields outside of SessionStore.

  • Needs a changelog entry.
  • If practical, could we have some unit test for the following:
    • SessionStore
    • AttributeStore
    • GridField_FormAction::getAttribute()

Didn't quite get to run locally to confirm the change work as expected. I'll do that tomorrow.

/**
* Stores GridField action state on an attribute on the action and then analyses request parameters to load it back
*/
class AttributeStore extends AbstractRequestAwareStore implements StateStore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't AbstractRequestAwareStore be the class implementing the StateStore interface, instead of its child class? Is there a case where you would want a RequestAwareStore that wasn't a StateStore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair call. I'll change this and add some tests hopefully tomorrow.

Copy link
Contributor

@maxime-rainville maxime-rainville left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me happy. SessionStore is still implementing StateStore. Feel free to address this or not.

/**
* Stores GridField action state in the session in exactly the same way it has in the past
*/
class SessionStore extends AbstractRequestAwareStore implements StateStore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AbstractRequestAwareStore is implementing StateStore now. This implements statement is kind of redundant.

Copy link
Contributor

@maxime-rainville maxime-rainville left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still happy.

@maxime-rainville
Copy link
Contributor

maxime-rainville commented Dec 2, 2018

@ScopeyNZ The Travis fail isn't related to behat. It's something about the History Viewer controller. Is this related to this PR?

@ScopeyNZ
Copy link
Contributor Author

ScopeyNZ commented Dec 2, 2018

I think this was just resolved in CMS. Rerunning the job.

@maxime-rainville
Copy link
Contributor

Cool ... I'll merge on green.

@maxime-rainville maxime-rainville merged commit 731ef00 into silverstripe:4.3 Dec 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants