Rails authentication with Devise

In this tutorial, we will be dealing with Rails authentication with Devise.

If you don’t want to use Devise for authentication, you may refer to the article create basic authentication in Ruby on Rails instead.

Devise has been around for some time now and have been used and reviewed thousands of times and still receives security updates to date.

Not only that, it is also composed of other features such as reset password, email integration, and many more that can be a pain when creating your own authentication system.

Step 1: Initialize a Rails application

You can initalize a Rails application by running the following commands in the terminal.

rails new devise-app
cd devise-app

Step 2: Install the Devise gem

In your Gemfile add the following:

gem 'devise'

And run the following in the terminal.

bundle install
rails g devise:install

This will install and initialize the Devise gem in our Rails application.

Step 3: Create your model for authentication

In this example, we would be using User as our model for authentication.

Run the following commands in the terminal.

rails g devise user
rails db:migrate

This will create a migration file for our User model then migrate it to complete.

Step 4: Add messages and navigation link to views

In your app/views/layouts/application.html.erb, add the following:

<% if user_signed_in? %>
  Logged in as <strong><%= current_user.email %></strong>.
  <%= link_to 'Edit Profile', edit_user_registration_path %> |
  <%= link_to 'Sign out', destroy_user_session_path, :method => :delete %>
<% else %>
  <%= link_to 'Sign up', new_user_registration_path %> |
  <%= link_to 'Sign in', new_user_session_path %>
<% end %>
<%= notice if notice.present? %>
<%= alert if alert.present? %>

This will show the current signed in user, a link to edit profile, a link to sign-out if there is a current user and will show a sign-up link and sign-in link instead otherwise.

The notice and alert are used to notify the user for the actions that have been made.

Step 5: Register a user

Now that we have a model, we can now register a new User. The Devise gem already created everything we need such as routes, models, etc. for our authentication to work.

Run your Rails server by running the following in the terminal.

rails s

Navigate to http://localhost:3000/users/sign_up in your browser and you should see a form.

Fill up the form and click Sign up.

It is up to you now to create your own views and to see all available routes generated by Devise you may run the following command in the terminal.

rake routes

That’s it, we have now created a Rails authentication with Devise.

Dynamically import class with no SSR in Next.js

In this tutorial, I will show you how to dynamically import class with no SSR in Next.js.

Probably you already have seen this Next.js documentation regarding dynamically importing component with no SSR. But the problem with this is that it can only be used on React components, but what if we need to dynamically import a non-React component class?

This guide will show you how. I will use FullCalendar plugin called FullCalendar interaction as this has a class that I need to dynamically import to make it work. (READ: How to use FullCalendar in Next.js)

Step 1: Initialize a Next.js project

Initialize a Next.js project by running the following commands in the terminal.

mkdir import-class-nextjs
cd import-class-nextjs
touch package.json

Paste the following inside package.json that we have created.

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

Install the necessary Next.js packages by running the following command in the terminal.

npm install --save next react react-dom

Let’s create an index or landing page for our project using the following commands.

mkdir pages
touch pages/index.js

Page the following inside pages/index.js that we have created.

export default function Home() {
  return <div>Welcome to Next.js!</div>
}

This is just a simple component for our landing page which just returns a single div component.

Step 2: Dynamically import class with no SSR

Install the package that you need. In this example, we are going to use FullCalendar interaction plugin.

npm install --save @fullcalendar/core @fullcalendar/interaction

If you tried to import this the usual way (import interactionPlugin from '@fullcalendar/interaction') you will get an error Element is not defined as Element is non-existent on the server side rendering.

The solution is to make sure that the window is already defined when you import the package.

Since Next.js is a React framework we can take advantage of using componentDidMount if you are using class components or useEffect when using functional components.

The importing of package can be written as follows.

import { useEffect } from 'react'



export default function Home() {

  useEffect(() => {
    dynamicallyImportPackage()
  }, [])

  let dynamicallyImportPackage = async () => {
    const FullCalendarInteraction = await import('@fullcalendar/interaction')

    // you can now use the package in here
  }

  return <div>Welcome to Next.js!</div>
}

That’s it, we have now successfully imported a non-React component class with no SSR in Next.js.

Test Doubles: Stubbing & Mocking

What are test doubles?

Test doubles are a substitute for production apps used for testing purposes. There are at least five (5) types of test doubles according to Gerard Meszaros but we are going to focus on two (2) which are stubbing and mocking.

The difference between stubbing & mocking

Stubbing

  • Doesn’t fail.
  • Makes sure that the method returns the correct value.
  • Returns hard-coded values.
  • Answers the question ‘What is the result?’.

Mocking

  • Can fail.
  • Runs the actual method and making sure everything is correct before returning the correct value.
  • Returns calculated values.
  • Answers the question ‘How the result has been achieved?’.

Why use stubbing & mocking?

  • To prevent tests failing intermittently due to connectivity issues.
  • For faster test suites.
  • To avoid hitting API limits when using 3rd party APIs.
  • To prevent creating/updating/deleting real data from tests.

Real life example

Imagine your kid has a glass plate and he starts playing with it. Now, you’re afraid that it will break, so you gave him a plastic plate instead. The plastic plate is mocking the glass plate. (same behavior and usage)

Now, say you don’t have a plastic replacement, so you can say to your kid “If you continue playing with it, it will break!”. The quoted words are a stub. (you predefined the outcome in advance)

Source: https://stackoverflow.com/a/53189007/9375533

Code example

Imagine that we have a simple calculator app that uses 3rd party API, in this case, our 3rd party API is math.js.

The thing is, we don’t want to make a request to math.js’s endpoint every time we ran our tests as this will make our tests slow since we need to wait for the response before we can proceed to the next test suite.

The tests can also fail if we don’t have an internet connection and we also might hit the API limit of their free service.

Without stubbing & mocking

result = '3*3' # EXAMPLE 1
result = '4*4' # EXAMPLE 2
uri = URI "https://api.mathjs.org/v4/?expr=#{result}"

expect(Net::HTTP.get(uri)).to eq(9)
# EXAMPLE 1 RETURNS 9 (SUCCESS)
# EXAMPLE 2 RETURNS 16 (FAILS)

With stubbing

result = '3*3' # EXAMPLE 1
result = '4*4' # EXAMPLE 2

stub_request(:get, "https://api.mathjs.org/v4/?expr=#{result}").to_return :body => "9"

uri = URI "https://api.mathjs.org/v4/?expr=#{result}"

expect(Net::HTTP.get(uri)).to eq(9)
# EXAMPLE 1 RETURNS 9 (SUCCESS)
# EXAMPLE 2 RETURNS 9 (SUCCESS)

What we did here is, we basically stubbed some data to the endpoint that we are going to call for our test.

In this case, we stubbed data to the endpoint with a body of 9. Which means, whenever we call the endpoint that we stubbed it will always return 9.

With mocking

result = '3*3' # EXAMPLE 1
result = '4*4' # EXAMPLE 2

expect(eval(result)).to eq(9)
# EXAMPLE 1 RETURNS 9 (SUCCESS)
# EXAMPLE 2 RETURNS 16 (FAILS)

We know that the endpoint simply calculates the expression params then return its result. Now we can mock it ourselves. eval(result) is the mock of the API endpoint as it returns the same output exclusively for our test suites.

That’s what I can share about stubbing & mocking. I hope you learned something. Using a test double type really depends on your needs. In my case, I use both stubbing & mocking at the same time.

How to use FullCalendar in Next.js

In this tutorial, we would be learning on how to use FullCalendar in Next.js.

You might have been able to come here because you have encountered an error Element is not defined when you are trying to use FullCalendar in Next.js project.

Before we proceed with the solution, I will just point out the cause of that error.

Next.js renders the page on the server side. Since it was on the server side, window is not available as it is only available on the client side. If the window is not available, then the element is also not available.

Now that we understand the problem, let’s proceed with the solution.

Step 1: Initialize a Next.js project

Let’s start with initializing our Next.js project by running the following commands in the terminal.

mkdir nextjs-fullcalendar
cd nextjs-fullcalendar
touch package.json

In the package.json that we have created, paste the following.

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

In the terminal, install Next.js by running the following command.

npm install --save next react react-dom

Now, let’s create our landing page. Run the following commands in the terminal.

mkdir pages
touch pages/index.js

In the file pages/index.js, paste the following.

export default function Home() {
  return <div>Welcome to Next.js!</div>
}

We just created a Home component that simply shows a short message on our landing page.

Step 2: Setup Next.js CSS support

Since FullCalendar has custom CSS, we must setup our Next.js project to support CSS.

In the terminal run the following.

npm install --save node-sass @zeit/next-sass @zeit/next-css
touch next.config.js

In the file next.config.js, paste the following.

const withSass = require('@zeit/next-sass')
module.exports = withSass()

Step 3: Install FullCalendar

Now, let’s install FullCalendar to our project. Run the following command in the terminal.

npm install --save @fullcalendar/core @fullcalendar/react @fullcalendar/daygrid @fullcalendar/timegrid

Let’s create an SCSS file for the FullCalendar CSS by running the following commands in the terminal.

mkdir styles
touch styles/calendar.scss

In the file styles/calendar.scss, paste the following.

@import '[email protected]/core/main.css';
@import '[email protected]/daygrid/main.css';
@import '[email protected]/timegrid/main.css';

Step 4: Create your own no SSR FullCalendar component

Let’s start by creating a directory for our components. Run the following commands in the terminal.

mkdir components
touch components/fullcalendar.js

In the file components/fullcalendar.js, paste the following.

import dynamic from 'next/dynamic'
import { useEffect, useState } from 'react'

import '../styles/calendar.scss'

let CalendarComponent



export default function FullCalendar(props) {

  const [calendarLoaded, setCalendarLoaded] = useState(false)

  useEffect(() => {
    CalendarComponent = dynamic({
      modules: () => ({
        calendar: import('@fullcalendar/react'),
        dayGridPlugin: import('@fullcalendar/daygrid'),
        timeGridPlugin: import('@fullcalendar/timegrid')
      }),
      render: (props, { calendar: Calendar, ...plugins }) => (
        <Calendar {...props} plugins={Object.values(plugins)} ref={props.myRef} />
      ),
      ssr: false
    })

    setCalendarLoaded(true)
  })

  let showCalendar = (props) => {
    if ( !calendarLoaded ) return <div>Loading ...</div>

    return (
      <CalendarComponent {...props} />
    )
  }

  return (
    <div>
      {showCalendar(props)}
    </div>
  )

}

The problem that we have is that Next.js renders our page in the server. In the docs, there is a way to dynamically import packages with no ssr.

We simply imported the FullCalendar component dynamically using Next.js’s dynamic function with an attribute ssr: false to make sure that it is not rendered on the server.

We also imported it in React’s useEffect() hook just to be sure that the window is already defined.

Step 5: Use our own FullCalendar component

In the file pages/index.js, paste the following.

import FullCalendar from '../components/fullcalendar'



export default function Home() {
  return (
    <div>
      <FullCalendar defaultView='dayGridMonth' />
      <FullCalendar defaultView='timeGridWeek' />
    </div>
  )
}

Now if you run npm run dev in the terminal and navigate to http://localhost:3000 in your browser, you should be able to see the two (2) calendars, one in month view and one in week view rendered properly without any errors.

TL;DR

Install FullCalendar using the following command.

npm install --save @fullcalendar/core @fullcalendar/react @fullcalendar/daygrid @fullcalendar/timegrid

Import the CSS in your styles.

@import '[email protected]/core/main.css';
@import '[email protected]/daygrid/main.css';
@import '[email protected]/timegrid/main.css';

Dynamically import FullCalendar with no SSR and use the component in your project.

import dynamic from 'next/dynamic'
import { useEffect, useState } from 'react'

import '../styles/calendar.scss'

let FullCalendar



export default function Home(props) {

  const [calendarLoaded, setCalendarLoaded] = useState(false)

  useEffect(() => {
    FullCalendar = dynamic({
      modules: () => ({
        calendar: import('@fullcalendar/react'),
        dayGridPlugin: import('@fullcalendar/daygrid'),
        timeGridPlugin: import('@fullcalendar/timegrid')
      }),
      render: (props, { calendar: Calendar, ...plugins }) => (
        <Calendar {...props} plugins={Object.values(plugins)} ref={props.myRef} />
      ),
      ssr: false
    })

    setCalendarLoaded(true)
  })

  let showCalendar = (props) => {
    if ( !calendarLoaded ) return <div>Loading ...</div>

    return (
      <FullCalendar {...props} />
    )
  }

  return (
    <div>
      {showCalendar(props)}
    </div>
  )

}

That’s it, we have learned on how to use FullCalendar in Next.js project.

Reference: Using fullcalendar with Next.js

Rails file upload using Active Storage

In this tutorial, we would be learning to upload files in Rails. Rails file upload have become easier since the release of Active Storage. It allows us to handle file uploads without using any gem.

This tutorial aims to deliver the simplest way to integrate Rails file upload. We would be skipping the details on how Active Storage works in the back and focus on how to integrate it correctly.

If you are already on an existing project and has a model that already has a CRUD operation then you may skip to Step 3.

Step 1: Create a new Rails project.

In your terminal run the following commands.

rails new file-upload
cd file-upload

This will simply create a new Rails project and change our current directory in the newly created project.

Step 2: Create a CRUD.

Since our focus is on file upload, we would be using scaffold to create our CRUD operations.

Scaffolding requires the following command in the terminal.

rails g scaffold post title:string content:text

This will create a Post model with a title and content columns, a database migration, and all views.

Step 3: Install Active Storage.

You need to run the following command in the terminal.

rails active_storage:install
rake db:migrate

The command creates a database migration that hold the necessary data and makes file uploading possible. Since we have a new database migration, we would need to migrate it.

