Jump to content

Building a reusable function to generate responsive images


MoritzLost
 Share

Recommended Posts

Hello there,
I've started using ProcessWire at work a while ago and I have been really enjoying building modular, clean and fast sites based on the CMS (at work, I usually post as @schwarzdesign). While building my first couple of websites with ProcessWire, I have written some useful helper functions for repetitive tasks. In this post I want to showcase and explain a particular function that generates a responsive image tag based on an image field, in the hope that some of you will find it useful :)

I'll give a short explanation of responsive images and then walk through the different steps involved in generating the necessary markup & image variations. I want to keep this beginner-friendly, so most of you can probably skip over some parts.

What are responsive images

I want to keep this part short, there's a really good in-depth article about responsive images on MDN if you are interested in the details. The short version is that a responsive image tag is simply an <img>-tag that includes a couple of alternative image sources with different resolutions for the browser to choose from. This way, smaller screens can download the small image variant and save data, whereas high-resolution retina displays can download the extra-large variants for a crisp display experience. This information is contained in two special attributes:

  • srcset - This attribute contains a list of source URLs for this image. For each source, the width of the image in pixels is specified.
  • sizes - This attribute tells the browser how wide a space is available for the image, based on media queries (usually the width of the viewport).

This is what a complete responsive image tag may look like:

<img srcset="/site/assets/files/1015/happy_sheep_07.300x0.jpg 300w,
            /site/assets/files/1015/happy_sheep_07.600x0.jpg 600w,
            /site/assets/files/1015/happy_sheep_07.900x0.jpg 900w,
            /site/assets/files/1015/happy_sheep_07.1200x0.jpg 1200w,
            /site/assets/files/1015/happy_sheep_07.1800x0.jpg 1800w,
            /site/assets/files/1015/happy_sheep_07.2400x0.jpg 2400w"
    sizes="(min-width: 1140px) 350px,
            (min-width: 992px) 480px,
            (min-width: 576px) 540px,
            100vw"
    src="/site/assets/files/1015/happy_sheep_07.1200x0.jpg" alt="One sheep">

This tells the browser that there are six different sources for this image available, ranging from 300px to 2400px wide variants (those are all the same image, just in different resolutions). It also tells the browser how wide the space for the image will be:

  • 350px for viewports >= 1140px
  • 480px for viewports >= 992px
  • 540px for viewports >= 576px
  • 100vw (full viewport width) for smaller viewports

The sizes queries are checked in order of appearance and the browser uses the first one that matches. So now, the browser can calculate how large the image needs to be and then select the best fit from the srcset list to download. For browsers that don't support responsive images, a medium-sized variant is included as the normal src-Attribute.

This is quite a lot of markup which I don't want to write by hand every time I want to place an image in a ProcessWire template. The helper function will need to generate both the markup and the variations of the original image.

Building a reusable responsive image function

Let's start with a function that takes two parameters: a Pageimage object and a standard width. Every time you access an image field through the API in a template (e.g. $page->my_image_field), you get a Pageimage object. Let's start with a skeleton for our function:

function buildResponsiveImage(
    Pageimage $img,
    int $standard_width
): string {
    $default_img = $img->maxWidth($standard_width);
    return '<img src="' . $default_img->url() . '" alt="' . $img->description() . '">';
}

// usage example
echo buildResponsiveImage($page->my_image_field, 1200);

This is already enough for a normal img tag (and it will serve as a fallback for older browsers). Now let's start adding to this, trying to keep the function as flexible and reusable as possible.

Generating alternate resolutions

We want to add a parameter that will allow the caller to specify in what sizes the alternatives should be generated. We could just accept an array parameter that contains the desired sizes as integers. But that is not very extendible, as we'll need to specify those sizes in each function call and change them all if the normal size of the image in the layout changes. Instead, we can use an array of factors; that will allow us to set a reasonable default, and still enable us to manually overwrite it. In the following, the function gets an optional parameter $variant_factor.

// get the original image in full size
$original_img = $img->getOriginal() ?? $img;

// the default image for the src attribute, it wont be upscaled
$default_image = $original_img->width($standard_width, ['upscaling' => false]);

// the maximum size for our generated images
$full_image_width = $original_img->width();

// fill the variant factors with defaults if not set
if (empty($variant_factors)) {
    $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2];
}

// build the srcset attribute string, and generate the corresponding widths
$srcset = [];
foreach ($variant_factors as $factor) {
    // round up, srcset doesn't allow fractions
    $width = ceil($standard_width * $factor);
    // we won't upscale images
    if ($width <= $full_image_width) {
        $srcset[] = $original_img->width($width)->url() . " {$width}w";
    }
}
$srcset = implode(', ', $srcset);

// example usage
echo buildResponsiveImage($page->my_image_field, 1200, [0.4, 0.5, 0.6, 0.8, 1, 1.25, 1.5, 2]);

Note that for resizing purposes, we want to get the original image through the API first, as we will generate some larger alternatives of the images for retina displays. We also don't want to generate upscaled versions of the image if the original image isn't wide enough, so I added a constraint for that.

The great thing about the foreach-loop is that it generates the markup and the images on the server at the same time. When we call $original_img->width($width), ProcessWire automatically generates a variant of the image in that size if it doesn't exist already. So we need to do little work in terms of image manipulation.

Generating the sizes attribute markup

For this, we could build elaborate abstractions of the normal media queries, but for now, I've kept it very simple. The sizes attribute is defined through another array parameter that contains the media queries as strings in order of appearance.

$sizes_attribute = implode(', ', $sizes_queries);

The media queries are always separated by commas followed by a space character, so that part can be handled by the function. We'll still need to manually write the media queries when calling the function though, so that is something that can be improved upon.

Finetuning & improvements

This is what the function looks like now:

function buildResponsiveImage(
    Pageimage $img,
    int $standard_width,
    array $sizes_queries,
    ?array $variant_factors = []
): string {

    // get the original image in full size
    $original_img = $img->getOriginal() ?? $img;
    
    // the default image for the src attribute, it wont be upscaled
    $default_image = $original_img->width($standard_width, ['upscaling' => false]);

    // the maximum size for our generated images
    $full_image_width = $original_img->width();

    // fill the variant factors with defaults if not set
    if (empty($variant_factors)) {
        $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2];
    }

    // build the srcset attribute string, and generate the corresponding widths
    $srcset = [];
    foreach ($variant_factors as $factor) {
        // round up, srcset doesn't allow fractions
        $width = ceil($standard_width * $factor);
        // we won't upscale images
        if ($width <= $full_image_width) {
            $srcset[] = $original_img->width($width)->url() . " {$width}w";
        }
    }
    $srcset = implode(', ', $srcset);

    return '<img src="' . $default_img->url() . '" alt="' . $img->description() . '" sizes="' . $sizes_attribute . '" srcset="' . $srcset . '">';
}

It contains all the part we need, but there are some optimizations to make.

First, we can make the $sizes_queries parameters optional. The sizes attribute default to 100vw (so the browser will always download an image large enough to fill the entire viewport width). This isn't optimal as it wastes bandwidth if the image doesn't fill the viewport, but it's good enough as a fallback.

We can also make the width optional. When I have used this function in a project, the image I passed in was oftentimes already resized to the correct size. So we can make $standard_width an optional parameter that defaults to the width of the passed image.

if (empty($standard_width)) {
    $standard_width = $img->width();
}

Finally, we want to be able to pass in arbitrary attributes that will be added to the element. For now, we can just add a parameter $attributes that will be an associative array of attribute => value pairs. Then we need to collapse those into html markup.

$attr_string = implode(
    ' ',
    array_map(
        function($attr, $value) {
            return $attr . '="' . $value . '"';
        },
        array_keys($attributes),
        $attributes
    )
);

This will also allow for some cleanup in the way the other attributes are generated, as we can simply add those to the $attributes array along the way.

Here's the final version of this function with typehints and PHPDoc. Feel free to use this is your own projects.

