How to use `str::slug`

Looking at the toolkit API I found the str::slug function.

I tried to read this topic to get a sense of how to use the function, but it seems overkill for what I need.

I just need to split and slug a foreach containing tags. How could I do that?

PS. maybe only useful for noobs, but if in the API page there would be an example for each method, it would make learning kirby and PHP much easier I think!

1 Like

This is the function:

/**
   * Convert a string to a safe version to be used in a URL
   *
   * @param  string  $string The unsafe string
   * @param  string  $separator To be used instead of space and other non-word characters.
   * @return string  The safe string
   */
  public static function slug($string, $separator = null, $allowed = null) {

    $separator = $separator ?: static::$defaults['slug']['separator'];
    $allowed   = $allowed   ?: static::$defaults['slug']['allowed'];

    $string = trim($string);
    $string = static::lower($string);
    $string = static::ascii($string);

    // replace spaces with simple dashes
    $string = preg_replace('![^' . $allowed . ']!i', $separator, $string);
    // remove double dashes
    $string = preg_replace('![' . preg_quote($separator) . ']{2,}!', $separator, $string);
    // trim trailing and leading dashes
    $string = trim($string, $separator);
    // replace slashes with dashes
    $string = str_replace('/', $separator, $string);

    return $string;

  }

(/kirby/toolkit/lib/str.php)

I hope the comments are self-explanatory, if not, please don’t hesitate to ask.

An example:

<?= str::slug(' Das ist aber eine Überraschung!!!') ?>

Will result in:

das-ist-aber-eine-ueberraschung

But you are right, some documentation would be great.

Not sure if that’s what you are after, but to loop through list of comma separated tags, you can do this:

<ul>
<?php
$tags = $page->tags()->split();
foreach($tags as $tag): ?>
  <li><?= $tag ?></li>
<?php endforeach ?>
</ul>
1 Like

(Sorry)
I had time to try this out only today.

I am trying to sluggify a foreach loop outputting tags.

I tried to simplify and adjust the code from this post to my needs, but it’s not working.

In blog.php's controller I have:

return function($site, $pages, $page) {

  // get all articles
  $articles = $page->children()->visible()->flip();

  // fetch all tags
  $topics = $articles->pluck('topics', ',', false);

  //add tag filter (`topic`)
  if(!empty(param('topics'))) {
    $topics = str::slug(param('topics'));
    $articles = $articles->filter(function($article) use($topics) {
      // split tags and convert to lowercase
      $topics = array_map('str::slug', $article->topics()->split(','));
    });
  }
  return compact('articles', 'topics', 'topic');

};

and in the blog.php template:

<?php foreach($topics as $topic): ?>
  <div class="c-tag-collection__tag-name filter" data-filter=".<?php echo html($topic) ?>">
	<?php echo html($topic) ?>
  </div>
<?php endforeach ?>

Is it better to add slug:: in the controller or directly in the template?
What am I doing wrong?

Your filter does not return anything, try this:

return function($site, $pages, $page) {

  // get all articles
  $articles = $page->children()->visible()->flip();

  // fetch all tags
  $topics = $articles->pluck('topics', ',', false);

  //add tag filter (`topic`)
  if(!empty(param('topics'))) {
    $filter = str::slug(param('topics'));
    $articles = $articles->filter(function($article) use($topics) {
      // split tags and convert to lowercase
      $topics = array_map('str::slug', $article->topics()->split(','));
      // return the page if the filter is in the array
      if(in_array($filter, $topics)) return true;
    });
  }
return compact('articles', 'topics');

};

Updated, but it still output tags with spaces when there is a multi-word tag with a space.

You would have to str::slug your topics in the template as well, or array_map them in the controller:

controller

$topics = array_map('str::slug', $articles->pluck('topics', ',', false));

or

<?php foreach($topics as $topic): ?>
  <div class="c-tag-collection__tag-name filter" data-filter=".<?php echo str::slug($topic) ?>">
	<?php echo str::slug($topic)) ?>
  </div>
<?php endforeach ?>

Great!

Last thing, is there a way to combine the implode function with the slug one?

What I’d like to accomplish would be something like:

<div class="blog-entry__element flex mix tag-1 tag-word-word tag-etc">

This could work:

$topics = implode(' ', array_map('str::slug', $article->topics()->split(',')));

Amazing! It works, thank you :tada: