Filter by authors (same way as tags)

Hi,

I’m trying to filter subpages by authors:

controllers:

if($author = param('author')) {
    $author = urldecode(param('author'));
    $items = $items->filterBy('authors', $author, ',');
  } 

$authors = $items->pluck('authors', ',', true);

Template:

   <ul class="nav-authors">
      <?php foreach($authors as $author): ?> 
      <?php if($author == urldecode(param('author'))): ?>     
      <li> <a class="active" href="<?= $page->url() ?>"> <span>x</span><?= html($author) ?></a> </li>
      <?php else : ?>
      <li><a  href="<?= url($page->url(), ['params' => ['author' => urldecode($author)]]) ?>"><?= html($author) ?></a> </li>
      <?php endif;?>
      <?php endforeach ?>
    </ul>

This works: when author link is clicked, page shows articles from correspondant author
Problem: It shows author email, not his name, and if multiple authors are used, the pluck does nor seem to work.

So I tried (from cookbook) to make it this way:

Controllers:


if($author = param('author')) {
$author = urldecode(param('author'));
    $items = $items->filterBy('authors', $author, ',');
  } 
// fetch all used authors emails from all articles
  $emails = $page->children()->listed()->pluck('authors', '-', true);
  // get a list of all authors filtered by the ones with articles
  $authors = $kirby->users()->filterBy('email', 'in', $emails);

and in the template:

<ul class="nav-authors">
      <?php foreach($authors as $author): ?> 
      <?php if($author == urldecode(param('author'))): ?>     
      <li> <a class="active" href="<?= $page->url() ?>"> <span>x</span><?= html($author->name()) ?></a> </li>
      <?php else : ?>
      <li><a  href="<?= url($page->url(), ['params' => ['author' => urldecode($author)]]) ?>"><?= html($author->name()) ?></a> </li>
      <?php endif;?>
      <?php endforeach ?>
    </ul>

This works: Only the 2 authors are shown. they are not duplicate, and their name are correctly shown.
But when we try to filter their own articles, there is no result :frowning:

Thank you for help telling me what I’m doing wrong.

How are you storing the authors, in a users field?

Yes:

authors:
  type: users

As far as I know, the users are stored as yaml not as comma separated list, so using pluck() with the comma separator doesn’t make that much sense.

$authors = $items->pluck('author', '-', true)

seems to work fine.

I`m a bit surprised that filtering works as expected, given that the comma separator shouldn’t work here.

hmm, unfortunatly, I can’t make it work. Or emails and duplicates authors/emails are displayed, or clicking author does not show nothing.

this is my complete controllers:

<?php

return function ($kirby, $page) {

  //Display news list article sort by date
  $items = $page->children()->listed()->sortBy('date', 'desc');  



  if($tag = param('tag')) {
    //Used for multiple words tags
    $tag = urldecode(param('tag'));    
    $items = $items->filterBy('tags', $tag, ',');
  }


 
  if($author = param('author')) {
   
    $author = urldecode(param('author'));
    $items = $items->filterBy('authors', $author, ',');
  } 



  if($category = param('category')) {
  
    $category = urldecode(param('category'));    
    $items = $items->filterBy('categories', $category, ',');
  }


  if($year = param('year')) { 
    $items = $items->filter(function($item) use($year) {
      return $item->date()->toDate('%Y') === $year;
    });
  }



  // fetch all tags
  $tags = $page->children()->listed()->pluck('tags', ',', true);

  // fetch all categories
  $categories = $page->children()->listed()->pluck('categories', ',', true);  

  //fetch all years
  $years = $page->children()->listed()->pluck('date');
  $years = array_unique(array_map(function($p) {
    return date('Y', strtotime($p));
  }, $years)); 

  // fetch all users
  $authors = $page->children()->listed()->pluck('authors', '-', true);


  // fetch all used authors emails from all articles
  //  $emails = $page->children()->listed()->pluck('selectauthor', '-', false);
  // get a list of all authors filtered by the ones with articles
  //  $authors = $kirby->users()->filterBy('email', 'in', $emails);






  return [
    'categories' => $categories,
    'category' => $category,
    'items' => $items,
    'tags' => $tags,
    'tag' => $tag,
    'years' => $years,
    'year' => $year,
    'author' => $author,
    'authors' => $authors,
    //    'emails' => $emails
  ];

};

With users field authors are stored like this:

Authors:
 - author1@mail.com
 - author2@mail.com

With a multiselect field like this:
author1@mail.com, author2@mail.com

I tried both but result is the same

As I said, the filtering can’t work like this, because of the yaml format in which the authors are stored.

$filtered = $items->filter(function($item) use($author){
  return in_array($author, $item->author()->yaml());
});

And if you’d rather display the username instead of the email, you’d have to get the user object and then display the desired property.

$user = $kirby->users()->findBy('email', $author);
if($user) {
   echo $user->name();
}
3 Likes

Thank you Sonja.

I’m currently adding a filtering by author functionality to my page and it’s working well thanks to this forum entry, but I’d rather want to filter by a username than an email address (i.E. mypage.com/blog/author:marie instead of mypage.com/blog/author:marie@somepage.com)—I guess I’d need to define the username in my user blueprint, but how would I use that string as filter value? Thanks in advance!

Since the user field stores the email address not the user name (unless you use a select field instead of the user field?), you can do something like this:

 if($author = param('author')) {
   
   $items = $items->filter(function($item) use($author) {
      if ($user = kirby()->users()->findBy('username', urldecode($author)) {
        return in_array($user->email(), $item->author()->yaml());
     }
   });
  } 

Note that the username may not be unique and findBy() return the first user it finds.

Amazing, thanks!
Is there a way to change the formatting of urldecode() somehow though that’s more similar to the way that page urls are automatically generated in the panel? My authors’ names most have first and last names in their username & I find the “author:Firstname%20Lastname” decoding a bit aesthetically unpleasing, would rather like “author:firstname-lastname”. Or would it be easier to just add a blueprint field for a username value?

If you urlencode() the param like you should (otherwise decoding doesn’t make sense), you should get author:firstname+lastname, not %20.

Ok, I found my own error (made a typo…)
on another note, urlencode() doesn’t do firstname+lastname but Firstname+Lastname (capital letters).
Thanks for your help! :~)