Jump to content

Recommended Posts

I decided to rebuild an old image gallery site with PW. I know there's countless image-gallery scripts out there, but for my liking, most of them are too bloated. Also, I thought this was another good opportunity to dive further into PW-world.

I'm keeping the old basic gallery structure: 95% of the galleries are in date-folders (monthly), like a blog archive. But I'd like to add some filter / search functionality, because there's just so many pics - when you come to the site and look for a specific subject, there was no way to do that on the old site.

Q 1:

How can I create a simple "search by image tag"? I want the results to list all images (thumbs) with tag x, and a link to the parent page.

Q2:

How would I most efficiently grab all tags from all pages, all images, and present it alphabetically (not necessary a fancy tag-cloud, but making sure I list each unique tag only once)? So when the user clicks on one tag link, he sees all image thumbs that have that tag on the results page.

$p = $pages->find("images.tags~=$tag") works fine. I can explode("|", $p) to get a page-id array, but where would I go from here?

A tag can be any anywhere, in various galleries (pages), and most pics have multiple tags. I'd like to group the results by page, so I guess I need to add 1-2 foreach loops, no? Can somebody help with some example code? I'm a bit stuck right now.

Link to comment
Share on other sites

Thanks, but that's not what I'm looking for.

I need individual tags for each image. I can set them alright, finding all of them is where I'm a bit stuck.

AFAIK, this was only introduced in PW 2.3 - the blog profile is 2.2. The tags there are per page, not per image. I'm already familar with page-selectors, the page input field etc. It's the specific image tags I'm having problems with.

Link to comment
Share on other sites

$p = $pages->find("images.tags~=$tag") works fine. I can explode("|", $p) to get a page-id array, but where would I go from here?

Every find will return a PageArray already, no need to explode to get the page id as array you already got them...

My guess is that most people try to echo $p, and see that there's something like "1002|1230|2130" ... but that's not what's really behind $p and seems a common misunderstanding of non-coders. It's the magic toString() method of PageArray that returns the id's in that way. See https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/core/PageArray.php#L407. So what you trying to do an extra step to explode something that PW implodes. 

This stringification is handy if you want to use the result $p in a selector.  "id!=$p" will result in "id!=1002|1230|2130".

But in your case you already have an PageArray and simply can loop the pages inside $p.

$p = $pages->find("images.tags~=$tag")
foreach($p as $onepage) {
    echo $onepage->title;
}
  • Like 4
Link to comment
Share on other sites

Nobody? :D

Ok, since the tags introduced to the image field is just a plain text field, there's no other way than to loop all pages that have images loop all images to grab all tags, parse them to array and merge them. This gets you there but doesn't scale well.

(Would those tags be page relations it would make it very easy to output all tags. I'm more of a fan of using page fields for tagging, but there's no image field with page tags and I use different image system if there's a lot of images with tagging and galleries. My ImageManager is one of the tools to have a page as image and you can attach as many fields to the image template to get those things done.)

I often thought about what would be the best ways, with words in text fields all over the site, to collect them and I think you have two possibilities. Raw SQL query, or the above loop all pages and make that a markup cached snippet, if the amount and time to generate the list takes long, that runs every other day or hour. Also have as many restrictions, for template or parent, as you can get to make the query a little more efficient.

To show what would be the API way you already got a good start and intuition with 2 foreach. The following collects all tags and makes a unique array to create a link list.

EDIT: You can use url segments or GET parameter for the links. Since PW urls doesn't allow special chars there's a workaround inside the code now.

/**
 * collect all tags
 * ======================================
 */

$alltags = array(); // container
$use_urlsegments = false;

// find all pages that have images with tags
$parray = $pages->find("template=basic-page|gallery, images.tags!=''");

// loop pages found and collect tags from images
foreach($parray as $p) {

    // find all images that have no empty tags, yeah you can
    // also use find on Pagefiles array!
    $images = $p->images->find("tags!=''");

    // loop them and add tags to array
    foreach($images as $im) {
        $tags = $im->tags;

        // convert "," and "|" to space and create array using explode
        if(strpos($tags, ',') !== false) $tags = str_replace(',', ' ', $tags);
        if(strpos($tags, '|') !== false) $tags = str_replace('|', ' ', $tags);
        $tags = explode(' ', $tags);

        // convert tag value to a page name using beautifyer, ü => ue, ö => oe
        // since special chars are not allowed in PW urls
        foreach($tags as $tag) {
            $alltags[$sanitizer->pageName($tag, Sanitizer::translate)] = $tag;
        }
    }
}

/**
 * generate links with tags as urlsegment
 * ======================================
 */

// make the array unique and create a tags nav from it
// add tags to the url of the page to later read it and
// render a list of pages

echo "<ul>";
foreach(array_unique($alltags) as $key => $tag) {
    if($use_urlsegments) {
        echo "<li><a href='{$page->url}$key'>$tag</a></li>";
    } else {
        echo "<li><a href='{$page->url}?tag=$tag'>$tag</a></li>";
    }
}
echo "</ul>";

/**
 * find all pages with the supplied tag
 * ======================================
 */

// enable url segments on the template if using url segments
if($input->urlSegment1 || $input->get->tag){

    if($input->urlSegment1) {
        $tagvalue = $input->urlSegment1;
        // get the original tag value text from the cached array
        $tagvalue = $alltags[$tagvalue];
    }
    if($input->get->tag) {
        $tagvalue = $sanitizer->selectorValue($input->get->tag);
    }

    // find pages with images having the requested tag
    $pa = $pages->find("images.tags~='$tagvalue'");
    if(count($pa)) {
        echo "<h2>Pages found</h2>";
        echo "<ul>";
        foreach($pa as $p) echo "<li><a href='$p->url'>$p->title</a></li>";
        echo "</ul>";
    }
}

What was the other question again? :)

