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

API Refactor template layer into its own module #11405

Draft
wants to merge 1 commit into
base: 6
Choose a base branch
from

Conversation

GuySartorelli
Copy link
Member

Includes the following large-scale changes:

  • Impoved barrier between model and view layers
  • Improved casting of scalar to relevant DBField types
  • Improved capabilities for rendering arbitrary data in templates

Issue

@GuySartorelli GuySartorelli marked this pull request as draft September 26, 2024 04:22
@GuySartorelli GuySartorelli force-pushed the pulls/6/refactor-template-layer branch 3 times, most recently from 649a6fc to da2ebdc Compare September 27, 2024 02:34
@GuySartorelli GuySartorelli marked this pull request as ready for review September 27, 2024 02:37
@GuySartorelli GuySartorelli marked this pull request as draft September 27, 2024 02:37
@GuySartorelli GuySartorelli force-pushed the pulls/6/refactor-template-layer branch 15 times, most recently from 2b00a05 to fff294a Compare October 2, 2024 03:20
@GuySartorelli GuySartorelli force-pushed the pulls/6/refactor-template-layer branch from fff294a to ad22082 Compare October 2, 2024 03:47
@GuySartorelli GuySartorelli force-pushed the pulls/6/refactor-template-layer branch 8 times, most recently from 3687523 to 6af9801 Compare October 10, 2024 01:04
* Return the default "casting helper" for use when no explicit casting helper is defined.
* This helper will be a subclass of DBField. See castingHelper()
*/
protected function defaultCastingHelper(string $field): string
Copy link
Member Author

Choose a reason for hiding this comment

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

This method was also an early addition to CMS 6 (used to just get the default_cast config directly from castingHelper), so we don't need to deprecate this either.

Comment on lines -523 to -526
public function __toString(): string
{
return (string)$this->forTemplate();
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Exists on the superclass (which actually does exactly this anyway) so no need to deprecate.

Copy link
Member

Choose a reason for hiding this comment

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

I left some comments about the use of ModelData::__toString() which I'm not really a fan of

Includes the following large-scale changes:
- Impoved barrier between model and view layers
- Improved casting of scalar to relevant DBField types
- Improved capabilities for rendering arbitrary data in templates
Copy link
Member

@emteknetnz emteknetnz left a comment

Choose a reason for hiding this comment

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

I'm really unsure how central to all of this working the implements Stringable and __toString() and the ModelData::__toString() now rendering a template is, could you please clarify what it's all for

src/Control/Controller.php Show resolved Hide resolved
src/Control/Email/Email.php Show resolved Hide resolved
@@ -14,7 +15,7 @@
*
* @see GridField
*/
class GridState extends HiddenField
class GridState extends HiddenField implements Stringable
Copy link
Member

Choose a reason for hiding this comment

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

Is there actually a reason why we're adding implements Stringable all over the place? Or is this unrelated refactoring?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's unrelated refactoring and recommended best practice when the __toString() method is implemented.
When the CMS 5 PRs are merged I'll be annotating these PRs with that sort of context.

Copy link
Member

@emteknetnz emteknetnz Oct 15, 2024

Choose a reason for hiding this comment

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

A large PR like this is really not the best PR to be doing unrelated refactoring of this nature

Just having a look at some of the things that got implements Stringable added to them, I can't help thinking we'd be better off converting the __tostring() methods to regular public methods and updating whatever's calling them so it's more obvious what's happening, rather than using a magic method. Not saying we should do that in this PR (we shouldn't).

Copy link
Member Author

@GuySartorelli GuySartorelli Oct 15, 2024

Choose a reason for hiding this comment

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

I'm not sure if you're asking me to change something? Even if you are I think it doesn't affect the CMS 5 PRs, right? Or is this just now a continuation of the discussion in #11405 (comment)?

@@ -305,12 +306,18 @@ public function exists(): bool
return true;
}

public function __toString(): string
{
return $this->forTemplate();
Copy link
Member

Choose a reason for hiding this comment

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

Won't this risk kind of drastically changing somethings rather negatively, possibly some parts of the debugging experience?

We exactly are we calling forTemplate() in __toString()? This just seems wrong somehow, like it's a misuse of __toString()?

Copy link
Member Author

Choose a reason for hiding this comment

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

Won't this risk kind of drastically changing somethings rather negatively, possibly some parts of the debugging experience?

If someone is relying on (string) $model instead of get_class($model) they're relying on very flaky behaviour which we already don't respect in DBField which does exactly what this method is doing.
Doing this here accomplishes the following:

  1. Stops people relying on string casting to check the class name, which is flaky at best and breaks down as soon as a subclass wants to return something else from this method
  2. Makes this method's usage more consistent across subclasses - it's taking what was already happening in DBField and making it standard for all models.

We exactly are we calling forTemplate() in __toString()? This just seems wrong somehow, like it's a misuse of __toString()?

https://www.php.net/manual/en/stringable.tostring.php "Returns the string representation of the object."

This method is specifically for this purpose. The rendered markup of a model is the string representation of that model.

This is what other template rendering engines will call to try to get the textual markup that should be injected into templates.

What do you think this method should be used for instead of this?

Copy link
Member

Choose a reason for hiding this comment

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

If someone is relying on (string) $model instead of get_class($model) they're relying on very flaky behaviour which we already don't respect in DBField which does exactly what this method is doing.

I'm not sure how that's relevant. By itself that's not a good reason to implement a magic method here.

it's taking what was already happening in DBField and making it standard for all models.

Having a look at that code, while that does call DBField::forTemplate(), ultimately all that's doing is calling DBField::getValue() which won't be rendering any HTML

The rendered markup of a model is the string representation of that model.

I'm not sure that is correct? While that makes sense in a template context, ModelData is not a class that's exclusively used in templates. Wasn't that why we renamed ViewableData to ModelData, as lots of ViewableData subclasses weren't exclusively used in a template context?

I might be making an incorrect assumption, though won't ModelData::forTemplate() be rendering HTML is some cases, which will be out of context and unhelpful some of the time?

I also just generally disklike magic methods as it's really not clear what's going on. Do we even need __toString() here? Can we not simply just update whatever "calls" thing to simply use ->forTemplate() instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure how that's relevant. By itself that's not a good reason to implement a magic method here.

The method was already implemented. I'm just changing what it returns.

Having a look at that code, while that does call DBField::forTemplate(), ultimately all that's doing is calling DBField::getValue() which won't be rendering any HTML

I don't understand your point here... is your problem with the implementation of ModelData::__toString() that it returns HTML markup?

won't ModelData::forTemplate() be rendering HTML is some cases

Yes.

which will be out of context and unhelpful some of the time?

In cases where that's not helpful, people probably shouldn't be casting the object to a string. You cast something as a string when you want the string representation of it, usually to present to the end user. In what case would you do that for ModelData outside of presenting it in the browser?

In an API, for example, you'd be sending individual fields, probably as JSON, not sending a string representation of the whole model.

I also just generally disklike magic methods

The method was already there, so if your problem is with the method in general this isn't the place to discuss it.

Copy link
Member Author

Choose a reason for hiding this comment

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

This doesn't affect the CMS 5 PRs in any case. So feel free to continue this discussion, but it shouldn't block those PRs from being merged.

Copy link
Member Author

@GuySartorelli GuySartorelli Oct 15, 2024

Choose a reason for hiding this comment

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

The abstraction I've done doesn't, in general, rely on this change to __toString(). I just wanted to make this method more standard across ModelData and dissuade people from relying on it for checking the class since that's a really bad way to check the class.

Copy link
Member

@emteknetnz emteknetnz Oct 15, 2024

Choose a reason for hiding this comment

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

Can we revert it to how it was then? Whether or not it's a good idea, it seems like it's just entirely unrelated to this PR and it's kind of just derailing the actual peer review.

Could also please remove all of the added implements Stringable stuff too, they don't belong in the PR either. My preference for those would be to get rid of the __toString() methods though that's a discussion for an entirely different card

src/View/SSViewer.php Show resolved Hide resolved
src/View/SSViewer.php Show resolved Hide resolved
use Stringable;
use Traversable;

class ViewLayerData implements IteratorAggregate, Stringable
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
class ViewLayerData implements IteratorAggregate, Stringable
class TemplateData implements IteratorAggregate, Stringable

Is TemplateData an accurate name? ViewLayerData is kind of weird and makes me just want to call it ViewableData, which we shouldn't do :-) We don't call things ModelLayerData so seems like we shouldn't include the word 'Layer' here either

Copy link
Member Author

Choose a reason for hiding this comment

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

It can be used anywhere in the view layer, it doesn't have to be restricted to templates.

We don't call things ModelLayerData so seems like we shouldn't include the word 'Layer' here either

That's because it is the data that represents a model, it's not data for the model layer.
This, on the other hand, is data for the view layer, it doesn't represent the view or the entirety of the view layer.

All of that said, I really don't feel strongly about it, so if you want me to call it TemplateData I can do that. Just know that makes it harder to justify using it for other areas in the view layer later if we want to.

Copy link
Member

Choose a reason for hiding this comment

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

"View layer" is not a term that I've seen previously used. What specifically makes up the "view layer"?

Copy link
Member Author

@GuySartorelli GuySartorelli Oct 15, 2024

Choose a reason for hiding this comment

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

It's often called the "presentation layer" which you've probably heard Jackson reference a few times. It's basically any part of "presenting" data to the end user. So we could use this class to wrap data inside GridField in order to help generate the GridField markup, for example.

throw new InvalidArgumentException('$data must not be null');
}
if ($data instanceof ViewLayerData) {
$data = $data->data;
Copy link
Member

Choose a reason for hiding this comment

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

->data is a private property?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it's a private property in this class which means this class can access it.

src/View/TemplateEngine.php Show resolved Hide resolved
@GuySartorelli
Copy link
Member Author

I'll make the requested changes after the CMS 5 PRs have been merged and I can rebase on top of them.

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.

2 participants