Jump to content

Recommended Posts

This is related to the Wireframe::page() static utility method (https://wireframe-framework.com/docs/static-utility-methods/) but not specifically covered in the docs, I think. This is also one of the things I struggled trying to explain briefly in the changelog 🙂

The general idea is that since Wireframe works through the Alternate Template Filename setting of templates, in earlier versions you couldn't do things like $pages->get(1234)->setLayout(null)->render() and expect the output to come from a Wireframe view UNLESS the Alternate Template Filename for the template this page uses had the alt filename pointing to a Wireframe bootstrap file (i.e. /site/templates/wireframe.php).

After this update the syntax mentioned above will — in most cases at least — work right out of the box. And even in cases where it won't work (such as when you're fetching data from ProcessWire via a command-line script), Wireframe::page(1234)->setLayout(null)->render() will almost certainly work.

Typical use case for me are pages that are not supposed to be directly accessible, like a contact page that lives under a shared "bucket" (e.g. /data/contacts/lastname-firstname/) and is only ever made visible when rendered as part of another page's markup. If I only need to output a few fields from the page I can do that directly in the view file for the containing page, but if I need to reuse this data all over the site, I prefer to define a view file for the contact (e.g. /site/templates/views/contact/embed.php) and render it using that:

<!-- some page that lists contacts -->

<?php foreach ($page->contacts as $contact): ?>
    <?= $contact->setLayout(null)->setView('embed')->render() ?>
<?php endforeach; ?>

Hope this makes more sense 🙂

  • Like 3

Share this post


Link to post
Share on other sites

Ehy @teppo I'm really digging Wireframe and I really think that it's a brilliant work. Thank you also for mentioning my old TemplateEngineBlade module in the list of alternatives.

I'm currently restyling one of my websites and I decided to use Wireframe for the purpose. Everything is going great so far! I also decided to create a renderer module that uses the Blade template engine, because I don't like Twig that much. Check it out https://github.com/mauricius/WireframeRendererBlade

  • Like 3
  • Thanks 1

Share this post


Link to post
Share on other sites

Hi @teppo

First I have to say: WOW! I took another look at your module because I got again frustrated with my setup and I wanted to build something on my own. Luckily I remembered your module and came back to the docs before developing something on my own 🙂 

  • Thx for the docs, they are great! Some suggestions:
    • It would be great to have < prev | next > links on each page at the bottom. I almost missed all the other great pages when reaching the bottom of https://wireframe-framework.com/docs/ (the menu is not visible in the sidebar on such long pages)
    • It would be great to have a simple hello-world walkthrough for setting up a custom wireframe template (instead of providing a full-blown site profile). It's always easier to understand something if you start from scratch than finding your way around several files not knowing which pieces came before and after another.

And then something more advanced and more important: I think it would be great to get a little more (or different) control over where Wireframe does look for files. I read about the config settings, but IMHO they are a little limiting. As far as I understood one can define a path for every type that Wireframe is based on (views, controllers, components, etc). And as far as I understood it is possible for components to define custom view files via Wireframe::component('foo')::setView('bar'); Is that correct?

The problem with that approach is that it is not possible to load components from outside of the wireframe folder structure. That's a quite big deal, because if that were possible, we could ship custom components/views/partials (I'm planning on working on styles using RockLESS) directly within our modules and that would just be awesome! Take this example:

module RockSearch
lives in /site/modules/RockSearch
 |- Wireframe
 |  |- js
 |  |  '- search.js
 |  |- less
 |  |  '- searchform.less
 |  '- partials
 |     '- searchform.php
 '- RockSearch.module.php

What if we had a new Wireframe type called "package"? This could be included in any Wireframe layout like this:

<?php
$css = RockLESS::css([
  $packages->RockSearch->less->searchform.less,
  $packages->MyGreatModule->less->style1.less,
]);
?>
<html>
  <head>
    <link rel="stylesheet" href="<?= $css ?>">
    <?= $packages->RockSEO->partials->meta() ?>
  </head>
  <body>
    <section><?= $partials->header() ?></section>
    <section><?= $packages->RockSearch->partials->searchform() ?></section>
    ...
  </body>
</html>

Finally we'd only need to tell Wireframe about the packages:

$config->wireframe = [
  ...
  'packages' => [
    'RockSearch' => $config->paths->siteModules."RockSearch",
    'RockSEO' => $config->paths->siteModules."RockSEO",
  ],
];

// or something like this
Wireframe::addPackage($config->paths->siteModules."RockSearch");

This would finally bring some standards to the ProcessWire frontend which would make a huge difference regarding reusability! If we found a bug in RockSearch, we could directly fix it in the modules partials which would make it instantly available to all projects using it after a simple modules update!

Thx for your great work again! What do you think?

PS: Defining different views/partials for RockSearch should also be possible making it easy to support different frameworks or framework versions, eg $packages->RockSearch->partials->uikit3() or ->bootstrap4() etc.

PPS: I didn't quite get where the differences between using controllers+views or components+views/partials are?!

  • Like 2

Share this post


Link to post
Share on other sites
Quote

First I have to say: WOW! I took another look at your module because I got again frustrated with my setup and I wanted to build something on my own. Luckily I remembered your module and came back to the docs before developing something on my own 🙂 

🙇‍♂️

Quote

It would be great to have < prev | next > links on each page at the bottom. I almost missed all the other great pages when reaching the bottom of https://wireframe-framework.com/docs/ (the menu is not visible in the sidebar on such long pages)

Makes sense. I've added this to my todo list, will take care of it soon.

Quote

It would be great to have a simple hello-world walkthrough for setting up a custom wireframe template (instead of providing a full-blown site profile). It's always easier to understand something if you start from scratch than finding your way around several files not knowing which pieces came before and after another.

Again, makes sense. I'm personally not a huge fan of tutorials — it's just not my thing. I prefer to learn by finding a project to work on, or alternatively by digging into existing implementations. Probably explains why there's no tutorial available for Wireframe either.

Another item on my todo list 🙂

Quote

As far as I understood one can define a path for every type that Wireframe is based on (views, controllers, components, etc). And as far as I understood it is possible for components to define custom view files via Wireframe::component('foo')::setView('bar'); Is that correct?

Yes. On a very minor note the API for redefining component view would look like Wireframe::component('foo')->setView('bar'). Also: I typically choose the view in the component, i.e. the constructor picks the most suitable view based on the params it received. Both approaches are fine though, depends on the context! 🙂

Quote

The problem with that approach is that it is not possible to load components from outside of the wireframe folder structure. That's a quite big deal, because if that were possible, we could ship custom components/views/partials (I'm planning on working on styles using RockLESS) directly within our modules and that would just be awesome! [...]

What if we had a new Wireframe type called "package"? This could be included in any Wireframe layout like this:

Interesting idea! I can definitely see value in this, but I'd still like to give it a bit more thought. It's easy to add features, but hard to remove (or significantly alter) them, so I prefer not to rush things 🙂

Out of interest, how do you see this comparing to Markup modules using render method(s)? For an example:

<!-- RockSearch with partial -->
<section><?= $packages->RockSearch->partials->searchform() ?></section>

<!-- SearchEngine -->
<section><?= $modules->SearchEngine->renderForm() ?></section>

One benefit would be that if I wanted to use the default search form as a starting point and start building on top of that, I could just copy the file to local partials directory and change the reference in the layout. This would, obviously, mean no more easy updates, so it's a double edged sword.

In SearchEngine I decided to go with a set of interconnected render methods, each tasked with some specific part of the markup. I felt that this provided the best balance between reusability and customization: one can get pretty far by modifying config settings, but if that isn't enough, it's also possible to make more drastic changes using hooks.

