Jump to content


Photo

Simple tagging for dummy (me)

tagging

  • Please log in to reply
16 replies to this topic

#1 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 22 March 2012 - 04:24 PM

I am trying to implement a KISS (Keep It Simple Stupid) tagging system, one depth, i.e tags not hierarchical categories.

Each page would display any tag that was added to it as a link; the link goes to a page where all other pages sharing that tag are listed, quite basic.

In my attempts to do this I have been reading up, posting, getting help (thank you patient helpers), trying, failing and have slightly run aground. I know instinctively from the little I've done with PW that this is going to be easy, when I can see 'how'. These are the three threads I've been looking at, but perhaps each was not quite what I am trying to do and that, plus my PHP weakness, stopped me succeeding.

These are my assumptions as to what I would do:
  • have an AsmSelect field called "tags" on each page in the Admin so content authors may select or add tags per page
  • have a page below Home called "tags" which will have a child page per tag
  • don't have a .php file for the "tags" page (is that right?)
  • don't have a .php file for each children page of the "tags" page
  • use a file-less template for the "tags" page and children pages and that template to include only the title field (is that right?)
  • use my main/single .php template file for all pages and detect if it is being used to display tag-related content and change its outs put accordingly.
If you're able to point me to a thread I may have missed containing code samples so someone like me can get going, or post a few snippets to show me the basic code and correct any of my assumptions that look wrong I would be very grateful. Cheers, -Alan

#2 Pete

Pete

    Administrator

  • Administrators
  • 1,756 posts
  • 658

  • LocationChester, England

Posted 22 March 2012 - 04:40 PM

You're fine up until 4.

4. Create a tags template and make sure your tag pages use that template - you can link tags to it from your other pages that have the tagging field by doing something like:

foreach ($page->tags as $tag) {
	echo "<a href='{$tag->url}'>{$tag->title}</a> ";
}

The tags will be separated by a space and link to something like /tags/tagtitle/

Then in the tags template you can do:

$tagged_pages = $pages->find("tag=$page"); // assuming "tag" is your tag field name. $page is the current page object, so this may seem odd but when echo'd or used as part of a selector this actually outputs  the page ID, so this is the same as $page->id but shorter, which is nice
foreach ($tagged_pages as $tagged) {
	 echo "<p><a href='{$tagged->url}'>{$tagged->title}</a></p>";
}

Then I think you can skip your remaining steps.

Hope that helps.

In terms of other examples, I posted some earlier that do similar things in a different scenario: http://processwire.c...ndpost__p__9564 That set of examples doesn't really tell you what to do with templates, but does hopefully give you a few more ideas about how to find related data.

#3 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 22 March 2012 - 04:41 PM

THANKS Pete, off to go and try this, cheers!

#4 Pete

Pete

    Administrator

  • Administrators
  • 1,756 posts
  • 658

  • LocationChester, England

Posted 22 March 2012 - 04:45 PM

Just updated that post above, so there's possibly more things to try :)

#5 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 22 March 2012 - 04:48 PM

Oo, thanks I can see. So far so good :)

#6 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 22 March 2012 - 05:05 PM

Brilliant :D works perfectly, thanks Pete! And thanks for the link to the other post, gonna go read.

I have one problem that is likely down to me creating/deleting pages, templates, fields called tag and tags all afternoon — each time I go to edit any page I am getting:
Invalid value sent to Page::setTemplate
Do you think I would be well advised restoring from a db backup? Or should I try to work out a fix in case this happens again or is of interest?

Correction, this happens for some pages, just working out what the common link is...

Update: Backed up and saved 'broken' db in case of help to you Pete/Ryan etc and then restored and re-created the items I needed and all works perfectly, thanks again!

Edited by alan, 22 March 2012 - 05:31 PM.


#7 ryan

ryan

    Hero Member

  • Administrators
  • 5,780 posts
  • 3125

  • LocationAtlanta, GA

Posted 23 March 2012 - 05:45 AM

Thanks Alan - I'm limited to mobile today but will do more research on that error when I get back to the computer this weekend. Thanks for saving that DB, that may be helpful to determine what happened.

#8 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 23 March 2012 - 05:50 AM

Welcome Ryan, happy if it helps at all. And I had been adding/removing/adding/removing/adding/removing fields, templates, pages called "tags" in my (feeble) search to work out how to do what in the end Pete showed me was wonderfully simple (as I knew it would be! :)) So I have zero loss in confidence from that error condition.

#9 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 23 March 2012 - 11:18 AM

I've made real progress (big smiles here :D) but have run into a silly problem, any pointers anyone please?

Background: Taking Pete's most helpful advice as a starting point, I wanted to make the focus of the tagging not the tag names but the titles of 'pages-like-this-one'. E.g. My Home page is tagged "butter" and also my Food page is tagged "butter", I wanted on my Home page to say "Other related pages: Food" and my Food page to say "Other related pages: Home".

I should note in case it's relevant all pages are using one template for now (I have dropped the 'tags' template).

<?php
// Link to title of each page that shares one or more tags with this one
// ---

// find this pages tags

$this_pages_tags = $page->tags;

// each tag, find other pages that match the tag
// TODO limit the find?

foreach ($this_pages_tags as $tag) {
  $other_pages = $pages->find("tags=$tag")->not($page);
}

// output title of

foreach ($other_pages as $matched) {
  echo "<a href='{$matched->url}'>{$matched->title}</a> ";
}

?>
No problem: Home and Food pages share the "butter" tag and each shows a link to the other as expected. If I also add the "marmite" tag to the Contact page then as expected the other pages do not show a link to the Contact page (as they don't share a tag).
Problem: When I add the "marmite" tag to the Home page it starts showing a link to the Contact page (good as they now share a tag) but no longer shows a link to the Food page even though they also still share the "butter" tag.

It's feels sort of like I am building a one-depth list when it needs to be N-depth, but I can't work out the code to address this. Any help/comments most appreciated.

#10 Pete

Pete

    Administrator

  • Administrators
  • 1,756 posts
  • 658

  • LocationChester, England

Posted 23 March 2012 - 11:30 AM

Try this:

<?php
// Link to title of each page that shares one or more tags with this one
// ---

// find this pages tags

//$this_pages_tags = $page->tags; // Not really needed as we can just use $page->tags as below

// each tag, find other pages that match the tag
// TODO limit the find?

$other_pages = $pages->find("tags=$page->tags"); // $page->tags when used in a selector will be something like 123|32|667 (page IDs separated by a pipe character, which means OR)

// Following is not needed as we grabbed them all via the above find()
/*foreach ($this_pages_tags as $tag) {
  $other_pages = $pages->find("tags=$tag")->not($page);
}*/

// output title of

foreach ($other_pages as $matched) {
  echo "<a href='{$matched->url}'>{$matched->title}</a> ";
}

?>
It'll display all pages linked by tags this way, but won't give you access (easily anyway) to what they matched on. That would be harder to accomplish, but it would maybe allow you to prioritise and have pages with more related tags appear higher up the list. I'd question how useful it would be beyond just being cool though :)

#11 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 23 March 2012 - 12:47 PM

Thanks Pete, works a treat! And thanks for showing me my code-bloat, I'll go through that and learn for next time :D

#12 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 26 March 2012 - 10:41 AM

This KISS tagging is working really well, with just one feature that I can't crack; a summary view where each tag is shown and under it the pages that have that tag applied. I've skinned my PHP/PW-knees trying to do this but failed :( The following code is nearly what I want, producing this output which shows each page and under it the tags that have been applied (the opposite of what I want):
echo "<h1>Tags pages</h1>";
$tag_pages = $pages->get("/tags/"); // TODO can this line and the one below be combined?
$all_tags_pages = $tag_pages->children; // TODO can this line and the one above be combined?
$tagged_pages = $pages->find("tags.count>0, sort=title");
echo "<dl>";
  foreach ($tagged_pages as $matched) {
   foreach ($matched->tags as $tag_pages) {
	echo "<dt>{$tag_pages->title}</dt>";
   }
   echo "<dd><a href='{$matched->url}'>{$matched->title}</a></dd>";
  }
echo "</dl>";
In trying to solve this I've been working on the assumption I need three loops/nested loops/tests
# a list of the tag pages (the pages used to represent the tag names)
# the list of tagged pages (the pages that have one tag or more applied)
# for each tag page, the tagged pages that include it's tag -> output the page title

I'm still working on solving this as it would round off a great little soft sub-indexing strategy, but if anyone can see the answer and can drop me a clue I would be most grateful, thanks in advance!

#13 diogo

diogo

    Hero Member

  • Moderators
  • 2,014 posts
  • 1092

  • LocationPorto, Portugal

Posted 26 March 2012 - 01:55 PM

Still didn't read everything, but I'll answer to the TODOs. They can be conbined like this:
$all_tags_pages = $pages->get("/tags/")->children; // they can <img src='http://processwire.com/talk/public/style_emoticons/<#EMO_DIR#>/smile.png' class='bbc_emoticon' alt=':)' />

edit: i was trying to answer your main question, but i won't have time right now and i don't understand how you have the pages organized on the tree.
just two quick things:

1. Right now you are printing two <dt> for each <dd>. You will have to bring the <dd> out of the loop, and organize the things inside on a new list inside it. (sorry folks, i gave a wrong information, you can indeed have multiple terms per definition as well as the opposite)
2. you are not using the variable all_tag_pages anywhere in your code

sorry, for not being able to help more for now. i'm sure someone will jump in soon

#14 Pete

Pete

    Administrator

  • Administrators
  • 1,756 posts
  • 658

  • LocationChester, England

Posted 26 March 2012 - 02:26 PM

Try this:

echo "<h1>Tags pages</h1>";
foreach ($pages->find("tags.count>0, sort=title") as $tag_pages) {
	echo "<h3>{$tag_pages->title}</h3>";
	echo "<dl>";
	foreach ($tag_pages->tags as $tag) {
		echo "<dt>{$tag->title}</dt>";
	}
	echo "</dl>";
}

Not tested it but it should work.

Basically, it should be iterating through the pages that have tags, and then the tags on those pages.

This might seem like it's getting repetitive in that 10 pages might have the same tag and it feels like it might be fetching those tags 10 times, but if I'm right (and ryan or someone else more knowledgeable than me will have to confirm) those pages stay in memory until the end of the script.

#15 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 26 March 2012 - 02:54 PM

Oo! Thanks @diogo and @Pete.

@diogo thanks for concatenating those lines for me, I was sure they were wrong/inefficient as I had them and thanks the re HTML placement.

@Pete thanks a lot for the code to try, I'll go do that know, I must admit at the back of my mind I was wondering if when it's working it might be a bit intensive and/or really need care as regards caching, and interested in your point about arrays(?) staying in memory while executing. When I get it working completely I will post the code and see if anyone thinks it's gonna be 'bad' for the server and/or need particular care re caching.

#16 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 26 March 2012 - 02:56 PM

@Pete. IT WORKS! I learnt some stuff I am sure, but I was loop-de-looping for a long time trying to work that out. Thanks SO much, I'll go read through your code now and try to learn! Cheers! :D

#17 alanfluff

alanfluff

    Sr. Member

  • Members
  • PipPipPipPip
  • 405 posts
  • 118

  • LocationOttawa, Canada

Posted 27 March 2012 - 08:16 AM

Good news for my little learning curve; thanks to the help I've received from this thread I hit another problem and solved it myself \o/
I realized I've been committing the cardinal sin of trying to solve a problem that was not fully fully defined. @Pete's last code taught me some stuff so I then went on and produced this code:
$tags = $pages->get("/tags/")->children->find("sort=title");
$tagged_pages = $pages->find("tags.count>0, sort=title");
echo "<dl>";
foreach ($tags as $tag) {
echo "<dt>{$tag->title}</dt>";
foreach ($tagged_pages as $tagged_page) {
  foreach ($tagged_page->tags as $tag_to_test) {
   if($tag_to_test->title == $tag->title)
    echo "<dd><a href='{$tagged_page->url}'>{$tagged_page->title}</a></dd>";
  }
}
}
echo "</dl>";
which now produces this output; which was really what I needed.

One improvement I am going to add is to check the URL to see if we're on /tags/ in which case the above will be used or /tags/a-tag in which case I will only show the pages related to a-tag. So — thanks everyone who has helped me, when this is fully finished I'll write it up for any poor souls who are are PHP-weak as I am ;) Cheers!




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users