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

RFC: True MVC blocks using DTO's #1008

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft

Conversation

defunctl
Copy link
Collaborator

What does this do/fix?

A proof of concept for a single block to show the possibility of true MVC blocks.

This:

  1. Heavily reduces the boilerplate required to make a block.
  2. Retains type hinting for fields using Data Transfer Objects for the models, just match the properties to the fields and the system does the rest.
  3. Seriously reduces the size of controllers, now we only need to inject the block model and that's it. This will make adding new fields much quicker and easier to implement.
  4. Uses PHP-DI's autowiring, allowing controllers to inject other dependencies when you need to make more complex blocks or bring in external classes to do more work.

TODO

  • Update tribe-libs generators.
  • Move dependencies to their proper libraries.

QA

Screenshots/video:

Icon Grid Edit Screen:
image

Icon Grid Frontend:
image

Tests

Does this have tests?

  • Yes
  • Not yet...
  • No, I need help figuring out how to write the tests.

@defunctl defunctl added the WIP label Jun 25, 2022

class Icon_Grid_Controller extends Abstract_Controller {

public const ATTRS = 'attrs';
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Look at everything that got removed from this controller! 😍


class Icon_Grid_Model extends Block_Model {

public string $title = '';
Copy link
Collaborator Author

@defunctl defunctl Jun 25, 2022

Choose a reason for hiding this comment

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

These class properties should match the field names you defined in your Block_Config class, then they will get auto populated with the data.

Add a new field to the Block_Config, add it here and then it's automatically available in the controller's $this->model instance.

No more adding defines, controller properties or defaults, that's all done here now!

'category' => 'common', // core categories: common, formatting, layout, widgets, embed
'supports' => [
'align' => false,
'anchor' => true,
'html' => false,
],
'model' => Icon_Grid_Model::class,
'view' => 'icon_grid',
'controller' => Icon_Grid_Controller::class,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note: this is where you'd define your block's Model, View and Controller. Of course, we'd update the block generator to do this automatically.

I'm open to better ways for this as it's ideal, but it will work and provide flexibility if absolutely required.


protected function required(): array {
return [
self::CLASSES => [
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Still need to implement a system that does this.

* @throws \DI\DependencyException
* @throws \DI\NotFoundException
*/
public function render_template( array $block, string $content, bool $is_preview, int $post_id, ?WP_Block $WP_block, array $context = [] ): void {
Copy link
Collaborator Author

@defunctl defunctl Jun 25, 2022

Choose a reason for hiding this comment

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

This is where the model and controller are instantiated because it's the only time the data we need is available.

All the magic happens here, fields get populated etc...this still needs some tweaks and clean up but it shows it will work and once I implement and interface for it, we can even replace it on a per project basis if the situation calls for it.

We can fine tune control of what gets passed to the view as well which was a thing that is badly missing from get_template_part(), but standard blocks really just need the controller.

@dpellenwood
Copy link
Contributor

dpellenwood commented Jun 27, 2022

Thanks for mocking this up, @defunctl . The only concern/question I have relates to being able to use a block template/view with data other than an ACF block's registered fields. For example, We often end up with patterns where a Hero Block and a Post Subheader use the same data, layout options, etc but the latter's data source is ACF fields defined for a post type rather than via a block. Can you help me understand how we might accomplish that with this architecture? (Ideally not having to duplicate the template file, Controller classes, CSS/JS, etc.)

@defunctl
Copy link
Collaborator Author

@dpellenwood can definitely look into that, can you point me to code where we're doing that now so I can have a look?

@dpellenwood
Copy link
Contributor

@dpellenwood can definitely look into that, can you point me to code where we're doing that now so I can have a look?

Sure thing. Harvard Public Health has a recent example: Hero Block & Post "Subheader" built by calling the Hero Block template and setting up the necessary data via Post meta fields.

@defunctl
Copy link
Collaborator Author

@dpellenwood can definitely look into that, can you point me to code where we're doing that now so I can have a look?

Sure thing. Harvard Public Health has a recent example: Hero Block & Post "Subheader" built by calling the Hero Block template and setting up the necessary data via Post meta fields.

Awesome, I'll check it out this eve and get back to you.

@defunctl
Copy link
Collaborator Author

defunctl commented Jun 28, 2022

@dpellenwood see the updated code pushes. I converted the Hero block into this MVC Field Model system and am now using it in the Single Controller/View, similar to your code example. It still needs work, but definitely shows the basic idea once it's been polished up some.

Screenshot of the hero block being used inside the Single Controller/View:

image

@dpellenwood
Copy link
Contributor

@dpellenwood see the updated code pushes. I converted the Hero block into this MVC Field Model system and am now using it in the Single Controller/View, similar to your code example. It still needs work, but definitely shows the basic idea once it's been polished up some.

Lovely. Makes total sense to me. Thanks for demoing it!

@ChrisMKindred ChrisMKindred marked this pull request as ready for review June 30, 2022 15:05
@ChrisMKindred ChrisMKindred marked this pull request as draft June 30, 2022 15:06
@ChrisMKindred
Copy link
Contributor

I know this PR is still a WIP but I am going to brain dump some thoughts after a scan of everything:

  • I really like having everything about the block in a single place (Model, Config, etc...). Everything is much more easily ported to other projects.
  • Can we move the blocks into the plugin? I am not liking it being all in the theme and if we are in Multisite now we are forced to use child themes or double up the code...
  • I'd love to be able to filter the view path. Could I filter the view path to load either out of the plugin folder, or just set it up to run a locate_template for the current active theme?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants