Jump to content

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 abbreviations 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 28
  • Thanks 7

Share this post


Link to post
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 7
  • Thanks 1

Share this post


Link to post
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 6

Share this post


Link to post
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

Share this post


Link to post
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 5

Share this post


Link to post
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 1

Share this post


Link to post
Share on other sites

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?

  • Like 1

Share this post


Link to post
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

Share this post


Link to post
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 2

Share this post


Link to post
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 2

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
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 3

Share this post


Link to post
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

Share this post


Link to post
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 3

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

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 7

Share this post


Link to post
Share on other sites

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 8

Share this post


Link to post
Share on other sites

Teppo,

I'm writing my first message on this forum (hi!) to thank you for this module. I'm testing it on a small local test site at the moment, and really liking it so far!

  • Like 4

Share this post


Link to post
Share on other sites
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 2

Share this post


Link to post
Share on other sites

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 6

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Gadgetto
      Status update links (inside this thread) for SnipWire development will be always posted here:
      2019-11-15 2019-10-18 2019-08-08 2019-06-15 2019-06-02 2019-05-25 If you are interested, you can test the current state of development:
      https://github.com/gadgetto/SnipWire
      Please note that the software is not yet intended for use in a production system (alpha version).
      If you like, you can also submit feature requests and suggestions for improvement. I also accept pull requests.
      ---- INITIAL POST FROM 2019-05-25 ----
      I wanted to let you know that I am currently working on a new ProcessWire module that fully integrates the Snipcart Shopping Cart System into ProcessWire. (this is a customer project, so I had to postpone the development of my other module GroupMailer).
      The new module SnipWire offers full integration of the Snipcart Shopping Cart System into ProcessWire.
      Here are some highlights:
      simple setup with (optional) pre-installed templates, product fields, sample products (quasi a complete shop system to get started immediately) store dashboard with all data from the snipcart system (no change to the snipcart dashboard itself required) Integrated REST API for controlling and querying snipcart data webhooks to trigger events from Snipcart (new order, new customer, etc.) multi currency support self-defined/configurable tax rates etc. Development is already well advanced and I plan to release the module in the next 2-3 months.
      I'm not sure yet if this will be a "Pro" module or if it will be made available for free.
      I would be grateful for suggestions and hints!
      (please have a look at the screenshots to get an idea what I'm talking about)
       




    • By Robin S
      I have had this module sitting in a 95% complete state for a while now and have finally made the push to get it out there. Thanks to @teppo for his Hanna Code Helper module which I referred to and borrowed from during development.
      http://modules.processwire.com/modules/hanna-code-dialog/
      https://github.com/Toutouwai/HannaCodeDialog
      HannaCodeDialog
      Provides a number of enhancements for working with Hanna Code tags in CKEditor. The main enhancement is that Hanna tags in a CKEditor field may be double-clicked to edit their attributes using core ProcessWire inputfields in a modal dialog.
      Requires the Hanna Code module and >= ProcessWire v3.0.0.
      Installation
      Install the HannaCodeDialog module using any of the normal methods.
      For any CKEditor field where you want the "Insert Hanna tag" dropdown menu to appear in the CKEditor toolbar, visit the field settings and add "HannaDropdown" to the "CKEditor Toolbar" settings field.
      Module configuration
      Visit the module configuration screen to set any of the following:
      Exclude prefix: Hanna tags named with this prefix will not appear in the CKEditor toolbar dropdown menu for Hanna tag insertion. Exclude Hanna tags: Hanna tags selected here will not appear in the CKEditor toolbar dropdown menu for Hanna tag insertion. Background colour of tag widgets: you can customise the background colour used for Hanna tags in CKEditor if you like. Dialog width: in pixels Dialog height: in pixels Features
      Insert tag from toolbar dropdown menu
      Place the cursor in the CKEditor window where you want to insert your Hanna tag, then select the tag from the "Insert Hanna tag" dropdown.

      Advanced: if you want to control which tags appear in the dropdown on particular pages or templates you can hook HannaCodeDialog::getDropdownTags. See the forum support thread for examples .
      Edit tag attributes in modal dialog
      Insert a tag using the dropdown or double-click an existing tag in the CKEditor window to edit the tag attributes in a modal dialog.

      Tags are widgets
      Hanna tags that have been inserted in a CKEditor window are "widgets" - they have a background colour for easy identification, are protected from accidental editing, and can be moved within the text by drag-and-drop.

      Options for tag attributes may be defined
      You can define options for a tag attribute so that editors must choose an option rather than type text. This is useful for when only certain strings are valid for an attribute and also has the benefit of avoiding typos.
      Add a new attribute for the Hanna tag, named the same as the existing attribute you want to add options for, followed by "__options". The options themselves are defined as a string, using a pipe character as a delimiter between options. Example for an existing attribute named "vegetables":
      vegetables__options=Spinach|Pumpkin|Celery|Tomato|Brussels Sprout|Potato You can define a default for an attribute as normal. Use a pipe delimiter if defining multiple options as the default, for example:
      vegetables=Tomato|Potato Dynamic options
      Besides defining static options as above, you can use one Hanna tag to dynamically generate options for another. For instance, you could create a Hanna tag that generates options based on images that have been uploaded to the page, or the titles of children of the page.
      Your Hanna tag that generates the options should echo a string of options delimited by pipe characters (i.e. the same format as a static options string).
      You will probably want to name the Hanna tag that generates the options so that it starts with an underscore (or whatever prefix you have configured as the "exclude" prefix in the module config), to avoid it appearing as an insertable tag in the HannaCodeDialog dropdown menu.
      Example for an existing attribute named "image":
      image__options=[[_images_on_page]] And the code for the _images_on_page tag:
      <?php $image_names = array(); $image_fields = $page->fields->find('type=FieldtypeImage')->explode('name'); foreach($image_fields as $image_field) { $image_names = array_unique( array_merge($image_names, $page->$image_field->explode('name') ) ); } echo implode('|', $image_names); Choice of inputfield for attribute
      You can choose the inputfield that is used for an attribute in the dialog.
      For text attributes the supported inputfields are text (this is the default inputfield for text attributes so it isn't necessary to specify it if you want it) and textarea. Note: any manual line breaks inside a textarea are removed because these will break the CKEditor tag widget.
      Inputfields that support the selection of a single option are select (this is the default inputfield for attributes with options so it isn't necessary to specify it if you want it) and radios.
      Inputfields that support the selection of multiple options are selectmultiple, asmselect and checkboxes.
      You can also specify a checkbox inputfield - this is not for attributes with defined options but will limit an attribute to an integer value of 1 or 0.
      The names of the inputfield types are case-insensitive.
      Example for an existing attribute named "vegetables":
      vegetables__type=asmselect Descriptions and notes for inputfields
      You can add a description or notes to an attribute and these will be displayed in the dialog.
      Example for an existing attribute named "vegetables":
      vegetables__description=Please select vegetables for your soup. vegetables__notes=Pumpkin and celery is a delicious combination. Notes
      When creating or editing a Hanna tag you can view a basic cheatsheet outlining the HannaCodeDialog features relating to attributes below the "Attributes" config inputfield.
      Advanced
      Define or manipulate options in a hook
      You can hook HannaCodeDialog::prepareOptions to define or manipulate options for a Hanna tag attribute. Your Hanna tag must include a someattribute__options attribute in order for the hook to fire. The prepareOptions method receives the following arguments that can be used in your hook:
      options_string Any existing string of options you have set for the attribute attribute_name The name of the attribute the options are for tag_name The name of the Hanna tag page The page being edited If you hook after HannaCodeDialog::prepareOptions then your hook should set $event->return to an array of option values, or an associative array in the form of $value => $label.
      Build entire dialog form in a hook
      You can hook after HannaCodeDialog::buildForm to add inputfields to the dialog form. You can define options for the inputfields when you add them. Using a hook like this can be useful if you prefer to configure inputfield type/options/descriptions/notes in your IDE rather than as extra attributes in the Hanna tag settings. It's also useful if you want to use inputfield settings such as showIf.
      When you add the inputfields you must set both the name and the id of the inputfield to match the attribute name.
      You only need to set an inputfield value in the hook if you want to force the value - otherwise the current values from the tag are automatically applied.
      To use this hook you only have to define the essential attributes (the "fields" for the tag) in the Hanna Code settings and then all the other inputfield settings can be set in the hook.
      Example buildForm() hook
      The Hanna Code attributes defined for tag "meal" (a default value is defined for "vegetables"):
      vegetables=Carrot meat cooking_style comments The hook code in /site/ready.php:
      $wire->addHookAfter('HannaCodeDialog::buildForm', function(HookEvent $event) { // The Hanna tag that is being opened in the dialog $tag_name = $event->arguments(0); // Other arguments if you need them /* @var Page $edited_page */ $edited_page = $event->arguments(1); // The page open in Page Edit $current_attributes = $event->arguments(2); // The current attribute values $default_attributes = $event->arguments(3); // The default attribute values // The form rendered in the dialog /* @var InputfieldForm $form */ $form = $event->return; if($tag_name === 'meal') { $modules = $event->wire('modules'); /* @var InputfieldCheckboxes $f */ $f = $modules->InputfieldCheckboxes; $f->name = 'vegetables'; // Set name to match attribute $f->id = 'vegetables'; // Set id to match attribute $f->label = 'Vegetables'; $f->description = 'Please select some vegetables.'; $f->notes = "If you don't eat your vegetables you can't have any pudding."; $f->addOptions(['Carrot', 'Cabbage', 'Celery'], false); $form->add($f); /* @var InputfieldRadios $f */ $f = $modules->InputfieldRadios; $f->name = 'meat'; $f->id = 'meat'; $f->label = 'Meat'; $f->addOptions(['Pork', 'Beef', 'Chicken', 'Lamb'], false); $form->add($f); /* @var InputfieldSelect $f */ $f = $modules->InputfieldSelect; $f->name = 'cooking_style'; $f->id = 'cooking_style'; $f->label = 'How would you like it cooked?'; $f->addOptions(['Fried', 'Boiled', 'Baked'], false); $form->add($f); /* @var InputfieldText $f */ $f = $modules->InputfieldText; $f->name = 'comments'; $f->id = 'comments'; $f->label = 'Comments for the chef'; $f->showIf = 'cooking_style=Fried'; $form->add($f); } }); Troubleshooting
      HannaCodeDialog includes and automatically loads the third-party CKEditor plugins Line Utilities and Widget. If you have added these plugins to your CKEditor field already for some purpose and experience problems with HannaCodeDialog try deactivating those plugins from the CKEditor field settings.
    • By horst
      Wire Mail SMTP

      An extension to the (new) WireMail base class that uses SMTP-transport

      This module integrates EmailMessage, SMTP and SASL php-libraries from Manuel Lemos into ProcessWire. I use this continously evolved libraries for about 10 years now and there was never a reason or occasion not to do so. I use it nearly every day in my office for automated composing and sending personalized messages with attachments, requests for Disposition Notifications, etc. Also I have used it for sending personalized Bulkmails many times.

      The WireMailSmtp module extends the new email-related WireMail base class introduced in ProcessWire 2.4.1 (while this writing, the dev-branch only).
       
      Here are Ryans announcement.



      Current Version 0.4.2
      Changelog: https://github.com/horst-n/WireMailSmtp/blob/master/CHANGELOG.md
      get it from the Modules Directory Install and Configure

      Download the module into your site/modules/ directory and install it.

      In the config page you fill in settings for the SMTP server and optionaly the (default) sender, like email address, name and signature.
      You can test the smtp settings directly there. If it says "SUCCESS! SMTP settings appear to work correctly." you are ready to start using it in templates, modules or bootstrap scripts.


      Usage Examples
      The simplest way to use it:
      $numSent = wireMail($to, $from, $subject, $textBody); $numSent = wireMail($to, '', $subject, $textBody); // or with a default sender emailaddress on config page This will send a plain text message to each recipient.
       
      You may also use the object oriented style:
      $mail = wireMail(); // calling an empty wireMail() returns a wireMail object $mail->to($toEmail, $toName); $mail->from = $yourEmailaddress; // if you don't have set a default sender in config // or if you want to override that $mail->subject($subject); $mail->body($textBody); $numSent = $mail->send(); Or chained, like everywhere in ProcessWire:
      $mail = wireMail(); $numSent = $mail->to($toEmail)->subject($subject)->body($textBody)->send(); Additionaly to the basics there are more options available with WireMailSmtp. The main difference compared to the WireMail BaseClass is the sendSingle option. With it you can set only one To-Recipient but additional CC-Recipients.
      $mail = wireMail(); $mail->sendSingle(true)->to($toEmail, $toName)->cc(array('person1@example.com', 'person2@example.com', 'person3@example.com')); $numSent = $mail->subject($subject)->body($textBody)->send(); The same as function call with options array:
      $options = array( 'sendSingle' => true, 'cc' => array('person1@example.com', 'person2@example.com', 'person3@example.com') ); $numSent = wireMail($to, '', $subject, $textBody, $options); There are methods to your disposal to check if you have the right WireMail-Class and if the SMTP-settings are working:
      $mail = wireMail(); if($mail->className != 'WireMailSmtp') { // Uups, wrong WireMail-Class: do something to inform the user and quit echo "<p>Couldn't get the right WireMail-Module (WireMailSmtp). found: {$mail->className}</p>"; return; } if(!$mail->testConnection()) { // Connection not working: echo "<p>Couldn't connect to the SMTP server. Please check the {$mail->className} modules config settings!</p>"; return; }  
      A MORE ADVANCED DEBUG METHOD!
      You can add some debug code into a template file and call a page with it:
      $to = array('me@example.com'); $subject = 'Wiremail-SMTP Test ' . date('H:i:s') . ' äöü ÄÖÜ ß'; $mail = wireMail(); if($mail->className != 'WireMailSmtp') { echo "<p>Couldn't get the right WireMail-Module (WireMailSmtp). found: {$mail->className}</p>"; } else { $mail->from = '--INSERT YOUR SENDER ADDRESS HERE --'; // <--- !!!! $mail->to($to); $mail->subject($subject); $mail->sendSingle(true); $mail->body("Titel\n\ntext text TEXT text text\n"); $mail->bodyHTML("<h1>Titel</h1><p>text text <strong>TEXT</strong> text text</p>"); $dump = $mail->debugSend(1); } So, in short, instead of using $mail->send(), use $mail->debugSend(1) to get output on a frontend testpage.
      The output is PRE formatted and contains the areas: SETTINGS, RESULT, ERRORS and a complete debuglog of the server connection, like this one:
       
      Following are a ...


      List of all options and features


      testConnection () - returns true on success, false on failures


      sendSingle ( true | false ) - default is false

      sendBulk ( true | false ) - default is false, Set this to true if you have lots of recipients (50+)


      to ($recipients) - one emailaddress or array with multiple emailaddresses

      cc ($recipients) - only available with mode sendSingle, one emailaddress or array with multiple emailaddresses

      bcc ($recipients) - one emailaddress or array with multiple emailaddresses

       
      from = 'person@example.com' - emailaddress, can be set in module config (called Sender Emailaddress) but it can be overwritten here

      fromName = 'Name Surname' - optional, can be set in module config (called Sender Name) but it can be overwritten here


      priority (3) - 1 = Highest | 2 = High | 3 = Normal | 4 = Low | 5 = Lowest

      dispositionNotification () or notification () - request a Disposition Notification


      subject ($subject) - subject of the message

      body ($textBody) - use this one alone to create and send plainText emailmessages

      bodyHTML ($htmlBody) - use this to create a Multipart Alternative Emailmessage (containing a HTML-Part and a Plaintext-Part as fallback)

      addSignature ( true | false ) - the default-behave is selectable in config screen, this can be overridden here
      (only available if a signature is defined in the config screen)

      attachment ($filename, $alternativeBasename = "") - add attachment file, optionally alternative basename


      send () - send the message(s) and return number of successful sent messages


      debugSend(1) - returns and / or outputs a (pre formatted) dump that contains the areas: SETTINGS, RESULT, ERRORS and a complete debuglog of the server connection. (See above the example code under ADVANCED DEBUG METHOD for further instructions!)


      getResult () - returns a dump (array) with all recipients (to, cc, bcc) and settings you have selected with the message, the message subject and body, and lists of successfull addresses and failed addresses,


      logActivity ($logmessage) - you may log success if you want

      logError ($logmessage) - you may log warnings, too. - Errors are logged automaticaly
       
       
      useSentLog (true | false) - intended for usage with e.g. third party newsletter modules - tells the send() method to make usage of the sentLog-methods - the following three sentLog methods are hookable, e.g. if you don't want log into files you may provide your own storage, or add additional functionality here

      sentLogReset ()  - starts a new LogSession - Best usage would be interactively once when setting up a new Newsletter

      sentLogGet ()  - is called automaticly within the send() method - returns an array containing all previously used emailaddresses

      sentLogAdd ($emailaddress)  - is called automaticly within the send() method
      Changelog: https://github.com/horst-n/WireMailSmtp/blob/master/CHANGELOG.md
       
       
    • By Robin S
      Sanitizer EasySlugger
      Allows the use of the EasySlugger library as Sanitizer methods.
      Installation
      Install the Sanitizer EasySlugger module.
      Usage
      The module adds four new sanitizer methods.
      slugger($string, $options)
      Similar to $sanitizer->pageName() - I'm not sure if there are any advantages over that method. Included because it is one of the methods offered by EasySlugger.
      $slug = $sanitizer->slugger('Lorem Ipsum'); // Result: lorem-ipsum utf8Slugger($string, $options)
      Creates slugs from non-latin alphabets.
      $slug = $sanitizer->utf8Slugger('这个用汉语怎么说'); // Result: zhe-ge-yong-han-yu-zen-me-shuo seoSlugger($string, $options)
      Augments the string before turning it into a slug. The conversions are related to numbers, currencies, email addresses and other common symbols.
      $slug = $sanitizer->seoSlugger('The price is $5.99'); // Result: the-price-is-5-dollars-99-cents See the EasySlugger readme for some more examples.
      seoUtf8Slugger($string, $options)
      A combination of utf8Slugger() and seoSlugger().
      $slug = $sanitizer->seoUtf8Slugger('价钱是 $5.99'); // Result: jia-qian-shi-5-dollars-99-cents $options argument
      Each of the methods can take an $options array as a second argument.
      separator (string): the character that separates words in the slug. Default: - unique (bool): Determines whether a random suffix is added at the end of the slug. Default: false $slug = $sanitizer->utf8Slugger('这个用汉语怎么说', ['separator' => '_', 'unique' => true]); // Result: zhe_ge_yong_han_yu_zen_me_shuo_3ad66c4  
      https://github.com/Toutouwai/SanitizerEasySlugger
      https://modules.processwire.com/modules/sanitizer-easy-slugger/
    • By Robin S
      If you've ever needed to insert links to a large number of files within CKEditor you may have found that the standard PW link modal is a somewhat slow way to do it.
      This module provides a quicker way to insert links to files on the page being edited. You can insert a link to an individual file, or insert an unordered list of links to all files on the page with a single click.
      CKEditor Link Files
      Adds a menu to CKEditor to allow the quick insertion of links to files on the page being edited.

      Features
      Hover a menu item to see the "Description" of the corresponding file (if present). Click a menu item to insert a link to the corresponding file at the current cursor position. The filename is used as the link text. If you Alt-click a menu item the file description is used as the link text (with fallback to filename if no description entered). If text is currently selected in the editor then the selected text is used as the link text. Click "* Insert links to all files *" to insert an unordered list of links to all files on the page. Also works with the Alt-click option. Menu is built via AJAX so newly uploaded files are included in the menu without the page needing to be saved. However, descriptions are not available for newly uploaded files until the page is saved. There is an option in the module config to include files from Repeater fields in the edited page. Nested Repeater fields (files inside a Repeater inside another Repeater) are not supported. Installation
      Install the CKEditor Link Files module.
      For any CKEditor field where you want the "Insert link to file" dropdown menu to appear in the CKEditor toolbar, visit the field settings and add "LinkFilesMenu" to the "CKEditor Toolbar" settings field.
       
      http://modules.processwire.com/modules/cke-link-files/
      https://github.com/Toutouwai/CkeLinkFiles
×
×
  • Create New...