/**
* Builds a responsive image element including different resolutions
* of the passed image and optionally a sizes attribute build from
* the passed queries.
*
* @param \Processwire\Pageimage $img The base image.
* @param int|null $standard_width The standard width for this image. Use 0 or NULL to use the inherent size of the passed image.
* @param array|null $attributes Optional array of html attributes.
* @param array|null $sizes_queries The full queries and sizes for the sizes attribute.
* @param array|null $variant_factors The multiplication factors for the alternate resolutions.
* @return string
*/
function buildResponsiveImage(
    \Processwire\Pageimage $img,
    ?int $standard_width = 0,
    ?array $attributes = [],
    ?array $sizes_queries = [],
    ?array $variant_factors = []
): string {

    // if $attributes is null, default to an empty array
    $attributes = $attributes ?? [];

    // if the standard width is empty, use the inherent width of the image
    if (empty($standard_width)) {
        $standard_width = $img->width();
    }

    // get the original image in full size
    $original_img = $img->getOriginal() ?? $img;

    // the default image for the src attribute, it wont be
    // upscaled if the desired width is larger than the original
    $default_image = $original_img->width($standard_width, ['upscaling' => false]);

    // we won't create images larger than the original
    $full_image_width = $original_img->width();

    // fill the variant factors with defaults
    if (empty($variant_factors)) {
        $variant_factors = [0.25, 0.5, 0.75, 1, 1.5, 2];
    }

    // build the srcset attribute string, and generate the corresponding widths
    $srcset = [];
    foreach ($variant_factors as $factor) {
        // round up, srcset doesn't allow fractions
        $width = ceil($standard_width * $factor);
        // we won't upscale images
        if ($width <= $full_image_width) {
            $srcset[] = $original_img->width($width)->url() . " {$width}w";
        }
    }
    $attributes['srcset'] = implode(', ', $srcset);

    // build the sizes attribute string
    if ($sizes_queries) {
        $attributes['sizes'] = implode(', ', $sizes_queries);
    }

    // add src fallback and alt attribute
    $attributes['src'] = $default_image->url();
    if ($img->description()) {
        $attriutes['alt'] = $img->description();
    }
 
    // implode the attributes array to html markup
    $attr_string = implode(' ', array_map(function($attr, $value) {
        return $attr . '="' . $value . '"';
    }, array_keys($attributes), $attributes));

    return "<img ${attr_string}>";

}

Example usage with all arguments:

echo buildResponsiveImage(
    $page->testimage,
    1200,
    ['class' => 'img-fluid', 'id' => 'photo'],
    [
        '(min-width: 1140px) 350px',
        '(min-width: 992px) 480px',
        '(min-width: 576px) 540px',
        '100vw'
    ],
    [0.4, 0.5, 0.6, 0.8, 1, 1.25, 1.5, 2]
);

Result:

<img class="img-fluid" id="photo" srcset="/site/assets/files/1/sean-pierce-1053024-unsplash.480x0.jpg 480w, /site/assets/files/1/sean-pierce-1053024-unsplash.600x0.jpg 600w, /site/assets/files/1/sean-pierce-1053024-unsplash.720x0.jpg 720w, /site/assets/files/1/sean-pierce-1053024-unsplash.960x0.jpg 960w, /site/assets/files/1/sean-pierce-1053024-unsplash.1200x0.jpg 1200w, /site/assets/files/1/sean-pierce-1053024-unsplash.1500x0.jpg 1500w, /site/assets/files/1/sean-pierce-1053024-unsplash.1800x0.jpg 1800w, /site/assets/files/1/sean-pierce-1053024-unsplash.2400x0.jpg 2400w" sizes="(min-width: 1140px) 350px, (min-width: 992px) 480px, (min-width: 576px) 540px, 100vw" src="/site/assets/files/1/sean-pierce-1053024-unsplash.1200x0.jpg" alt="by Sean Pierce">

Now this is actually too much functionality for one function; also, some of the code will be exactly the same for other, similar helper functions. If some of you are interested, I'll write a second part on how to split this into multiple smaller helper functions with some ideas on how to build upon it. But this has gotten long enough, so yeah, I hope this will be helpful or interesting to some of you :)

Also, if you recognized any problems with this approach, or can point out some possible improvements, let me know. Thanks for reading!

  • Like 22
Link to comment
Share on other sites

Hi, thank you for the write up. 👍

For me it looks you found the right balance for beginners and experienced users.

A following second part would be very nice and useful.

Additionally to that, if you don't mind, I would be interested in a third part too. This one should cover how the function(s) recognize or act upon changes of a focus point or zoom value, if used. 

Does it recognize it, and recreate all variations? 

Do you also include / provide or already use a function to remove exactly the previously created variations? Maybe useful during developing a site, when my first used sizes need to be adjusted. (Otherwise I would have a lot unused variations). If you don't use this already, I think it could be done very easy by using a suffix for the variations. 

  • Like 1
Link to comment
Share on other sites

20 hours ago, bernhard said:

hi @MoritzLost , thanks for the detailed tutorial! do you know this module?

If yes, you might elaborate what is different on your approach 🙂 

Hi Bernard, I didn't know that module, looks interesting as well 🙂 Well, it's a different approach in that I'm not using javascript and instead generate the entire function based on the passed parameters. I'm just using native functionality instead of Javascript, which is good if you don't have to support older browsers. The generation of the srcset and corresponding images seems to be handled in a similar way though, so it comes down to preferences I guess.

20 hours ago, horst said:

Additionally to that, if you don't mind, I would be interested in a third part too. This one should cover how the function(s) recognize or act upon changes of a focus point or zoom value, if used.  

Does it recognize it, and recreate all variations?  

Hi horst, thanks 🙂 I actually don't know how this interacts with changing focus points, my last projects didn't really involve heavy usage of the focus points. I'll need to do some testing, I'll update the function when I get to it.

20 hours ago, horst said:

Do you also include / provide or already use a function to remove exactly the previously created variations? Maybe useful during developing a side, when my first used sizes need to be adjusted. (Otherwise I would have a lot unused variations). If you don't use this already, I think it could be done very easy by using a suffix for the variations. 

For my last projects I just temporarily put $img->removeVariations() in the function and clicked through the site (should be easy to automate based on the sitemap). Though this will be trouble when the editors manually create variations. Hm, using a suffix for the generated images is a good idea, this way they could all be invalidated at once. I haven't used suffixes yet, I'll look into that. Do you have a suggestion on how to modify the function accordingly? 🙂

  • Like 1
Link to comment
Share on other sites

1 hour ago, MoritzLost said:

it's a different approach in that I'm not using javascript

MarkupSrcSet doesn't use JS to generate the markup. I guess the JSON format that misled you, in fact the module was rewritten a year ago, see this post:

https://processwire.com/talk/topic/12981-markupsrcset/?do=findComment&amp;comment=150129

 

  • Like 3
Link to comment
Share on other sites

21 hours ago, tpr said:

MarkupSrcSet doesn't use JS to generate the markup. I guess the JSON format that misled you, in fact the module was rewritten a year ago, see this post:

https://processwire.com/talk/topic/12981-markupsrcset/?do=findComment&amp;comment=150129

 

I see, in this case I guess it's just two different approaches toward the same goal 🙂 All your examples use the data-srcset attribute to be consumed by lazysizes, though the syntax is identical with the regular srcset attribute, so I'd say at the core the functionality is similar.

  • Like 1
Link to comment
Share on other sites

My module was also adding other attributes beforehand, eg classes, but that was too restricting, and IDEs were complaining about missing img attributes. That's why I decided to rewrite it to generate the srcset attribute only that you can use the way you need. It even works with lazysizes bgset attribute that I often use.

  • Like 3
Link to comment
Share on other sites

On 9/12/2018 at 8:49 PM, MoritzLost said:

For my last projects I just temporarily put $img->removeVariations() in the function

Yep, it then definetly removes all variations. 🙂

But in some cases it may become handy to use a scalpell instead of a hammer. For example, if you use the same images in different locations, as with centralized image pages, you may have a total of 100 variations but only 7 do belong to your current template.

Just a thought: Maybe a programmatically created suffix, based on the (page | template | imagefield), can be embedded into the variation names? This way, you only need a delete-function that gets passed that suffix, to remove only variations containing it in their names.

  • Like 1
Link to comment
Share on other sites

On 9/14/2018 at 9:26 PM, horst said:

Yep, it then definetly removes all variations. 🙂

But in some cases it may become handy to use a scalpell instead of a hammer. For example, if you use the same images in different locations, as with centralized image pages, you may have a total of 100 variations but only 7 do belong to your current template.

Just a thought: Maybe a programmatically created suffix, based on the (page | template | imagefield), can be embedded into the variation names? This way, you only need a delete-function that gets passed that suffix, to remove only variations containing it in their names.

That's a good point, I'll give that a shot!

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

@horst I finally got around to test some stuff now regarding the focus points and orphaned image files.

I actually had a problem with the version of the function in my tutorial above; because the function accessed the original image, the generated images always had the same aspect ratio of the original image, instead of the aspect ratio of the image I passed in. So I forewent using the original image for the tests.

As for the focus points, I did some testing on how Processwire handles this. My takeaways (someone correct me if I'm wrong):

  • Image variations are only saved to disk, not to the database. So the only permanent record of an image variation is the physical file on the disk, identified by the appended height and width and suffix string.
  • Whenever I change the focus/zoom of an image and save the page, all variations are automatically regenerated.

In combination with my responsive image function, this actually lead to a curious problem. I uploaded a 1920x1080 image to an image field, then created an 800x800 variation through the API. Then I passed that one into the responsive image function, at this point a couple of square-sized variations were created. However, since I only specified a width, the height-parameter in the filename was set to "0". So when I changed the focus point of that image, the variations were recreated in the original 16/9 format instead of the desired square format.

I'm not sure if this is intentional or a bug, for now I've restructured the responsive image function to take both a width and a height parameter so that the aspect ratio is fixed, and added shortcut functions that only take height / width as an argument. That should solve the focus point problem.

Regarding the cleanup of orphaned files, I'm still not sure if there is an elegant solution. I've added a suffix 'auto_srcset', but the generated images are still spread across multiple folders corresponding to the page id. I feel like cleaning those up all at once would rather be a job for a cleanup script than a single function. Something like the script ryan posted here:

Anyway, I can't posted the updated code right now (I'm in the process of writing a small utility library for which I converted all those helper functions into static object methods), but I'll try to post that second part soon and include the updated functions; I might only get to that after my vacation though.
  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Very nice writeup. Thank you for sharing! There are so many different approaches to handling responsive images and it is great to see how others do it. Would love to see more approaches from others here, too.

To get the concept of responsive images, I read the anatomy of responsive images which explains them very well. Then I used to implement a quite similar approach to @MoritzLost for some time utilizing the MarkupSrcset module and also my own functions. Until I stumbled upon an article about creating responsive images  which shows an approach that I now prefer.

In this approach you go through all your different images in the finished layout and determine their exact respective size at your breakpoints. Then you build srcset and sizes attributes for those exact values. So there is no more approximation using scaling factors. You get exactly the amount of images at the sizes you need them.

I pass an array with the breakpoints and the required sizes for that breakpoint and build srcset and sizes from that array. Here's the code:

/**
 * Builds responsive img tag with srcset and sizes attributes
 * @param \Processwire\Pageimage $img
 * @param array $breakpointsizes array of associative arrays that describe media min breakpoints and width/height of images from the respective
 * breakpoint to the next
 * Example:
 * $breakpointSizes = [
 *   ['bp' => 320, 'sz' => [500, 0]],
 *   ['bp' => 576, 'sz' => [992, 0]],
 *   ['bp' => 992, 'sz' => [690, 0]]
 *   ['bp' => 1200, 'sz' => [835, 0]]
 * ];
 * @return string 
 */
function renderImageResponsive($img, $breakpointSizes) {

  $imgSrc = $img->url; // use original image as fallback src
  $alt = ($img->description) ? $img->description : $img->page->title; // if no description use page title 

  // construct sizes attribute
  $sizes = array();
  foreach(array_reverse($breakpointSizes) as $bs) {
    $sizes[] = "(min-width:{$bs['bp']}px) {$bs['sz'][0]}px";
  }
  $sizes = implode(', ', $sizes) . ', 100vw';

  // construct srcset attribute
  $srcSet = array();
  foreach($breakpointSizes as $bs) {
    $srcSet[] = $img->size($bs['sz'][0], $bs['sz'][1])->url . " {$bs['sz'][0]}w";
  }
  $srcSet = implode(', ', $srcSet);

  return "<img class='img-fluid' src='{$imgSrc}' srcset='{$srcSet}' sizes='{$sizes}' alt='{$alt}'>";

}

Obviously it does not account for Retina which needs to be added. I will also extend my function to add support for other attributes like you showed it. That is a great idea.

  • Like 6
Link to comment
Share on other sites

Quote

no more approximation using scaling factors. You get exactly the amount of images at the sizes you need them. 

Thanks for sharing this. This opens up a better way than the usual sandwich responsive style.

Until now I used this css padding-bottom a lot to keep images truly responsive on all devices.

 

.your class {
background-image: url(/site/assets/files/1014/background.jpg);
background-size: 100%;
padding-bottom: x%;
background-repeat: no-repeat;
}

 

Link to comment
Share on other sites

  • 5 months later...

@MoritzLost Great post, thank you.

New to PW.
I am having trouble getting the beginning of your function to work in the Intermediate profile.

<?php
// ##########################
// I have this in _func.php