All in all I really like the idea, but it will require a bit more thought and probably some experimenting to strike the perfect balance 🙂

Quote

PPS: I didn't quite get where the differences between using controllers+views or components+views/partials are?!

Hopefully I'm answering the right question:

  • Controllers and views are specific to a single template. Controllers' job is to accept arguments, process data, and pass processed data to the View layer. View — which consists of layout(s), view files, and partials — is there to output said data, so it should be as "dumb" as possible. View doesn't need to know anything about what's going on behind the scenes.
  • Partials were originally just "include files" without the ability to accept params or a dedicated approach for processing data. They were great when you had a relatively static block you wanted to repeat in multiple templates, but if you wanted to pass them params, you'd have to define them in the parent context, etc.
  • Components were added to fill this void. There's always a class that can accept arguments and process data, and usually there's at least one view file meant to render output. (Components can also render output directly by implementing the render() method, or they might not produce any markup at all, so technically component views are optional.)

... and then things changed a bit when partials also got the ability to accept params. Now the biggest difference between components and partials is the class: I prefer not to mix code with markup, which means that if I need a reusable "thing" that needs to, say, fetch data from an API, it's a component. On the other hand if I just need to reuse a block of HTML and perhaps iterate/output some variables in there, most of the time I go with a partial.

In my mind controllers + views (and layout(s)) are the typical way to use Wireframe. Components are handy when you need an element that should be reusable across multiple templates. I guess they're sort of "template-agnostic miniature controller + view" bundles 🙂

Note: how you actually use Wireframe may vary. Your site might not have any controllers, relying on components instead. Or you could produce all the markup in the layout and have no other files (apart from the bootstrap file). What I've described above is just the way I prefer to do things 👌

  • Like 3

Share this post


Link to post
Share on other sites

Hi Teppo,

thx for your thoughts! 🙂

19 hours ago, teppo said:

Out of interest, how do you see this comparing to Markup modules using render method(s)? For an example:


<!-- RockSearch with partial -->
<section><?= $packages->RockSearch->partials->searchform() ?></section>

<!-- SearchEngine -->
<section><?= $modules->SearchEngine->renderForm() ?></section>

Interesting question. I'll try to brainstorm some answers/questions and refer to the two solutions as packages- and modules-route:

  • Modules could only support one markup (how would you ship markup for uikit/bootstrap/tailwind/... ? Or you would need to define renderFormUikit(), renderFormBootstrap().
  • Modules would have markup code in module code (ugly string concats etc) or would need to implement $files->render(...).
  • Related to the previous point: Render files in module-route could have any name, live in any folder, etc - there's no standard, wheras using the packages route a module could have /partials or /views folder and everybody familiar with Wireframe would get what's going on
  • Everybody not familiar would not get what's going on or could maybe even not use it 😐 
  • Maybe it would add more overhead then necessary?! Your modules syntax seems very clear to me!
  • I guess the request came with some background in my mind: I wanted to have packages with PHP (logic + view) code, LESS for style and JS for frontend. For example the searchForm might need some JS to work that you need to add to your theme. But that would also be easy to add to the layout with one single include, so that would also be no real argument for the packages route 🙂 
  • Packages would have the same context as all Wireframe view files (including other partials etc; not sure if that makes sense though, because packages should work as-is (standalone) without any theme related partials...).

I think I have to just try both approaches on my current project and report back my findings 🙂 

19 hours ago, teppo said:

One benefit would be that if I wanted to use the default search form as a starting point and start building on top of that, I could just copy the file to local partials directory and change the reference in the layout. This would, obviously, mean no more easy updates, so it's a double edged sword.

I really want to avoid copying files over to an extra place that breaks updates! 🙂 

  • Like 1

Share this post


Link to post
Share on other sites

Hi @teppo

finally did some more testing, this was my experience:

Ok, it does not stop, but it leads to https://wireframe-framework.com/docs/ -> this is all great informations, but it does not help getting a quick grasp of how to output something on the frontpage...

Please don't get me wrong. Your docs are great, but for me (and maybe others) I think some small improvements can make the experience even greater. Maybe something like this (based on my efforts from today):

---

Here's a rough, unpolished tutorial for getting started with wireframe and getting the concepts on the road:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Default Layout</title>
</head>
<body>
  <h1>Wireframe default layout</h1>
</body>
</html>

Congratulations, that's your first layout! Now add some php variables:

<p>My name is: <?= $view->myName ?></p>

nZ2Bpz7.png

Of course, why should the layout know the name? We need a controller that tells the view what value is in the myName property!

Create /controllers/HomeController.php

<?php namespace Wireframe\Controller;
class HomeController extends \Wireframe\Controller {

  public function myName() {
    return 'Bernhard';
  }
}

XYkOjaR.png

What about my favourite CMS?

<p>My favourite CMS is: <?= $view->cms ?></p>
<?php namespace Wireframe\Controller;
class HomeController extends \Wireframe\Controller {

  public function myName() {
    return 'Bernhard';
  }

  public function cms() {
    return "ProcessWire";
  }
}

ObY797f.png

Ok, I got it - and that's great stuff! 🙂 

 

Now set the alternate template for "basic-page" to "wireframe". Visit a "basic-page" site (eg the 404 page) and you'll see the default layout:

YLvfsj8.png

The variables are empty, because the HomeController is only loaded on the home template and that's for some good reasons. Read about views in the docs: https://wireframe-framework.com/docs/view/views/

Partials to the rescue 🙂 We modify the default layout:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Default Layout</title>
</head>
<body>
  <h1>Wireframe default layout</h1>
  <?php include $partials->name ?>
  <?php include $partials->cms ?>
</body>
</html>

And the partials:

// partials/name.php
<p>My name is <strong>Bernhard</strong>.</p>

// partials/cms.php
<p>My favourite CMS is <strong>ProcessWire</strong>.</p>

Let's make that dynamic!

// name.php
<p>My name is <strong><?= $name ?></strong>.</p>

// cms.php
<p>My favourite CMS is <strong><?= $cms ?></strong>.</p>

// default.php
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Default Layout</title>
</head>
<body>
  <h1>Wireframe default layout</h1>
  <h2>Viewing template "<?= $page->template ?>"</h2>
  <?= $partials->name(['name' => 'Bernhard']) ?>
  <?= $partials->cms(['cms' => 'ProcessWire']) ?>
</body>
</html>

Note that the partials are now echoed instead of included and we added the line that outputs the template of the viewed page!

EXpagcB.pngouH0DL1.png

Note though, that partials are really just for simple chunks of code. At best even static ones. As soon as things get more dynamic, start using components instead of partials: https://wireframe-framework.com/docs/view/components/

What do you think?

  • Like 5

Share this post


Link to post
Share on other sites
1 hour ago, bernhard said:

What do you think?

In the midst of something else right now, so just a quick overall impression: I like where you're going with this!

A tutorial type getting started section has been on my todo-list for a while. Not sure if this is that, or perhaps a "light version", or something? Anyway, I'll give this a bit more thought once I've got some free time, definitely a good starting point 🙂

Edit: just wanted to add that I've been working on a new release for Wireframe. It's going to be a pretty big update, and after that's out I should have some time to revisit the docs.

  • Like 3

Share this post


Link to post
Share on other sites

s26wXJz.png

CYKQjtl.png

Any reason why the resources folder is not created via config?

24 minutes ago, teppo said:

Edit: just wanted to add that I've been working on a new release for Wireframe. It's going to be a pretty big update, and after that's out I should have some time to revisit the docs.

Not sure if that is good news for me 😄 Should I wait for that or will the update be smooth? Thx for the info though 🙂 

Share this post


Link to post
Share on other sites
47 minutes ago, bernhard said:

Any reason why the resources folder is not created via config?

Seems like an oversight from my part, good catch 🙂

47 minutes ago, bernhard said:

Not sure if that is good news for me 😄 Should I wait for that or will the update be smooth? Thx for the info though 🙂 

There shouldn't be any backwards compatibility breaks. At least not intentional ones... 😅

  • Thanks 1

Share this post


Link to post
Share on other sites
15 hours ago, teppo said:

Seems like an oversight from my part, good catch 🙂

Spoke too soon — there was actually a reason for this: "resources" is not a "path" for Wireframe, just an "URL". This may sound weird, but the point is that while Wireframe needs to keep track of code related paths (so that it knows where views, layouts, controllers, etc. are located), it doesn't really need to know where your static resources (CSS, JS, images, fonts, etc.) are.

URLs are added as helpers, so that you can refer from code to your resources directory with <?= $config->urls->resources ?> instead of writing a long directory name over and over again.

Anyway, upcoming 0.12.0 release will provide an option to create directories for specified URLs as well. This obviously only works if they are relative paths (they don't have to be — in some cases I've pointed resources directly to a CDN service, etc.)

  • Like 1

Share this post


Link to post
Share on other sites

Hi @teppo,

could you please give me a quick hint what would be the best way to set a layout for a custom pageclass? I'm creating a page via RockMigrations and it has the PageClass TeaserPage:

class TeaserPage extends Page {
	public function __construct(Template $tpl = null) {
    parent::__construct($tpl);
    $this->_wireframe_layout = 'teaser';
  }
}

This works, but $this->setLayout('teaser') does not. I get an Error Method TeaserPage::setLayout does not exist or is not callable in this context

The docs say that the layout can be defined via hook, but I could not find an example? Thx

Edit: The solution above is not possible: You must assign a template to the page before setting custom field values (_wireframe_layout)

Edit2:

class TeaserPage extends Page {

  public function init() {
    $this->addHookBefore("Wireframe::render", function($event) {
      bd('fired');
      bd($event->page->getLayout(), 'layout');
      $event->page->setLayout('teaser');
      bd($event->page->getLayout(), 'layout');
    });
  }

}

This hook fires but does not change the layout?

GnOGb4J.png

Edit 3: Even this does not work?!

// init Wireframe
$wireframe = $modules->get('Wireframe');
$wireframe->init();

// render the page
if($page->template == GemeindeGuru::tpl_teaser) {
    $page->setLayout('teaser');
}

echo $wireframe->render([
    // 'site_name' => 'Lorem Ipsum',
    // 'lang' => 'en',
    // 'home' => $pages->get(1),
]);

 

Share this post


Link to post
Share on other sites

Hey @bernhard!

That's an interesting question, but sadly one that I don't have an easy answer to. I've never used (read: needed) custom Page classes myself, so not entirely sure of the best approach.

What I can say, though, is that accessing _wireframe_layout property directly is a potentially risky approach: this property is intended for internal use, and there's no guarantee that what you're doing will keep working in future Wireframe versions. If it turns out to be the only viable solution, then maybe this can be rethought, though (now that I'm aware that someone might access it directly, I'll try to keep this in mind that making changes to this may cause issues.)

To answer your question about the method not working, the big question is: at what point does TeaserPage::__construct() execute? This is very likely the issue here; the hooks attached by Wireframe are not available yet.

Note also that Wireframe is an output framework and only intended to run when a Page is being rendered, not when a Page object is being constructed. If you initialize Wireframe in __construct(), you'll likely end up running some Wireframe features in Admin etc. Even if this has no negative consequences, at the very least it's going to result in some unnecessary overhead 🙂

--

What I would currently recommend:

Typically one would set the layout in a) Wireframe bootstrap file (wireframe.php), or b) the Controller class. This would be the easy solution, and also the one that is most in line with how Wireframe is intended to work.

