Jump to content

Wireframe


teppo

Recommended Posts

Hey folks!

I'm happy to finally introduce a project I've been working on for quite a while now: it's called Wireframe, and it is an output framework for ProcessWire.

Note that I'm posting this in the module development area, maily because this project is still in rather early stage. I've built a couple of sites with it myself, and parts of the codebase have been powering some pretty big and complex sites for many years now, but this should still be considered a soft launch ?

--

Long story short, Wireframe is a module that provides the "backbone" for building sites (and apps) with ProcessWire using an MVC (or perhaps MVVM... one of those three or four letter acronyms anyway) inspired methodology. You could say that it's an output strategy, but I prefer the term "output framework", since in my mind the word "strategy" means something less tangible. A way of doing things, rather than a tool that actually does things.

Wireframe (the module) provides a basic implementation for some familiar MVC concepts, such as Controllers and a View layer – the latter of which consists of layouts, partials, and template-specific views. There's no "model" layer, since in this context ProcessWire is the model. As a module Wireframe is actually quite simple – not even nearly the biggest one I've built – but there's still quite a bit of stuff to "get", so I've put together a demo & documentation site for it at https://wireframe-framework.com/.

In addition to the core module, I'm also working on a couple of site profiles based on it. My current idea is actually to keep the module very light-weight, and implement most of the "opinionated" stuff in site profiles and/or companion modules. For an example MarkupMenu (which I released a while ago) was developed as one of those "companion modules" when I needed a menu module to use on the site profiles.

Currently there are two public site profiles based on Wireframe:

  • site-wireframe-docs is the demo&docs site mentioned above, just with placeholder content replaced with placeholder content. It's not a particularly complex site, but I believe it's still a pretty nice way to dig into the Wireframe module.
  • site-wireframe-boilerplate is a boilerplate (or starter) site profile based on the docs site. This is still very much a work in progress, but essentially I'm trying to build a flexible yet full-featured starter profile you can just grab and start building upon. There will be a proper build process for resources, it will include most of the basic features one tends to need from site to site, etc.

--

Requirements and getting started:

  • Wireframe can be installed just like any ProcessWire module. Just clone or download it to your site/modules/ directory and install. It doesn't, though, do a whole lot of stuff on itself – please check out the documentation site for a step-by-step guide on setting up the directory structure, adding the "bootstrap file", etc.
    • You may find it easier to install one of the site profiles mentioned above, but note that this process involves the use of Composer. In the case of the site profiles you can install ProcessWire as usual and download or clone the site profile directory into your setup, but after that you should run "composer install" to get all the dependencies – including the Wireframe module – in place.
  • Hard requirements for Wireframe are ProcessWire 3.0.112 and PHP 7.1+. The codebase is authored with current PHP versions in mind, and while running it on 7.0 may be possible, anything below that definitely won't work.