function buildResponsiveImage(Pageimage $img, int $standard_width): string
{
	$default_img = $img->maxWidth($standard_width);
	return '<img src="' . $default_img->url() . '" alt="' . $img->description() . '">';
}


// ##########################
// In a Gallery page I created and where I added a gallery-page template that does have the Images field I put this.
// There I also added two images, both larger than 7000px width.
<?php namespace ProcessWire;

// gallery-page.php template file

$content = $page->images;

$content .= buildResponsiveImage($page->images, 1200);


// ##########################
// In the _main.php file I like to output the code in the main tag.
<!-- main content -->
<div id='content'>
    <h1><?php echo $title; ?></h1>
    <?php echo $content; ?>
</div>

"Fatal error: Uncaught TypeError: Argument 1 passed to ProcessWire\buildResponsiveImage() must be an instance of ProcessWire\Pageimage, instance of ProcessWire\Pageimages given".
Thank you for any help or learning resources to get this right.

  • Like 1
Link to comment
Share on other sites

@happywire There are two classes that ProcessWire uses for images: Pageimage and Pageimages (note the s). Each instance of Pageimage holds a single image, instances of Pageimages can hold multiple images (the class is basically an array wrapper around Pageimage objects). The function needs a single image, so you need to give it a single image. If your images fields contains multiple images (I suspect so because of the plural), you could for example loop through them and build a responsive image out of each of them, or just use the first one:

// build a responsive image from each image in this field
foreach ($page->images as $image) {
	$content .= buildResponsiveImage($image, 1200);	
}

// build a responsive image from the first image in this field
$content .= buildResponsiveImage($page->images->first(), 1200);

You can also tell the API whether to return a Pageimage or Pageimages instance for this field by default. In the field settings for your images field, go to the Details tab; under formatted value, you can select Array of items to always return a Pageimages instance, or Single item to always return a Pageimage (this only works if your field is limited to one image).

When in doubt, use get_class to find out what kind of object you're dealing with. Note you also have to check for an empty field, or the function will throw an error if there's no image in your field.

// check object class

echo get_class($page->images); // ProcessWire\Pageimages
echo get_class($page->images->first()); // ProcessWire\Pageimage


// make sure the field isn't empty before your pass the image into the function

// for a Pageimage
if ($page->images !== null) {
    $content .= buildResponsiveImage($page->images, 1200);
}

// for Pageimages
if ($page->images->count() > 0) {
    $content .= buildResponsiveImage($page->images->first(), 1200);
}

Let me know if it doesn't work for you. Cheers 🙂

Edit: Check out the documentation for the Pageimages and Pageimage classes.

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

15 minutes ago, MoritzLost said:

Note you also have to check for an empty field, or the function will throw an error if there's no image in your field.

Yes, it's always good to do this but I was wondering if maybe the function itself should (also) do this check? ...just to make it more robust. Thanks.

  • Like 1
Link to comment
Share on other sites

1 hour ago, kongondo said:

Yes, it's always good to do this but I was wondering if maybe the function itself should (also) do this check? ...just to make it more robust. Thanks.

Personally, but I don't like to have my functions silently fail on invalid input. In this case, there's no useful thing the function can do if it doesn't receive a Pageimage, so you'd end up returning an empty string or null in this case (would need to make the return typehint nullable for the latter):

function buildResponsiveImage(?Pageimage $img, int $standard_width): string
{
	if ($img === null) {
		return '';
	}
	/* ... */
}

To me that feels like I'm creating a hard to debug error down the road, when I can't figure out why an image is not being displayed. Also, I want to be able to see all permutations of a given template by looking at it's source code; if an image field is optional, I want to see a conditional clause covering the case of an empty image field. Also, though I wrap the functions as static methods in a class, it's really more of a functional approach, so I'd rather create a higher-order function to wrap around this one and catch empty image fields in case I want to build on this.

But this really comes down to personal preferences, and after all it was a tutorial on how to build such a function. I'd encourage everyone to build upon it and adjust it to their personal workflow / preferences, especially for things like error handling and default arguments 🙂

  • Like 3
Link to comment
Share on other sites

1 hour ago, MoritzLost said:

but I don't like to have my functions silently fail on invalid input

Oh gosh, no, that's not what I meant 😃....just a more friendly error than a fatal one. I didn't notice though that you were using type hinting in the function arguments, so, my bad. 

  • Like 1
Link to comment
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
 Share

×
×
  • Create New...