Step 4: Add the necessary association.

In the file app/models/post.rb add the following association.

class Post < ApplicationRecord

  has_one_attached :image

end

This will tell that our model allows an attachment image. The word image is user-defined and can be changed to anything you want.

Since we are using has_one_attached, we are only expected to attach one image per record. If you want to attach many files in a single record you may want to use has_many_attached instead.

Step 5: Add the file upload field in the form.

The scaffold the we ran in Step 2 includes the views needed in our CRUD.

In the file app/views/posts/_form.html.erb, inside the form_with, add the following.

<div class="field">
  <%= form.label :image %>
  <%= form.file_field :image %>
</div>

This will add a file upload field in the form.

Step 6: Process the new field in the controller.

All changes in this step will be done in the file app/controllers/posts_controller.rb.

First, we would need to whitelist the new field in the parameters. This is necessary to prevent attackers from passing malicious parameters to our controller.

def post_params
  params.require(:post).permit(:title, :content, :image)
end

Second, we would need to attach our image in the controller’s create method.

# POST /posts
# POST /posts.json
def create
  @post = Post.new(post_params)
  @post.image.attach(post_params[:image])

  respond_to do |format|
    if @post.save
      format.html { redirect_to @post, notice: 'Post was successfully created.' }
      format.json { render :show, status: :created, location: @post }
    else
      format.html { render :new }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end

Notice that we just added @post.image.attach(post_params[:image]) in the create method.

Third, we also need to attach our image in the update method.

# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
  @post.image.purge
  @post.image.attach(post_params[:image])

  respond_to do |format|
    if @post.update(post_params)
      format.html { redirect_to @post, notice: 'Post was successfully updated.' }
      format.json { render :show, status: :ok, location: @post }
    else
      format.html { render :edit }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end

In the update method we added @post.image.purge to ensure that the old attachments are deleted before we attach the new file. The next line after purge is the same as what we did in create method.

Step 7: Show the uploaded file in the view.

Now that we are able to attach files on our records, we would want to view them in the view right?

In your file app/views/posts/show.html.erb, add the following.

<p>
  <% if @post.image.attached? %>
    <p>
      <strong>Image:</strong>
      <br>
      <%= image_tag @post.image %>
    </p>
  <% end %>
</p>

It will check if there is a file attached and if it does it will show the file.

You may want to run your app using rails s and navigate to http://localhost:3000/posts to confirm that everything works.

That’s it, we have now implemented a Rails file upload using Active Storage. Take note that all files will be saved on your local disk, you can also change this but that would be for another tutorial as this aims to deliver the most basic Rails file upload.

How to create basic authentication in Ruby on Rails

In this tutorial, we will be creating a basic authentication in Ruby on Rails.

Step 1: Adding a password_digest field to the model.

This field will be used for authenticating the user.

Step 1.1-A: Coming from a fresh Rails project.

If you are doing this from a fresh project then you must first create a User model by running rails g model User in the terminal.

This will create a file in db/migrate/xxxxx_create_users.rb where xxxxx is the current date and time you ran the command.

In that file add the following:

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :username
      t.string :password_digest
      t.timestamps
    end
  end
end

Return to your terminal and run:

rake db:migrate

This will create a users table in the database with columns username and password digest.

Step 1.1-B: Coming from an existing Rails project.

If you are doing this from an existing project then you must add password_digest field to your existing model.

In this tutorial, we will be using User as the model. Feel free to change it on your preference.

Run the following command in the terminal:

rails g migration add_password_digest_to_users

This will create a file in db/migrate/xxxxx_add_password_digest_to_users.rb

In that file add the following:

class AddPasswordDigestToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :password_digest, :string
  end
end

Return to your terminal and run:

rake db:migrate

This will add a string column password_digest to your users table in the database.

Note:

In this step, you may notice that ActiveRecord::Migration[5.2] has 5.2 in it, you must change it to the correct version, otherwise, this might not work.

You might want to check your Rails version by running the following command in the terminal:

rails -v

This tutorial is created using Rails 5.2.3.

Step 2: Creating routes for the app.

Open config/routes.rb in your text editor.

In this file, we will define the links in our app. For this tutorial, we will be creating six (6) routes. one (1) that needs authentication and five (5) unauthenticated routes.

Rails.application.routes.draw do

  # AUTHENTICATED ROUTES
  get '/dashboard' => 'dashboard#index' # THIS WILL BE THE DASHBOARD OF THE USER

  # UNAUTHENTICATED ROUTES
  get '/sign-up' => 'users#new' # THIS IS WHERE THE USER WILL REGISTER
  post '/save-user' => 'users#create' # THIS WILL BE THE PROCESS OF REGISTERING THE USER

  get '/' => 'sessions#new' # THIS IS WHERETHE USER WILL SIGN IN
  post '/' => 'sessions#create' # THIS WILL BE THE PROCESS OF SIGNING IN THE USER
  delete '/sign-out' => 'sessions#destroy' # THIS WILL BE THE PROCESS OF SIGNING OUT THE USER