And of course also found in my ever growing gist archive https://gist.github.com/somatonic/5808897

I'm too lazy to brew a SQL that does the collecting part. But there's people more clever than me that can help. 

  • Like 6
Link to comment
Share on other sites

Awesome, thank you!

I'm on the road today but I'll tinker around this evening and tomorrow.

(btw, the much bigger work is actually tagging thousands of pics in the first place...)

Link to comment
Share on other sites

That works like a charm! Thanks again.

iirc, image tags are supposed to be separated by space, so the two lines 

        if(strpos($tags, ',') !== false) $tags = str_replace(',', ' ', $tags);
        if(strpos($tags, '|') !== false) $tags = str_replace('|', ' ', $tags);

can be omitted.

The URL segments tip is golden, I definitely have to explore that further. So far, I've only used it in a multi-language setup. Didn't even occur to me to use that here...

Link to comment
Share on other sites

That works like a charm! Thanks again.

iirc, image tags are supposed to be separated by space, so the two lines 

        if(strpos($tags, ',') !== false) $tags = str_replace(',', ' ', $tags);
        if(strpos($tags, '|') !== false) $tags = str_replace('|', ' ', $tags);

can be omitted.

Well this code is from the PW core. It has some API methods to search for tags like for Pagefiles $images->findTag("tuggy") or $image->hasTag("taggy") etc...

Link to comment
Share on other sites

I stumbled upon a problem with URL segments. URL segment 1 is supposed to be the image tag. Works fine (= 2nd half of your example code "find all pages with the supplied tag")

However, when I call a page like /tags/Genève I get an error 404. In other words: image tags with umlauts (àèöéç etc.) are not being recognized when called with 

$tagvalue = $sanitizer->selectorValue($input->urlSegment1);

Do I have to change something in the code or config to make image tags work with umlauts?

btw: simply using page/?tag=Frühling works fine, i.e.     

$tagvalue = trim($_GET["tag"]);

Link to comment
Share on other sites

Ah ok I looked a little closer and of course PW doesn't allow special chars in urls.

So if you want to use url segments, you can with a little workaround. I updated my code to allow for url segments or GET. Set the variable at the top.

Also made some changes to not have lower case the tag. But if you use url segments the $alltags array is used with key=>value to get around it.

So it would store "fruehling" => "Frühling", and then take that to get the original value in the image tags find.

The sanitizer method

$sanitizer->pageName(string, Sanitizer::translate);

Sanitizer::translate can be used to convert special chars as you configured in InputfieldPageName module settings.

There you can specify that 'ü' will convert to 'ue'. This way you'll get url segments like "fruehling" instead of "frhling" or "fruhling".

After all the using of GET urls parameter may be the easiest as you don't have to take care if using cyrillic chars.

  • Like 1
Link to comment
Share on other sites

Thanks. I changed it back to GET. However, when doing so, I get an absolutely puzzling 403 error for one certain tag only: No umlauts there, and it appears in two images.

You don't have permission to access /_pw/misc/tags/ on this server.

Using one of the other tags for the same images, it works fine. I could maybe understand an error 404, but 403?

e.g.

tags/?tag=poker = error 403

these work fine:

tags/?tag=cardgame

tags/?tag=gambling

tags/?tag=jassen

tags/?tag=Jass

Guess my hoster is paranoid and blocking certain keywords... o_O

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...