--

Now, if you really wanted to do this in __construct(), you'd have to make sure that Wireframe is initialized first. Assuming that this method runs after the API is ready — which it might not, mind you! — you could perhaps do something like this:

__construct(Template $tpl) {
    parent::__construct($tpl);
    $this->wire('modules')->get('Wireframe')->initOnce();
    $this->setLayout('teaser');
}

... or just access the _wireframe_layout property directly. That way you wouldn't have to (mostly unnecessarily) load Wireframe in Admin etc. Though again, that's not guaranteed to keep working in future versions, and overall I'm not sure that this is the most sensible approach 🙂

--

Going forward, this could be something that needs more attention, though since it's not something I personally use (or see myself using much in the future for that matter) I don't have a whole lot of insight into this whole subject.

1 hour ago, bernhard said:

The docs say that the layout can be defined via hook, but I could not find an example? Thx

I'm not sure which part of the docs you're referring to, but there are many ways to do this. For an example one could hook into Wireframe::render() and alter the layout there. Or hook to Page::render(), or...

There are too many places to list, I'm afraid 🙂

Share this post


Link to post
Share on other sites

Thx for the quick headsup @teppo

2 minutes ago, teppo said:

What I can say, though, is that accessing _wireframe_layout property directly is a potentially risky approach

Yeah, I thought so, that's why I was asking 🙂 

2 minutes ago, teppo said:

To answer your question about the method not working, the big question is: at what point does TeaserPage::__construct() execute? This is very likely the issue here; the hooks attached by Wireframe are not available yet.

Note also that Wireframe is an output framework and only intended to run when a Page is being rendered, not when a Page object is being constructed. If you initialize Wireframe in __construct(), you'll likely end up running some Wireframe features in Admin etc. Even if this has no negative consequences, at the very least it's going to result in some unnecessary overhead 🙂

Yeah, that's what I tried! Please see edit3 in the above post! I've just tried making the page a regular PW page and still it does not work 😞

// init Wireframe
$wireframe = $modules->get('Wireframe');
$wireframe->init();

// render the page
$page->setLayout('teaser');
echo $wireframe->render([
    // 'site_name' => 'Lorem Ipsum',
    // 'lang' => 'en',
    // 'home' => $pages->get(1),
]);

 

Share this post


Link to post
Share on other sites

@bernhard, sorry by the way, looks like your edits were not visible yet when I wrote the answer. Quick answers:

3 hours ago, bernhard said:

Edit: The solution above is not possible: You must assign a template to the page before setting custom field values (_wireframe_layout)

Right. To be honest this falls to the area of which I have no experience whatsoever, but apparently ProcessWire prevents setting any values at this point 🙂

3 hours ago, bernhard said:

Edit2:


class TeaserPage extends Page {

  public function init() {
    $this->addHookBefore("Wireframe::render", function($event) {
      bd('fired');
      bd($event->page->getLayout(), 'layout');
      $event->page->setLayout('teaser');
      bd($event->page->getLayout(), 'layout');
    });
  }

}

This hook fires but does not change the layout?

GnOGb4J.png

When Wireframe::render() is called, Page::render() has already been called earlier, so this has no effect on current render page request. When you're in the wireframe bootstrap file or Controller file and want to change the layout used for current request, you need to go through the $view object:

$this->view->setLayout('teaser');

(In the case of your hook, I believe $event->view->setLayout() should work. Though not sure I've actually tried to do this.)

Page object methods are mostly intended for cases where you want to render some other page, i.e. something like this:

<?php foreach ($page->children as $child): ?>
    <?= $child->setLayout(null)->setView('card')->render() ?>
<?php endforeach; ?>

This is likely something I need to clarify in the docs.

3 hours ago, bernhard said:

Edit 3: Even this does not work?!


// init Wireframe
$wireframe = $modules->get('Wireframe');
$wireframe->init();

// render the page
if($page->template == GemeindeGuru::tpl_teaser) {
    $page->setLayout('teaser');
}

echo $wireframe->render([
    // 'site_name' => 'Lorem Ipsum',
    // 'lang' => 'en',
    // 'home' => $pages->get(1),
]);

 

Same issue here.

  • Thanks 1

Share this post


Link to post
Share on other sites

@bernhard, here's a quick test based on your hook approach:

$wire->addHookBefore('Wireframe::render', function(HookEvent $event) {
	$event->view->setLayout('default');
});

At least from init.php this works as expected. In my own projects I would rather set this in Controller or wireframe.php, though, since this is a little unexpected (and impossible to override without modifying the custom Page class itself, though not sure if that's a problem for your use case.)

  • Thanks 1

Share this post


Link to post
Share on other sites

Thx again @teppo 🙂 

I hope you don't mind if I throw some suggestions here while I'm exploring wireframe 🙂 One more suggestion for the docs:

I think this docs page is a little bloated. Don't get me wrong, I really appreciate the huge amount of informations, but for me it would be easier to start from the minimum working setup and then expand from that. See the docs here: https://wireframe-framework.com/docs/view/components/

It seems to be quite complicated to create components, while all you actually have to do is this:

/components/Card.php

<?php namespace Wireframe\Component;
class Card extends \Wireframe\Component {
}

/components/Card/default.php

I am a Card component!

/layouts/default.php

echo Wireframe::component("Card");

Once I got that via try&error your "advanced" example was great 🙂 

PS: Maybe add a note that /components/Card/default.php could be replaced by adding a render method in /components/Card.php 🙂 

  • Like 3

Share this post


Link to post
Share on other sites
2 minutes ago, bernhard said:

I hope you don't mind if I throw some suggestions here while I'm exploring wireframe 🙂 

Keep 'em coming! 🙂

I'm currently digging into a few Wireframe core things based on your earlier comments. It's really helpful to hear how others are using / getting started with Wireframe, and what sort of troubles they may be running into while doing it. Your comments have already been quite helpful, and I may be able to simplify / streamline some things in the next release.

Docs definitely need some love. I'm still learning how to write useful documentation — one that doesn't omit anything that might prove out to be valuable later on, yet still feels "light enough" to get you up and running quickly. Every time I dive into a new library or language or something like that I seem to run into the very same "just give me the damn facts so that I can try it!" thing. I mean... yes, I get that this is all important and all, but do I really need to know all that just to give it a quick try... 😄

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, teppo said:

@bernhard, here's a quick test based on your hook approach:


$wire->addHookBefore('Wireframe::render', function(HookEvent $event) {
	$event->view->setLayout('default');
});

 

Thx, here is my working version 🙂

class TeaserPage extends Page {

  public function init() {
    $this->addHookBefore("Wireframe::render", function($event) {
      if(!$event->page instanceof self) return;
      $event->view->setLayout('teaser');
    });
  }
}

 

1 hour ago, teppo said:

In my own projects I would rather set this in Controller or wireframe.php

Hm. That confuses me a little. What I'm doing is basically just creating a "coming soon" page. So I'd really just need a template for that. Let's call it "teasertpl". Now to keep things managable in bigger projects I learned that custom pageClasses are a HUGE help. There are always little tweaks we have to do, right? And we all love PW and hooks, right? But in my experience this can quickly get extremely chaotic.

My first approach was to split ready.php into several sections and do this:

// ready.php
include('foo.php'); // all foo related things
include('bar.php'); // all bar related things

The problem with that approach? One day you need to add a hook on init() instead of ready() and all the trouble begins...

Using pageClasses on the other hand is really clean (and also easy):

class MyPage extends Page {

  public function init() {
    // add init hook
  }

  public function ready() {
    // add ready hook
  }

}

I love that approach, because you can even use class constants like MyPage::foofield and you can quickly open that file in VSCode via CTRL+P, type "MyPage", boom --> all there! Want to hook the page edit screen for MyPage pages? Add a buildForm hook on that pageClass! Want a custom random pagename? Add a saveReady hook. Want to auto-publish MyPage pages? One additional line in the saveready hook. 😎

And you get all that by just setting the pageClass in RockMigrations:

$rm->migrate([
  'templates' => [
    'my-page' => ['pageClass' => 'MyPage'],
  ],
]);

Ok sorry, I got a little off-topic, but that was the context 😄  Back to your statement...

1 hour ago, teppo said:

In my own projects I would rather set this in Controller or wireframe.php

Ok, so I have this single "teaser" template and want to render "layouts/teaser.php". I dont have (want/need) a Controller, because that's really just a single page with a logo. Where would you place the setLayout() ? In wireframe.php like this?

$page->view->setLayout('teaser');

This throws an error Call to a member function setLayout() on null

Maybe I'm missing something again? I must admit that it does not feel like I really got the concept yet 😅 

Or would I use a view for something like this that uses the default layout? My idea was to have one default layout for all other templates of this website and one for this "coming soon" page. The 404 page would also be a candidate for a custom layout where I don't need any menus, headers, footers etc.;

Or would you create ONE layout for all that and then show/hide sections based on the page (template) that is currently viewed?

Thx a lot for you help! I'm really looking forward to implementing a streamlined workflow for my projects. Yesterday I had to work on an old project and I got lots of "where did I place this, where did I define that, what the ** did I do here?" moments 😂

  • Thanks 1

Share this post


Link to post
Share on other sites
1 hour ago, bernhard said:

Ok, so I have this single "teaser" template and want to render "layouts/teaser.php". I dont have (want/need) a Controller, because that's really just a single page with a logo. Where would you place the setLayout() ? In wireframe.php like this?


$page->view->setLayout('teaser');

This throws an error Call to a member function setLayout() on null

Maybe I'm missing something again? I must admit that it does not feel like I really got the concept yet 😅 

No worries 🙂

The error is pretty clear: there's no $page->view, but in the context of the wireframe bootstrap file you can access $this->view (or wire('view') / $this->wire('view'), that's the same thing). API variable $view — behind the scenes it's an instance of Wireframe\View, which in turn extends TemplateFile — is basically how you access the View layer (layouts, view files, etc.) from a) the bootstrap file, or b) controller classes.

What I often do is something along these lines:

if ($page->is('template=some|template|or|another')) {
    $this->view->setLayout('teaser');
}

Although if it's just about this one template, then I typically create a controller class that sets this. This keeps things neatly in context — just like you mentioned about custom Page classes 🙂

<?php namespaces Wireframe\Controller;
class TeaserController extends \Wireframe\Controller {
    public function render() {
        $this->view->setLayout('teaser');
    }
}

Note: personally I don't mind having a Controller that is as simple as the one above 🤷‍♂️

Originally Controllers were built to solve more or less the same things that can now often be dealt with custom Page classes, though personally I still prefer the Controller approach. That being said, your idea about handling init/ready hooks is interesting. In a typical project I don't have that many hooks, so I usually just stash them in init.php/ready.php, or perhaps create a module for a specific (bigger) feature (particularly if they apply to more than one template), but this might be worth exploring further in some future project.

(Also a little off-topic, but we've bundled a bunch of "very often needed" hooks into a shared module that we install on all our new projects, and I personally try to keep hooks to minimum — they are powerful, but the more you have the more complex the project gets. Basically the same thing you said about it getting chaotic; I try to fend that chaos off by saying "no" to new features/hooks/etc. as often as possible 😅)

1 hour ago, bernhard said:

Or would I use a view for something like this that uses the default layout? My idea was to have one default layout for all other templates of this website and one for this "coming soon" page. The 404 page would also be a candidate for a custom layout where I don't need any menus, headers, footers etc.;

Or would you create ONE layout for all that and then show/hide sections based on the page (template) that is currently viewed?

This does sound like a good use case for multiple layouts. Again I try to keep my layouts pretty "dumb": in my experience too many bells and whistles and flags and toggles result in unmaintainable code. Everything that is specific to a single template generally belongs to view files instead of layouts; sometimes when I have a single page with a completely custom look — say, a campaign one-pager within a bigger site — I disable layout completely for that page, and put everything in the view (as long as it doesn't get massively complex in itself... in which case components and partials can come in handy) 🙂

2 hours ago, bernhard said:

I'm really looking forward to implementing a streamlined workflow for my projects. Yesterday I had to work on an old project and I got lots of "where did I place this, where did I define that, what the ** did I do here?" moments 😂

 I feel your pain 😛

This was the main motivation for creating the original pw-mvc "framework". Imagine that you've got a team of developers all working on their own projects and inventing their own ways to structure things and solve the same issues. And not just some one-off projects that get handed to a client, but ones that the team has to maintain for many, many years to come.

Proper level of standardization == happiness.

  • Like 2

Share this post


Link to post
Share on other sites

Thx @teppo, greatly appreciated!

30 minutes ago, teppo said:

The error is pretty clear: there's no $page->view, but in the context of the wireframe bootstrap file you can access $this->view (or wire('view') / $this->wire('view'), that's the same thing). API variable $view — behind the scenes it's an instance of Wireframe\View, which in turn extends TemplateFile — is basically how you access the View layer (layouts, view files, etc.) from a) the bootstrap file, or b) controller classes.

Thx, that clarifies it. $page->wire->view does also work 🙂 

30 minutes ago, teppo said:

Originally Controllers were built to solve more or less the same things that can now often be dealt with custom Page classes, though personally I still prefer the Controller approach. That being said, your idea about handling init/ready hooks is interesting. In a typical project I don't have that many hooks, so I usually just stash them in init.php/ready.php, or perhaps create a module for a specific (bigger) feature (particularly if they apply to more than one template), but this might be worth exploring further in some future project.

Well, the great thing about custom pageClasses is that they do also work on the backend. You can use them for hooking the pageedit form for example. Or today I did some custom redirects when a page is save+exit'ed. This really belongs to the page object imho, so it makes sense to have everything in a class extending the Page class. I'm not sure yet where to draw the line between page classes and controllers for wireframe...

30 minutes ago, teppo said:

This was the main motivation for creating the original pw-mvc "framework". Imagine that you've got a team of developers all working on their own projects and inventing their own ways to structure things and solve the same issues. And not just some one-off projects that get handed to a client, but ones that the team has to maintain for many, many years to come.

Proper level of standardization == happiness.

👍

Hm, may I ask for one more advice so that I can continue working on that tomorrow...

My need is quite common, I think:

Templates:

  • home
  • foo
  • bar

Then I want to have 2 layouts:

  • frontpage
    • body sections: header, search, slider, quotes, news, ..., cta, footer
  • default
    • body sections: header, search, main, footer

The links should be:

  • home.php --> frontpage
  • foo.php --> default
  • bar.php --> default

My question is about how you structure such a setup. When using custom "dump" layout files, the <head> for both layouts would be duplicated (same assets, same title, same meta tags etc). If I added a third layout I'd have 3x the same code...

Or do you do something like this?

<head>
  <?= $partials->head->title ?>
  <?= $partials->head->seo ?>
  <?= $partials->head->assets ?>
</head>

I guess placeholders come into play a little later on the road when I start outputting template specific things? Like news-cards with page content of that news-page?

Sorry for all those beginner questions 🙂 

Share this post


Link to post
Share on other sites
17 hours ago, bernhard said:

Well, the great thing about custom pageClasses is that they do also work on the backend. You can use them for hooking the pageedit form for example. Or today I did some custom redirects when a page is save+exit'ed. This really belongs to the page object imho, so it makes sense to have everything in a class extending the Page class. I'm not sure yet where to draw the line between page classes and controllers for wireframe...

The way I see it: if it's output-related, I prefer to put it into a Controller.

This is the layer that handles everything that isn't needed in Admin or when loading the page outside Wireframe render context (hooks etc.) In my "ideal project" Controllers provide the backend implementation for the front-end; an API of sorts. I prefer to keep my view files as "low-logic" as possible, and everything that looks like code (external API calls, database queries, complex loops that iterate over content and convert it into some sort of list, etc.) go to Controllers instead.

Controllers also provide out-of-the-box some caching features that I've found pretty handy: all Controller method return values go into run-time cache (non-persistent, for a single request), and they have built-in support for persistent caching. In the next release Controllers will also interact with the new companion module WireframeAPI. And yes, obviously one of the reasons I personally prefer Controllers over custom Page classes is that I can bundle in features that I find useful in multiple (most) projects 😛

Note that while Controllers are by default tied to a specific Template (TeaserController etc.) that's not a must — especially in the later versions of Wireframe this line gets blurrier (is that a word?) and in some situations a single Controller might be applicable to more than one Template.

All that being said, it's true that the line between custom Page classes and Controllers can be a little blurry, and it's often a matter of preference 🙂

17 hours ago, bernhard said:

Hm, may I ask for one more advice so that I can continue working on that tomorrow...

...

My question is about how you structure such a setup. When using custom "dump" layout files, the <head> for both layouts would be duplicated (same assets, same title, same meta tags etc). If I added a third layout I'd have 3x the same code...

Or do you do something like this?


<head>
  <?= $partials->head->title ?>
  <?= $partials->head->seo ?>
  <?= $partials->head->assets ?>
</head>

I guess placeholders come into play a little later on the road when I start outputting template specific things? Like news-cards with page content of that news-page?

  • For <title> tag and SEO tags (metadata) I use a Markup module. Sadly the module that we use in our projects is not publicly available (yet... @Fokke, what do you think, would you mind releasing it? 😉)
  • Assets often these depend on the layout, so I typically don't bother to convert them to a partial. Unless it's something like a GA or chatbot tag that also includes a snippet of code and an API key or something, those I prefer to split into partials for maintainability.

Now, I'm not entirely sure if I really "got" your question, but there are multiple ways to do this, and I don't think any of them is really "wrong":

  • If you have multiple layouts that share some parts, you could convert those shared parts to partials, components, or even markup modules, depending on the complexity of each of these parts.
  • If it's a one-off thing, i.e. all templates have essentially the same layout except that the Teaser template needs to include JS library X in the head section, sometimes the easy way out is to include a template-specific if statement in a shared layout file. (Emphasis on "sometimes".)
  • If it's something "repeatable", like an array of JavaScript file URLs, you could define it in wireframe.php, Controller class, etc. and then iterate those items and output something like script tags in the layout.
  • ... or you could define a placeholder:
// layouts/default.php
<head>
  <title><?= $page->title ?></title>
  <?= $partials->head->seo ?>
  <?= $placeholders->head ?>
</head>

// views/Teaser/head.php
<meta name="author" content="<?= $page->author ?>">
<script src="my-awesome-library.js"></script>

Placeholders are just a way to "inject" a specific view of a template (or multiple templates) into a predefined location in the layout file. Placeholder slots can also be filled from Controller, though I've found little use for that; sometimes this might be handy if you, say, have a Markup module that should generate the placeholder content, and you already have a controller class. Or something along those lines 🤔

In some relatively simple projects I've had a single layout file, and then placeholders for a) custom stuff in the head area, b) aside content, and c) main content (default view — technically this is a placeholder as well). This works nicely if the page layout is the same across all / most templates, and they just need to inject their own content elements into specific locations 🙂

  • Thanks 1

Share this post


Link to post
Share on other sites

Hi @teppo

Thx, it made click in my head 🙂 I'll write about my journey and come up with suggestions at the end of the post:

First try:

<body>
  <?php
  if($page->is(['template' => [GemeindeGuru::tpl_teaser]])) {
    echo $partials->{GemeindeGuru::tpl_teaser}->body;
  }
  else {
    echo Wireframe::component("Header");
    echo Wireframe::component("Search");
    echo Wireframe::component("Slider");
  }
  ?>
</body>

Nice, this already works 🙂

2nd try:

<body>
  <?php
  switch($page->template->name) {
    case GemeindeGuru::tpl_teaser:
      echo $partials->{GemeindeGuru::tpl_teaser}->body;
    break;
    default:
      echo Wireframe::component("Header");
      echo Wireframe::component("Search");
      echo Wireframe::component("Slider");
    break;
  }
  ?>
</body>

A lot better imho, but what if I wanted to add a custom 404 design? I'd need to duplicate the switch case:

<body>
  <?php
  switch($page->template->name) {
    case GemeindeGuru::tpl_teaser:
      echo $partials->{GemeindeGuru::tpl_teaser}->body;
    break;
    case GemeindeGuru::tpl_404:
      echo $partials->{GemeindeGuru::tpl_404}->body;
    break;
    default:
      echo Wireframe::component("Header");
      echo Wireframe::component("Search");
      echo Wireframe::component("Slider");
    break;
  }
  ?>
</body>

Not bad as well, but I wanted something better 🙂 Next try:

<body>
  <?php
  $body = $partials->{$page->template->name}->body;
  if($body) echo $body;
  else {
    echo Wireframe::component("Header");
    echo Wireframe::component("Search");
    echo Wireframe::component("Slider");
  }
  ?>
</body>
ProcessWire\WireException
Partial file not found: C:/laragon/www/kaumberg/site/templates/partials/home.php

Fast-forward: I did some more testing and realized that I was actually mixing up partials and placeholders! The final (and great) version looks like this:

// layouts/default.php
<body>
  <?php
  if($placeholders->body) echo $placeholders->body;
  else {
    echo Wireframe::component("Header");
    echo Wireframe::component("Search");
    echo Wireframe::component("Slider");
  }
  ?>
</body>

// views/home/body.php
echo Wireframe::component("Header");
echo Wireframe::component("Search");
echo Wireframe::component("Slider");
echo Wireframe::component("News");
echo Wireframe::component("Contact");
echo Wireframe::component("Footer");

// views/teaser/body.php
<div class='uk-height-viewport uk-flex uk-flex-center uk-flex-middle'>
  <div class='uk-text-center'>
    <div><img src='mylogo.svg' class='logo'></div>
    <div class='teaser'><?= $page->title ?></div>
  </div>
</div>

This simply means: If the view file /views/[template]/body.php exists, render it, otherwise render the default sections. That should make it really easy to create custom layouts for some templates whily still having all other assets and scripts in place (like seo, styles, analytics etc). I think I'm happy with this setup 🙂 

I realized, that I was missing the link between $placeholders->foo and /views/[template]/foo.php

I read the docs again and I saw that this information IS already there 🙂 My personal addition to this sentence: If you know where you have to look 😉 

wireframe_layout_view-placeholder-view.1

This illustration makes a lot of sense now. But I didn't get it quickly I have to admit.

Here's my conclusion and suggestion:

IMHO your docs are great explaining "how it works", but they could be improved in regards of "how I can work with it". In my experience this can make a huge difference in the experience of someone looking at it the first time. It's basically what you said earlier:

20 hours ago, teppo said:

Docs definitely need some love. I'm still learning how to write useful documentation — one that doesn't omit anything that might prove out to be valuable later on, yet still feels "light enough" to get you up and running quickly. Every time I dive into a new library or language or something like that I seem to run into the very same "just give me the damn facts so that I can try it!" thing. I mean... yes, I get that this is all important and all, but do I really need to know all that just to give it a quick try... 😄

Exactly that! I think if the docs where split into two sections, the first being "quickstart" and the second being what you already have, that would be brilliant. I know that what I'm asking for is a lot of work, but if we (you 😄) want to build some kind of standard, it should be as easy as possible for everybody to adopt to it 🙂 

Finally I've got a question/request that you have to review from a technical point of view. As stated above, it took some time for me to grasp this easy concept:

$placeholder->default   loads /views/MyTemplate/default.php
$placeholder->head      loads /views/MyTemplate/head.php

If you look at the rest of wireframe this becomes quite obvious:

echo Wireframe::component("Header")        --> /components/Header/default.php
echo $partials->menu->top()                --> /partials/menu/top.php
echo $placeholders->body                   --> /views/[template]/body.php

This really confused me when starting with wireframe! I'd expect placeholders to live in /placeholders or, the other way round, the variable to be called $views

echo Wireframe::component("Header")        --> /components/Header/default.php
echo $partials->menu->top()                --> /partials/menu/top.php
echo $views->body                          --> /views/[template]/body.php

These 3 lines would make it obvious what options you have in your layout file. But maybe that's because I've already a better understanding of what's going on, so maybe other newcomers could comment on this?

What I was really missing was a guide that helps me go through a typical web project: You got a design, you got a PW installation, you installed Wireframe, what then?

Now after 2 days of testing this was the way that worked great for me (so far):

 

  • Set wireframe as alternate template file for all templates that should be handles via wieframe (for this walkthrough we take home.php and basic-page.php of the default site profile).
  • Create a default layout: /layouts/default.php
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title><?= $page->title ?></title>
    </head>
    <body>
      <h1>I am the default layout</h1>
    </body>
    </html>
  • Open the root page of your site and the 404 page --> both should display the default layout

  • We want a common page header and footer that is displayed on all templates. Shared markup with little to none business logic is best placed in a partial.

  • Create 2 files:

    /partials/header.php
    <div style='border: 1px solid blue; padding: 20px;'>I am the page header</div>
    
    /partials/footer.php
    <div style='border: 1px solid red; padding: 20px;'>I am the page footer</div>
  • Now you can output these partials in your layout's body:

    <body>
      <?= $partials->header ?>
      <h1>I am the default layout</h1>
      <?= $partials->footer ?>
    </body>

     

  • What if we wanted a slider on the frontpage? And ONLY on the frontpage? That's what view files are for and they are accessed via $placeholders. (Don't you think that this is really misleading?)
    /views/home/slider.php
    <div style='padding: 20px; border: green;'>I am a SLIDER</div>

    And add it to the layout:

    <body>
      <?= $partials->header ?>
      <?= $placeholders->slider ?>
      <h1>I am the default layout</h1>
      <?= $partials->footer ?>
    </body>
  • Now open both pages on the frontend and you'll see that the slider does only show up on pages having the [home] template. That magic is simply applied via file structure /views/[template]/slider.php
  • Ok now we want to add the current local time of the user visiting the page. That will obviously need some PHP logic and as we want to separate concerns as much as possible we do NOT want to put that code into a partial. Instead, we create a component that consists of business locig (a PHP class extending \Wireframe\Component and a view file that is responsible for the output.
     
    <?php namespace Wireframe\Component;
    class LocalTime extends \Wireframe\Component {
      public function render() {
        return "<div>
          Your timezone: <strong>XXX</strong><br>
          Your local time: <strong>XXX</strong>
          </div>";
      }
    }
    And add it after the footer of our layout:
    <body>
      <?= $partials->header ?>
      <?= $placeholders->slider ?>
      <h1>I am the default layout</h1>
      <?= $partials->footer ?>
      <?= Wireframe::component('LocalTime') ?>
    </body>
    Nice! We get the output at the bottom of all of our pages!
     
  • We know that doing lots of output within php classes is ugly, so we refactor our setup a little and adopt another concept of wireframe: View files for components:
    /components/LocalTime.php
    <?php namespace Wireframe\Component;
    class LocalTime extends \Wireframe\Component {
    }
    
    /components/LocalTime/default.php
    <div>
      Your timezone: <strong>XXX</strong><br>
      Your local time: <strong>XXX</strong>
    </div>

    Of course, this is no a lot better for now, but it is a lot more future proof! Check your frontend, it should still display the same markup for your local time.

  • Now we can add the "business logic" to our component:
     

    <?php namespace Wireframe\Component;
    
    use ProcessWire\WireData;
    use ProcessWire\WireHttp;
    
    class LocalTime extends \Wireframe\Component {
    
      public function __construct() {
        $http = new WireHttp();
        $data = new WireData();
        $data->setArray($http->getJSON("http://worldtimeapi.org/api/ip"));
        $this->timezone = $data->timezone;
        $this->unix = $data->unixtime;
      }
    
    }

    And update our view to output the component properties (note how they magically become available as view variables!):
     

    <div>
      Your timezone: <strong><?= $timezone ?></strong><br>
      Your local time: <strong><?= $unix ?></strong>
    </div>

    Go to your frontend and see the magic!
     

    Your timezone: Europe/Vienna
    Your local time: 1598007959

    What, you don't want a unix timestamp? Ok... let's provide a formatter method so that you can easily output the time in different formats:

    <?php namespace Wireframe\Component;
    
    use ProcessWire\WireData;
    use ProcessWire\WireHttp;
    
    class LocalTime extends \Wireframe\Component {
    
      public function __construct() {
        $http = new WireHttp();
        $data = new WireData();
        $data->setArray($http->getJSON("http://worldtimeapi.org/api/ip"));
        $this->timezone = $data->timezone;
        $this->unix = $data->unixtime;
      }
    
      public function date($format = "d.m.Y H:i:s") {
        return date($format, $this->unix);
      }
    
    }

    @teppo now I got stuck again 😄 How would I display that formatted date in my view file for this component? Or would I need to provide the date format as variable for the constructor ( echo Wireframe::component('LocalTime', ['format' => 'Y-m-d']); )

PS: Regarding the last question. Controllers do expose methods to views. Is there a similar concept for components? I did implement the local time as component because this does not belong to a single template (then it would have been a controller, right?)

  • Like 4

Share this post


Link to post
Share on other sites
On 8/20/2020 at 4:49 PM, bernhard said:

Using pageClasses on the other hand is really clean (and also easy):


class MyPage extends Page {

  public function init() {
    // add init hook
  }

  public function ready() {
    // add ready hook
  }

}

@Zeka wrote me that this snippet does not work for him...

That's because I have a custom pageClass loader in RockMigrations that triggers the init(): https://github.com/BernhardBaumrock/RockMigrations/blob/a034abce7e7fa5436756a2506d9d5f17d8a1b361/RockMigrations.module.php#L1733

So the ready() method in that case would not be triggered still, but you can easily trigger that yourself. the key is that you keep the hooks in the class itself for maintainability!

  • Like 1

Share this post


Link to post
Share on other sites
8 hours ago, bernhard said:

What, you don't want a unix timestamp? Ok... let's provide a formatter method so that you can easily output the time in different formats:

  • @teppo now I got stuck again 😄 How would I display that formatted date in my view file for this component? Or would I need to provide the date format as variable for the constructor ( echo Wireframe::component('LocalTime', ['format' => 'Y-m-d']); )

PS: Regarding the last question. Controllers do expose methods to views. Is there a similar concept for components? I did implement the local time as component because this does not belong to a single template (then it would have been a controller, right?)

Currently the component view file has no way to access the component class. Instead the component class has to pass all the required variables to the view. View can obviously perform actions that seem sensible in that context (such as formatting a date).

have considered adding access to component methods but wasn't entirely sure if it was a good idea. This is still on my todo list and I'll give it more thought in the near future, likely after 0.12.0 has been released. On one hand it might be handy (and in line with how controllers/views behave), but on the other hand every new feature introduced to the module increases complexity, so I want to make sure that it also provides substantial value.

Decisions, decisions 🙂

Your current options would be to a) pass the variable formatted from the controller class to the vie file — and if individual instances of this component could require different formats, then passing the format as a parameter is indeed a sensible approach — or b) handle formatting in the component view file (and again optionally pass the format as a parameter to the component and from there to the view file).

