DEV Community

James Robb
James Robb

Posted on • Edited on

How to load your posts from dev.to onto your site/app

While working on my website after some time of non-maintenance, I noticed that I had previously been hand coding the links to blog posts I had made on the DEV platform. This was pretty unruly and didn't look great in the codebase, nor was it efficient of me to keep doing.

My website runs on a PHP and I use Pico CMS which has a really nice plugin setup, so I decided to create a new plugin for Pico to grab the posts programattically for me rather than continuing the hard coded madness of before.

To simplify things though for readers who may not have used something like PICO before, I will just show you the regular PHP implementation without any framework being used.

Before we begin

To follow along and use the implementation below, you will first need to get an API key for the DEV API. This is quite simple to do, so follow these steps before we begin to access your key:

  1. Go to your profile settings page
  2. Enter the description of what you want the API key for
  3. Click Generate API Key
  4. Copy the generated key
  5. Done 😁

The DevPostFetcher class

/**
 * DevPostFetcher - A class to fetch posts from your dev.to profile
 * @author James Robb
 * @version 1.0.0
 */
class DevPostFetcher
{
  /** @var int The page of posts to access */
  private $page = 1;
  /** @var int How many posts to provide per page request */
  private $per_page = 10;
  /** @var string dev.to API Key to access posts */
  private $api_key;

  public function __construct(string $api_key) {
    $this->api_key = $api_key;
  }

  /**
   * @param   int   $per_page The amount of posts to fetch per page request
   * @return  void
   */
  public function setPerPage(int $per_page): void {
    $this->per_page = $per_page;
  }

  /**
   * @return  int
   */
  public function getPerPage(): int {
    return $this->per_page;
  }

  /**
   * @param   int   $page  The page of posts to fetch
   * @return  void
   */
  public function setPage(int $page): void {
    $this->page = $page;
  }

  /**
   * @return  int
   */
  public function getPage(): int {
    return $this->page;
  }

  /**
   * @throws Exception if the DEV API curl request fails
   * @return array An associative (key => value) array of DEV posts
  */
  public function fetch() {
    $page = $this->getPage();
    $per_page = $this->getPerPage();
    $api_key = $this->api_key;
    $ch = curl_init(
      "https://dev.to/api/articles/me?page=$page&per_page=$per_page"
    );
    $requestHeaders = [
      "api-key:$api_key"
    ];

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
    $posts = curl_exec($ch);
    if (curl_errno($ch)) {
      throw new Exception(curl_error($ch));
    }
    curl_close($ch);
    return json_decode($posts, true);
  }
}
Enter fullscreen mode Exit fullscreen mode

The class revolves around 3 private properties:

  1. $page - the page of posts we want to access
  2. $per_page - how many posts to load per page request
  3. $api_key - the api key we generated earlier

We also have a getter and setter for $page and $per_page setup, these become useful for pagination for example as we will see later in this post.

A sidenote

Sometimes you might get curl errors relating to SSL Certificates on your local machine, to get around these you can add the following lines of code into the fetch() method:

  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
Enter fullscreen mode Exit fullscreen mode

Do not use these in production: Use in local development only since this is a security issue and you don't want insecure requests coming in and out on a live server.

Using the class

Latest posts

Let's say we are happy with only getting the latest 10 posts from your account and you want to display them, we can do the following for example:

<?php
  $devPosts = new DevPostFetcher("your_api_key");
  $devPostsList = $devPosts->fetch();
?>
<ul>
  <?php foreach($devPostsList as $post): ?>
    <li>
      <a href="<?php echo $post['url']; ?>">
        <?php echo $post['title']; ?>
      </a>
    </li>
  <?php endforeach; ?>
</ul>

Enter fullscreen mode Exit fullscreen mode

Paginating posts

Let's say we want to paginate our posts, again, it's pretty easy:

<?php
  $devPosts = new DevPostFetcher("your_api_key");
  $firstPagePosts = $devPosts->fetch(); // posts 1 - 10
  $devPosts->setPage(2);
  $secondPagePosts = $devPosts->fetch(); // posts 11 - 20
?>
Enter fullscreen mode Exit fullscreen mode

Note: In the next post of this series, we will build a properly paginated posts application using the DevPostFetcher class, Composer and Flight.

Load more than the default post count

You probably guessed by now but if you wanted to load more than 10 posts per fetch, we could do:

<?php
  $devPosts = new DevPostFetcher("your_api_key");
  $devPosts->setPerPage(20);
  $firstPagePosts = $devPosts->fetch(); // posts 1 - 20
?>
Enter fullscreen mode Exit fullscreen mode

Putting it all together

A full example could be something like this:

<?php
$devPosts = new DevPostFetcher("your_api_key");
$devPosts->setPerPage(5);
$devPosts->setPage(2);
$devPostsList = $devPosts->fetch();
?>
<ul>
  <?php foreach($devPostsList as $post): ?>
    <li>
      <a href="<?php echo $post['url']; ?>">
        <?php echo $post['title']; ?>
      </a>
    </li>
  <?php endforeach; ?>
</ul>
Enter fullscreen mode Exit fullscreen mode

In conclusion

The DEV API is basic but well formed from what I have experienced with this foray into using it, why not go and check it out for yourself?

This series will continue with us building a paginated blog application using the DevPostFetcher class from this post, Composer and Flight.

Top comments (2)

Collapse
 
devhammed profile image
Hammed Oyedele • Edited

You can just get the current page with

$pageQuery = $_GET['page'];
$currentPage = isset($pageQuery) ? $pageQuery : 1;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jamesrweb profile image
James Robb • Edited

Hey Hammed, thanks for the comment!

I didn't want to add the use of super globals like $_GET, $_POST, $_SESSION, etc since this class should be framework agnostic. By this, I mean, for example you could do any of the following:

Using PHP:

$devPosts = new DevPostFetcher("your_api_key");
$pageQueryParam = $_GET['page'];
$page = 1;

if(isset($pageQueryParam)) {
  $page = (int) $pageQueryParam;
  $page = $page !== 0 ? $page : 1;
}

$devPosts->setPage($page);
$posts = $devPosts->fetch();
require_once 'path/to/template';
Enter fullscreen mode Exit fullscreen mode

Using the Flight framework:

Flight::route('/posts/@page:[1-9]+', function($page) {
  $devPosts = new DevPostFetcher("your_api_key");
  $devPosts->setPage((int) $page);
  $posts = $devPosts->fetch();
  return Flight::render('posts_page', ['posts' => $posts]);
});
Enter fullscreen mode Exit fullscreen mode

Using the Symfony framework:

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class PostsController extends AbstractController
{
    /**
     * @Route("/posts/{page}", name="post_list", requirements={"page"="\d+"})
     */
    public function list(int $page)
    {
         $devPosts = new DevPostFetcher("your_api_key");
         $devPosts->setPage($page);
         $posts = $devPosts->fetch();
         return $this->render('posts/index.twig', ['posts' => $posts]);
    }
}
Enter fullscreen mode Exit fullscreen mode

The idea, in short, was to not bias anyones implementation choice but merely to show a reusable class that we will build on top of in later posts and that anyone can use, anywhere. I hope that clears up things a bit for anyone else reading the post and this comment later too 😄.