Adding estimated time to read an article in Laravel

Categorized as Laravel Tagged

If you’re wondering how to do that, the short answer is here: we are going to use one private method and Laravel Accessor. Don’t be afraid, it’s pretty simple.

First off, we’re going to count seconds and minutes to read using this formula:

$minutes = (int) floor($wordCount / 200);
$seconds = (int) floor($wordCount % 200 / (200 / 60));

Let’s make a whole function using that formula and put in into our Model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

use Illuminate\Database\Eloquent\Casts\Attribute;

class Article extends Model
{

    ...

    /**
     * Returns an estimated reading time in a string
     * @param  string $content the content to be read
     * @param int $wpm
     * @return string estimated read time eg. 1 min or 31 sec
     **/

    private function getEstimateReadingTime($content, $wpm = 200) {

        $wordCount = str_word_count(strip_tags($content));

        $minutes = (int) floor($wordCount / $wpm);
        $seconds = (int) floor($wordCount % $wpm / ($wpm / 60));

        if ($minutes === 0) {
            return "{$seconds} sec read";
        } else {
            return "{$minutes} min read";
        }  

    }

}

You can also use a slightly advanced method, showing pluralized minutes and seconds. Don’t forget to add the Str::helper support to your Model:

use Illuminate\Support\Str;

/**
 * Returns an estimated reading time in a string
 * @param  string $content the content to be read
 * @param int $wpm
 * @return string estimated read time eg. 1 minute, 30 seconds
 **/
private function getEstimateReadingTime($content, $wpm = 200) {

    $wordCount = str_word_count(strip_tags($content));

    $minutes = (int) floor($wordCount / $wpm);
    $seconds = (int) floor($wordCount % $wpm / ($wpm / 60));   

    if ($minutes === 0) {
        return $seconds." ".Str::of('second')->plural($seconds);
    } else {
        return $minutes." ".Str::of('minute')->plural($minutes);
    }

}

Now we add an Accessor, which allows us to add (or change) Eloquent attribute values on model instances:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

use Illuminate\Database\Eloquent\Casts\Attribute;

class Article extends Model
{

    ...


    private function getEstimateReadingTime($content, $wpm = 200) {

        ...

    }

    
    protected function timeToRead(): Attribute {

        return Attribute::make(
            get: function ($value, $attributes) {

                $value = $this->getEstimateReadingTime($attributes['body']);

                return $value;

            }
        );
    }    

Finally, we can update our template file to show the estimated reading time:

@foreach($article as $item)
    ...
    {{ $item->time_to_read }}
    ...
@endforeach

Leave a reply

Your email address will not be published. Required fields are marked *