In the previous post in this series we discussed how we could setup a clean implementation for interactive with the DEV API.
In this post we will now use the DevPostFetcher
class that we implemented during the previous post to build a simple paginated MVC application. For this we will be using PHP
, Composer
as our package manager and Flight-php
as our router.
In the next post in this series we will refactor our code from a one page script to a proper MVC architecture.
Setting up
Initialise our project files
Let's create our initially required files by running the following in the terminal:
# Mac/Linux
touch .htaccess composer.json index.php
# Windows without WSL/GitBash installed
echo "" | tee .htaccess composer.json index.php
.HTACCESS
Note: The .htaccess
is an apache server configuration file, if you prefer NGINX or another alternative, feel free to use that. Also, if you are following along in another language such as node, you can just ignore this file too.
Let's setup our .htaccess
file by adding the following content to it:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
This basically just says that if the request url is not an accessible file or directory, send the request to the index.php. Using QSA
stands for "Query String Append" and tells the request that if any query parameters such as ?id=1
for example were passed in the original request, it should also send these parameters also to the index.php during the rewrite. The L
flag stands for "Last" and tells the .htaccess to not use any other rewrite rules as this should be the last one processed.
Project dependencies
Currently we only have one project dependency required and that is Flight
. To install Flight we will use the Composer
package manager. This is a package manager for PHP
just like NPM
is for Node
or Nuget
is for .NET
.
Firstly we need to install composer, to do this follow the composer installation instructions. Once this is done we can verify our installation by running composer -V
in our terminal. If any errors occur, please refer back to the installation instructions.
To install Flight, all we need to do now is run:
composer require mikecao/flight
The composer.json
should look like this:
{
"require": {
"mikecao/flight": "^1.3"
}
}
You will also have noticed that a composer.lock
file and a vendor
directory have been generated. The lock file tells composer how your dependencies work together and at what versions. The vendor folder is where all your dependencies live and an autoloader
is provided, we will use this soon to be able to use our project dependencies.
index.php
This is our applications entry point and where we will be working from now on. To get started, we should add the following to our index.php file:
<?php
require_once __DIR__ . "/vendor/autoload.php";
This will require the autoload
file generated when we required the Flight framework via Composer in the previous section. Now we are setup and ready to begin.
Routing
Flight is a really simple but powerful micro framework for routing, you can build simple APIs all the way to MVC applications once you know how it works should you wish. I personally use Flight if I work on PHP services since it has a small footprint and provides an enjoyable experience to use.
To begin, in our index.php
we can add the following lines to register our first route:
use flight\Engine as Router;
$router = new Router();
$router->route("/", function(){
echo "Hello world!";
});
This will call the flight\Engine
namespace from the autoload file and allow us to use it with the name Router
. The $router
variable is an instance of a new Router
and we register a route using the route()
function on that instance. In this case, it will simply echo back the message "Hello world!" to us when we load the homepage. We can test this by running the following in our terminal:
php -S localhost:8080
This will run the built-in server from PHP itself on localhost:8080 and when we load that url, we can see our message: "Hello world!". Simple, right?
Posts routing
Now we understand the very basic usage of Flight we can begin building out our posts
routes. In our index.php
lets add a new /posts
route:
$router->route("/posts", function() {
$request = Flight::request();
$devPosts = new DevPostFetcher("your_api_key");
if(isset($request->query["page"])) {
$page = (int) $request->query["page"];
$page = $page !== 0 ? $page : 1;
$devPosts->setPage($page);
}
$posts = $devPosts->fetch();
print_r($posts);
});
This registers the /posts
route, uses the global request
static method on the Flight
constant afforded to us by the Flight library. Checks if there is a query parameter called page
and if there is, cast it to an integer and if the integer casting comes back as 0, we know it wasn't a number set as the page
parameter and thus we default to 1, otherwise we use the page number provided.
This won't work yet however as we don't currently have access to the DevPostFetcher class. To use this we should create another file in the project root called DevPostFetcher.php
and paste in the DevPostFetcher
class but I advise reading the previous article before continuing this post as it offers some insight into the workings of the class and also some information of issues with local development and SSL.
Once you have created the DevPostFetcher.php
file and added the class into it, we just need to do one more thing and that is to require that file into our index.php
, we can do this by adding the following below our previous require_once
statement:
require_once __DIR__ . "/DevPostFetcher.php";
Now go to localhost:8080/posts
and you should see a JSON dump of your Dev.to posts appearing. If you have any authorisation or SSL errors, ensure you have set things up correctly as outlined in the previous post in this series.
Pagination
Pagination is now really simple since we just need to pass a query parameter calles page
with a valid number, for example: /posts?page=3
which will return, by default, post number 20 up to 30. If no posts exist on page 3 however, for example if we only have 10 posts, ofcourse any page above page 1 by default won't return anything. In this case, we can do whatever we like from a redirect to /posts
to show page 1 or we could show a special UI for this case or we can even just throw an error. For me, I will build a custom UI for this case and in the next section we will add a simple UI for our posts list.
Building the user interface
Flight makes rendering views a doddle. First we create a views
directory and add a layout.php
, no_posts.php
and posts.php
inside it.
In the layout.php
add the following content:
<html>
<head>
<title><?php echo $page_title ?></title>
</head>
<body>
<?php echo $body_content ?>
</body>
</html>
In the no_posts.php add the following content:
<p>No posts found!</p>
Finally, in the posts.php add the following content:
<ul>
<?php foreach($posts as $post): ?>
<li>
<a href="<?php echo $post['url']; ?>">
<?php echo $post['title']; ?>
</a>
</li>
<?php endforeach; ?>
</ul>
Then we can change our /posts
route to look like so:
$router->route("/posts", function() {
$request = Flight::request();
$devPosts = new DevPostFetcher("your_api_key");
if(isset($request->query["page"])) {
$page = (int) $request->query["page"];
$page = $page !== 0 ? $page : 1;
$devPosts->setPage($page);
}
$posts = $devPosts->fetch();
if(count($posts) < 1) {
Flight::render('no_posts', [], 'body_content');
} else {
Flight::render('posts', ['posts' => $posts], 'body_content');
}
Flight::render('layout', ['page_title' => "Posts"]);
});
This will check if any posts were returned for the page requested and if there weren't any, we render the no_posts
template into the body_content
variable. If posts were returned, we render the posts
template into the body_content
variable. Either condition that is met to generate the body_content
variable will be rendered into the layout
file.
Conclusions
In this post we setup a very simple paginated blog using PHP
, Composer
and Flight
. In the next post we will tidy up the codebase and refactor our setup to follow less of a "scratch pad" approach and use a more traditional MVC structure.
Top comments (0)