--

I'll have to get back to some of your other points — which were really good, by the way! — later 🙂

  • Like 1

Share this post


Link to post
Share on other sites
11 hours ago, teppo said:

On one hand it might be handy (and in line with how controllers/views behave), but on the other hand every new feature introduced to the module increases complexity, so I want to make sure that it also provides substantial value.

Totally agree and please don't forget to validate all my requests before implementing (as you already do) 🙂 I'm just throwing in ideas and questions as they arise.

What would really be great would be some kind of illustration of the process that one has to go through when adding content to a site (layout). As you said: Decisions, decisions 😄 They should be made as simple as possible 🙂 

Wote4fD.png

built with https://www.yworks.com/products/yed (free and all major platforms); Of course this is totally simplified and missing other decisions that have to be made, like "could it be reused in other projects? --> build a module"

11 hours ago, teppo said:

Currently the component view file has no way to access the component class. Instead the component class has to pass all the required variables to the view. View can obviously perform actions that seem sensible in that context (such as formatting a date).

Hm. One thing that made me really curious about wireframe was this:

Quote

Other public methods are made available as params in Layouts and View Files

If you have a public method called myName() in a Controller class, accessing $this->myName in a layout or view file for related template will refer to the return value of said method.

Thx for the clarification! This feels like a quite big limitation for me. What if I have some piece of code that is needed on several templates (controllers are out) that has some business logic? Should I create a pw module for that? I think that's often too much overhead (you need to install it, define moduleinfo stuff etc). Imho there should be an easy way to handle such situations. Components sound great, but accessing methods should also be easy. I've now tried this:

<?php namespace Wireframe\Component;
class Slider extends \Wireframe\Component {

  public function __construct() {
    $this->component = $this;
  }

  public function foo() {
    return "foo!";
  }
}
<p class='uk-container'>I am a slider! <?= $component->foo() ?></p>
I am a slider! foo!

Ok, that would be a quite easy solution. I'm wondering 2 things though:

  1. Couldn't (shouldn't) that or something like that happen automatically? Just like it's done in controller view files?
  2. Shouldn't Controllers and Components work the same with the only difference that one is applied automatically to the loaded template and the other one has to be loaded manually via Wireframe::component('foo') ? Or are there any other important differences that I'm missing?

---

Thx for all your efforts on this!! 🙂 

11 hours ago, teppo said:

I'll have to get back to some of your other points — which were really good, by the way! — later 🙂

Looking forward 😎

 

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Cybermano
      Food Allergens Module
      A simple List of Food Allergens
      My needs were to provide a simple list of food allergens for our clients with restaurant related activity.
      The idea was to simply output the list (to speed up the data entry) without leaving the food menu editing, eg. opening another page in new tab or window.
      This isn't a perfect solution, but it works fine for my needs and I decided to share the base idea.
      This could also be easily used to show little notes or short "vademecum", not only for the list of food allergens.
      ---
      Main features
      The basis
      All moves from a short editing of the module in this tutorial: How to create custom admin pages by @bernhard
      First of all it creates an empty admin page, with a dedicated permission to let safe-user to see it (this permission has to be created as a new ones, manually or by the module).
      Once the page is created, I have hooked its behaviour into the ready.php, to show the content (basically a list).
      A step further
      With the tips of  @bernhard, @Soma (and many others), see here , the magic happens. 
      The new page will be shown as a panel, so editors will not abandon their data entry to have a quick view to the list.
      A little further
      Why scroll to the top of the page to click a link?
      The next step was to create a sticky button only in the food menu pages.
      Again with a @bernhard tip I moved into the customization of this simple module and the related hook.
      ---
      How to use this module
      After installed, it creates the page /admin/page/allergens/ and the module is to be setted up. The first field is a CKEditor with multi-language. This is the place where to write the informations that will be shown into the page. The next field is a simply text-area where to place a bit of JS that will be appended to the markup of the text (omit the 'script' tags). I also putted a checkbox with a silly statement: this to think at least twice on the safety of the written JS. Now comes the first way to display the link to the page
      Field Note with Link. Enable and save it. The module will display a new row with 4 selects (1 standard and 3 ASM):
      View mode (to show the page as Panel or as Modal PopUp); Templates to select: select one or more and save before proceed, so the  asm-select of the pages will be populated showing all the pages of the selected templates. Pages to select: also here select at least one and save before proceed to populate the asm-select for fields only with the ones that belong to the selected pages. Select the fields where to place the note and save again. That's all: now you will find into the notes of the selected fields the link "See the List of Allergens".
      At the same way, the option for the sticky button, but with a plus
      The field select is obviously unnecessary, but you could play with the last row: the inline styles to fix your sticky button where you like. Here you could set the sticky position of the <div> and the absolute ones of the <a>.

      Video Explanation
      In these screencasts you could see a custom JS that show a "copy" button near a "hanna-code" call.
      This because I've set a specific one for each allergen to show up a tooltip in the front end.

      Registrazione #33.mp4  

      Registrazione #34.mp4 ---
      Last but not the least
      Actually it works fine for my needs, even if it's much improvable: I'm working on the permissions creation, the uninstall section, a separate configs and defaults and how to include the hook into the module leaving free the ready.php. According to a simpler uninstall. Also I would make the link text as a dynamic text field, so it will be more flexible.
      I always learn a lot here, so I would share my code for whom it could be interested.
      I removed the hanna code references, but I provide you the html list of the allergens, English and Italian too, so you can paste them into the "source" of the CKEditor field to have a ready to use module.
      Obviously you are free to modify the code as per your needs.
      Please, keep in mind that I'm not a pro coder and I beg your pardon for my verbosity (speaking and coding). 😉
      I hope be helpful or for inspiration.
      Bye
      ready.phpList-ITA.htmlList-ENG.htmlAllergens.module
      README.md
    • By Robin S
      This module is sort of an upgrade to my earlier ImageToMarkdown module, and might be useful to anyone working with Markdown in ProcessWire.
      Copy Markdown
      Adds icons to images and files that allow you to copy a Markdown string to the clipboard. When you click the icon a message at the top left of the screen notifies you that the copying has occurred.
      Screencast

      Note: in the screencast an EasyMDE inputfield is used to preview the Markdown. It's not required to use EasyMDE - an ordinary textarea field could be used.
      Usage: Images
      When you hover on an item in an Images field an asterisk icon appears on the thumbnail. Click the icon to copy an image Markdown string to clipboard. If the "Description" field is populated it is used as the alt text.
      You can also open the "Variations" modal for an image and click the asterisk icon to copy an image Markdown string for an individual variation.
      Usage: Files
      When you hover on an item in a Files field an asterisk icon appears next to the filename. Click the icon to copy a link Markdown string to the clipboard. If the "Description" field is populated it is used as the link text, otherwise the filename is used.
       
      https://github.com/Toutouwai/CopyMarkdown
      https://processwire.com/modules/copy-markdown/
    • By BitPoet
      I've realized that I've been jumping back and forth between the PW API docs and the source code for site modules far too much. The idea to hold all necessary documentation locally in one place has occurred to me before, but getting PHPDocumentor et al set up and running reliably (and producing readable output) as always been too much of a hassle. Today I was asked how I find the right hooks and their arguments, and that inspired me to finally get my backside down on the chair and whip something up, namely the
      Module Api Doc Viewer
      ProcessModuleApiDoc
      It lets you browse the inline documentation and public (optionally also protected) class/method/property information for all modules, core classes and template files in the ProcessWire instance. The documentation is generated on the fly, so you don't have to remember to update your docs whenever you update a module.
      The module is quite fresh, so expect some bugs there. Behind the scenes it uses PHP-Parser together with a custom class that extracts the information I needed, and the core TextformatterMarkdownExtra module for rendering the description part in the phpdoc style comments.
      This is not a replacement / competitor to the API Viewer included in the commercial ProDevTools package. There is quite some information included in the inline documentation that my module can't (and won't) parse, but which makes up parts of the official ProcessWire API docs.
      This, instead, is a kind of Swiss army knife to view PHPDoc style information and get a quick class or function reference.
      If you feel daring and want to give it a spin, or if you just want to read a bit more, visit the module's GitHub repository.
      This is the overview page under "Setup" -> "Module API Docs":

      And this is what the documentation for an individual class looks like:

      The core module documentation can of course be found online, but it didn't make sense not to include them.
      Let me know what you think!

    • By tcnet
      File Manager for ProcessWire is a module to manager files and folders from the CMS backend. It supports creating, deleting, renaming, packing, unpacking, uploading, downloading and editing of files and folders. The integrated code editor ACE supports highlighting of all common programming languages.
      https://github.com/techcnet/ProcessFileManager

      Warning
      This module is probably the most powerful module. You might destroy your processwire installation if you don't exactly know what you doing. Be careful and use it at your own risk!
      ACE code editor
      This module uses ACE code editor available from: https://github.com/ajaxorg/ace

      Dragscroll
      This module uses the JavaScript dragscroll available from: http://github.com/asvd/dragscroll. Dragscroll adds the ability to drag the table horizontally with the mouse pointer.
      PHP File Manager
      This module uses a modified version of PHP File Manager available from: https://github.com/alexantr/filemanager
       
    • By teppo
      Admin Restrict Branch Select is an add-on / companion module for Admin Restrict Branch. With this module enabled, you can manually select more than one branch parent per user via the branch_parent field, and users with more than one option selected will be able to switch between those while editing site content. Switching between branches is done via a select field injected at the top of the page tree.
      Note that users are still limited to one branch at a time: this module will not make it possible to view multiple branches at the same time.
      When the module is installed, it will automatically update the branch_parent field if deemed necessary, i.e. if it isn't yet configured to allow selecting multiple pages. You can make changes to the field later, if you want to restrict selectable options by template, use asmSelect instead of PageListSelectMultiple, etc. 
      This is an early beta release, so be sure to test carefully before enabling this module in production!
      https://processwire.com/modules/admin-restrict-branch-select/
      https://github.com/teppokoivula/AdminRestrictBranchSelect
×
×
  • Create New...