end

Step 3: Creating controllers for the routes.

At this point, our app will raise an error because the routes we defined doesn’t have a matching controller.

We can see that we used three (3) different controllers in Step 2. These are the dashboard, users, and sessions.

To create a matching controller for these routes you can run the following command in the terminal. One (1) at a time.

rails g controller dashboard
rails g controller users
rails g controller sessions

This will create files in app/controllers directory.

In the dashboard controller app/controllers/dashboard_controller.rb add the following method:

def index
end

In the users controller app/controllers/users_controller.rb add the following method:

def new
end

def create
end

In the sessions controller app/controllers/sessions_controller.rb add the following method:

def new
end

def create
end

def destroy
end

If you observe the routes, it does not only matches to the controller but also matches to the methods in that controller.

The creation of method above ensures that our controller matches our routes.

Step 4: Creating the dashboard page.

In the app/views/dashboard/index.html.erb paste the following code (create the file if it does not exists):

DASHBOARD

This page will simply return a text DASHBOARD when /dashboard is accessed in the browser.

Step 5: Creating a sign up page.

From the steps above, we know that the users controller handles these processes.

In your app/controllers/users_controller.rb, inside the new method, add the following:

@user = User.new

In the app/views/users/new.html.erb paste the following code (create the file if it does not exists):

<h1>Sign Up</h1>

<%= form_with :model => @user, :url => '/save-user' do |f| %>
  <%= f.label :username %>
  <br>
  <%= f.text_field :username %>
  <br>
  <br>
  <%= f.label :password %>
  <br>
  <%= f.password_field :password %>
  <br>
  <br>
  <%= f.label :password_confirmation %>
  <br>
  <%= f.password_field :password_confirmation %>
  <br>
  <br>
  <%= f.submit %>
<% end %>

This will show an HTML form when we access the link /sign-up in our browser that will make a POST request to /save-user when submitted.

Now, we are going to edit the method that will handle the data from our form.

In the same controller file add the following method:

private

def user_params
  params.require(:user).permit :username, :password, :password_confirmation
end

This is to let to know that our app only permits username, password, and password_confirmation which is coming from our form to prevent malicious parameters to be processed.

In the create method add the following code:

@user = User.new user_params

if @user.save
  session[:user_id] = @user.id

  redirect_to '/dashboard'
else
  redirect_to '/sign-up'
end

Basically, we are just creating a new instance of User and its parameters.

If it was saved successfully then we will set the newly registered user to be the current user and redirect it the dashboard but returns back to sign up page if the registration fails.

Step 6: Creating the sign in page.

In the app/views/sessions/new.html.erb paste the following code (create the file if it does not exists):

<h1>Sign In</h1>

<%= form_with :url => '/' do |f| %>
  <%= f.label :username %>
  <br>
  <%= f.text_field :username %>
  <br>
  <br>
  <%= f.label :password %>
  <br>
  <%= f.password_field :password %>
  <br>
  <br>
  <%= f.submit %>
<% end %>

Now that we created the form, we now have to authenticate the credentials if it matches a record in the database.

In your app/controllers/sessions_controller.rb, inside the create method, add the following:

@user = User.find_by :username => params[:username]

if @user && @user.authenticate(params[:password])
  session[:user_id] = @user.id

  redirect_to '/dashboard'
else
  redirect_to '/'
end

What the code does is that it finds the user with the supplied username and authenticates its password.

It sets the session and redirects to the dashboard if it succeeds then redirects back it to the sign in page if it doesn’t.

Step 7: Creating the sign out function.

In your app/controllers/sessions_controller.rb, inside the destroy method, add the following:

session[:user_id] = nil

redirect_to '/'

We are just going to set the session[:user_id] that we defined in the sign up and sign in back to its original value which is nil and redirects back the user to the sign in page.

Step 8: Creating the helper methods.

In your app/controllers/application_controller.rb, add the following:

helper_method :current_user



def current_user
  @current_user ||= User.find_by :id => session[:user_id]
end

def authorize
  redirect_to '/' unless current_user
end

A helper method is a method that we can access in our views. The helper method current user holds the data of the currently authenticated user.

The authorize method is a method that we can call to our routes that needs a user to be authenticated before allowing access to a resource.

In this case, we defined /dashboard to be the only route that needs authentication before being accessed.

In your app/controllers/dashboard_controller.rb, add the following:

class DashboardController < ApplicationController

  def index
    authorize

    # YOUR OTHER CODE HERE
  end

end

This simply calls authorize method and redirects back to the user to the sign in page if no user is authenticated.

If you want the whole controller to be authenticated instead:

class DashboardController < ApplicationController

  before_action :authorize



  def index
  end

  def method1
  end

  def method2
  end

end

This will authenticate not just the index method but also method1 and method2.

Step 9: Adding the links in the view.

We created all this routes, pages, and stuff but we didn’t have links to access this pages at all without manually typing the links in the browser.

In your app/views/layouts/application.html.erb, add the following inside the <body> above the <%= yield %>:

<% if current_user %>
  Hi <%= current_user.username %>! | <%= link_to 'Sign Out', '/sign-out', :method => :delete %>
<% else %>
  <%= link_to 'Sign In', '/' %> | <%= link_to 'Sign Up', '/sign-up' %>
<% end %>
<br>
<br>

What it does is printing the name of user and a sign out link if it’s authenticated and sign in and sign up links if it’s unauthenticated.

Step 10: Installing bcrypt gem.

Our app may look complete at this point, but if you run it at this state, you will just encounter a lot of errors because we are lacking the core function of this tutorial.

In your Gemfile uncomment the following:

gem 'bcrypt', '~> 3.1.7'

But if it does not exists, add it instead.

In your terminal run:

bundle install

In your app/models/user.rbadd the following:

class User < ApplicationRecord

  has_secure_password

end

Step 11: Run the app.

In your terminal run:

rails s

You should be able to see the sign in page as your startup page if you access http://localhost:3000/ in your browser.

That’s it. This is just a basic authentication in Rails for learning purposes.

How to convert HTML to PDF in PHP

How to convert HTML to PDF in PHP

In this tutorial, we will be learning on how to convert HTML to PDF in PHP.

There are many PHP libraries around the internet nowadays but most of them just don’t work like you expected it to be. I have been required by my client to automatically generate documents in pdf format. I have tried many PHP pdf libraries and this library really makes it possible.

Step 1: Create the HTML file.

This static HTML file will be converted to pdf. You may use all HTML tags that you know combined with CSS.

This is somehow WYSIWYG like process, where the look of the output in your HTML file will also be the look when it was converted to pdf.

In this example, I will just create a simple invoice document. We will name the file as file.html and the content as follows.

<html>
<head>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      font-family: Times New Roman;
    }
  </style>
</head>
<body>
  <table cellpadding="10" cellspacing="0" width="100%">
    <tr style="background-color: #333; color: #fff;">
      <td align="center">
        <img src="https://placehold.it/450x150&text=YOUR%20LOGO"><br><br><b>1234 Central Avenue, Ermita, Manila</b>
      </td>
    </tr>
    <tr>
      <td>
        <h1>INVOICE #4321</h1>
        April 14, 2050<br><br>
        <b>Bill To:</b><br>
        John Smith<br>
        Beehive Co.<br>
        Beehive Building, 1226 South Avenue<br>
        Manila, 1000<br>
        +63 (917) 543-2109<br>
        [email protected]
      </td>
    </tr>
  </table>
  <br><br>
  <table cellpadding="10" cellspacing="0" width="100%">
    <tr>
      <th width="80%" style="background-color: #333; color: #fff; border: 1px solid #000;">DESCRIPTION</th>
      <th width="20%" style="background-color: #333; color: #fff; border: 1px solid #000;">AMOUNT</th>
    </tr>
    <tr>
      <td width="80%" style="border: 1px solid #000;">Sample Item 1</td>
      <td width="20%" style="border: 1px solid #000;" align="right">100,000.00 PHP</td>
    </tr>
    <tr>
      <td width="80%" style="border: 1px solid #000;">Sample Item 2</td>
      <td width="20%" style="border: 1px solid #000;" align="right">100,000.00 PHP</td>
    </tr>
    <tr>
      <td width="80%" style="border: 1px solid #000;">Sample Item 3</td>
      <td width="20%" style="border: 1px solid #000;" align="right">100,000.00 PHP</td>
    </tr>
    <tr>
      <td width="80%" style="border: 1px solid #000;">Sample Item 4</td>
      <td width="20%" style="border: 1px solid #000;" align="right">100,000.00 PHP</td>
    </tr>
    <tr>
      <td width="80%" style="border: 1px solid #000;">Sample Item 5</td>
      <td width="20%" style="border: 1px solid #000;" align="right">100,000.00 PHP</td>
    </tr>
    <tr>
      <td width="80%" style="border: 1px solid #000;">Sample Item 6</td>
      <td width="20%" style="border: 1px solid #000;" align="right">100,000.00 PHP</td>
    </tr>
    <tr>
      <td style="border: 1px solid #000;" align="right"><b>TOTAL</b></td>
      <td style="background-color: #333; color: #fff; border: 1px solid #000;" align="right">600,000.00 PHP</td>
    </tr>
  </table>
  <br><br>
  If you have any concerns about this invoice, please contact<br>[Accounting Department, +63 (917) 543-2109, [email protected]]
</body>
</html>

This will be the output of file.html when it is opened in a browser.

The output of file.html in the browser.
Picture 1.1. The output of file.html in the browser.

Step 2: Download the TCPDF library.

You may download the TCPDF library on the following links: link1 / link2

This library will allow us to convert the HTML file that we have created into a PDF file format.

Step 3: Create a PHP script file.

This PHP file will execute the library together with the HTML file to have a PDF output.

In this example, we will name it as index.php

<?php
require_once('tcpdf/tcpdf.php');
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$pdf->SetPrintHeader(false);
$pdf->SetPrintFooter(false);
$pdf->AddPage();
$html = file_get_contents('file.html');
$pdf->writeHTML($html, true, false, true, false, '');
$pdf->lastPage();
$pdf->Output('file.pdf', 'D');

The code above will require the library in our file so that we can initialize the TCPDF class in the library.

The variable $html will hold the HTML file that we have created earlier and the library will do the rest to convert it to PDF.

The last line has two (2) parameters, the first parameter will be the filename of our PDF file while the second parameter is the action that we are going to do with the file.

There are many types of action that we can choose from for the second paramter. There are I, D, F, S, FI, FD, and E.

The I action is it will send the file inline with the browser, allowing it to be viewed in the browser without actually saving the file.

The D action means the file will be forced to be downloaded on the local machine given the filename in the first parameter.

The F action means the file will be saved on the local server that can be retrieved and downloaded later.

The S action will return the document as a string where the filename is ignored.

The FI action is the combination of F and I action. It will be both viewed in the browser and saved on the local server.

The FD action is the combination of F and D action. It will be both downloaded and saved on the local server.

The E action will return the document as a base64 mime multi-part email attachment (RFC 2045). This is used when the file is going to be attached to an email.

In this example, we will choose to download the pdf file that we are going to create.

The directory will have 1 folder and 2 files.

This is how the directory must look like.
Picture 3.1. This is how the directory must look like.

If you don’t have encountered an error when executing index.php then you must have the output as follows.

The output of file.pdf opened in Google Chrome browser.
Picture 3.2. The output of file.pdf opened in Google Chrome browser.

That’s it, we have now converted an HTML file into a PDF file format document in PHP.

How to resize an image in PHP

How to resize an image in PHP

In this tutorial, we will learn to resize an image in PHP.

Whenever you are doing something like a CMS for your website that requires an image to be uploaded then you can have a problem where users upload images in different dimensions.

This can be a problem in the front-end as your design will not be the same for all.

This is why we need to learn to resize an image in PHP. As long as the file is on the server then you can use this method to resize your images.

This can also be used to resize an image on upload meaning after uploading the original image it will automatically be resized after uploading.

Step 1: Get a copy of this PHP function.