A feature I added just today to the Wireframe module is that in case ProcessWire has write access to your site/templates/ directory, you can use the module settings screen to create the expected directories automatically. Currently that's all, and the module won't – for an example – create Controllers or layouts for you, so you should check out the site profiles for examples on these. (I'm probably going to include some additional helper features in the near future.)

--

This project is loosely based on an earlier project called pw-mvc, i.e. the main concepts (such as Controllers and the View layer) are very similar. That being said, Wireframe is a major upgrade in terms of both functionality and architecture: namespaces and autoloader support are now baked in, the codebase requires PHP 7, Controllers are classes extending \Wireframe\Controller (instead of regular "flat" PHP files), implementation based on a module instead of a collection of drop-in files, etc.

While Wireframe is indeed still in a relatively early stage (0.3.0 was launched today, in case version numbers matter) for the most part I'm happy with the way it works, and likely won't change it too drastically anytime soon – so feel free to give it a try, and if you do, please let me know how it went. I will continue building upon this project, and I am also constantly working on various side projects, such as the site profiles and a few unannounced helper modules.

I should probably add that while Wireframe is not hard to use, it is more geared towards those interested in "software development" type methodology. With future updates to the module, the site profiles, and the docs I hope to lower the learning curve, but certain level of "developer focus" will remain. Although of course the optimal outcome would be if I could use this project to lure more folks towards that end of the spectrum... ?

--

Please let me know what you think – and thanks in advance!

  • Like 31
  • Thanks 8
Link to comment
Share on other sites

I discovered Wireframe on Sunday after exploring the GitHub repo for the ProcessWire Composer Installer project that you mentioned in the latest PW Weekly. The documentation you've written for Wireframe is just awesome ? - engaging, comprehensive and clear.

I have a short question and a long question...

Does it make sense to use Wireframe together with Markup Regions in any way, or would a developer choose Wireframe or Markup Regions but not both?

 

The long question relates to what you said here:

2 hours ago, teppo said:

Although of course the optimal outcome would be if I could use this project to lure more folks towards that end of the spectrum...

I take this to be a reference to the superiority of a MVC approach versus the "default" approach of mixing business logic and UI logic together in a PW template file. I'd like to hear more of your views on this because you didn't talk about it much in the documentation, probably because MVC and the separation of concerns is discussed in plenty of other places around the web. But I was wondering if you think a MVC approach is always the way to go, or if is something you would weigh up the pros and cons of taking into account the parameters of each specific project, e.g. the scale and complexity of the project, if a team will be working on the project, etc.

Personally I use Markup Regions and don't use any separation of business logic and UI logic into different files or folders. From time to time I think about changing to an MVC approach because so many people seem to recommend it, but when I weigh it up I don't see enough advantage versus disadvantage for the kinds of projects I work on. But maybe I'm overlooking something so I'd appreciate your comments.

The main benefits of MVC as I understand it are...

1. If you have a team of people working on a project, maybe with different skill sets (e.g. a front-end dev and a back-end dev), then it lets each person focus on the parts that matter to them. For example the front-end person can focus on the view file without having to see any business logic which might distract or even be unintelligible to them. This totally makes sense to me and if I worked as part of a team this by itself would make an MVC approach worthwhile. But in my case I do everything alone from start to finish - design, front-end, back-end.

2. The business logic doesn't "belong" in the same file as the markup - it's better to keep it separate and it makes it easier to update a site. This seems more contentious to me, and perhaps depends on how much business logic there is. If I have some variable that I'm using within the markup I find it very handy to have the definition/construction of that variable present alongside its usage in output. So I'm not left wondering "what was it that I put into $related_products?" and needing to navigate to some other file to find out.

Now if there were heaps of business logic it might start to feel like clutter within the markup, but when I look at the template files for my projects in most cases there's actually very little business logic present. Maybe that's partly because PW is quite elegant in that you can do a lot with a few lines of code, and partly because most of my projects are not very complex. But even if I do have a lot of business logic I find that placing it at the top of the template file is no problem at all. If I need to work on it I'd rather just scroll up than open another file, and I can use my IDE to collapse any blocks of code that I don't need to focus on. Would you say that MVC is an approach that is more suited to complex projects rather than simple ones?

Things like layouts and partials that are offered through Wireframe are cool, but those are also possible with Markup Regions (layout = _main.php) and built-in PW methods (partials = $files->render).

I have no doubt that Wireframe is a powerful tool, but do you see it as being the right solution for all projects or just some projects? 

  • Like 8
  • Thanks 1
Link to comment
Share on other sites

Hey @Robin S. First of all thanks for your thoughtful message! I'll have to get back to some of your points later, but just wanted to share a few initial thoughts ?

About using Markup Regions and Wirerame together: I honestly don't know. I'm aware on a basic level of how Markup Regions work, but I've never used it. I will definitely dig into the specifics, though – this is an interesting area. On a more general note I've intentionally kept to the "alternate template file" strategy (instead of hooking into Page rendering or something similar) so that Wireframe can be used for some parts of the site (some templates), while other parts use whatever other approach makes sense.

So yeah, you can use them together, I'm just not yet sure to what extent that would make sense... if that makes sense to you? ?

I don't really want to preach the superiority of MVC (or any other three or four letter architecture), but apart from some of the simplest sites I've built – isit.pw has one file with one form, so it for an example doesn't benefit from a whole lot of structure – I do generally prefer to separate "code" from "markup". This is a topic I'd love to cover in more detail later, but your two points summarise very well what it is all about. Project scope (and complexity) and working with or without a team are definitely key factors there. After that it comes down to personal preference.

TL;DR: I wouldn't use Wireframe for everything, but I do think that it fits nicely just about any project.

I often end up with multiple "views" for the same data – not sure if this is really a common need, but it has been in my projects, which is another reason to keep the views as basic (and as markup-y) as possible, and instead move all the business logic, data structuring, etc. somewhere else. Overall I've found that this sort of structure helps me keep things neat so that when things eventually grow and scale it doesn't become a mess, and allows me to make changes and additions with little extra overhead once they are due.

That being said, there's a good reason why Controllers – for an example – are actually optional components in Wireframe. Some of my templates make use of the whole package – Controller, layout, view file(s), and partials – yet others have just a single view file, and that's all. I like that I don't have to use more than I really need, but if I do need more later, I can just bolt it in without a whole lot of refactoring ?

It's true that you can use ProcessWire's built-in features to do everything that Wireframe does. That's exactly what Wireframe itself does: I've tried not to reinvent the wheel where possible, so the View (for an example) is a light-weight wrapper for TemplateFile, etc. Originally the predecessor of this project (pw-mvc) was all about standardising some best practices so that as we (as a team) build a project after project there's some common ground there. In a perfect world you could jump into a new project you've never touched before and instantly see what's going on in there.

It's a bit more than that now, though.

... and this is the point where I realise that my "few initial thoughts" are already getting quite long-winded ?

  • Like 7
Link to comment
Share on other sites

Thanks @teppo. I'm going to spend some time exploring Wireframe to see how it feels compared to my existing approach - I may yet become a convert ?.

Thanks again for creating this tool and doing such a great job on the documentation website.

  • Like 4
Link to comment
Share on other sites

I am currently building first site using Wireframe. I really like it so far. I have almost done with the site (pretty simple one) and I didn't use any controllers... until now when I noticed that homepage shows feed that mixes news and events. Usually nothing too complex (just add both templates to selector), but in this case events are from separate system, pulled from REST API. So this was perfect case where I use controller for:

  • pulling data from external API
  • caching that data, so that we fetch only once in hour
  • merging and sorting it to one PageArray with local news items
  • passing that final PageArray to view file for easy rendering

Without Wireframe I would probably build much of that logic into functions, but I really like that there is "documented place where this kind of stuff should live". 

In future if I (or some other lucky fellow) need to make changes to layout - it is simple and one doesn't have to think about that "wow, all kinds of stuff is happening here", maybe remove that events stuff or add even more sources - I can do it easily without messing with layout. Also this model usually helps to produce clearer code: instead of many ifs (showing little bit different data for events vs. news) while rendering, I have that kind of logic separate from actual markup.

One downside is of course that one have to "understand" the mvc-framework and it's logic. You have to know that there might be controller involved and where it lives for example.

  • Like 6
Link to comment
Share on other sites

On 7/5/2019 at 10:23 PM, Markus (Blue Tomato) said:

Sounds nice, we built some similar (but highly customised on some special internal needs...). But found many very similar things in it. ?

Nice! Would be interesting to hear if you can think of anything particularly cool that your version did and Wireframe doesn't do. Always looking for new ideas ?

Small update to the docs site: added a page for patterns and practices. Currently only one page there (utility classes), but will be adding more as I figure out what should be there. Doesn't make sense to bundle everything with the module or even the site profiles, so needed some place to stash this sort of stuff.

  • Like 2
Link to comment
Share on other sites

Two more)

  1. How is it possible to separate logic for partials from their presentation? I often find the need to prepare content for partials same way as for the views.
  2. Do you have experience of using Wireframe with RepeaterMatrix based content?
  • Like 1
Link to comment
Share on other sites

32 minutes ago, Ivan Gretsky said:

This time you came prepared, @teppo! Great intro post and a cool documentation site. Congratulations!

I did not have time to dig into it (yet). The 1st question that comes to mind is can twig or some other templating language be plugged in for the views?

Thanks, Ivan! ?

I haven't figured out the details yet, but at some point I'm going to look into adding templating language support. For the time being all options are open, so don't really have anything else to share at this point than "I will look into it eventually".

10 minutes ago, Ivan Gretsky said:

Two more)

  1. How is it possible to separate logic for partials from their presentation? I often find the need to prepare content for partials same way as for the views.
  2. Do you have experience of using Wireframe with RepeaterMatrix based content?

1. Currently partials are intended as simple drop-in (include) files with very little logic. I do have some ideas in my backlog that come pretty close to this topic, but I'll have to give them a bit more thought before implementing. Wouldn't want to put something in now, and then regret it right away... ?

I've often preferred render functions, i.e. functions that generate markup, in case I need to bake some logic into smaller bits and pieces of the site. A "RenderUtils" class with multiple render methods would be one solution, and of course you can already use ProcessWire's own features to render a partial: $page->renderValue(), $files->render(), TemplateFile::render(), etc. In the long term perhaps we could add some kind of a helper class to the framework for handling these situations – just not quite sure yet how to approach this.

If you could give me an example or two of what sort of situations you tend to run into, I'd be grateful – would be good to know that I'm actually solving the right problem ?

2. Not Wireframe specifically, but I did build a few rather large sites with pw-mvc and RepeaterMatrix. In those cases I ended up using field templates (/site/templates/fields/) for the bulk of the RepeaterMatrix stuff. Worked quite nicely, really. I'll add a note to write a bit about this to the "Patterns and practices" page.

  • Like 3
Link to comment
Share on other sites

This is just a rough idea, not sure if it's going to go anywhere but I'm actually wondering if I should extend the $partials object a bit?

Currently it's actually a bit silly – basically an "object oriented" way to replace <?php include 'partials/menu/top.php' ?> with <?php include $partials->menu->top ?>. If each property could be used as a function, this would allow us to pass the partial arguments: <?= $partials->menu->meta([ 'description' => 'some text' ]) ?>, etc. Or we could pass in a string, in which case a Controller method would be used to pass the data to the partial.

Again, just thinking out loud here. Not sure if this makes any sense, but I'm kind of liking the idea ?

Another idea I've been toying with would be subcontrollers (or child controllers, or partial controllers, or whatever terminology makes most sense). These could solve situations where you end up needing the same stuff from template to template. You can always create custom base controllers and extend them, but this might allow for easier composition.

  • Like 4
Link to comment
Share on other sites

  • 3 weeks later...
On 7/10/2019 at 1:05 AM, teppo said:

1. Currently partials are intended as simple drop-in (include) files with very little logic. I do have some ideas in my backlog that come pretty close to this topic, but I'll have to give them a bit more thought before implementing. Wouldn't want to put something in now, and then regret it right away... ?

I've often preferred render functions, i.e. functions that generate markup, in case I need to bake some logic into smaller bits and pieces of the site. A "RenderUtils" class with multiple render methods would be one solution, and of course you can already use ProcessWire's own features to render a partial: $page->renderValue(), $files->render(), TemplateFile::render(), etc. In the long term perhaps we could add some kind of a helper class to the framework for handling these situations – just not quite sure yet how to approach this. 

If you could give me an example or two of what sort of situations you tend to run into, I'd be grateful – would be good to know that I'm actually solving the right problem ?

As MVC is about separation of concerns, as you said a number of times yourself, @teppo, I think render functions are not so good here. Because a html/css is mixed too much with php. Logic and presentation together again)

I was talking about something like described here, with dedicated controller and view.

I looked through recent projects for examples of those partials. These are common ones:

  • menus;
  • breadcrumbs;
  • sidebar;
  • repeating forms (any repeating elements really, anything like wordpress widgets / joomla modules).

 

  • Thanks 1
Link to comment
Share on other sites

1 hour ago, Ivan Gretsky said:

As MVC is about separation of concerns, as you said a number of times yourself, @teppo, I think render functions are not so good here. Because a html/css is mixed too much with php. Logic and presentation together again)

I get your point, and agree – to a point. When I first came across the idea of render functions (with that specific name it was some bit of code from Ryan I believe), this was exactly what I thought ?

Since then I've used render-functions in numerous projects, and while in some cases it still feels a bit "dirty", in real world it can be a tremendously helpful strategy and hasn't really caused me any notable headaches so far – quite the opposite. These days I don't have a major issue with code that generates markup as long as it's clearly separated into its own container, i.e. "siloed" properly. While I don't encourage mixing business logic with presentation, this is one of those cases where the line is a bit blurry.

Also, as a minor note I don't think that we should separate logic and presentation based on the language alone. PHP can be used to create "presentational" content, so overall it's more about what you do than how you do it. Something like Twig is supposed to be "purely presentational", but you wouldn't believe the complexity of some of the logic I've seen implemented in/with it. (But this is a topic for another discussion – templating languages vs. using PHP as a templating language, and so on and so forth.)

1 hour ago, Ivan Gretsky said:

I was talking about something like described here, with dedicated controller and view.

Awesome! As far as I can tell this is actually really close to this rough idea I mentioned earlier – or, more precisely, it would be one possible use case for (or variation of) it:

Another idea I've been toying with would be subcontrollers (or child controllers, or partial controllers, or whatever terminology makes most sense)

I'm not entirely sure if it actually needs to be a separate concept ("chunks", or whatever that would be called), but that's not necessarily a bad idea, if it makes the concept easier to grasp. Technically it should already be possible to instantiate a new Controller, provide it with a View file, and then render the output within, say, another Controller. That's what Wireframe itself does for the page, though it all happens automatically.

Another thing that might not be obvious yet (I'll have to check the docs relating to this) is that there are two use cases for views:

  1. They provide alternative ways to render a Page. For an example the default view might render markup (HTML) for the entire page, and another might render an RSS feed for a list of news items.
  2. They can be used to populate View Placeholders: if you request <?= $placeholders->aside ?> in a layout file used for the "home" template and add a view file at views/home/aside.php, Wireframe will automatically populate the "aside" placeholder with the content of current page rendered with that view file.

Anyway, I'll definitely dig into this topic a bit more and come back with some sort of solution, hopefully – thanks for linking to the TemplateEngineFactory example ?

1 hour ago, Ivan Gretsky said:

I looked through recent projects for examples of those partials. These are common ones:

  • menus;
  • breadcrumbs;
  • sidebar;
  • repeating forms (any repeating elements really, anything like wordpress widgets / joomla modules).

Thanks – this list was quite helpful. For the record, here's how I've handled these examples in my past projects:

  • For menus I've used MarkupSimpleNavigation, and later MarkupMenu. In the boilerplate profile I do have partials for these, but they just call the render function of a markup module.
  • Breadcrumbs is in my case usually a simple list of parents, thus I've used a "dumb" partial file with a foreach loop. Here's an example.
  • Sidebar, again, tends to be either a single RTE field (something like sidebar, aside, or perhaps right_column or left_column), or sometimes a predefined list of multiple fields. Again a valid use case for so-called dumb partial files, but this is actually exactly the use case for which I originally added the ability to use View files as View Placeholders.
  • Repeating elements covers a wide area of different content, but in my projects these have usually meant one of two things:
    • A news list or something similar, with predefined header, list of items, and often some sort of "footer" area as well. These use cases I've usually solved with render functions.
    • Repeater, RepeaterMatrix, or some other repeatable fieldtype. "Dumb" partials with some foreach rules, or native field rendering (particularly useful for RepeaterMatrix).

Note that I'm not saying that your use cases are invalid, or that the list above would be the "correct" way to handle these. It's just that I can see why this hasn't been a notable issue for me – but this also helps me understand the kind of use cases you've run into, and thus figure out how to continue from here on ?

  • Like 4
Link to comment
Share on other sites

On 7/25/2019 at 6:14 PM, teppo said:

Also, as a minor note I don't think that we should separate logic and presentation based on the language alone. PHP can be used to create "presentational" content...

I can relate to that. But the thing is I often do not do my html/css/js myself and pretty often people with very little knowledge of php are to make minor changes to view code. That's why I try to have my markup as close to raw html as possible. And render functions are clearly the opposite.

On 7/25/2019 at 6:14 PM, teppo said:

I'm not entirely sure if it actually needs to be a separate concept ("chunks", or whatever that would be called), but that's not necessarily a bad idea, if it makes the concept easier to grasp. Technically it should already be possible to instantiate a new Controller, provide it with a View file, and then render the output within, say, another Controller. That's what Wireframe itself does for the page, though it all happens automatically.

A am with you here. Entities should not be multiplied without necessity)) And that's why I do not quite like the placeholder thing. I think it should be that generic call for controller and view. And in this specific case sidebar usually is not tied to a particular template. So this thing seems "too opinionated" for me) I even think that the whole layout thing can be moved to view folder. I now think it is more clear to keep all the view related stuff in one place (though i did use dedicated layout folder in the past - now it is inside the view folder in my recent projects).

  • Like 3
Link to comment
Share on other sites

44 minutes ago, Ivan Gretsky said:

I can relate to that. But the thing is I often do not do my html/css/js myself and pretty often people with very little knowledge of php are to make minor changes to view code. That's why I try to have my markup as close to raw html as possible. And render functions are clearly the opposite.

That changes things quite a bit. Personally I've never been a part of this kind of team – as strange as it may sound, I've worked in teams where there are backend developers and so-called full-stack developers, but never a "pure" front-end developer ?

