Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/26/2022 in all areas

  1. Migzen.net is a site we designed and built for Lancaster and Birmingham Universities that gathers academic research into the effect Brexit is having on migration. Obviously we'd best not get into the whole Brexit debate here, so just let me apologise profoundly on behalf of my country and move on to the site... It was a new project, so we were able to create the brand and design from scratch which is always nice. It also gives us the opportunity to make sure colours are accessible for use on the website. As well as the usual blog posts and pages, there's a couple of slightly fancy things we did for them; one of which was to automatically import pages and media whenever they add a new episode to their podcast feeds. We do this using a cron job which loads the MarkupLoadRSS module and parses their XML podcast feeds. If we find any new episodes then we create pages for them with the episode summary from the XML and a nicely styled embedded media player to listen to the audio. We also built them a searchable database of academic resources. That was fairly straighforward, but we do make use of WireCache to build the tag cloud when a page is saved - we use $page->references to work out an article count for each tag which would be expensive to do each page load. There's also a neat looking Timeline which is just built using a repeater field. Honestly PW is ideal for this kind of thing. The site gets good scores in Observatory and Lighthouse (thanks ProCache), and most importantly the client is very happy using PW to keep the site up to date.
    10 points
  2. Just wanted to send a big thanks @flydev ?? for being my first Github sponsor!
    3 points
  3. May I ask you why are you trying to resize svg? Svg are scalable by nature, so my personal approach you be to size them via css or html attributes on the svg tag itself.
    3 points
  4. I've written before about how to use Twig with ProcessWire (see my tutorials on integrating Twig into ProcessWire and extending Twig with custom functionality for ProcessWire). But those posts don't really talk about why I like to use Twig instead of plain PHP templates. For me, this comes down to one killer feature that I'm going to talk about below. But first, let's look at some of the more commonly mentioned advantages of Twig and why I don't actually think they're all that important in the context of ProcessWire: The syntax is nicer. While I personally agree with this, it's entirely subjective (and familiarity is comforting while trying something new is scary). Autoescaping provides security by default. This is true to a degree, but most ProcessWire projects (at least for me) aren't the type of expansive community-driven sites with lots of user-generated content where this would be most relevant. Most of my ProcessWire projects so far have featured a few trusted editors managing content, where you don't really need autoescaping for every template to make sure nobody slips in some malicious code. Twig forces separation of concerns between logic and presentation. Again, this is true, but not relevant to most ProcessWire projects. Most of my ProcessWire projects (and, judging by the showcase, most ProcessWire projects period) are mostly classic brochure sites without a lot of interactivity or app-like behaviour. Those projects are 99% presentation with only some small snippets of logic in between, so separating the two isn't really an issue. With that out of the way, let's talk about the killer feature that makes Twig essential to my work: Inheritance and block-based overwrites. To explain why this is important, I'll start out with a basic template for a header component in PHP and see how it can handle additional content being added to it. Then I'll write the same component in Twig for comparison. If you need a general guide on template inheritance in Twig, read this first: https://twig.symfony.com/doc/3.x/tags/extends.html The reusable header template Here's our basic reusable header template written in PHP: <header class="header"> <h1 class="header__headline"><?= $page->title ?></h1> <?php if ($page->subline): ?> <p class="header__subline"><?= $page->subline ?></p> <?php endif; ?> <?php if ($page->image) echo wireRenderFile('inc/responsive-image.php', ['image' => $page->image]) ?> </header> Sidenote, I'll use wireRenderFile to keep the examples brief, you could also write the image tag inline here. The header component may be included in a page template like this: <?= wireRenderFile('inc/header.php') ?> You have two options for where to do this. Option one is to include this template in every template that needs it (templates/home.php, templates/project.php, templates/news.php). Option two is to use the appendTemplateFile setting to keep the basic page layout in a shared template file that's always included at the end of the request (_main.php). Option one allows you to pass the template different variables depending on context, but it also means you've already started with the code duplication. Option two is probably the more common approach, but with this option you can only pass it one set of variables – those variables might be overwritten by the page template, but this will also lead to some problems as you'll see shortly. Let's introduce our first change request, one particular page needs to display a video instead of an image. No problem, we can just check if the page has a video field and display it conditionally: if ($page->video) { wireRenderFile('inc/video.php', ['video' => $page->video]); } elseif ($page->image) { wireRenderFile('inc/responsive-image.php', ['image' => $page->image]); } This still works fine. But crucially, the logic for the video header is now part of the header template, not part of the template for the page with video headers. This means that every time I want to edit the header template, this little piece of conditional logic is something I have to deal with. But that's fine, multiple pages might need a video header, so having this switch in the header template is acceptable. But then another change request come in: In the page template for some kind of project page, instead of the image, we want to display a list of project data coming from a separate project_data field. Again, we can adjust the template: if ($page->project_data) { wireRenderFile('inc/project-data.php', ['data' => $page->project_data]); } elseif ($page->video) { wireRenderFile('inc/video.php', ['video' => $page->video]); } elseif ($page->image) { wireRenderFile('inc/responsive-image.php', ['image' => $page->image]); } But now some display logic that's specific to one template is part of the global header template, not part of the project.php. This trend will continue: every custom feature required for the header of any page template will inflate the header.php file, and every adjustment requires reading all of it and making sure my change doesn't break any of the other features. This is unsustainable and inherently unscalable. Another example, what if a specific page has both the video and the image fields, but I want to display the image instead of the video? Currently, this is not possible. Now I have to build in some kind of switch: $preferImage = $preferImage ?? false; if ($page->project_data) { wireRenderFile('inc/project-data.php', ['data' => $page->project_data]); } elseif ($page->video && !$preferImage) { wireRenderFile('inc/video.php', ['video' => $page->video]); } elseif ($page->image) { wireRenderFile('inc/responsive-image.php', ['image' => $page->image]); } Again, this solution doesn't scale. Did you notice the subtle bug in there? The noise to signal ratio is becoming worse with every feature. Now you're probably thinking that you would approach those change requests in a different way. Let's look at some of the possible solutions to those problems. Lots of variables You can solve this to a degree by using lots of variables to control what you're template is doing. If we're using a shared _main.php template file that includes the inc/header.php template, the project-specific template (e.g. templates/project.php) is loaded first. So those templates can set some variables that change the content and behaviour of the header component. For example, say you want to do keep the template for the project data block in your project.php so it's easy to find. Let's go back to the original header template and introduce an optional variable that can be used to replace the image with something else: <?= $headerImageContent ?? wireRenderFile('inc/responsive-image.php', ['image' => $page->image]); Now you can set the $headerImageContent variable in your project.php and it will replace the image. But what if I want both the normal image (without duplicating code) AND some custom content? No problem, add even more variables: <?= $headerImageBeforeContent ?? '' ?> <?= $headerImageContent ?? wireRenderFile('inc/responsive-image.php', ['image' => $page->image]); <?= $headerImageAfterContent ?? '' ?> Now repeat that for every part of the header template which might need to be adjusted for some of the page templates (hint: it's all of them). You end up with a template that uses tons of variables, the signal to noise ratio becomes abhorrent. Throw in the fact that those variables are all unscoped, so there's no way to tell where they are being set or overwritten, and variable names have be very specific to avoid collisions. All of this might make sense to you the day you've written it, but what about your colleague that hasn't touched this project yet? What about yourself in six months? Make templates more granular Another solution is to make the templates more granular. I've started this trend above by using wireRenderFile to put little isolated template parts into their own dedicated template – for example, to display a single responsive image or an HTML5 video player. In the same grain, you can split up the header.php into multiple smaller template to mix and match and include include those you want to in each specific context. But this has downsides as well: You end up with a fractal nightmare, a deluge of templates with increasing granularity and decreasing utility, just to be able to include those smaller templates separately from each other. Cohesion and readability is reduced, and there's no way from directory structure alone to tell which templates go together in what ways. Splitting an existing template into two smaller templates is not backwards compatible – you have to make an adjustment in every place the original template was included. Or you keep the original template but change it to just include the two new templates. I said fractal nightmare already, didn't I? Duplicate code You can, of course, just keep separate header templates for each page type. But then you're duplicating the common parts of those templates all over again, and changing those means you have to touch a lot of separate files – definitely not DRY. Most real-life solutions will include a mix of the three approaches. I tried to be fair and write the templates in the leanest and cleanest way possible, but things still got out of hand quickly. Now let's look at the same component written in Twig: Resuable components in Twig Here's the basic header template but written in Twig: {# components/header.twig #} <header class="header"> <h1 class="header__headline">{{ page.title }}</h1> {% if page.subline %} <p class="header__subline">{{ page.subline }}</p> {% endif %} {% block header_image %} {% if page.image %} {{ include('components/responsive-image', { image: page.image, }) }} {% endif %} {% endblock %} </header> One important difference is the block tag defining the header_image block. So far we don't need that, but it will become important in a second. For the page templates, it's common to have a base template that all other templates inherit from: {# html.twig #} <!doctype html> <html lang="en" dir="ltr"> <head> <title>{%- block title -%}{%- endblock -%}</title> {% block seo %} {{ include('components/seo', with_context = false) }} {% endblock %} </head> <body> {% block header %} {{ include('components/header') }} {% endblock %} {% block content %}{% endblock %} {% block footer %} {{ include('components/footer') }} {% endblock %} </body> The base template defines some blocks and includes some default components (seo, header, footer). Now the template for the project page just inherits this: {# project.twig #} {% extends 'html' %} With the PHP template, things got difficult once we wanted to overwrite part of the header template with some content specific to one page template. This is where the header_image block comes in handy: {# project.twig #} {% extends 'html' %} {% block header %} {% embed "components/header" %} {% block header_image %} {# project data template … #} {% endblock %} {% endembed %} {% endblock %} Now the project.twig extends the base html.twig template and overwrites the header block. Then it includes the components/header template and overwrites only the header_image block while keeping the rest. This approach has some major advantages over the plain PHP template: All the code for the project template is in one place – to see what's special about this particular page in comparison to the base template, I just have to look at one template. I didn't have to repeat any of the header template code, so I can still change the header in a central place. The components/header template stays small and manageable, it doesn't know or care what other templates extend it and which parts get overwritten where. As a sidenote, some people may not like the embed syntax. Another approach would be to once again create a custom header template for the project template. But this time, we don't need to repeat any code because we can use inheritance: {# components/project-header.twig #} {% extends "components/header" %} {% block header_image %} {# project data template … #} {% endblock %} I prefer the embed approach because it keeps all the related code together. But both approaches allow for full flexibility with no code duplication. Now what if you want to change other parts of the components/header.twig template in an extending template? In this case, you can always add more blocks: {# components/header.twig #} {% block header_headline %} <h1 class="header__headline">{{ page.title }}</h1> {% endblock %} Adding blocks doesn't change anything about the base template, so it's 100% backwards-compatible. You can always add more blocks without ever having to worry about breaking any existing templates or introducing bugs. Another challenge for the PHP template was to add some additional content to a part of the header template while still keeping the default content. Let's say we want to display a publication date above the headline in a news template, but keep the headline as is. No problem: {# project.twig #} {% block header_headline %} <time>{{ entry.published_date }}<time> {{ parent() }} {% endblock %} The parent() function returns the content of the block in the base template, so you can extend a block without overwriting it completely. Conclusion You can solve all the challenges I posed here in PHP. Most solutions will include a combination of the three approaches mentioned above (making templates more granular, using lots of variables and duplicating code). And a well thought-out mix of those approaches can work reasonably well. The problem is that while those solutions improve reusability and scalability, they usually require lots of boilerplate code and unscoped variables. This reduces the readability and makes the system harder to modify, while making it easier for bugs to creep in. Again, there are solutions for those problems that introduce other problems until the solutions cancel each other out in trade-offs. To me, Twig is a great alternative that requires fewer trade-offs. It allows you to achieve complete freedom and flexibility in your templates all while keeping your templates DRY and keeping code that belongs together in a single file. On top of that, Twig uses a nice, readable syntax (warning: personal opinion) and provides a lot of utility methods and other features to improve your template structure. Some notable caveats to all of this: All of the discussed problems are about scaling a project to a larger scope or team size. For small projects that will never need to scale in this way, this doesn't really matter. ProcessWire's built-in markup regions seem to tackle a lot of the same problems I mentioned in this post. Can't really speak for it as I haven't tried it yet. If this all sounds interesting to you and you want to learn more, you can check out my tutorials on integrating Twig into ProcessWire and extending Twig with custom functionality for ProcessWire.
    2 points
  5. <?php namespace ProcessWire; $showsidebar = 0; // Set $showsidebar to 1 if the page has children or siblings if($page->hasChildren() || count($page->siblings(false))) $showsidebar = 1; if($showsidebar) { // Code with Sidebar } else { // Code without Sidebar } I think this should work, untested ?
    1 point
  6. Busted link to UKRI in footer. It's a bit weird, but there's a leading slash in the href - <a class=partner-link href=/https://www.ukri.org/> Other than that ? great-looking site and outstanding performance. (I'd like to see anyone achieve those scores with WordPress.)
    1 point
  7. https://processwire.com/talk/topic/4452-reset-config-appendtemplatefile-for-certain-templates/
    1 point
  8. How Craft warns you of changes to the YAML files that you haven't applied yet (pulled from the repo): (I'm so used to the simplification of PW's tree that just seeing a "Routes" button in settings gives me the chills ?) And this is the apply changes screen: First chance I get I'm going to have a serious look at RockMigrations. Every new project I get is increasingly demanding on maintenance, with multiple devs, and this is a really big deal. This and SPAs, but thats a different discussion.
    1 point
  9. // Get images of the author if they exist if count($page->images): echo $page->images->each(function($image) { return "<li><a href='$image->size(100,100)->url'>$image->description</a></li>"; // Just an output example }); // Otherwise get images of their book covers else: foreach($page->page_ref_for_their_books as $all_pages): foreach($all_pages->images as $image): echo "<li><a href='$image->size(100,100)->url'>$image->description</a></li>"; endforeach; endforeach; endif;
    1 point
  10. Hi @Micthew It means $imagesAvatar always return a value. try replace $imagesAvatar with $page->images in the third line of your code and see if it works. Gideon
    1 point
  11. This is my code... But, when i create "else {}" and the image in data is empty, placeholder-user.png is not showing, so i re-type "$config->urls->templates.'assets/img/placeholder-user.png" to $avatar on the top variable, and then when the image in data is empty, placeholder-user.png is showing. I don't know whats wrong with my code, but the code is working for me. Micthew
    1 point
  12. I might get into the same "issue" in a project I am working on. If I find a solution I will inform you here ?
    1 point
  13. To add my two cents here... I'm totally on board with this and I started to donate in 2019, through 2021 to some devs for their modules. Most often through their publicly available and mentioned ways, such like PayPal, BuyMeACoffee, ... There is one thing with this... as I wrote some of them about it just to tell and check... they said: "Oh... don't have that account anymore!" or "Oh... that changed!". So... to all of us in some kind: please check if those PayPals and Co. still are up to date. To those that donate: write a message to your favourite devs. Feels awkward, but at least you can be sure the money goes where it should go. Another almost unrelated thing: I stopped using my DEV-licenses for client projects. Sure... that's what they are for but... even a hairdresser can afford FormBuilder and ProCache. Why stop there? I use those only for my very personal projects now and I feel much better with this.
    1 point
  14. I was about to create a similar topic, but why do it if this one already exists))) Not so long ago I started donating to some ProcessWire modules' authors. And each 1st of the month I am looking at my Patreon receipt in my email with a kind of a pride. I think that this thing is my little contribution to the community I love so much and which gave so much to me for free. I do know that Ryan likes to receive his support with pro modules payment. But many modules really shouldn't be paid/pro as they are so essential to many (and so fun to build). But their authors still would be glad to receive some money to sustain their enthusiasm supporting them and creating new features. I am sure the appreciation itself is equally important, but what is an easier way to show it than ???))) The other thing is that I sometimes notice some great improvements that seem to happen to modules as soon as some support comes in. I am writing this to encourage everyone to support the PW ecosystem by donating to module authors. I will list the modules I know that clearly asked for donations. I am sure that is not an extensive list. But at least something to start with. Tracy Debugger Mystique and other modules by ukyo (please don't miss this one @Jonathan Lahijani))) Rock Migrations and a bunch of rock stuff by bernhard All the great stuff from teppo I really like the Patreon/OpenCollective/Github donations, but only 1 of 3 listed used them. Sooooo..... Start throwing money at these fine gentlemen)) Support yourself by making you favorite modules not go away. I wish we could bring back @tpr to support his AOS and wire shell by @marcus and @justb3a. Would they keep supporting their thing is they had some donations coming in? Who knows. And please feel free to list you other module authors that you know asked for support below. Edit 2022-10-27: added teppo's link
    1 point
  15. Hi everyone ?? I hope everyone is doing well here. @ryan It's something I have in mind for months, even if am not actively participating to the community (?), I am still using this tool for quite everything and I know that we can support the project by buying license of Pro Modules. That's said, personnaly I am not using them as I mostly build custom modules, or even use it as is. I would like to have the ability to financially support you and people whish maintain this tool. Eventually, Github's Sponsors could be a good choice; You could choose a one-time payment or a recuring one. What do you think about it ?
    1 point
  16. In Germany you only have to be 16 to buy beer
    1 point
×
×
  • Create New...