<?php
function resizeImage($filename, $newwidth, $newheight) {
  list($width, $height) = getimagesize($filename);

  if (pathinfo($filename, PATHINFO_EXTENSION) == 'jpg' || pathinfo($filename,PATHINFO_EXTENSION) == 'jpeg') {
    $src = imagecreatefromjpeg($filename);
    $dst = imagecreatetruecolor($newwidth, $newheight);
    imagecopyresampled($dst, $src, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
    imagejpeg($dst, $filename, 100);
  }

  if (pathinfo($filename,PATHINFO_EXTENSION) == 'png') {
    $src = imagecreatefrompng($filename);
    $dst = imagecreatetruecolor($newwidth, $newheight);
    imagecopyresampled($dst, $src, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
    imagepng($dst, $filename, 0);
  }

  if (pathinfo($filename,PATHINFO_EXTENSION) == 'gif') {
    $src = imagecreatefromgif($filename);
    $dst = imagecreatetruecolor($newwidth, $newheight);
    imagecopyresampled($dst, $src, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
    imagegif($dst, $filename, 100);
  }
}

The PHP function accepts three (3) parameters. The first parameter is a string which has the filename of the image that is going to be resized. The second parameter is an integer, this will be the new width of your image. The third and last parameter is also an integer, this will be the new height of your image.

Step 2: Use the PHP function.

Our sample image laptop.jpg having a dimension of 1920x1280.
Picture 2.1. Our sample image laptop.jpg having a dimension of 1920×1280.

In this example, we are going to have an image laptop.jpg which has a dimension of 1920×1280 and we will want to downscale this image to the quarter of its original size (480×320).

Now, the second file that we are going to have is resize.php that contains the function above and this file will also make the resizing possible.

Since the filename of our sample image is laptop.jpg and it is also in the same directory then we can use the function as follows.

resize_image('laptop.jpg', 480, 320);

After executing the file resize.php we can see now that our image has lesser file size and has a lower dimension.

Our sample image laptop.jpg having a new dimension of 480x320.
Picture 2.2. Our sample image laptop.jpg having a new dimension of 480×320.

That’s it, we have now successfully resized an image in PHP.

You may notice that whenever you use PHP file upload, the file will be uploaded to the server. After a successful upload, just call the function after the file has been uploaded for it to be resized.

Converting timestamp to time ago in PHP like Facebook

Converting timestamp to time ago in PHP like Facebook

In this tutorial, we will be learning on how to convert timestamp to time ago in PHP.

Most of you have noticed that the famous social networking site Facebook uses the time ago instead of posting the original timestamp.

Time ago is calculated based on the difference between the current time and date to the actual post time and date.

We will use a PHP function that has a parameter that accepts a timestamp it will return it in time ago format based on your server time.

Step 1: Get a copy of the following PHP function.

<?php
function timeago($datetime, $full = false) {
  date_default_timezone_set('Asia/Manila');
  $now = new DateTime;
  $ago = new DateTime($datetime);
  $diff = $now->diff($ago);
  $diff->w = floor($diff->d / 7);
  $diff->d -= $diff->w * 7;
  $string = array(
    'y' => 'yr',
    'm' => 'mon',
    'w' => 'week',
    'd' => 'day',
    'h' => 'hr',
    'i' => 'min',
    's' => 'sec',
  );

  foreach ($string as $k => &$v) {
    if ($diff->$k) {
      $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
    } 
    else {
      unset($string[$k]);
    }
  }

  if (!$full) {
    $string = array_slice($string, 0, 1);
  }
  
  return $string ? implode(', ', $string) . '' : 'just now';
}

The original source of the PHP function can be found on this thread. It is answered by Glavić on September 3, 2013.

The PHP function accepts any supported date and time format. It does not only return the time ago but also gave you the privilege to return the full exact time ago. You will understand this in the later part.

Step 2: Use the PHP function.

You will be able to use the function by calling the function name. This function can either accept one (1) or two (2) parameters where the first parameter must be a valid timestamp/datetime and the second one is a boolean(true/false).

Using the true on the boolean parameter makes it return the whole time ago.

Assuming that the current time and date is exactly 2018-04-13 11:46:58

echo timeago("2018-02-12 02:07:42"); //2 mons
echo timeago("2018-03-12 02:07:42",true); //2 mons, 1 day, 17 hrs, 39 mins, 16 secs

That’s it if you want to change the suffixes of the numbers you can do so by editing them in the $string variable in the function above.

Don’t forget to add the word ago if you really need it.

How to display number of post views WordPress

How to display number of post views WordPress

In this tutorial, we would be learning on how to display number of post views WordPress using a WordPress plugin.

Tracking the views of your post and see them growing can be a satisfaction for the writer. This is why we are going to use a WordPress plugin to show the unique view count in the front-end.

It will also include an option to sort your posts based on the view count.

Step 1: Install the Posts Unique View Counter.

Login to your WordPress admin.

Hover to Plugins in the sidebar menu then click Add New.

On the search box that can be found on the upper right corner of the screen type the keyword Posts Unique View Counter or David Angulo.

Installing the Posts Unique View Counter Plugin by David Angulo
Picture 1.1. Installing the Posts Unique View Counter plugin by David Angulo

Step 2: Activate the Posts Unique View Counter plugin.

On the sidebar menu, click Plugins to see the list of installed plugins. Simply find the Posts Unique View Counter plugin then click Activate.

Activating the Posts Unique View Counter plugin by David Angulo
Picture 2.1. Activating the Posts Unique View Counter plugin by David Angulo

Step 3: View your WordPress post list.

All view count will start at nothing. You can view the posts list by hovering to Posts in the sidebar menu then click All Posts.

You can see an additional column entitled Views that is sortable.

The count will start once a user views your post.

A sortable view count column in WordPress posts list.
Picture 3.1. A sortable view count column in WordPress posts list.

Step 4: View one of your post.

You can also view one of your posts and see just before the content the current view count of your post.

The plugin shows the view count in the front-end of the post.
Picture 4.1. The plugin shows the view count in the front-end of the post.

That’s it. This plugin only counts the unique view meaning a person that viewed the same post twice will only be counted as one.