You've definitely given me something to think about here!

44 minutes ago, Ivan Gretsky said:

And that's why I do not quite like the placeholder thing. I think it should be that generic call for controller and view. And in this specific case sidebar usually is not tied to a particular template. So this thing seems "too opinionated" for me) I even think that the whole layout thing can be moved to view folder. I now think it is more clear to keep all the view related stuff in one place (though i did use dedicated layout folder in the past - now it is inside the view folder in my recent projects).

Features that are not tied to a specific template have in my use cases usually been partials with very little "logic" behind them, but this would actually be a nice use case for shared (sub)controllers as well, which is something I'll likely dive into soon. Basically the idea is to have a controller that isn't tied to a single template, but rather can be used by multiple templates. Also controllers that inherit other controllers – "base controllers", or whatever they might be called – is another way to achieve shared features.

Overall there's a lot you can do with current codebase, but since there are different use cases, it's very much about finding a balance between "not opinionated enough" and "too opinionated" ?

As for layouts, I've found the concept really useful, and since I use them in pretty much all projects I work on, this is something I wanted to have built-in and ready-to-use within Wireframe. Placeholders are a key part of this, as they provide an easy way to embed views (of which there may be one or more for each specific template) within commonly shared layouts –aAnd because layouts serve a different purpose from view files (individual views for templates) and partials (smaller pieces of content intended to be embedded within layouts or view files), I've opted to store them separately.

This is again one of those cases where it's all about finding the right balance and figuring out what should be available out of the box and what to leave for individual developers to figure out on their own, and I feel that current split between layouts/views/partials is a reasonable one – but because I know there are different opinions, much of this is actually already configurable. Also, since layouts are optional, one can always opt out from using them ?

  • Like 4
Link to comment
Share on other sites

Thanks for your answers and for you generously shared work, @teppo!

I really like your Wireframe idea - a well-established and documented way to structure code in ProcessWire MVC way. So one could follow rules that are thought out and described. I understand quite well that in order for this to work one should give up some of his own preferences) Will be looking forward to next additions to the project we've been talking about here.

  • Like 4
Link to comment
Share on other sites

  • 3 weeks later...

Hey folks – a quick update: Wireframe 0.5.0 was released couple of days ago. Compared to 0.4.x this version mainly fixes bugs and improves performance – nothing particularly major, but if you're using Wireframe, it's a recommended update.

On a loosely related note, Tracy Debugger has been really helpful in figuring things out, identifying bottlenecks, etc. Brilliant module ?

  • Like 8
Link to comment
Share on other sites

  • 4 weeks later...

Hey folks!

I've been a bit quiet here about Wireframe, but the thing is that I've finally got a couple of "real sites" (as in not my own projects) to implement it on, and that has brought up some new requirements and a few issues. I haven't had the time to dig into a whole lot of really new stuff (components and such), but I have made a lot of other updates to the framework. As such, Wireframe 0.6 was released late yesterday.

(A release on Friday the 13th, consisting of a total of 13 commits – not being superstitious here.)

I've been updating the docs at wireframe-framework.com as well, but some parts remain a bit outdated. A lot of what happened in this version doesn't really affect the use of Wireframe, but some things do – and one change may even break some existing implementations:

  • It used to be possible to set the view file or layout from Controller with $this->view->layout = "some-layout" or $this->view->view = "json". This has been deprecated and removed in favour of a setter/getter API: $this->view->setLayout("some-layout") and $this->view->setView("json"), and getLayout() / getView() for reading the value. In addition to some obvious benefits from type hinting etc. this also makes it harder to accidentally change something when you thought you were just passing a variable to the View ?
  • There are now three ways to perform actions and pass data from a Controller to the View: Controller::init() (triggered as soon as the Controller class is loaded), public methods (which are made accessible in the View as $this->method_name), and a new addition Controller::render() (called right before the page is rendered). In most cases where one might've used the init() method before, render() is a better choice, as there's less chance that code will get executed "unnecessarily".
  • In addition to existing Page::layout() and Page::view() methods there are now specific setters/getters: Page::setLayout("layout-name"), Page::getLayout(), etc. Original "unified setter+getter" methods may actually get removed at some point, as they're often ambiguous and make code arguably less readable.

Quite a few other changes as well, some of which improve performance and others that make the API more polished. Some bug fixes too, though those mostly apply to what I'd consider "border cases" ?

Here's the full changelog for Wireframe 0.6.0:

### Added
- New Page methods Page::getLayout(), Page::setLayout(), Page::getView(), and Page::setView().
- New Controller::render() method, executed right before a page is actually rendered.
- New ViewData class for storing (internal) data required by the View class.
- New getter/setter methods for ViewData properties for the View class.
- New method Wireframe::getConfig() for getting current config settings.
- New method ViewPlaceholders::has() for checking if a placeholder has already been populated.

### Changed
- Various View-related features moved from Wireframe module and ViewPlaceholders class to the View class.
- Removed access to local get* and set* methods via the PHP's magic setter method __set() and getter method __get() in the View class.
- Redirect feature no longer fails if provided with a WireArray data type; in these cases the first item is used as the redirect target.
- Improvements to PHPDoc comments.

### Fixed
- An issue with Config class where the "all directories exist" message was sometimes displayed unintentionally.
- An issue where View Placeholder values might've been overwritten because existence of earlier value was checked inproperly.
- An issue where empty / null view file would be automatically replaced with value "default".

On a related note, for this version I decided to give sonarcloud a try. For those who don't know it, it's a sort of a code quality inspector, and it's free for public projects. It didn't have a whole lot to complain about at this point, but it's good to have some sort of validation in place just in case. I will also be digging deeper into reported "code smells", as some of them definitely make sense to me ?

538753547_Screenshot2019-09-13at23_05_52.thumb.png.d6acfdb90d6f4afaf6f1699a4697bf6f.png

  • Like 9
Link to comment
Share on other sites

  • 3 weeks later...
On 9/14/2019 at 8:23 AM, teppo said:

On a related note, for this version I decided to give sonarcloud a try. For those who don't know it, it's a sort of a code quality inspector, and it's free for public projects. It didn't have a whole lot to complain about at this point, but it's good to have some sort of validation in place just in case. I will also be digging deeper into reported "code smells", as some of them definitely make sense to me

I don't want to derail this thread, but just want to say thanks so much for mentioning this. Coincidence or not? Just today someone on a client project mentioned SonarCube (which is basically the same, but installed locally, and also from the same company), and now I took a look at SonarLint, which can be added to IDEs as plugins. I will definitely try out their IntelliJ plugin. Judging from the SonarCloud results for WireFrame, it really sounds like a great tool. Sort of like ESLint, but on steroids :-) The estimated "time to fix" and references to rule (definitions) are a nice touch.

  • Like 3
Link to comment
Share on other sites

  • 5 weeks later...

Just released Wireframe 0.7.0.

This version is all about caching: when requesting Controller methods from view (<?= $this->some_controller_method ?> etc.) the return value is now cached (runtime cache). Additionally if method names are added to protected $cacheable_methods property of the controller class, along with an applicable expire time (i.e. "protected $cacheable_methods = [ 'method_name' => 3600 ]"), Wireframe automatically caches the value using WireCache (persistent cache).

Caching is usually a good thing, but not always preferred, which is why runtime caching can be prevented by adding the method name to (protected) Controller property $uncacheable_methods (protected $uncacheable_methods = [ 'method_name' ]). Persistent caching is always opt-in.

Changelog:

### Added
- Runtime caching support for Controller method return values. Values are cached *unless* the name of the method is found from Controller::$uncacheable_methods.
- Persistent caching support for Controller method return values. Values are cached only when found from the Controller::$cacheable_methods array.

### Changed
- In the View class all internal requests for Controller properties are routed through View::getFromController().

 

  • Like 7
Link to comment
Share on other sites

  • 1 month later...

Hey folks! I've been a bit quiet here, but that's mostly because I've been busy building stuff with ProcessWire and Wireframe ?

Just wanted to give a quick heads-up for a new feature called components that I'm currently testing (it's in the dev branch of the module already), in case anyone wants to comment on it before it gets merged to the master branch.

Wireframe components are classes extending an abstract base class (\Wireframe\Component), and they can either render output directly by implementing the render() method, or they can be rendered using a separate Component View file. This is probably a familiar concept to most, but the general idea is that while partials ("dumb" files with no way to process params, and no clean way to separate code from markup) are enough if you just have some relatively static snippet that you need repeatedly, components add an extra layer where you can a) specify which params they accept, b) process those params, and c) include any additional "business logic" that should be triggered when the component is rendered.

Here's a simplified example of how this comes together:

/site/templates/components/Card.php (class for the Card component)

