Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/12/2018 in all areas

  1. 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!
    5 points
  2. I don't remember where I pulled this illustration from. I maybe found it somewhere here in the forum or here https://www.cmscritic.com. It corresponds largely to my personal experience. Another dimension, called 'security' is missing and would also help to make a good decision ...
    5 points
  3. @OllieMackJames Measure clicks to "Call to action" buttons you can by using Google Analytics Events. There an example of simple A\B testing plugin from Kirby's forum https://getkirby.com/docs/cookbook/a-b-testing. It would be easy to rewrite it for Processwire. If you want to do some kind of AB testing than you should also take care of returned visitors as they should see the same version of the page that they saw on the first visit.
    4 points
  4. I'm someone who has been developing websites for a while, mostly doing full stack .NET development and ASP scripting before that, while using Silverstripe and some other PHP frameworks for some smaller projects which worked well enough for quite some time. Now I have some time on my hands and decided to evaluate some different CMS options. Diving in as a newbie into: Wordpress Processwire CraftCMS After a short while I decided that Wordpress was out while I'm still evaluating Craft and Processwire. My question is, is there any interest here in reading about my experiences in trying to get the feature set I want up and running, with these other systems?
    2 points
  5. I'd like to share with you guys our latest published project. This time a website for a German Food Photographer based in Cologne: http://www.elaruether.de/ Ela is an interesting case. Her relation with food started as a chef and evolved to blogging and later photography. She's now a professional food photographer. The only 3rd party modules that we installed were Admin on steroids and Tracy debugger (thanks to @tpr and @adrian for the continuous great work). Admin on steroids was particularly handy to invert the order of the blog posts and to add thumbnails to the portfolio pages on the admin: For the slideshows and project pages we used a heavy customised version of the excellent Owl Caroussel
    2 points
  6. Hey @pwired The point is that when you do this ... <ul> <?php foreach ($pages->find("template=news-item, limit=5, sort=created") as $item): ?> <li><a href="<?= $item->url ?>"><?= $item->title ?></a></li> <?php endforeach; ?> </ul> ... it looks a bit cleaner (as in "less like code mixed with markup") than doing something like this: <ul> <?php foreach ($pages->find("template=news-item, limit=5, sort=created") as $item) { echo "<li><a href='" . $item->url . "'>" . $item->title . "</a></li>"; } ?> </ul> ... or this: <ul> <?php foreach ($pages->find("template=news-item, limit=5, sort=created") as $item) { ?> <li><a href="<?php echo $item->url; ?>"><?php echo $item->title; ?></a></li> <?php } ?> </ul> I wouldn't say that there's anything wrong with using selectors within template files, although personally I try to avoid even that when possible. What we're discussing here is simply the syntax used in template files: if you use "full-blown", especially multiline <?php ... ?> code blocks it can seem like you're mixing view side (markup) with business logic (code) even if you're not. On the other hand (probably not what you meant, but just to clarify) if you're wondering why business logic and markup should be separated in the first place, there are a number of reasons to do that. In my case the biggest benefits are a) the ability to alter data structure (model), business logic, or representation (markup) without touching other parts of the site or app, and b) the ability to switch entire view – or use multiple views simultaneously – without having to duplicate any actual "code". (There's a sh*tload of stuff about this in the web, so I won't go into too much detail here. Just google "separation of concerns".) --- Slightly off-topic, but regarding selector use within markup, and why I personally prefer to avoid that: this is in part because in my opinion it looks cleaner, but also because this way I can tweak template names, data structures, etc. without having to modify my views at all. This in turn becomes more useful when you have multiple views for the same data. So, once again, "depends on the use case" ?
    2 points
  7. Great idea, thanks! Added in v0.1.8.
    2 points
  8. Just had another thought ? For pages with more than 25 siblings, I wonder if it would be worth putting the current page in the middle of the list of options, with 12 before and 12 after. At the moment, if the page being edited is beyond the first 25 it doesn't show in the list and so there is no way to access pages on either side of the current one. I think this is fairly efficient: public function ___getSiblings($page) { $numSiblings = $page->parent->numChildren(); $start = $numSiblings > 25 ? ($page->index - 12 > 0 ? $page->index - 12 : 0) : 0; while($numSiblings - $start < 25) $start--; $siblings = $page->siblings("start=$start, limit=25, include=all"); $siblings->filter("listable=1"); return $siblings; } Instead of this where you can't see page "27" in the list: you now get this with it right in the middle: You probably need to do some more testing to make sure there are no issues with sorting. Maybe the selector needs: sort=sort Did some testing here and it seems to be working great.
    2 points
  9. I can't think of any reason it wouldn't work on multi-language fields. What specifically is not working for you? Double-check that you have added the Remove Blocks textformatter to the field in question... ...and you have placed your delimiters around the content you want removed.
    2 points
  10. 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
    1 point
  11. Sure! ? Maybe - if it is easy - it would also make sense to have a fullscreen for the console code input. But that's only some ideas. Everything works great for me as it is. Just suggestions for improvements.
    1 point
  12. Hi @MischaK, welcome to the forum and the world of ProcessWire ? It's always interesting to read about experiences of others - depending on what your intention is on that topic, we also have the pub and dev talk forums. If you have questions related to processwire you'll always find a helping hand here in the forum. Enjoy ?
    1 point
  13. Probably a namespace issue - try new \ProcessWire\WireUpload
    1 point
  14. function visitorgroup($which = null) { $ip = wire('session')->getIP(); $int = ip2long($ip); $group = ($int % 2) ? 'a' : 'b'; if(is_null($which)) return $group; return $group == $which; } if($page->id == 1 && $page->page2use4homepage) { if(visitorgroup('a')): $page = $page->page2use4homepageoption1; elseif: $page = $page->page2use4homepageoption2;
    1 point
  15. Kind of similar as in providing snippets. The main focus will be different and will include solutions for often used scripts like OwlCarousel and modules as well. I want to show how certain thinks can be done in/with ProcessWire.
    1 point
  16. Thanks for that... Before CMS systems became a "thing" I was pretty active in designing and working with modern web standards static sites. But my day job was photography. I tried a bunch of systems before setting on the first branch of MODx, namely Evo. The thing I liked about MODx was that it was easier to design custom sites that fit into the structure. What was confusing to me (and many others) was the reliance on system syntax specific "chunks" and a hard to to grapple navigation system. Wayfinder was powerful but a pain. Kongondo was one of THE best MODx contributors back then. Especially with helping to build the community knowledge and comfort level with creating flexible and complicated connections between pages. But I sort of felt that the creation of the new branch of MODx, namely Revo, made the whole thing sort of awkward? The two different branches happening at the same time, etc. It changed the focus, and key guys went in new directions. Processwire wire is a vastly superior product. I can still design things on paper and use the very flexible template and field combination without fighting the whole structure. My issue has been a long standing limitation: I just could never learn to code PHP and Javascript, etc. I tried but just never gave it the commitment required. For many these skills come easily. Not for me. Looking back one of my frustrations with my early MODx days was getting elegant looking dynamic image galleries to work. I guess some things never change? MaxiGallery had promise but it was a pain to work with. The cool thing with Concrete5 is that it comes with a nice paginated and functioning gallery system, plus a blog system, etc, all without having to use a front end plus a back end editing routine. But the issue is how easy is it to customize a theme or deploy a custom design? There is always a road block with any system it seems. The other thing I like about Processwire is the size of the whole project and the community. Wordpress and Drupal have massive numbers but that can add confusion, too many choices and doubts as to how long the widget you are deploying will be actively maintained. Some systems have too little community evolvement and support. Processwire just has a nice balance.
    1 point
  17. Are you using a JS framework? You need to make sure the X-Requested-With header is sent so that PHP (and therefore Tracy) can recognize it as an AJAX call. jQuery does this by default, but other frameworks may not, eg Angular. In pure/vanilla JS, you can do: xmlhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest"); In Angular you can do: $http.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
    1 point
  18. If your module is namespaced on top of your .module file <?php namespace ProcessWire; you can use in your functions $this->log // or $this->pages
    1 point
  19. @pwired, it's just about separation of concerns (code and markup), and honestly it's also largely about looks: if there are multiline PHP code blocks within your template files, it appears that you're mixing code with markup, which is generally discouraged. In contrast, by using only the short syntax of certain statements (such as <?php foreach (): ?> .. <?php endforeach; ?>) or short echo statements (<?= $variable ?>) you're making it as obvious as possible that you're using PHP as a templating language and indeed not entangling business logic with markup.
    1 point
  20. Hello @Hurme, if your string tranlastion is in a template file, it should work like this. If your string is in a included or prepended file (f. e. _main.php), this doesn't work in my experience. For this you could use text domains inside your string translations. I used to have a single included file (translations.php) in which I collected all string translations and used a text domain to point all string translations to this included file: Anywhere: echo __("This will be translated", "/site/templates/includes/translations.php"); In translations.php __("This will be translated"); You could save the text domain into a variable so you don't have to write the whole path every time. Nowadays I'm thinking about using the ProModule Functional Fields for string translations only, because it gives you more freedom. For example if the original string translation needs to be changed, you have to edit it in your template and enter the translations again in the back-end, because they have lost the connection. With Functional Fields a editor can edit the translations and the original string without losing the connection. If you have the ProFields, it is worth checking out Functional Fields. ? You have to add those files with string translations manually via the "Translate files" button. You should see there every file with string translations, either in the "site" or "wire" folder. Regards, Andreas
    1 point
  21. This post takes a comprehensive look at the new Verified URL Fieldtype added to the ProcessWire ProFields package. We also review updates for the latest version of ProcessWire, 3.0.112 on the dev branch: https://processwire.com/blog/posts/processwire-3.0.112-and-new-verified-url-fieldtype/
    1 point
  22. For some simple ad banners with click measurment, what I have done is using Google Analytics as measurement of the click events, i feel it just offers A LOT more out of the box. Let's you filter by a bunch of things, present data in a lot of ways, make reports in Google Design Studio, etc. Though in all honesty, don't know how it compares to the previous solution.
    1 point
  23. Just for the record, $page->render('some_field') does indeed call renderField(), but only if the given param is a field name. Take a look at PageRender for more details. If you provide a filename as a param, $page->render() will use that file to render given page. If you provide a filename (actual filename, not just something like "file") to $page->render() as the first param, you can provide an array as the second param, and the values stored in that array can then be accessed via $options. It seems to me that you're trying to use $page->render() to something it's really not meant for: $page->render() is intended for rendering a specific page (optionally using a specific file), while wireRenderFile() renders a specific file without the need for a Page object. Those are two different use cases. I might've gotten your intention wrong, but that's the impression I'm getting
    1 point
  24. hi Tom, i was also using wireRenderFile a lot and tried render() and renderValue() a lot in my current project and its great! example for my blogitem: <article class="tm-article uk-margin-large-bottom"> <?= $config->alfred->render($page) ?> <?= $page->render('pic') ?> <?= $page->render('date') ?> <?= $page->edit('body') ?> <?= $page->render('files') ?> <?= $page->render('gallery') ?> <hr> <div class="uk-grid blognav"> <div class="uk-width-1-1 uk-width-large-1-2 uk-text-left uk-text-center-medium"> <?php if($page->prev->id) echo '<a href="' . $page->prev->url . '"><i class="uk-icon-arrow-left"></i> ' . $page->prev->title . '</a>'; ?> </div> <div class="uk-width-1-1 uk-width-large-1-2 uk-text-right uk-text-center-medium"> <?php if($page->next->id) echo '<a href="' . $page->next->url . '">' . $page->next->title . ' <i class="uk-icon-arrow-right"></i></a>'; ?> </div> </div> </article> this will render for example the file /site/templates/fields/gallery.php <?php if(!count($value)) return; ?> <p class="uk-text-bold">Galerie</p> <?php foreach($value as $item): ?> <figure class="uk-overlay uk-overlay-hover tm-padding-gallery"> <img class="uk-overlay-spin" src="<?= $item->size(120, 120)->url ?>" alt="<?= $item->description ?>"> <div class="uk-overlay-panel uk-overlay-icon tm-overlay-icon-zoom uk-overlay-background uk-overlay-fade"></div> <a class="uk-position-cover" href="<?= $item->maxSize(800, 800)->url ?>" data-uk-lightbox="{group:'gallery<?= $page->id ?>'}" data-uk-lightbox title="<?= $item->description ?>"></a> </figure> <?php endforeach; ?> Example for renderValue: <div id="tm-page-heading" class="uk-container uk-container-center"> <div class="uk-flex uk-flex-middle uk-flex-center"> <h1><?= $page->renderValue($page, 'title') ?></h1> </div> </div> this will render the file /site/templates/fields/title.php (because of the "title" inside the <h1>[...]</h1>): <?php if($page->template == "person") echo $page->personFullName; else echo $page->get('headline|title'); in this file you will have the $page variable available, because that was the first parameter of your rendervalue call. other examples could be: $page->renderValue($yourarray, 'your-array-view1'); $page->renderValue($yourarray, 'your-array-view2'); $page->renderValue($booking, 'your-booking-view'); ps: found this one but didn't check if that answers your questions:
    1 point
  25. This was helpful debugging one issue. In later PW-versions you have to add findOne into $options array to make it work: $selectors = new Selectors("template=blog-post, limit=5, sort=name"); $pagefinder = new PageFinder(); $options = array('returnVerbose' => true, 'findOne' => false); $sql = $pagefinder->getQuery($selectors, $options)->getQuery();
    1 point
  26. None that I'm aware of. Of course you can always use PageFinder for this, but it's going to look a bit hacky: $selectors = new Selectors("template=blog-post, limit=5, sort=name"); $pagefinder = new PageFinder(); $options = array('returnVerbose' => true); $sql = $pagefinder->getQuery($selectors->getArray(), $options)->getQuery(); // .. and so on, depending on what exactly you're looking for
    1 point
×
×
  • Create New...