<?php

namespace Wireframe\Component;

/**
 * Card component
 */
class Card extends \Wireframe\Component {

	/**
	 * Constructor method
	 *
	 * @param \ProcessWire\Page $item Page related to current Card.
	 */
	public function __construct(\ProcessWire\Page $item) {

		// Pass properties to view
		$this->title = $item->title;
		$this->summary = $item->summary;
		$this->image = $item->get('image|hero_image');
	}

}

/site/templates/components/Card/default.php (default view for the Card component)

<?php namespace ProcessWire; ?>
<div class="card">

	<?php if ($image): ?>
		<img src="<?= $image->size(640, 480)->url ?>" alt="<?= $image->description ?>">
	<?php endif; ?>

	<h3><?= $title ?></h3>

	<?php if ($summary): ?>
		<p><?= $summary ?></p>
	<?php endif; ?>

</div>

... and for fetching and rendering the component, there's a little static helper in the Wireframe module class:

<?php foreach ($cards as item): ?>
    <?= Wireframe::component('Card', [$item]) ?>
<?php endforeach; ?>

Note that Wireframe::component() actually returns an instance of the component class, not a string, but since \Wireframe\Component::__toString() calls \Wireframe\Component::render(), what we're doing here is essentially the same as echoing out the result of (new \Wireframe\Component\Card($card))->render() ?

So, anyway, that's basically what I've been working on here. I'd be happy to hear if you have any comments on this addition – I haven't yet merged it to master since I'm still experimenting with it, and I'd like to avoid as many breaking changes in the master branch as I can. So far this has worked great for me, but to be honest my requirements have been pretty basic.

Thoughts?

  • Like 12
Link to comment
Share on other sites

2 hours ago, teppo said:

Thoughts?

I think this is a great addition!

I made something similar a while ago (called Widgets) but it wasn't as thorough, simple, or effective as your Component implementation. I look forward to trying it out ?

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

Heya! Just released Wireframe 0.9.0. Here's the changelog for this version:

### Added
- New EventListenerTrait. Currently used by Components only. Adds support for listening to and emitting events.
- Support for Renderer modules for adding templating engine support for view files, component view files, etc.
- New Page methods Page::viewTemplate(), Page::getViewTemplate(), and Page::setViewTemplate().
- New method Component::getData() for manually defining the data passed to the component view.

### Changed
- Controller::init() and Controller::ready() are now hookable methods.
- Component::setView() and Component::getView() are now final methods, preventing accidental overrides.
- Layout file is no longer necessary; if it's missing, the page can be rendered using just a view file.

The component system is a bit more mature now, and there are a couple of new features:

  1. Component::on('event-name', callable $callback) can be used to listen to events emitted by a Component instance, and in a Component class events can be emitted with $this->emit('event-name', array $args). This is mostly just syntactic sugar on top of the hook system, but it seemed fun and potentially useful, so... ?
  2. The Component base class extends WireData, so by default what gets sent to the component view when a component is rendered is the internal $data array. Since 0.9.0 it's possible to alter this behaviour by implementing getData() method which returns the data that the component should expose to its view.

Another "big" update is support for renderers. These are add-on modules that add support for different rendering / templating engines to Wireframe, and the first example is the Wireframe Renderer Twig module. I haven't really used a templating engine in a while so it's possible that I've missed something important, but in my limited tests the Twig engine seemed to work quite well right out of the box. I'll likely add a separate module for Latte at some point, just to make sure that the logic works for other engines as well ?

By the way, it looks like I forgot to post here about Wireframe 0.8.0. It was released last month, and here's the related changelog entry:

### Added
- Support for Components, along with a new static factory method Wireframe::component($component_name, $args).
- Support for rendering pages that have not been "routed" to Wireframe using the altFilename template setting.
- New static getter/factory/utility method Wireframe::page($source, $args).
- New static utility method Wireframe::isInitialized().

### Changed
- Wireframe::$initialized is now a static property. This was a necessary change so that Wireframe::isInitialized() could be implemented effectively.

On a related note I'm considering tagging the next release as 1.0.0. We've been using Wireframe on production sites for a while now, and I can't recall any truly breaking changes so far, so I think it's already quite stable ?

  • Like 7
Link to comment
Share on other sites

  • teppo pinned this topic
  • teppo locked this topic
Guest
This topic is now closed to further replies.
×
×
  • Create New...