Sujip Thapa's Blog - Purely about Laravel, PHP, Web Development. https://sujipthapa.co A modern web development blog in PHP, Laravel and other interesting tutorials explaining the real features, tips & other cool stuff. en-us 2018-04-22T08:24:21+05:45 <![CDATA[Australia Post API — Location & Postcode Predictive Search]]> https://sujipthapa.co/blog/australia-post-api-location-postcode-predictive-search 7162cd34-5022-4348-8910-990334982898 2018-04-11T21:45:00+05:45 Mr. Sujip Thapa In this article, I'm writing about auto-suggesting postcode, the suburb of Australia with Australia Post API while filling up the address form.

I was recently working with an Australian client, it was an e-commerce platform to sell their products online.

I've also integrated shipping services like Temando, Transdirect, Auspost to calculate the postage cost in real-time. To protect their customer from not making mistake while filling up address form, they were interested in integrating real-time postcode, suburb auto-suggest. The idea was just for making the real-time postage cost calculation more consistent with the valid location.

1. Grab API Key

Before starting to build the feature and to make an API call, firstly we need to grab an API key from the Auspost developers center.Auspost API Key

2. Workflow

  • A customer starts typing at suburb, or postcode field in an HTML input box.
  • A twitter typeahead library detects the text change and fires an AJAX request, the server-side script again makes an API call behind the scene to get data from Auspost API.
  • The API call receives JSON data from Auspost API server, and it will be rendered as an auto-suggest popup.

3. Coding

I'm going to integrate this feature on top of Laravel framework v5.6, you are free to utilize it with any type of PHP project.

Adding new routes to display form, and for AJAX API call to the backend.

routes/web.php

Route::get('/postcode/search', [
    'name' => 'Australia Post Postcode Search',
    'as' => 'app.postcode.search',
    'uses' => 'AustraliaPostController@index',
]);

Route::get('/postcode/api/search', [
    'name' => 'Australia Post Postcode Search',
    'as' => 'app.postcode.api.search',
    'uses' => 'AustraliaPostController@search',
]);

app/Http/Controllers/AustraliaPostController.php

<?php

namespace App\Http\Controllers;

use App\Auspost\Request as Auspost;
use Illuminate\Http\Request;

/**
 * Class AustraliaPostController
 * @package App\Http\Controllers
 */
class AustraliaPostController extends Controller
{
    /**
     * @param Request $request
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    /**
     * Australia Post API search form.
     *
     * @return mixed
     */
    public function index()
    {
        return view('auspost.search');
    }

    /**
     * @return array
     */
    public function search()
    {
        $parameters = [
            'q' => $this->request->get('query'),
        ];

        $response = with(new Auspost(config('auspost.auth_key')))
            ->send($parameters);

        $localities = collect($response['localities']['locality'] ?? []);

        if ($localities->count()) {
            $collection = is_array($localities->first()) ? $localities : [$localities];

            return response()->json($collection);
        }

        return [];
    }
}

config/auspost.php

<?php

// replace xx with your real Auth Key sent by Auspost developers centre.

return [
    'auth_key' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
];

app/Auspost/Request.php

<?php

namespace App\Auspost;

use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;

/**
 * Class Request
 * @package App\Auspost
 */
class Request
{
    const END_POINT = 'https://digitalapi.auspost.com.au/postcode/search.json';

    /**
     * The API token.
     *
     * @var string
     */
    protected $token;

    /**
     * The guzzle http client.
     *
     * @var \GuzzleHttp\ClientInterface
     */
    protected $client;

    /**
     * Create a new request instance.
     *
     * @param string $token
     */
    public function __construct($token)
    {
        $this->token = $token;
        $this->client = new Client();
    }

    public function getEndpoint()
    {
        return self::END_POINT;
    }

    /**
     * Retrive data via API call from Auspost server.
     *
     * @param array $parameters
     * @param $method
     *
     * @return App\Auspost\Response
     */
    public function send(array $parameters = [], $method = 'get')
    {
        $url = $this->getEndpoint() . '?' . http_build_query($parameters);

        $parameters = [
            'headers' => [
                'Content-Type' => 'application/json',
                'Auth-Key' => $this->token,
            ],
        ];

        try {
            $response = $this->client->request($method, $url, $parameters);

        } catch (ClientException $exception) {
            return $exception;
        }

        return with(new Response($response))->toArray();
    }
}

app/Auspost/Response.php

<?php

namespace App\Auspost;

use GuzzleHttp\Psr7\Response as GuzzleResponse;

/**
 * Class Response.
 * @package App\Auspost
 */
class Response extends GuzzleResponse
{
    /**
     * The guzzle http client response.
     *
     * @var \GuzzleHttp\Message\Response
     */
    protected $response;

    /**
     * Create a new response instance.
     *
     * @param GuzzleResponse $response
     */
    public function __construct(GuzzleResponse $response)
    {
        $this->response = $response;
    }

    /**
     * @return mixed
     */
    public function toJson()
    {
        return (string) $this->response->getBody();
    }

    /**
     * @return mixed
     */
    public function toArray()
    {
        return json_decode($this->toJson(), true);
    }
}

resources/views/search.blade.php

<div class="card-body">
    <form method="POST" action="/register">
        @csrf

        <div class="form-group row">
            <label for="search" class="col-md-4 col-form-label text-md-right">Postcode</label>
            <div class="col-md-8">
                <input id="search" type="text" data-suggest-postcode="{{ route('app.postcode.api.search') }}" class="form-control" name="email">
            </div>
        </div>
    </form>
</div>

I have only shown a small block of code above for the form, to view the full source code for the above tutorial feel free to visit the repository on github.

4. Preview

Auspost API Search PreviewNote: To run the above example, it requires twitter typeahead javascript library v0.11.1, Laravel 5.6, guzzle 6.*, but you could still use it with lower version of Laravel and guzzle library.

Conclusion

Thanks for reading the full article up to the end, feel free to leave your feedback if you are happy reading it.

Happy Coding!

 

]]>
<![CDATA[PSR-2 Coding Standard – Automatically Format in Sublime Text]]> https://sujipthapa.co/blog/psr-2-coding-standard-automatically-format-in-sublime-text a51b37a5-4b89-44a7-8ce0-c2f8c3bd1b21 2018-04-07T13:57:00+05:45 Mr. Sujip Thapa It is always really nice when a code editor allows fixing your code to PSR2 coding standard by just hitting a single command.

At my work, we heavily use Laravel framework, which follows the PSR-2 coding standard. So, every day we create new packages, add new classes we first format our code to PSR-2 coding standard before submitting a pull request. We always strive to follow the coding standard and modern best practices to keep our code cleaner and elegant.

 

I personally use sublime as my primary code editor all the time, the main reason is its lightness and popularity among the developers around the world, another beneficial part is the huge availability of open-source packages for it.

Below, I'm going to cover how we format our PHP code to PSR-2 coding standard in sublime text. The team at symfony and the contributors have done really great job building a package that can be used with sublime text to format coding standard.

Step 1.

Installing php-cs-fixer via composer to run globally on your machine.

  composer global require friendsofphp/php-cs-fixer

After the installation process is completed open your terminal and simply run php-cs-fixer command just to confirm it's installed properly.

It is always a tedious job when you go and fix the formatting on each file you have in your project. Below, I have an example for you if you want to try to format your files manually.

 //Eg:
 php-cs-fixer fix /path/project --rules=@PSR2

Please refer to the official doc to discover all the available rules and commands you could use for your formatting.

 

Step 2.

To automate the code formatting process, we're going to set up a build process in a sublime text, so whenever you need to format your code instead of running the long command, you could just press the button on a keyword to fix it.

Open your sublime text editor.
Go to Tools Menu > Build System > New Build System

{
    "shell_cmd": "php-cs-fixer fix $file --rules=@PSR2"
}

Now, try to save, a prompt window will open, create a new folder called Builds at that position, save it inside that folder with name PSR-2.sublime-build

The build process setup is now ready to use, before using it we need to make a selection under Tools Menu > Build System > PSR 2 which is a new command we just added with the process above.

Usage

  • If you're Windows, Linux user just hit Ctrl + B
  • If you're Mac user ⌘ + B

Learn more about PSR-2 Coding Standard.

Conclusion

Thanks for reading the article up to the end, if you are happy reading it and was helpful to you, feel free to share and leave your feedback below in the comment section.

Happy Coding!

]]>
<![CDATA[Instagram Feed – Building Gallery with Real-time Data via Instagram API]]> https://sujipthapa.co/blog/instagram-feed-building-gallery-with-real-time-data-via-instagram-api 86275746-c168-4967-990e-acfab63825e2 2018-03-24T18:14:12+05:45 Mr. Sujip Thapa I recently wrote an article about building an RSS feed section to publish Facebook Instant Articles automatically. In this article, I will show how to build a gallery on your website with the picture you have uploaded to your Instagram account. In this tutorial I'll show you an easy way to pull down the data via API, using Vinkla’s Instagram package. I would like to thank him for creating a nice package.

 

API Credentials

Follow up the developer guide via their documentation, generate Client ID, Client Secret and you will need to go to another site to generate an access token.

Step 1

Let’s add new config array under existing file config/services.php

    'instagram' => [
        'access-token' => 'xxxxx.xxx.xxxx',
        //replace xxx with your actual access token
    ],

Step 2

Adding a new route to handle the URL /instagram/feed under routes/web.php

    Route::get('/instagram/feed', [
        'name' => 'Instagram Feed',
        'as' => 'app.instagram.feed',
        'uses' => 'InstagramController@feed',
    ]);

Step 3

Installing package via composer, use command from below or follow the installation guide from the package repository on github.

composer require vinkla/instagram php-http/message php-http/guzzle6-adapter
 

Step 4

Creating a new controller to handle the request and execute the API call.

    <?php

    namespace App\Http\Controllers;

    use Vinkla\Instagram\Instagram;

    /**
     * Class InstagramController
     * @package App\Http\Controllers
     */
    class InstagramController extends Controller
    {
        /**
         * @return mixed
         */
        public function feed()
        {
            $instagram = new Instagram(config('services.instagram.access-token'));

            $images = collect($instagram->get())->map(function ($each) {
                return $each->images->standard_resolution->url;
            });

            return view('gallery', compact('images'));
        }
    }

Now, look at the code above in the controller, I have added the logic to pull down the Instagram feeds via API and utilized the Laravel collection map() method to filter the images with standard resolution only. If you would like to show likes, a caption associated with those images you could customize that array and call them while rendering on the view file.

Step 5

Finally, its time to loop the images on gallery.blade.php file under resources/views.

    <div class="tz-gallery">
        <div class="row">
          @foreach($images as $key => $image)
            <div class="col-sm-12 col-md-4">
                <a class="lightbox" href="{{ $image }}">
                    <img src="{{ $image }}" alt="Instagram Feed" class="img-responsive">
                </a>
            </div>
            @endforeach
        </div>
    </div>
 

I have added a small block of code to just show the process of looping and displaying the images. The full source code I wrote for this tutorial is available on github, feel free to review the full pull request and utilize on your project.

Conclusion

Thanks for reading this article up to the end, if it was helpful to you, feel free share the knowledge with others, also don't forget to leave your feedback below in the comment section.

Happy Coding!

]]>
<![CDATA[How to Create A Trending Articles List using Google Analytics with Laravel]]> https://sujipthapa.co/blog/how-to-create-a-trending-articles-list-using-google-analytics-with-laravel 80c51a2a-845d-4407-8fa3-bf0b8846ba32 2018-03-17T13:45:00+05:45 Mr. Sujip Thapa In this article, I will guide you about building top trending articles using Google Analytics API with Laravel framework.

Recently I built this feature for my own blog, if you would like to view it open up the home page of my blog and see around the right sidebar.

I have used the popular laravel package laravel-analytics built by Spatie.

Prerequisites

Before starting to do the actual coding, first, you need to generate the API credentials required for API connectivity.

 

I am not going to cover the process of generating the API credentials, but I recommend you to follow the full steps explained by Spatie in their package documentation page.

Feature

The trending section I have built has following features, it was based on my own requirement.

If you would like to show the trending posts directly to your view without storing any records to the database or without any implicit logic, that is also provided by the package out of the box.

I will be doing it differently in this article, see below.

  • A console command to fetch the daily top views via Google Analytics API. Also, the command will purge the old records from database pulled before one week, that all depends on you, and you will be able to customize that all.
  • A helper class to fetch and manipulate the weekly page views pulled via API and group them by each blog.
  • The database process will be cached for every single day, so that will reduce queries to load the site faster.

Coding

Installing package via console mode with the composer.

composer require spatie/laravel-analytics

The package will be auto-registered in laravel v5.5 and above. If you are using lower version register the package by following the installation guide.

 

app/Blog.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

/**
 * Class Blog
 * @package App
 */
class Blog extends Model
{
    /**
     * @var string
     */
    protected $table = 'blog';

    /**
     * @var array
     */
    protected $dates = ['published_at', 'deleted_at'];

    /**
     * @var string
     */
    protected $fillable = ['user_id', 'name', 'slug', 'excerpt', 'content', 'status', 'published_at'];

    /**
     * @param $slug
     */
    public static function findByTitle($slug = null)
    {
        return self::where('slug', $slug)->first();
    }
}

I am assuming that you are running blogs already, my blog model looks like above.

Adding new migration for trendings table.

php artisan make:migration create_trendings_table --create=trendings
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTrendingsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('trendings', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('blog_id')->nullable();
            $table->integer('views')->nullable();
            $table->string('url', 1024)->nullable();
            $table->string('page_title', 1024)->nullable();
            $table->tinyInteger('status')->default(1)->nullable();
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('trendings');
    }
}

app/Trending.php

<?php

namespace App;

use App\Blog;
use Illuminate\Database\Eloquent\Model;
use Spatie\Analytics\Period;

/**
 * Class Trending
 * @package App
 */
class Trending extends Model
{
    /**
     * @var string
     */
    protected $table = 'trendings';

    /**
     * @var boolean
     */
    public $timestamps = true;

    /**
     * @var array
     */
    protected $dates = ['deleted_at'];

    /**
     * @var string
     */
    protected $fillable = ['blog_id', 'views', 'url', 'page_title'];

    /**
     * @return mixed
     */
    public function blog()
    {
        return $this->belongsTo(Blog::class);
    }

    /**
     * @param $query
     * @return mixed
     */
    public function scopeMonthly($query)
    {
        $period = Period::months(1);

        return $query->whereBetween('created_at', [
            $period->startDate,
            $period->endDate->endOfDay(),
        ]);
    }

    /**
     * @param $query
     * @return mixed
     */
    public function scopeWeekly($query)
    {
        $period = Period::days(7);

        return $query->whereBetween('created_at', [
            $period->startDate,
            $period->endDate->endOfDay(),
        ]);
    }
}

Adding new console command for API connectivity.

 

php artisan make:command TrendingCommand

<?php

namespace App\Console\Commands;

use App\Blog;
use App\Trending;
use Illuminate\Console\Command;
use Spatie\Analytics\AnalyticsFacade as Analytics;
use Spatie\Analytics\Period;

/**
 * Class TrendingCommand
 * @package App\Console\Commands
 */
class TrendingCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'analytics:trending';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Sync page view from Google Analytics API';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $pages = Analytics::fetchMostVisitedPages(Period::days(1), 300);

        if ($pages->count()) {
            $this->purge();

            $pages->map(function ($each) {
                $each = (object) $each;

                if (starts_with($each->url, '/blog/')) {
                    $slug = str_replace("/blog/", '', $each->url);
                    $blog = Blog::findByTitle($slug);

                    if (!empty($blog)) {
                        Trending::create([
                            'blog_id' => $blog->id,
                            'views' => $each->pageViews,
                            'status' => $blog->status,
                            'page_title' => $blog->name,
                            'url' => $each->url,
                        ]);

                        $this->info("{$blog->name} - {$each->pageViews} \n");
                    }
                }
            });
        }
    }

    /**
     * @return mixed
     */
    public function purge()
    {
        $period = Period::days(8);

        $this->info("Purging records before : {$period->startDate} \n");

        return Trending::where('created_at', '<', $period->startDate)
            ->forceDelete();
    }
}

Registering console command to run with the application.

app/Console/Kernel.php

    protected $commands = [
        'App\Console\Commands\TrendingCommand',
    ];

app/Helpers/Trending.php

<?php
namespace App\Helpers;

use App\Trending as Popular;
use Illuminate\Support\Facades\Cache;

/**
 * Class Trending
 * @package App\Helpers
 */
class Trending
{
    /**
     * @return mixed
     */
    public static function weekly($take = 15)
    {
        $collection = collect();

        $trendings = Cache::remember('popular', 60 * 24, function () {
            return Popular::with('blog')->weekly()->get();
        });

        $trendings->groupBy('blog_id')->map(function ($each) use ($collection) {
            $object = collect($each);

            $item = $object->first();
            $item->views = $object->sum('views');

            $collection->push($item);
        });

        return $collection->take($take);
    }
}

The job of the above helper class is to implicitly manipulate the seven days pages views and sort them out by the highest page views.

Now, creating new view partial to include it where ever you want to show the trending list.

resources/views/_trending.blade.php

@inject('trending', 'App\Helpers\Trending')

@php
    $trendings = $trending::weekly();
@endphp

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-12">
                <div class="card">
                    <div class="card-header">Trending</div>
                    <div class="card-body">
                        @if($trendings->count())
                            <ul>
                                @foreach($trendings as $key => $each)
                                   <li>
                                        <div class="article">
                                            <a href="{{ asset($each->url) }}" title="{{ $each->views }}">
                                                <span class="text-muted">{{ $each->blog->published_at->format('d M, Y') }}</span><br>
                                                {{ $each->blog->name }}
                                            </a>
                                        </div>
                                    </li>
                                @endforeach
                            </ul>
                        @endif
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Alternative

If you would like to consume the real-time data via API instead of going through the above feature I built for my use case, you could just follow the package documentation and it has enough resources to understand.

 
// pull down real time data via Google Analytics API

Analytics::fetchMostVisitedPages(Period::days(7));

Now after completing the coding work, the most important thing is to set up a cron job that runs mid-night at 12:00 am to pull down the complete page views for that day.

My cron job looks like below.

0 0 * * * /usr/bin/php /home/deployer/project/artisan analytics:trending

Conclusion

Thanks for reading the article up to the end, if you are happy reading it and was helpful to you, feel free to share and leave your feedback below in the comment section. The article was written based on Laravel v5.6, feel free to utilize it with higher or lower versions as per your requirement. The full source code is available on github.

Happy Coding!

]]>
<![CDATA[Publishing Facebook Instant Articles via RSS Feed with Laravel]]> https://sujipthapa.co/blog/publishing-facebook-instant-articles-via-rss-feed-with-laravel b6fb2f6a-59dd-45ec-a530-145d4e1b0706 2018-03-08T15:19:00+05:45 Mr. Sujip Thapa If you want to publish your web articles to Facebook Instant Articles platform and looking for the best way to export your quality articles from your blog to Instant Articles platform without worrying much about configurations, then you're at the right place now.

In this post, I will show you how to automatically export articles whenever you publish a new article on your website. I recently built this feature for my own website, and it was built on top of Laravel framework.

If your website runs other than WordPress and you want to integrate the RSS feed, this post will help you to understand how to do that for your platform. If you would like to know about exporting the Instant Articles from WordPress website, you can read this doc from facebook developers platform.

 

Two different ways to export Instant Articles.

RSS Publishing, API Publishing

The easiest way is setting up RSS feed, which will be automatically ingested periodically by Facebook. So there will be no other configurations required to pull down your fresh articles from your CMS.

Let's begin to build RSS feed, to allow Facebook to read in XML version.

Adding new routes under routes/web.php

Route::get('blog/rss', [
    'as' => 'app.blog.rss',
    'uses' => 'BlogController@rss',
]);

Route::get('blog/{slug}', [
    'as' => 'app.blog.view',
    'uses' => 'BlogController@view',
]);

Adding new Blog model app/Blog.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

/**
 * Class Blog
 * @package App
 */
class Blog extends Model
{
    const STATUS_ENABLED = 1;
    const STATUS_DISABLED = 0;

    /**
     * @var string
     */
    protected $table = 'blog';

    /**
     * @var array
     */
    protected $dates = ['published_at', 'deleted_at'];

    /**
     * @var string
     */
    protected $fillable = ['user_id', 'name', 'slug', 'guid', 'excerpt', 'content', 'status', 'published_at'];

    /**
     * @param Builder $query
     * @return mixed
     */
    public function scopePublished($query)
    {
        return $query->where('published_at', '<=', now());
    }

    /**
     * @param Builder $query
     * @return mixed
     */
    public function scopeEnabled($query)
    {
        return $query->where('status', self::STATUS_ENABLED);
    }
}

Adding new controller BlogController.php

<?php

namespace App\Http\Controllers;

use App\Blog;
use Illuminate\Support\Str;

/**
 * Class BlogController
 * @package App\Http\Controllers
 */
class BlogController extends Controller
{
    public function rss()
    {
        $blogs = Blog::enabled()
            ->published()
            ->latest('published_at')
            ->get();

        $blogs->map(function ($each) {
            if (empty($each->guid)) {
                $each->guid = Str::uuid();
            }
        });

        return view('rss', [
            'blogs' => $blogs,
            'title' => 'Short title about your blog',
        ]);
    }

    /**
     * @param $slug
     */
    public function view($slug)
    {
        return Blog::where('slug', $slug)->first();
    }
}

Str::uuid() was introduced with Laravel v5.6, with lower versions you could use my simple package for creating guid.

 
composer require sudiptpa/guid

To discover the features, please through the github link.

Adding new view resources/rss.blade.php

<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>{{ $title }}</title>
    <link>{{ url('/') }}</link>
    <description>
        A short description about your blog.
    </description>
    <language>en-us</language>
    <lastBuildDate>{{ date('c') }}</lastBuildDate>
    @forelse($blogs as $key => $blog)
      <item>
        <title><![CDATA[{{ $blog->name }}]]></title>
        <link>{{ route('app.blog.view', ['slug' => $blog->slug]) }}</link>
        <guid>{{ $blog->guid }}</guid>
        <pubDate>{{ date('c', strtotime($blog->published_at)) }}</pubDate>
        <author>Author Name</author>
        <description><![CDATA[{{ $blog->excerpt }}]]></description>
        <content:encoded>
          <![CDATA[
            @include('partials._rss')
          ]]>
        </content:encoded>
      </item>
    @empty
      <item>No feeds found</item>
    @endforelse
  </channel>
</rss>

Adding another partial file included with the above view. resources/partials/_rss.blade.php

<!doctype html>
<html lang="en" prefix="op: http://media.facebook.com/op#">
    <head>
        <meta charset="utf-8">
        <meta property="op:markup_version" content="v1.0">
        <meta property="fb:article_style" content="default"/>
        <link rel="canonical" href="{{ route('app.blog.view', ['slug' => $blog->slug]) }}">
        <title>{{ $blog->name }}</title>
    </head>
    <body>
    <article>
        <header>
            <h1>{{  $blog->name  }}</h1>
            <h2> {{ $blog->excerpt }}</h2>
            <h3 class="op-kicker">
                PHP <!-- Replace with your category name-->
            </h3>
            <address>
                Sujip Thapa <!-- Replace with your author name-->
            </address>
            <time class="op-published" dateTime="$blog->published_at->format('c') }}">{{ $blog->published_at->format('M d Y, h:i a') }}</time>
            <time class="op-modified" dateTime="{{ $blog->updated_at->format('c') }}">{{ $blog->updated_at->format('M d Y, h:i a') }}</time>
        </header>

        {{ $blog->content }}

        <footer>
        <aside>
            A short footer note for your each Instant Articles.
        </aside>
        <small>© Copyright {{ date('Y') }}</small>
        </footer>
    </article>
    </body>
</html>

Note: After exporting articles to Instant Articles platform, you will need to share the post on your facebook page, after sharing on that page article will start appearing as Instant Article.

Finally, to implement the above feature, follow the below steps.

  1. Facebook Page
  2. Publishing Tools
  3. Instant Articles
  4. Configuration
  5. Production RSS Feed

in the input box paste the link ending with blog/rss: (example: https://sujipthapa.co/blog/rss)

 

After saving the RSS feed URL, Facebook will start to retrieve fresh articles periodically.

Conclusion

Thanks for reading the article up to the end, feel free to give your feedback below in the comment section if you like.

Happy Coding!

]]>
<![CDATA[Define Your Google Preferred Domain with Laravel Middleware]]> https://sujipthapa.co/blog/define-your-google-preferred-domain-with-laravel-middleware 4ce2c044-7ed9-4aa1-a3ec-0057612eb5bc 2018-03-04T09:15:00+05:45 Mr. Sujip Thapa This is my second blog post about setting up Google preferred domain from code context for your Laravel application. In my previous post, I have explained a really simple way of doing it with almost no configuration available to set up the preferred version of your choice. In this post, I am going to cover the advanced way of doing it with some configuration available with certain options.

Why is defining preferred domain necessary?

Defining a Google preferred domain is a way of instructing Google to use a final version of your domain to be used forever. Your web pages link should be permanent and never be changing frequently so that Google can start indexing, which is a process of adding pages of your website into Google search. The indexed pages will appear in Google searches around the world with matching keywords.

 

The important part is if your web pages URL are not same when a google crawler runs on your site or the pages start to redirect to 404, Google will start removing them from indexing and they will not appear in search results.

The example of what I wanted to describe above is, your domain should always be like, either www or non-www (i.e. http://www.sujipthapa.co or http://sujipthapa.co).

The primary concern of setting up a google preferred domain is to improve the SEO of your website, that instructs Google to always recognize the final version of your URL to appear in search results.

I would like to recommend you to read this Google support article to understand in detail about the benefits of setting google preferred domain for your website.

Note: Below with the code, it will only focus on forcing your URL to be the preferred version you have selected on Google Search Console. The code will depend on the configuration and will instruct to redirect on the preferred version.

Setup

The first step of the setup is verifying your preferred domain on google end. Read the Google support article carefully and define your preferred version of your domain.

 

Let's start by adding new config file under config/domain.php

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Preferred Domain / Canonical Domain
    |--------------------------------------------------------------------------
    | Example: https://www.example.com (www), https://example.com (non-www)
    |
    | Available Mode: "//www.", "//"
     */

    'preferred' => '//',

    /*
    |--------------------------------------------------------------------------
    | HTTP Protocol
    |--------------------------------------------------------------------------
    |
    | Available Mode: "http://", "https://"
     */

    'protocol' => 'https://',
];

The above configuration file has two settings available, one is preferred domain, and another is for SSL mode. I highly recommend you to install SSL certificate for your website. If an SSL certificate is not installed, beginning in July 2018 your site visitors will see a “Not Secure” warning, recently Google Security Blog announced with the release of Chrome 68, it will mark all web pages with HTTP as "not secure" version.

Adding new middleware class PreferredDomain.php under app/Http/Middleware directory.

<?php

namespace App\Http\Middleware;

use App\Domain;
use Closure;

/**
 * Class PreferredDomain
 * @package App\Http\Middleware
 */
class PreferredDomain
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $domain = new Domain($request);

        if ($domain->diff()) {
            return redirect()->to($domain->translated(), 301);
        }

        return $next($request);
    }
}

This middleware should monitor every HTTP request on your website. So, register it like below under app/Http/Kernal.php

    /**
     * @var array
     */
    protected $middleware = [
        ...
        \App\Http\Middleware\PreferredDomain::class,
    ];

Again, adding new class Domain.php under app directory to apply a way of translating your non-preferred domain to preferred version automatically, it will be translated to the domain version you have defined in the configuration file. It will monitor each request to verify that each request URL is consistent with your preferred version on Google Search Console.

<?php

namespace App;

use Illuminate\Http\Request;

/**
 * Class Domain
 * @package App
 */
class Domain
{
    const FORCE_HTTP = 'http://';
    const FORCE_HTTPS = 'https://';

    const FORCE_WWW = '//www.';
    const FORCE_NOWWW = '//';

    const REGEX_FILTER_WWW = '/(\/\/www\.|\/\/)/';
    const REGEX_FILTER_HTTPS = '/^(http:\/\/|https:\/\/)/';

    /**
     * @var \Illuminate\Http\Request
     */
    protected $request;

    /**
     * @var string
     */
    protected $translated;

    /**
     * @param \Illuminate\Http\Request $request
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    /**
     * @return bool
     */
    public function isEqual(): bool
    {
        return $this->request->fullUrl() !== $this->translated();
    }

    /**
     * Determines if the original url differs with translated.
     *
     * @return bool
     */
    public function diff(): bool
    {
        $this->translated = $this->translate();

        return $this->isEqual();
    }

    /**
     * @return string
     */
    public function translated(): string
    {
        if (!$this->translated) {
            $this->translated = $this->translate();
        }

        return $this->translated;
    }

    /**
     * @return string
     */
    public function translate(): string
    {
        $url = $this->request->fullUrl();

        $protocol = $this->getProtocol();

        $filtered = preg_replace(self::REGEX_FILTER_HTTPS, $protocol, $url);

        $preferred = $this->getPreferred();

        return preg_replace(self::REGEX_FILTER_WWW, $preferred, $filtered);
    }

    /**
     * Determines if the request supports the https,
     * otherwise, fallback to default (http) protocol.
     *
     * @return string
     */
    public function getProtocol(): string
    {
        if (!$this->request->secure()) {
            return self::FORCE_HTTP;
        }

        return config('domain.protocol') ?? self::FORCE_HTTP;
    }

    /**
     * Determines the preferred domain from config.
     *
     * @return string
     */
    public function getPreferred(): string
    {
        return config('domain.preferred') ?? self::FORCE_NOWWW;
    }
}

Let's say my preferred version is - https://sujipthapa.co but user tries to go to https://www.sujipthapa.co now the middleware will monitor and redirect the non-preferred to preferred version. The job of the middleware is to translate the non-preferred to preferred version and issue a 301 redirect to correct version.

 

This is an implicit process that executes before the HTTP request enters into the application.

I have used the above middleware Laravel v5.5, but it is still compatible with the v5.6 version released in Feb 2018.

Conclusion

Thanks for reading the post up to the end, feel free to share, comment your feedback below in the comment section.

Happy Coding!

]]>
<![CDATA[Easily Switch PHP versions on Linux with Script]]> https://sujipthapa.co/blog/easily-switch-php-versions-on-linux-with-script 76409e7d-0499-4495-82a9-4e00a7719a4a 2018-02-11T01:18:00+05:45 Mr. Sujip Thapa If you are looking for best way to switch multiple PHP versions on your machine, you're at the right place to get the best solution for your requirement.

Let's say on your working computer you have installed multiple PHP versions (for eg: PHP 7.0 and PHP 7.2). As a default version, the PHP 7.0 is set to Nginx or Apache and CLI mode.

Of course, as a modern web developer who works on the different framework, packages need to have multiple PHP versions installed on their working machine. During their work on different projects, the project may not support all latest versions, so the developer may need to switch to a required version of PHP to run that project.

Below I will show you how to use the command line as well as a bash alias function to easily switch your versions.

You may want to go from PHP 7.2 to 7.0, use the commands below.

If you don't know how to check the php version on Linux Ubuntu type below command on terminal.

    php -v
//php 7.2 to php 7.0 switcher

sudo a2dismod php7.2 ; sudo a2enmod php7.0 ; sudo systemctl restart apache2

sudo ln -sfn /usr/bin/php7.0 /etc/alternatives/php

//if you are using nginx
sudo systemctl restart nginx

Similarly, while PHP version 7.0 is active, you can fall back to 7.2 with the following commands.

//php 7.0 to php 7.2 switcher

sudo a2dismod php7.0 ; sudo a2enmod php7.2 ; sudo systemctl restart apache2

sudo ln -sfn /usr/bin/php7.2 /etc/alternatives/php

//if you are using nginx
sudo systemctl restart nginx

Repeating the above process frequently is really irritating, time-wasting, right?

Finally, I decided to create a bash alias as a function to reduce my time to repeat the php switching and easy to remember.

Setup

Open up your terminal and type.

sudo nano ~/.bashrc

Scroll around the last line, where we want to paste the below function.

phpswap () {
    local IS_PHP7=`php --version|grep "PHP 7.2"`

    if [[ -z $IS_PHP7 ]]; then
        echo "Switching to PHP 7.2"
        sudo a2dismod php7.0;
        sudo a2enmod php7.2;
        sudo systemctl restart apache2;
        sudo ln -sfn /usr/bin/php7.2 /etc/alternatives/php
    else
        echo "Switching to PHP 7.0"
        sudo a2dismod php7.2;
        sudo a2enmod php7.0;
        sudo systemctl restart apache2;
        sudo ln -sfn /usr/bin/php7.0 /etc/alternatives/php
    fi
}

Usage

After completing the above process simply hit phpswap on your terminal.

I use multiple PHP versions, 7.2, 7.0 with Ubuntu 16.04.3 LTS. I simply run phpswapover the terminal, it detects the current default PHP version and switches to exactly reverse version which I need to go with.

Easy right?

We're done!

Conclusion

Thanks for reading this post up to the end, if you think this post is worth reading, feel free to share with others, also if you have feedback please post in the comment section below.

Happy Coding!

Last Updated: 16th Feb, 2018

]]>
<![CDATA[PayPal Instant Payment Notification (IPN) Handling with Laravel]]> https://sujipthapa.co/blog/paypal-instant-payment-notification-ipn-handling-with-laravel 1ae515cb-abb2-4734-b838-a96f6e6bb295 2018-02-10T09:43:00+05:45 Mr. Sujip Thapa This is a follow-up article from my previous post A guide to Integrate Omnipay PayPal with Laravel, at the end of that article I have mentioned that I will be writing about handling PayPal Instant Payment Notification (IPN) with Laravel. So finally it is published, so you can go through it.

Below, I am going to show in detail about the way of handling the webhook notification that comes from PayPal into the Laravel application.

If you are a beginner level developer I highly recommend you to read this official doc to understand the flow of PayPal Instant Payment Notification.

Why handling IPN is necessary?

Let's say a customer at your site is trying to pay for certain service or goods, your application takes the user to PayPal to complete the payment, the user makes payment, it is completed but due to some technical error the payment completed page from PayPal couldn't return to your application to handle completed orders. In that case, your application may fail to send emails, update the database for recently paid order status and its payment status.

To handle this situation PayPal gives a webhook service that sends POST request with a payload about the payment status.

Let's get started :)

Before diving into the code first let's understand how to set up a notification URL that PayPal actually uses to send a POST request to your web application. There are actually two different ways to setup IPN notification URL.

  • You can set up a static end that never changes and that is always ready to handle the PayPal webhook that may arrive at any time into your application. Follow the below steps from this official doc.
null
  • Another way is that we can send a dynamic notify URL with a payment request implicitly from the code, that way we don't need to worry about the setting up notify URL under PayPal > My Account. Below I will show how to send a notify URL with the payment request.

In the previous article, you can see how to send the notify URL with the payment request, I have made the notify URL to be dynamic with parameters like order_id, environment, so the URL changes for each order.

    $paypal = new PayPal;

    $response = $paypal->complete([
        'amount' => $paypal->formatAmount($order->amount),
        'transactionId' => $order->id,
        'currency' => 'USD',
        'cancelUrl' => $paypal->getCancelUrl($order),
        'returnUrl' => $paypal->getReturnUrl($order),
        'notifyUrl' => $paypal->getNotifyUrl($order),
    ]);

    //route for generating notify URL

    /**
     * @param $order
     */
    public function getNotifyUrl($order)
    {
        $env = config('paypal.credentials.sandbox') ? "sandbox" : "live";

        return route('webhook.paypal.ipn', [$order->id, $env]);
    }

To understand this article, I recommend you to read the previous post about integrating PayPal with Laravel.

Let's start diving into the code.

Installation

composer require sudiptpa/paypal-ipn

If you are using Laravel v4 stick with 1.0.x-dev in your composer.json file.

{
    "require": {
        "sudiptpa/paypal-ipn": "1.0.x-dev",
    }
}

Setup

Defining a route to handle POST request coming from PayPal server.

Route::post('/webhook/paypal/{order?}/{env?}', [
    'name' => 'PayPal Express IPN',
    'as' => 'webhook.paypal.ipn',
    'uses' => 'PayPalController@webhook',
]);

After creating a route to accept incoming POST request via PayPal, you need to keep in mind that Laravel by default filters each HTTP requests entering the application with VerifyCsrfToken middleware. In order to allow the above route to access the application the URL should be excluded from the csrf check.

I have previously covered about it with another article "Disabling CSRF on Specific Route via Middleware", so you can go throgh it to understand how to do that.

Let's create a migration for storing IPN records in the database.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTablePaypalIpnRecords extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('paypal_ipn_records', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('order_id')->nullable();
            $table->string('verified');
            $table->string('transaction_id');
            $table->string('payment_status');
            $table->string('request_method')->nullable();
            $table->string('request_url')->nullable();
            $table->longText('request_headers')->nullable();
            $table->longText('payload')->nullable();
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('paypal_ipn_records');
    }
}

A new model PayPalIPN.php for storing the IPN logs into database.

<?php

namespace App;

use App\Order;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * Class PayPalIPN
 * @package App
 */
class PayPalIPN extends Model
{
    use SoftDeletes;

    const COMPLETED = "Completed";
    const IPN_FAILURE = "FALIURE";
    const IPN_INVALID = "INVALID";
    const IPN_VERIFIED = "VERIFIED";

    /**
     * @var boolean
     */
    public $timestamps = true;

    /**
     * @var array
     */
    protected $dates = ['deleted_at'];

    /**
     * @var array
     */
    protected $fillable = ['order_id', 'verified', 'transaction_id', 'payment_status', 'request_method', 'request_url', 'request_headers', 'payload'];

    /**
     * @var string
     */
    protected $table = 'paypal_ipn_records';

    /**
     * @return boolena
     */
    public function isCompleted()
    {
        return in_array($this->payment_status, [self::COMPLETED]);
    }

    /**
     * @return boolena
     */
    public function isVerified()
    {
        return in_array($this->verified, [self::IPN_VERIFIED]);
    }

    /**
     * @return mixed
     */
    public function orders()
    {
        return $this->belongsTo(Order::class);
    }
}

Adding few new methods in Order.php model for easy condition check and model scope to perform database operation.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * Class Order
 * @package App
 */
class Order extends Model
{
    use SoftDeletes;

    const COMPLETED = 1;
    const PENDING = 0;

    /**
     * @var string
     */
    protected $table = 'orders';

    /**
     * @var array
     */
    protected $dates = ['deleted_at'];

    /**
     * @var array
     */
    protected $fillable = ['transaction_id', 'amount', 'payment_status'];

    /**
     * @param Builder $query
     * @param string $transaction_id
     * @return mixed
     */
    public function scopeFindByTransactionId($query, $transaction_id)
    {
        return $query->where('transaction_id', $transaction_id);
    }

    /**
     * Payment completed.
     *
     * @return boolean
     */
    public function paid()
    {
        return in_array($this->payment_status, [self::COMPLETED]);
    }

    /**
     * Payment is still pending.
     *
     * @return boolean
     */
    public function unpaid()
    {
        return in_array($this->payment_status, [self::PENDING]);
    }
}

Now creating a webhook() method on PayPalController.php

<?php

namespace App\Http\Controllers;

use App\Order;
use App\PayPal;
use App\Repositories\IPNRepository;
use Illuminate\Http\Request;
use PayPal\IPN\Listener\Http\ArrayListener;

/**
 * Class PayPalController
 * @package App\Http\Controllers
 */
class PayPalController extends Controller
{
    /**
     * @param IPNRepository $repository
     */
    public function __construct(IPNRepository $repository)
    {
        $this->repository = $repository;
    }

    /**
     * @param $order_id
     * @param $env
     * @param Request $request
     */
    public function webhook($order_id, $env, Request $request)
    {
        $listener = new ArrayListener;

        if ($env == 'sandbox') {
            $listener->useSandbox();
        }

        $listener->setData($request->all());

        $listener = $listener->run();

        $listener->onInvalid(function (IPNInvalid $event) use ($order_id) {
            $this->repository->handle($event, PayPalIPN::IPN_INVALID, $order_id);
        });

        $listener->onVerified(function (IPNVerified $event) use ($order_id) {
            $this->repository->handle($event, PayPalIPN::IPN_VERIFIED, $order_id);
        });

        $listener->onVerificationFailure(function (IPNVerificationFailure $event) use ($order_id) {
            $this->repository->handle($event, PayPalIPN::IPN_FAILURE, $order_id);
        });

        $listener->listen();
    }
}

Again, creating another class IPNRepository.php that act as a bridge between model and controller.

<?php
namespace App\Repositories;

use App\Order;
use App\PayPalIPN;
use Illuminate\Http\Request;

/**
 * Class IPNRepository
 * @package App\Repositories
 */
class IPNRepository
{
    /**
     * @param Request $request
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    /**
     * @param $event
     * @param $verified
     * @param $order_id
     */
    public function handle($event, $verified, $order_id)
    {
        $object = $event->getMessage();

        if (is_numeric($order_id)) {
            $order = Order::find($order_id);
        }

        if (empty($order)) {
            $order = Order::findByTransactionId(
                $object->get('txn_id')
            )->first();
        }

        $paypal = PayPalIPN::create([
            'verified' => $verified,
            'transaction_id' => $object->get('txn_id'),
            'order_id' => $order ? $order->id : null,
            'payment_status' => $object->get('payment_status'),
            'request_method' => $this->request->method(),
            'request_url' => $this->request->url(),
            'request_headers' => json_encode($this->request->header()),
            'payload' => json_encode($this->request->all()),
        ]);

        if ($paypal->isVerified() && $paypal->isCompleted()) {
            if ($order && $order->unpaid()) {
                $order->update([
                    'payment_status' => $order::COMPLETED,
                ]);

                // notify customer
                // notify order handling staff
                // update database logic
            }
        }
    }
}

Testing

PayPal provides an Instant Payment Notification (IPN) simulator to test your integration.

If you want to test with real sandbox credentials, use your staging server for your application, as PayPal never comes to your local development environment with IPN request.

I personally used POSTMAN, an API testing client to debug my IPN in my local development environment.

Conclusion

The complete source code is available in the github repository, where I push the real codes I prepare while creating every single tutorial on my blog.

Thanks for reading this post up to the end, if you think this post is worth reading, feel free to share with others, also if you have feedback please post in the comment section below.

Happy Coding!

]]>
<![CDATA[Disabling CSRF on Specific Route via Middleware]]> https://sujipthapa.co/blog/disabling-csrf-on-specific-route-via-middleware a0face84-4ebf-4183-93f0-5463b5eb7eae 2018-02-09T15:15:00+05:45 Mr. Sujip Thapa I was lately working with PayPal API on my Laravel project. In the process of coding and testing for Instant Payment Notification (IPN) part, I got an issue with csrf token. The issue was mainly with the POST request to the application via external service, so it threw TokenMismatchException via the VerifyCsrfToken middleware.

One best thing is Laravel ships with CSRF enabled by default for each HTTP request that enters the application, which is made really easy, it handles automatically.

If your application consumes third-party API service, that service may be a webhook to notify about any event and that sends HTTP request to your application. You need to be aware that Laravel filters the request that enters without csrf token, as it monitors all request entering into the application for security reason.

There is a good solution as well, and that also ships with the framework by default. See below how to disable checking csrf token for specific routes in your application, and that fixed my issue as well.

app/Http/Middleware/VerifyCsrfToken.php

    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        '/webhook/paypal/*',
    ];

 

You could specify multiple URLs on that array if you would like to exclude other routes.

Conclusion

Thanks for reading this post up to the end, if you think this post is worth reading, feel free to share with others, also if you have feedback please post in the comment section below.

Happy Coding!

]]>
<![CDATA[Eloquent: Query Builder Compare Two Date Columns]]> https://sujipthapa.co/blog/eloquent-query-builder-compare-two-date-columns 84ce2416-f337-4e5c-95bb-471842931352 2018-01-22T06:17:17+05:45 Mr. Sujip Thapa I regularly share my knowledge about Laravel, Web Development tips, and tutorials via my blog.

Now, here in this article, I am going to cover about comparing two different timestamp columns to filter out records from the database.

Recently I was working on filtering some API activity log to push them on notification interface, and the database design was with columns consisted of timestamp datatype as synced_at, optimized_at.

I wanted to filter the activity log based on values in two dates columns synced_at, optimized_at, my logic was to fetch database records if the timestamp in synced_at is greater optimized_at. The API process normally does a remote sync and does some database optimization task for making frontend stuff load faster with a database view.

I want to give you two different ways of comparing the columns.

Activity::where('synced_at', '>', DB::raw('optimized_at'))
    ->latest()
    ->get();

or

Activity::whereRaw('unsynced_at > optimized_at')
    ->latest()
    ->get();

Both of the database queries from the above code snippet give same results.

Conclusion

Thanks for reading this post up to the end, if you think this post is worth reading, feel free to share with others, also if you have feedback please post in the comment section below.

Happy Coding!

]]>
<![CDATA[Adding Custom Header with Response in Laravel]]> https://sujipthapa.co/blog/adding-custom-header-with-response-in-laravel 60a07da5-3925-40f1-826a-37a80abdb22f 2018-01-14T17:01:17+05:45 Mr. Sujip Thapa I was working out for the best way to add some custom header to my application for most of the view responses, I wanted the header to be "Cache-Control: no-cache, must-revalidate".

I came up with the best solution with a laravel middleware and decided to share the code snippets with the laravel community people as well. The code snippet will give an idea for you to work on next similar logic and the great solution for setting up the custom header.

Let's get started ;)

Create new middleware with an artisan command, it will be stored in \App\Http\Middleware\NoCache.php

php artisan make:middleware NoCache

<?php

namespace App\Http\Middleware;

use Closure;

/**
 * Class NoCache
 * @package App\Http\Middleware
 */
class NoCache
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        $response->header('Cache-Control', 'no-cache, must-revalidate');

        return $response;
    }
}

Open \App\Http\Kernel.php and register the middleware.

There are different ways you might want to use this middleware, you could either apply it globally on every HTTP request over the application or use only on specific routes.

 

a. Register like below to apply globally on every HTTP request.

    /**
     * @var array
     */
    protected $middleware = [
        ...
        \App\Http\Middleware\NoCache::class,
    ];

b. Another way is, registering with a key and apply it on routes with that key like below.

    /**
     * @var array
     */
    protected $routeMiddleware = [
        ...
        'nocache' => \App\Http\Middleware\NoCache::class,
    ];

Now, applying middleware on routes.

Route::group(['middleware' => 'nocache'], function () {
    Route::get('blog/{slug}', [
        'as' => 'app.blog.view',
        'uses' => 'BlogController@view',
    ]);
});

We are done!

Conclusion

Thanks for reading this post up to the end, if you think this post is worth reading, feel free to share with others, also if you have feedback please post in the comment section below.

Happy Coding!

]]>
<![CDATA[Automatically Posting to Facebook Page via Laravel Notifications]]> https://sujipthapa.co/blog/automatically-posting-to-facebook-page-via-laravel-notifications a25bb0d3-356c-4305-ab86-c583d29a8f8c 2018-01-10T15:40:00+05:45 Mr. Sujip Thapa  A powerful feature that came out with Laravel v5.3 was Laravel Notifications. There are several numbers of notification channels created by the contributors in a github organization called laravel-notification-channels.

The notifications feature is fully available with later versions after it was launched along with the framework version v5.3. After that people started creating different notifications channel that can be used with your laravel application.

Laravel framework ships with support for sending email, SMS(via Nexmo), and Slack. If you wish to store notifications in a database, they may also be stored, so you can display.

I’ve been working on a new update for this website to make auto-posting to the facebook page via API when a new blog post is published on the website. I'm using a package that has already been created by Ahmed Ashraf.

I will give a full overview of available methods and fully functional code snippets of how I implemented it to automatically post them on my Facebook page.

Package Installation

You can easily install this package via composer:

composer require laravel-notification-channels/facebook-poster

If you are below Laravel v5.5, you need to register the service provider class to config/app.php

...
'providers' => [
   ...    
   NotificationChannels\FacebookPoster\FacebookPosterServiceProvider::class,
],
...

Next, you need to go to Facebook to generate some API credentials, to set up this Facebook Poster service.

I recently wrote a detailed blog post to cover the process for generating API credentials, visit this post "Generating Never Expiring Facebook Page Access Token".

After completing the process of generating the API credentials, put them on config/services.php

'facebook_poster' => [
    'app_id' => env('FACEBOOK_APP_ID'),
    'app_secret' => env('FACEBOOK_APP_SECRET'),
    'access_token' => env('FACEBOOK_ACCESS_TOKEN'),
],

Setup Model

In my project setup, I have a Blog model that is responsible for storing and retrieving all blog posts. I'm observing the laravel model events, laravel fires many events in different context. In my case, I am listening to the created event to automate this process.

You can use your own model related to your topic that you already have. Simply use the Notifiable trait to it.

use Illuminate\Notifications\Notifiable;

/**
 * Class Blog
 * @package App
 */
class Blog extends Model
{
    use Notifiable;

Now, create a notification class with a command:

php artisan make:notification ArticlePublished

All you need to do is, adjust via() method, and create a new toFacebookPoster() method, which will be called via FacebookPosterChannel.php class, view the complete class below.

app\Notifications\ArticlePublished.php

<?php

namespace App\Notifications;

use App\Blog;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use NotificationChannels\FacebookPoster\FacebookPosterChannel;
use NotificationChannels\FacebookPoster\FacebookPosterPost;

/**
 * Class ArticlePublished
 * @package App\Notifications
 */
class ArticlePublished extends Notification
{
    use Queueable;

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return [FacebookPosterChannel::class];
    }

    /**
     * @param $blog
     */
    public function toFacebookPoster($blog)
    {
        return with(new FacebookPosterPost($blog->title))
            ->withLink($blog->getLink());
    }
}

Now, most interesting part is making the process automated. I'm creating an event listener to stay ready to fire up a notification when a new blog is stored in the database.

If you want to discover the supported methods, visit the github repository for the package.

You could either fire the notification via the controller after the blog post is created:

/**
 * Create a new blog instance.
 *
 * @param  Request  $request
 * @return Response
 */
public function store(Request $request)
{
    // Validate the request...

    $blog = Blog::create([
        'title' => $request->title,
        'slug' => $request->slug
        ]);

    $blog->notify(new ArticlePublished);
}

Another better way is listening to the model events, the cleaner way I like and the way I follow every single day.

Observing Model

Create a new event subscriber class under, app/Listenerscall it BlogEventSubscriber.php

<?php

namespace App\Listeners;

use App\Notifications\ArticlePublished;

/**
 * Class BlogEventSubscriber
 * @package App\Listeners
 */
class BlogEventSubscriber
{

    /**
     * Handle blog creating events.
     *
     * @param $blog
     */
    public function onCreated($blog)
    {
        $blog->notify(new ArticlePublished());
    }

    /**
     * Register the listeners for the subscriber.
     *
     * @param  Illuminate\Events\Dispatcher  $events
     */
    public function subscribe($events)
    {
        $events->listen(
            'eloquent.created: App\Blog',
            'App\Listeners\BlogEventSubscriber@onCreated'
        );
    }
}

We need to register this class in, app/Providers/EventServiceProvider.php

    /**
     * The subscriber classes to register.
     *
     * @var array
     */
    protected $subscribe = [
        'App\Listeners\BlogEventSubscriber',
    ];

Development Issue

I tried to send the localhost link along with Facebook post but it throws an exception while sending localhost URL, so be aware of that.

    public function toFacebookPoster($blog)
    {
        return with(new FacebookPosterPost($blog->title))
            ->withLink($blog->getLink());
    }

We are done!

Conclusion

Thanks for reading this article up to the end, if you have any feedback regarding the post please feel free to leave your comment below. The code snippets are fully tested, and the full source is available in the github repository.

]]>
<![CDATA[Generating Never Expiring Facebook Page Access Token]]> https://sujipthapa.co/blog/generating-never-expiring-facebook-page-access-token 09805c44-b467-4c39-9d91-4ce855f34d68 2018-01-09T15:17:17+05:45 Mr. Sujip Thapa I’ve been working on the new feature for my own blog to auto-posting the posts from the website to the facebook page via Facebook Graph API. My logic was just to make the process automated when I publish new blog post on my website end.

Below, I will cover the processes that are needed on facebook end to collect the required credentials to make the API connectivity successful.

The main credentials needed are, app_id, app_secrect, access_token for my use case.
We can easily get the app_id, app_secrect after creating the app, but to get the long-lived page access token there are some steps to follow.

 

Facebook gives short-lived, long-lived access tokens. I prefer to generate long-lived access token just to get rid of the expiry date and monitoring effort required for it.

I will cover all the steps we need to follow to get the never expiring page access token.

Steps

1. Sign up for facebook developers channel.

2. Create a Facebook App or use an existing app if you already have one.

null

3. Copy, App ID, App Secret

4. Now, navigate to Facebook Graph API Explorer, to generate short-lived access token.

a. Firstly, Select an app from Application drop-down.

b. Again, in the next drop-down select "Get user access token".

c. Once you click on “Get user access token in the drop down” you will see a pop-up window like below. There you will be able to select the permission(scopes) for the user access token.

d. Here for my use case I have only selected “publish pages” and “manager pages” permissions which are needed to create never expiring page access token. If would like to understand more about the scopes visit permissions doc.

Generate user access token by clicking on "Get Access Token" button, this will create short-lived user access token.

5. Now we will need to create a long-lived user access token.

Now, navigate to Access Token Tool this page, you will see short-lived user access token, app access token for all the apps you have under your account.

Press debug option at right side for the user access token for the current app we are trying to create a long-lived access token. This will again take you to Access Token Debugger where you will see the full information for the short-lived user access token.

null

Short-lived user access token means that will expire after an hour. So to extend the expiry date, we need to go to the bottom, there is an option to generate long-lived(2 months) user access token. Click on “Extend Access Token” and you will get the long-lived access token.

6. Lastly, creating never expiring Access Token.

Again, go to Graph API Explorer and paste the recently created long-lived user access token in the Access Token field.

Now, we need to change the API endpoint to access “/me/accounts” and click on "Submit" button. This process will show the result with all pages information including page access token i.e never expiring token.

7. Finally, we are at the verification step to make sure the page access token is never expiring.

null

Hurray!

Conclusion

Thanks for reading this article up to the end, if you have any feedback regarding the post please feel free to leave your comment below.

Happy Coding!

]]>
<![CDATA[Speed up your Composer Install or Update Process]]> https://sujipthapa.co/blog/speed-up-your-composer-install-or-update-process 083df214-0532-4eee-8038-18855ea79ab8 2018-01-05T21:31:18+05:45 Mr. Sujip Thapa We all know that, whenever we go for running composer update, we, of course, need to wait until the dependencies are loaded and download. It usually takes up to 5-10 minutes to complete the full process of pulling down from the package hosted server. This waiting time depends on the performance of your machine.

In this article, I am going to give an overview of how to speed up your composer update process. I recently used and experimented the process, so I wanted to share with everyone who wants to utilize this speedy way.

 

There are only two methods, I know and I did experiment with.

Disabling the Xdebug mode

Installing a third-party library prestissimo created by @hirak

If you want to check your xdebug mode, when running composer you can follow this StackOverflow link.

Another method is using a global composer plugin prestissimo, which is blazingly faster, this plugin helps to installs dependencies in parallel mode.

Follow the below command to install the composer plugin globally on your machine.

Install

composer global require hirak/prestissimo

Uninstall

composer global remove hirak/prestissimo

After installing the composer plugin globally you are done, next is to go to your terminal and hit composer update and test the speed.

I am personally using this composer plugin to make my composer process faster. I highly recommend you to try it. I haven't run into any problems using it at all. If you found any issues let people know about it, so feel free to leave your comment below.

 

In my case the composer update used to take around 10 minutes to finish, now after utilizing this package, it turned into 1 min and less, which is blazingly faster for me, a lot of time-saving while waiting for a package to install and move to code works.

Thanks for reading this article, If you like feel free to share with other people in your circle to let them know about the above.

Enjoy !

]]>
<![CDATA[Redirect to Previous URL After Logging in, Laravel v5.5]]> https://sujipthapa.co/blog/redirect-to-previous-url-after-logging-in-laravel-v55 7c402d62-3e69-48af-a7f7-7e61d721a946 2018-01-01T14:52:15+05:45 Mr. Sujip Thapa An end-user visit the website tries to view several products, services by navigating to different pages. If he finally wants to submit an order. Now the website, of course, takes him to login or register page to provide his personal information for shipping or billing purpose. After logging in, he might want the website to take him back again to the previous page where he was viewing the product information.

I was talking about the user experience while browsing the pages, the behavior for the end-user might differ to each other, but the website owner should make a policy for the better user experience. Especially, the e-commerce websites should consider this types of UI for their end-user.

 

Let's say you are running an e-commerce website, where people can view products, create orders. Let's assume you want the website easily navigates back the user to the previous page after logging into the website.

Let's start building the feature for redirecting the user back to the previous page.

Let's add a section in the login file.

resources/views/auth/login.blade.php

<div class="form-group">
    @if (Request::has('previous'))
        <input type="hidden" name="previous" value="{{ Request::get('previous') }}">
    @else
        <input type="hidden" name="previous" value="{{ URL::previous() }}">
    @endif
</div>

View the login.blade.php file on github for the full updated code.

Additionally, you might want to add a login link to your website pages like:

<a href="{{ route('login') . '?previous=' . Request::fullUrl() }}">
    Login
</a>

When a user clicks the login link, the page redirects to the login page, it then sets the previous URL to input that will be sent to login POST request.

    /**
     * @return string
     */
    public function redirectTo()
    {
        if ($this->request->has('previous')) {
            $this->redirectTo = $this->request->get('previous');
        }

        return $this->redirectTo ?? '/home';
    }

The LoginController.php will handle the previous input value to check if there is the previous link to redirect back. A method redirectTo() created in LoginController.php is responsible to determine the next redirect path after the user is successfully authenticated.

 

If you're not familiar with Laravel's auth system and confused that only defining the new redirectTo() method helps in redirecting to our intended URL, then you can have a look at, AuthenticatesUsers.php, RedirectsUsers.php. We cannot touch the core files shipped with Laravel framework, but there is a way we can override those methods to change the behavior as per our requirement.

Conclusion

Thanks for reading this article up to the end. If you have any feedback or the article was really useful to you please leave your comments below. Feel free to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[Integrating Medium Style Image Zoom]]> https://sujipthapa.co/blog/integrating-medium-style-image-zoom 37a2b5f2-588f-4631-9cf6-7988acaf1eec 2017-12-31T16:16:17+05:45 Mr. Sujip Thapa I'm sure you already know about Medium, a widely used and popular publishing platform to share, articles, important stores over the internet.

Medium – Read, write and share stories that matter.

Medium is an online publishing platform developed by Evan Williams, and launched in August 2012. It is owned by A Medium Corporation

Here I'm going to show how I implemented a medium style clean image zoom-in, zoom-out feature for your website. If you would like to see the medium style image zoom-in, out feature on my blog that I implemented recently with the new design published last week, visit some articles to try out the feature. for example: here is a post with few images, have a look at this blog post.

Before diving into the code, I would like to say big thanks to the package creator @francoischalifour, who built this awesome package as open source.

 

The package is available on the npm registry, with no dependencies.

Features

  • Responsive — scale on mobile and desktop.
  • Performant and lightweight — should be able to reach 60 fps.
  • High definition support — load the HD version of your image on zoom.
  • Image selection — apply the zoom to a selection of images.
  • Mouse, keyboard, and gesture friendly — click anywhere, press a key or scroll away to dismiss the zoom.
  • Event handling — trigger events when the zoom enters a new state.
  • Customization — set your own margin, background, and scroll offset.
  • Custom templates — extend the default look to match your UI.
  • Link support — open the link to the image in a new tab when a meta key is held (⌘ or Ctrl)
  • Image opener — when no link, open the image source in a new tab when a meta key is held (⌘ or Ctrl)

Installation

npm install --save medium-zoom

# or

yarn add medium-zoom

Or, If you want to use the CDN version:

<script src="https://unpkg.com/medium-zoom@0/dist/medium-zoom.min.js"></script>

Usage

Import the script:

<script src="node_modules/medium-zoom/dist/medium-zoom.min.js"></script>

Or, using the module syntax or imports:

const mediumZoom = require('medium-zoom')
// or
import mediumZoom from 'medium-zoom'

You don't need to worry about the CSS styling imports.

Integration

mediumZoom(<selector>, <options>)

In my case, I wanted to use medium style zoom, in-out feature for every image I add to the blog post. i.e It is only applied to the post detail page, where the user wants to view the image in zoom, in-out mode. I wanted my readers to enjoy the view of images without having to open it on a new tab to understand it clearly.

 
mediumZoom('.post__container img');

By default, the zoom is applied to all scaled images (with HTML or CSS properties). You can specify the zoomable images with a CSS selector and add options.

Additionally, you can pass an HTML Element, a NodeList, an HTMLCollection or an array of images to the plugin.

// CSS selector
mediumZoom('#cover')

// HTML Element
mediumZoom(document.getElementById('cover'))

// NodeList
mediumZoom(document.querySelectorAll('[data-action="zoom"]'))

// HTMLCollection
mediumZoom(document.images)

// Array
const imagesToZoom = [
  document.querySelector('#cover'),
  ...document.querySelectorAll('[data-action="zoom"]')
]

mediumZoom(imagesToZoom)

API

Options can be passed via a JavaScript object through the mediumZoom call.

Open up the github repo and go through the API options available if you wish to add the customized feature for your web application.

Go through the examples section if you wish to understand the complete possible features provided by the package.

 

Conclusion

Thanks for reading this article up to the end. If you have any feedback or the article was really useful to you please leave your comments below. Feel free to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[Authorizing New Device based on IP Address, with Laravel Middleware]]> https://sujipthapa.co/blog/authorizing-new-device-based-on-ip-address-with-laravel-middleware 6c757df3-5cad-4866-9f51-3eef2813fa3b 2017-12-27T16:17:16+05:45 Mr. Sujip Thapa I recently saw few of the e-commerce & payment gateway sites using device authorization system based on IP address, browser etc. I was also working on a same for my client's web app recently so wanted to share a detailed post with the community people.

In this blog post, I will go in detail to cover following stuff.

  • Allow the user to enter login credentials, if the login credentials are valid, also verify if the user's device is authorized with the current IP address assigned to the user's device.
  • If the user's device is not authorized to access the protected pages, like the dashboard, the application will send an email to the recently logged in user's email to ask for authorizing the device before proceeding.
  • After sending the email, the page will redirect to wait for email authorization, and that will keep refreshing on certain time interval to check if the user is authorized, so it can redirect to the dashboard.
  • If the user is not active and did not authorize the device within next 15 min after email is sent, it will log out the user as a reason for a timeout with a certain message.

 

I will be using Laravel v5.5 to build this feature. Also, for user's device information, IP to location and to generate a unique token for every new request I will be using my own two PHP packages published on packagist as open source, and browser detection library by @cbschuld. So make sure you have a composer, PHP 7.0 >= installed in your environment.

Package Installation

You can install those packages via composer.

composer require sudiptpa/guid

composer require sudiptpa/geoip


Now let's start with creating the foundation of the feature to implement with Laravel project.

app/routes/web.php

Route::group(['middleware' => ['authorize', 'auth']], function () {
    Route::get('/dashboard', [
        'name' => 'Dashboard',
        'as' => 'dashboard',
        'uses' => 'HomeController@dashboard',
    ]);
});

Route::group(['middleware' => ['auth']], function () {
    Route::get('/authorize/{token}', [
        'name' => 'Authorize Login',
        'as' => 'authorize.device',
        'uses' => 'Auth\AuthorizeController@verify',
    ]);

    Route::post('/authorize/resend', [
        'name' => 'Authorize',
        'as' => 'authorize.resend',
        'uses' => 'Auth\AuthorizeController@resend',
    ]);
});


The first grouped route shows, that the user cannot access the dashboard without logging in and, needs authorization. I will show the authorization middleware very soon below.

Similarly, the second grouped routes, show that only authenticated users can verify the device with associated IP address.

app/database/migrations

 

Now let's create a migration table to store the authorizes for all users.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateAuthorizesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('authorizes', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->nullable();
            $table->boolean('authorized')->nullable();
            $table->string('token')->nullable();
            $table->string('ip_address')->nullable();
            $table->string('browser')->nullable();
            $table->string('os')->nullable();
            $table->string('location')->nullable();
            $table->tinyInteger('attempt')->default(0)->nullable();
            $table->timestamp('authorized_at')->nullable();
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('authorizes');
    }
}


app/Authorize.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Request;

/**
 * Class Authorize
 * @package App
 */
class Authorize extends Model
{
    /**
     * @var string
     */
    protected $table = 'authorizes';

    /**
     * @var boolean
     */
    public $timestamps = true;

    /**
     * @var array
     */
    protected $dates = ['authorized_at', 'deleted_at'];

    /**
     * @var string
     */
    protected $fillable = [
        'user_id', 'authorized', 'token', 'ip_address', 'browser', 'os', 'location', 'attempt', 'authorized_at',
    ];

    /**
     * @param $query
     * @return mixed
     */
    public function scopeCurrentUser($query)
    {
        return $query->where('user_id', Auth::id());
    }

    /**
     * @param $date
     */
    public function setAuthorizedAtAttribute($date)
    {
        $this->attributes['authorized_at'] = Carbon::parse($date);
    }

    /**
     * @return mixed
     */
    public static function active()
    {
        return with(new self)
            ->where('ip_address', Request::ip())
            ->where('authorized', true)
            ->where('authorized_at', '<', Carbon::tomorrow())
            ->first();
    }

    /**
     * @return mixed
     */
    public function resetAttempt()
    {
        $this->update(['attempt' => 0]);

        return $this;
    }

    /**
     * @return mixed
     */
    public function noAttempt()
    {
        return $this->attempt < 1;
    }

    /**
     * @param $token
     */
    public static function validateToken($token = null)
    {
        $query = self::where([
            'token' => $token,
        ])->first();

        if (sizeof($query)) {
            $query->update([
                'authorized' => true,
                'authorized_at' => now(),
            ]);

            return self::active();
        }
    }

    /**
     * @return mixed
     */
    public static function make()
    {
        return self::firstOrCreate([
            'ip_address' => Request::ip(),
            'authorized' => false,
            'user_id' => Auth::id(),
        ]);
    }

    /**
     * @return mixed
     */
    public static function inactive()
    {
        $query = self::active();

        return $query ? null : true;
    }
}


In this model I have written few methods that actually work on authorization, you will see its usage below with middleware, controller.

In this blog post, I am only covering only the dashboard, login routes to be protected from the authorize middleware. I only forced the LoginController.php to apply on login routes only. Make sure you apply this middleware to other routes to be protected from unauthorized access to your application.

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/dashboard';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('authorize')->only('login');
        $this->middleware('guest')->except('logout');
    }


app/Http/Middleware/AuthorizeDevice.php

<?php

namespace App\Http\Middleware;

use App\Authorize;
use App\Mail\AuthorizeDevice as AuthorizeMail;
use Closure;
use Illuminate\Support\Facades\Mail;

/**
 * Class AuthorizeDevice
 * @package App\Http\Middleware
 */
class AuthorizeDevice
{
    /**
     * @var \App\Authorize
     */
    private $authorize;

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (Authorize::inactive() && auth()->check()) {
            $this->authorize = Authorize::make();

            if ($this->authorize->noAttempt()) {
                Mail::to($request->user())
                    ->send(new AuthorizeMail($this->authorize));

                $this->authorize->increment('attempt');
            }

            if ($this->timeout()) {
                auth()->guard()->logout();

                $request->session()->invalidate();

                return redirect('/')->with([
                    'status' => 'You are logged out of system, please follow the link we sent before 15 minutes to authorize your device, the link will be valid with same IP for 24hrs.',
                ]);
            }

            return response()->view('auth.authorize');
        }

        return $next($request);
    }

    /**
     * Determines if the authorize attempt is timed out.
     *
     * @return bool
     */
    private function timeout()
    {
        $waiting = $this->authorize
            ->created_at
            ->addMinutes(15);

        if (now() >= $waiting) {
            return true;
        }

        return false;
    }
}


Now register the middleware within app/Http/Kernel.php

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        ...
        'authorize' => \App\Http\Middleware\AuthorizeDevice::class,
    ];


The most interesting part of this blog is the above middleware class that handles the incoming request into an application that enters the actual routes which are protected before the user's device is authorized.

Open up the Authorize.php model from GitHub to understand the full methods about how it is working.

The middleware is before middleware, follow the laravel official doc if you don't know about it.

Actually, it works with the authenticated user's when the device is not authorized, so it protects the user's trying to enter the application without authorization.

The middleware first, checks, if there is an authorization, exists in the database or creates a new with the current IP address for currently logged in user.

It sends the email requesting authorization to access the application, see below for the simple preview I made while writing this blog post.

The middleware is also handling the session timeout for the authorization to be made within next 15 min after the email has been sent, otherwise, it will log out the user automatically.

If you wish to manage the separate middleware for the timeout feature you could use another class, and register it accordingly like we did above.

Alright, now let's move to email sending code.

app/Mail/AuthorizeDevice.php

<?php

namespace App\Mail;

use App\Browser;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Sujip\GeoIp\GeoIp;

/**
 * Class AuthorizeDevice
 * @package App\Mail
 */
class AuthorizeDevice extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * @var mixed
     */
    protected $authorize;

    /**
     * Create a new message instance.
     *
     * @param $authorize
     *  @return void
     */
    public function __construct($authorize)
    {
        $this->authorize = $authorize;
        $this->browser = new Browser;
    }

    /**
     * @return mixed
     */
    public function setBrowser()
    {
        $this->authorize->browser = $this->browser->getBrowser();

        return $this;
    }

    /**
     * @return mixed
     */
    public function setToken()
    {
        $this->authorize->token = guid();

        return $this;
    }

    /**
     * @return mixed
     */
    public function setLocation()
    {
        $location = with(new GeoIp(
            $this->authorize->ip_address
        ))->formatted();

        $this->authorize->location = $location;

        return $this;
    }

    /**
     * @return mixed
     */
    public function setPlatform()
    {
        $this->authorize->os = $this->browser->getPlatform();

        return $this;
    }

    public function saveAuthorize()
    {
        $this->authorize->save();
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        $this
            ->setBrowser()
            ->setToken()
            ->setLocation()
            ->setPlatform()
            ->saveAuthorize();

        return $this
            ->view('emails.auth.authorize')
            ->with(['authorize' => $this->authorize]);
    }
}


The mail sending class prepares data like location, IP, token, browser, platform(os) and email sending view. In order to collect the user's current device information, I am using a third-party class written by @cbschuld as open source code in GitHub. I would like to say thanks for the nice package to him.

Also, to generate the token, I am using a GUID generator package that I wrote recently for my own use case and published as opensource as well. The guid() helper function will return global unique identifier every time a user requires a token to authorize the device.

The coolest feature in the Laravel 5.5 Illuminate\Mail\Mailable is mail preview to test the email view before sending it. I loved it. :)

See example below how I tested myself.

Route::get('/mailable', function () {
    $authorize = App\Authorize::find(1);

    return new App\Mail\AuthorizeDevice($authorize);
});


After sending the email requesting you to authorize the device, the preview I built was like below, you are free to apply CSS as per your use case.

null

The AuthorizationController.php was written to handle, resend authorize email, and validate the token from the email sent to the current user.

<?php

namespace App\Http\Controllers\Auth;

use App\Authorize;
use App\Http\Controllers\Controller;
use App\Mail\AuthorizeDevice;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Redirect;

/**
 * Class AuthorizeController
 * @package App\Http\Controllers\Auth
 */
class AuthorizeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     *  Validate the token for the Authorization.
     *
     * @param $token
     * @return \Illuminate\Http\Response
     */
    public function verify($token = null)
    {
        if (Authorize::validateToken($token)) {
            return Redirect::route('dashboard')->with([
                'status' => 'Awesome ! you are now authorized !',
            ]);
        }

        return Redirect::route('login')->with([
            'error' => "Oh snap ! the authorization token is either expired or invalid. Click on Email didn't arraive ? again",
        ]);
    }

    /**
     * Get the needed authorization credentials from the request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function resend(Request $request)
    {
        if (Authorize::inactive() && auth()->check()) {
            $authorize = Authorize::make()
                ->resetAttempt();

            Mail::to($request->user())
                ->send(new AuthorizeDevice($authorize));

            $authorize->increment('attempt');

            return view('auth.authorize');
        }
    }
}


View the auth/authorize.blade.php from github to know how it was like.

 

View the complete code pushed to github while I was writing this blog post.

Conclusion

Thanks for reading this article up to the end. If you have any feedback or the article was really useful to you please leave your comments below. Feel free to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[A simple IP to Geo Location solution for PHP]]> https://sujipthapa.co/blog/a-simple-ip-to-geo-location-solution-for-php f3b854bd-24fb-4f7f-a30c-917a4bcff5ed 2017-12-25T12:23:10+05:45 Mr. Sujip Thapa I was working on my own use case to find out IP to Geo Location and finally found a free open source service from http://freegeoip.net. It is a community funded project. The service includes GeoLite2 data created by MaxMind

freegeoip.net provides a public HTTP API for software developers to search the geolocation of IP addresses. It uses a database of IP addresses that are associated with cities along with other relevant information like time zone, latitude, and longitude.

You're allowed up to 15,000 queries per hour by default. Once this limit is reached, all of your requests will result in HTTP 403, forbidden, until your quota is cleared.

The freegeoip web server is free and open source so if the public service limit is a problem for you, download it and run your own instance.

API support.

The HTTP API takes GET requests in XML, JSON, CSV format.

http://freegeoip.net/{format}/{IP_or_hostname}

While making an API call, if no IP or hostname is provided, then your own IP is looked up.

 

Package Implementation

To make this API call simple and organized I decided to create a package for my own use case as well as for the community people to use it as open source. It is available to view it on Github.

Installation

You can install the package via composer.

composer require sudiptpa/geoip


Usage

This package only supports json format for now.

Here are a few examples on how you can use the package:

$geo = new Sujip\GeoIp\GeoIp($ip);

$geo->country();

$geo->city();

$geo->region();

$geo->formatted(); // Jawalakhel, Central Region, Nepal

Also, have a look at the source code of Sujip\GeoIp\GeoIp to discover the methods you can use via GitHub.

Thanks for reading up to the end. If you have any feedback please leave your comments below. Feel free to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[A Complete Guide to Integrate Facebook Like and Share]]> https://sujipthapa.co/blog/a-complete-guide-to-integrate-facebook-like-and-share ec3eb66a-4c08-460f-8b0f-c26ca64f4112 2017-12-22T16:10:15+05:45 Mr. Sujip Thapa Nowadays social media platform has grown a lot more and really helpful in marketing, sharing information, communication etc. So here in this post, I am going into detail about how to integrate the Facebook social plugin, with real-time Like, Share button on your products links or blog posts on your web application.

I will be using Laravel framework to demonstrate this integration. So before starting, I will assume you have laravel framework setup, facebook account ready to use in facebook developers mode.

 

At the end of the post, you will get the below output.

Now let's start with creating the basic foundation to show the blog posts and view it.

app/routes/web.php

Route::group(['prefix' => 'blog'], function () {
    Route::get('/', [
        'as' => 'app.blog',
        'uses' => 'BlogController@index',
    ]);
    Route::get('/{slug}', [
        'as' => 'app.blog.view',
        'uses' => 'BlogController@view',
    ]);
});

app/database/migrations

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateBlogsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('blogs', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->string('slug');
            $table->string('excerpt');
            $table->longText('content')->nullable();
            $table->timestamp('published_at')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('blogs');
    }
}

app/Blog.php

<?php

namespace App;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;

/**
 * Class Blog
 * @package App
 */
class Blog extends Model
{
    /**
     * @var string
     */
    protected $table = 'blogs';

    /**
     * @var array
     */
    protected $dates = ['published_at'];

    /**
     * @var array
     */
    protected $fillable = ['title', 'slug', 'excerpt', 'content', 'published_at'];

    /**
     * @param $query
     * @return mixed
     */
    public function scopePublished($query)
    {
        return $query->where('published_at', '<=', Carbon::now());
    }

    /**
     * @param $date
     */
    public function setPublishedAtAttribute($date)
    {
        $this->attributes['published_at'] = Carbon::parse($date);
    }
}

app/Http/Controllers/BlogController.php

<?php

namespace App\Http\Controllers;

use App\Blog;
use Illuminate\Http\Request;

/**
 * Class BlogController
 * @package App\Http\Controllers
 */
class BlogController extends Controller
{
    /**
     * @var int
     */
    protected $paginate = 10;

    /**
     * @param Request $request
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function index()
    {
        $blogs = Blog::published()->paginate(
            $this->paginate
        );

        return view('blog.index', [
            'blogs' => $blogs,
            'title' => 'Blogs',
            'open_graph' => [
                'title' => 'Blogs',
                'image' => asset('assets/logo.jpeg'),
                'url' => $this->request->url(),
                'description' => 'A blog website to share tutorials and tips !',
                'keywords' => 'A Laravel Blog, Tips, Tutorials',
            ],
        ]);
    }

    /**
     * @param $slug
     */
    public function view($slug)
    {
        $blog = Blog::published()
            ->where('slug', $slug)
            ->firstOrFail();

        return view('blog.view', [
            'blog' => $blog,
            'open_graph' => [
                'title' => $blog->title,
                'image' => asset('assets/preview.jpeg'),
                'url' => $this->request->url(),
                'description' => $blog->excerpt,
            ],
        ]);
    }
}

Now, I will assume you have following code in your layout blade in your view folder. See how I have created my layout file here for this tutorial.

<!DOCTYPE html>
<html lang="en">
<head>
    ....
    ....
    ....

    @yield('head')

</head>
<body>

Open up the file form Github repository index.blade.php

Similarly, open up view.blade.php

 

The above integration will show the following output.

Blogs

null

 

Blog Preview

null

Now, let's navigate to facebook developers doc.

Scroll down to - Like Button Configurator

You are free to configure the URL to Like, Width, Layout, Action Type, Button Size, Show Friends's Faces, Include Share Button options as per your wish.

After completing the above configuration click on Get Code button. It will show a pop up window like below. Don't forget to read the information shown in the pop up window carefully. The share feature requires open graph meta tags on heads section of your html page, so have a look at the code to know how I implemented it.

There are two different way of implementing the facebook real-time like, share buttons.

 

Javascript SDK

Step 1: Include the JavaScript SDK on your page once, ideally right after the opening body tag.

<div id="fb-root"></div>
<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = 'https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.11&appId=180539309049634';
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>

Don't forget to replace the appId=180539309049634 with your own app. Select your facebook app before generating the code. If you don't have any app for your site, create one from facebook developers page under My App.

Step 2: Place this code wherever you want the plugin to appear on your page.

<div class="fb-like" data-href="{{ route('app.blog.view', ['slug' => $post->slug]) }}" data-layout="standard" data-action="like" data-size="small" data-show-faces="true" data-share="true"></div>

IFrame

<iframe src="https://www.facebook.com/plugins/like.php?href={{ route('app.blog.view', ['slug' => $post->slug]) }}&width=51&layout=box_count&action=like&size=small&show_faces=true&share=true&height=65&appId" width="51" height="65" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowTransparency="true"></iframe>

Place this code wherever you want the plugin to appear on your page.

null

Now, look at the index.blade.php, view.blade.php on my Github repo about how I implemented the code to show the facebook real-time like, share buttons.

 

View the complete code pushed to github while I was writing this tutorial.

Finally! now we have a complete guide setup for integrating facebook, real-time like, share buttons.

Conclusion

Thanks for reading up to the end. If you have any feedback please leave your comments below. Feel free to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[Create unique GUID with PHP]]> https://sujipthapa.co/blog/create-unique-guid-with-php 3520273c-c5e9-4df9-9827-1212d60c75ee 2017-10-06T06:02:15+05:45 Mr. Sujip Thapa In this blog post, I am going to write about my new PHP package for creating globally unique identifiers (GUID).

Begin with installation via Composer:

composer require sudiptpa/guid

I was inspired mainly to create this package while I was searching for GUID information in the php.net official PHP website. I found a good solution by a community member Dave Pearsonview source code here.

I was looking a solution for Laravel, so I created this package as reusable for everyone as an opensource package.

 

 Usage

In order to consume this package from Laravel application, register the package service provider within your config/app.phpfile.

'providers' => [
    Sujip\Guid\GuidServiceProvider::class,
]

'aliases' => [
   'Guid' => 'Sujip\Guid\Guid'
]

If you Laravel application is running with v5.5, this package has been configured for auto-discovery, Laravel will automatically register its service providers and facades when it is installed, creating a convenient installation experience for you.

If you are a Laravel Developer:

Make use of Laravel Facade:

echo "GUID: " . Guid::create(); //example output : 2b23924f-0eaa-4133-848e-7ce1edeca8c9

 or use a helper function guid()

echo "GUID: " . guid(); // example output: 2b23924f-0eaa-4133-848e-7ce1edeca8c9

 If you want to use this package outside of the framework.

use Sujip\Guid\Guid;


$guid = new Guid;

$guid = $guid->create();

Output

//Example: 2b23924f-0eaa-4133-848e-7ce1edeca8c9

Conclusion

This package is framework agnostic, which means it can be used in any type of PHP projects. 

Thanks for reading up to the end. If you have any feedback regarding this blog post feel free to leave your comment below. Also don't forget to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[Set your Preferred Domain on Laravel Application]]> https://sujipthapa.co/blog/set-your-preferred-domain-on-laravel-application d976c361-7a03-47ae-b2d9-b696a3bbb916 2017-09-09T08:45:00+05:45 Mr. Sujip Thapa In this blog post, I would like to show how to set preferred domain from your application context.

Before diving into the code, let's understand why the preferred domain is much important for SEO, web pages indexing on Search Engine.

Follow the link to read in detail from Google support article.

The preferred domain is the one that you would like used to index your website's pages (it is sometimes referred to as the canonical domain). Links may point to your site using both the www and non-www versions of the URL (for instance, http://www.example.com and http://example.com). The preferred domain is the version that you want to be used for your site in the search results.

 

Here, in this guide, I'm going to implement non-www preferred domain for every incoming HTTP request to the application. I have created a custom Laravel Middleware to set the preferred domain.

People usually do this with server configuration, (eg: with .htaccess for apache), but you can also force it from application context. I have used this mechanism with several Laravel applications I have built.

Let's start with creating a middleware with an artisan command.

php artisan make:middleware PreferredDomain

Open up your app/Http/Middleware/PreferredDomin.php and paste code below.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Redirect;

/**
 * Class PreferredDomain
 * @package App\Http\Middleware
 */
class PreferredDomain
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (starts_with($request->header('host'), 'www.')) {
            $host = str_replace('www.', '', $request->header('host'));
            $request->headers->set('host', $host);

            return Redirect::to($request->fullUrl(), 301);
        }

        return $next($request);
    }
}

We want to run this middle over every HTTP request that comes to our application, simply we need to register the middleware class in the $middleware property in app/Http/Kernel.php class.

    protected $middleware = [
        ...

        \App\Http\Middleware\PreferredDomain::class,
    ];

Note: If you're still using v4.2 version of Laravel don't worry I have solution for you too as well ;)

 

Open up your app/filters.php

App::before(function ($request) {
    if (starts_with($request->header('host'), 'www.')) {
        $host = str_replace('www.', '', $request->header('host'));
        $request->headers->set('host', $host);

        return Redirect::to($request->fullUrl(), 301);
    }
});


Conclusion

This tutorial heavily depends on Laravel framework, I primarily use Laravel to build my projects.

Thanks for reading up to the end. If you have any feedback please leave your comments below. Feel free to share with friends if you like it.

Last Updated: 21st Jan, 2018

Happy Coding!

]]>
<![CDATA[Laravel v5.5: Login, Register with Username or Email]]> https://sujipthapa.co/blog/laravel-v55-login-register-with-username-or-email dbf59aa9-4880-4a63-b33c-271196ec7ad5 2017-09-09T07:03:16+05:45 Mr. Sujip Thapa In this blog post, I would like to share some ideas on how to customize your Laravel v5.5 authentication to support username or email based login. Laravel v5.5 is a second LTS version, that @laravelphp announced on Twitter on 20 Feb 2017. Just simply like the previous LTS version, this will also include 2 years of bug fixes and 3 years of security updates.

Laravel v5.5 release announcement.

If you are new to my blog, view my previous post about Laravel v5.4 authentication with username or email.

 

In my previous blog post, people were requesting to cover register, login. So, here I am going to cover both process with new version v5.5 just released at the end of August 2017.

Let's start the tutorial :)

Before starting to look at the code you should be ready with running v5.5 or fresh installation setup on your machine.

If you need a guide for the installation process see the release note and follow the official guide.

Let's begin with creating authentication scaffolding, migrations for User model.

Generate authentication scaffolding with an artisan command.

php artisan make:auth

As we are going to add username field on the database. 

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('username')->nullable()->unique();
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

Now simply run migration and go ahead.

php artisan migrate

After running this command your database table is now ready with username field support.

 

Now, open up your User model, add the username field to the $fillable array.

    protected $fillable = [
        'name', 'email', 'password', 'username',
    ];

Open up your RegisterController.php, I have added a validation rule for the username in validator() method. Similarly, I have added username field to create() method as well.

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
     */

    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|string|max:255',
            'username' => 'required|string|max:20|unique:users',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6|confirmed',
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'username' => $data['username'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }
}

Also, I have changed a little bit on resources/views/auth/register.blade.php to support username, email. So, simply add username field on it.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Register</div>
                <div class="panel-body">
                    <form class="form-horizontal" method="POST" action="{{ route('register') }}">
                        {{ csrf_field() }}
                        <div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
                            <label for="name" class="col-md-4 control-label">Name</label>
                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control" name="name" value="{{ old('name') }}" required autofocus>
                                @if ($errors->has('name'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('name') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group{{ $errors->has('username') ? ' has-error' : '' }}">
                            <label for="username" class="col-md-4 control-label">Username</label>
                            <div class="col-md-6">
                                <input id="username" type="text" class="form-control" name="username" value="{{ old('username') }}" required>
                                @if ($errors->has('username'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('username') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
                            <label for="email" class="col-md-4 control-label">E-Mail Address</label>
                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required>
                                @if ($errors->has('email'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('email') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
                            <label for="password" class="col-md-4 control-label">Password</label>
                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control" name="password" required>
                                @if ($errors->has('password'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('password') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="password-confirm" class="col-md-4 control-label">Confirm Password</label>
                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-6 col-md-offset-4">
                                <button type="submit" class="btn btn-primary">
                                    Register
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

 Laravel v5.5 Regist, Login Form

You're done with the registration process. Now let's look at the login process.

Open up Illuminate\Foundation\Auth\AuthenticatesUsers it has several methods to handle authentication process. In our customization we only need to override one method credentials() to our App\Http\Controllers\Auth\LoginController.php

 

Also, don't forget to add an import use Illuminate\Http\Request; 

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
     */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    /**
     * Get the needed authorization credentials from the request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    protected function credentials(Request $request)
    {
        $field = filter_var($request->get($this->username()), FILTER_VALIDATE_EMAIL)
            ? $this->username()
            : 'username';

        return [
            $field => $request->get($this->username()),
            'password' => $request->password,
        ];
    }
}

 You will require changing the login form to support username, email field, simply open up resources/views/auth/login.blade.php and just change input type to be text from email, just leave other as it is.

<input id="email" type="text" class="form-control" name="email" value="{{ old('email') }}" required autofocus>

 Alright ! now we have a full functionality to support login, register with username, email with Laravel v5.5.

Conclusion

This tutorial heavily depends on Laravel 5.5, so if you are going to implement this on another version of framework please have a look at the traits used properly to override the credentials array. If you need guide about v5.4 view the previous post here.

Thanks for reading up to the end. If you have any feedback please leave your comments below. Feel free to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[A Guide to Integrate Omnipay PayPal with Laravel]]> https://sujipthapa.co/blog/a-guide-to-integrate-omnipay-paypal-with-laravel d27ac1da-999f-4212-9539-e4bd04d73e71 2017-07-01T05:59:14+05:45 Mr. Sujip Thapa In this blog post, I would like to share some idea about best way to integrate paypal express checkout using OmniPay with PHP. Omnipay is a framework agnostic, payment processing library for PHP 5.3 and higher.

Omnipay libraries are stable, consistent, and fully united tested by the developers and contributors around the globe. I would like to thank them for their great effort on these awesome open source packages.

Omnipay libraries are really easy to understand and integrate with any framework or non-framework based projects.

 

Also, have a look at their Github repository to discover the supported gateways you want to integrate with your project.

You can simply pull the packages which are hosted freely on packagist by using composer.

In this blog post, I will go step by steps to integrate the popular library for PayPal express checkout with omnipay/omnipay-paypal.

I am going to show the steps with laravel framework v5.4.

Before getting started with the guidelines you will need the following stuff ready for your machine.

  • Composer installed.
  • PHP >= 5.6.4 for Laravel, follow official doc.
  • Web server, the database on your machine. eg: Apache, MySQL

After successfully installing the laravel on your machine.

Important Note

If you are using Symfony 3 or Symfony 3 components, or laravel v5.* which uses Symfony 3 components please note that Omnipay 2.* version still uses guzzle 3.*, which depends on symfony/event-dispatcher 2.*. It conflicts with Symfony 3 in the regular installation. For this fix, we may need to wait until Omnipay 3.* release, which is still under development.

For the alternative fix, go to the command line cd to your project root to force the installation of symfony/event-dispatcher ^2.8 which is fully compatible with both Symfony 3 components and guzzle 3.*.

composer require symfony/event-dispatcher:^2.8

Go to `composer.json` and put the following.

{
    "require": {
        "omnipay/paypal": "~2.0"
    }
}

or

composer require omnipay/paypal

Now let's create some example route to handle the requests and Controller and Model for it.

app/routes/web.php

<?php

Route::get('/{order?}', [
    'name' => 'PayPal Express Checkout',
    'as' => 'app.home',
    'uses' => 'PayPalController@form',
]);

Route::post('/checkout/payment/{order}/paypal', [
    'name' => 'PayPal Express Checkout',
    'as' => 'checkout.payment.paypal',
    'uses' => 'PayPalController@checkout',
]);

Route::get('/paypal/checkout/{order}/completed', [
    'name' => 'PayPal Express Checkout',
    'as' => 'paypal.checkout.completed',
    'uses' => 'PayPalController@completed',
]);

Route::get('/paypal/checkout/{order}/cancelled', [
    'name' => 'PayPal Express Checkout',
    'as' => 'paypal.checkout.cancelled',
    'uses' => 'PayPalController@cancelled',
]);

Route::post('/webhook/paypal/{order?}/{env?}', [
    'name' => 'PayPal Express IPN',
    'as' => 'webhook.paypal.ipn',
    'uses' => 'PayPalController@webhook',
]);

In my projects I prefer writing named routes, we can simply use route() helper from our views. You can change the above web.php routes to your application specific standard URLs.

app/Order.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

/**
 * Class Order
 * @package App
 */
class Order extends Model
{
    /**
     * @var string
     */
    protected $table = 'orders';

    /**
     * @var array
     */
    protected $dates = ['deleted_at'];

    /**
     * @var array
     */
    protected $fillable = ['transaction_id', 'amount', 'payment_status'];
}

In the Order.php model, I am just covering basic requirements for all type projects. You are free to change and assign them in your application with the relevant type of database fields.

resources/views/form.blade.php

@extends('app')

@section('content')
    <div class="container">
        <div class="gateway--info">
            <div class="gateway--desc">
                @if(session()->has('message'))
                    <p class="message">
                        {{ session('message') }}
                    </p>
                @endif
                <p><strong>Order Overview !</strong></p>
                <hr>
                <p>Item : Yearly Subscription cost !</p>
                <p>Amount : ${{ $order->amount }}</p>
                <hr>
            </div>
            <div class="gateway--paypal">
                <form method="POST" action="{{ route('checkout.payment.paypal', ['order' => encrypt(mt_rand(1, 20))]) }}">
                    {{ csrf_field() }}
                    <button class="btn btn-pay">
                        <i class="fa fa-paypal" aria-hidden="true"></i> Pay with PayPal
                    </button>
                </form>
            </div>
        </div>
    </div>
@stop

In the traditional applications, people used to set values on forms and submit the data. I personally don't prefer that. In the above form.blade.php I have created a form action with a route() helper and encrypted my order id to make it safe.

app/config/paypal.com

<?php

return [
    'credentials' => [
        'username' => 'xxxxxxxxxxxxxxxxx',
        'password' => 'xxxxxxxxxxxxxxxxx',
        'signature' => 'xxxxxxxxxxxxxxxxx',
        'sandbox' => true,
    ],
];

If you wish to store the API credentials in the database that is your choice, for the example I have created a config file. So replace the xxx characters with your own credentials.

app/PayPal.php

<?php

namespace App;

use Omnipay\Omnipay;

/**
 * Class PayPal
 * @package App
 */
class PayPal
{
    /**
     * @return mixed
     */
    public function gateway()
    {
        $gateway = Omnipay::create('PayPal_Express');

        $gateway->setUsername(config('paypal.credentials.username'));
        $gateway->setPassword(config('paypal.credentials.password'));
        $gateway->setSignature(config('paypal.credentials.signature'));
        $gateway->setTestMode(config('paypal.credentials.sandbox'));

        return $gateway;
    }

    /**
     * @param array $parameters
     * @return mixed
     */
    public function purchase(array $parameters)
    {
        $response = $this->gateway()
            ->purchase($parameters)
            ->send();

        return $response;
    }

    /**
     * @param array $parameters
     */
    public function complete(array $parameters)
    {
        $response = $this->gateway()
            ->completePurchase($parameters)
            ->send();

        return $response;
    }

    /**
     * @param $amount
     */
    public function formatAmount($amount)
    {
        return number_format($amount, 2, '.', '');
    }

    /**
     * @param $order
     */
    public function getCancelUrl($order)
    {
        return route('paypal.checkout.cancelled', $order->id);
    }

    /**
     * @param $order
     */
    public function getReturnUrl($order)
    {
        return route('paypal.checkout.completed', $order->id);
    }

    /**
     * @param $order
     */
    public function getNotifyUrl($order)
    {
        $env = config('paypal.credentials.sandbox') ? "sandbox" : "live";

        return route('webhook.paypal.ipn', [$order->id, $env]);
    }
}

I prefer separation of concerns, in the example, I have a new class PayPal.php as a helper class for PayPalController.php

I think many people also like the clean code in the controller rather than over-complicating things on Controllers.

app/Http/Controllers/PayPalController.php

<?php

namespace App\Http\Controllers;

use App\Order;
use App\PayPal;
use Illuminate\Http\Request;

/**
 * Class PayPalController
 * @package App\Http\Controllers
 */
class PayPalController extends Controller
{
    /**
     * @param Request $request
     */
    public function form(Request $request, $order_id = null)
    {
        $order_id = $order_id ?: encrypt(1);

        $order = Order::findOrFail(decrypt($order_id));

        return view('form', compact('order'));
    }

    /**
     * @param $order_id
     * @param Request $request
     */
    public function checkout($order_id, Request $request)
    {
        $order = Order::findOrFail(decrypt($order_id));

        $paypal = new PayPal;

        $response = $paypal->purchase([
            'amount' => $paypal->formatAmount($order->amount),
            'transactionId' => $order->id,
            'currency' => 'USD',
            'cancelUrl' => $paypal->getCancelUrl($order),
            'returnUrl' => $paypal->getReturnUrl($order),
        ]);

        if ($response->isRedirect()) {
            $response->redirect();
        }

        return redirect()->back()->with([
            'message' => $response->getMessage(),
        ]);
    }

    /**
     * @param $order_id
     * @param Request $request
     * @return mixed
     */
    public function completed($order_id, Request $request)
    {
        $order = Order::findOrFail($order_id);

        $paypal = new PayPal;

        $response = $paypal->complete([
            'amount' => $paypal->formatAmount($order->amount),
            'transactionId' => $order->id,
            'currency' => 'USD',
            'cancelUrl' => $paypal->getCancelUrl($order),
            'returnUrl' => $paypal->getReturnUrl($order),
            'notifyUrl' => $paypal->getNotifyUrl($order),
        ]);

        if ($response->isSuccessful()) {
            $order->update(['transaction_id' => $response->getTransactionReference()]);

            return redirect()->route('app.home', encrypt($order_id))->with([
                'message' => 'You recent payment is sucessful with reference code ' . $response->getTransactionReference(),
            ]);
        }

        return redirect()->back()->with([
            'message' => $response->getMessage(),
        ]);
    }

    /**
     * @param $order_id
     */
    public function cancelled($order_id)
    {
        $order = Order::findOrFail($order_id);

        return redirect()->route('app.home', encrypt($order_id))->with([
            'message' => 'You have cancelled your recent PayPal payment !',
        ]);
    }

    /**
     * @param $order_id
     * @param $env
     */
    public function webhook($order_id, $env)
    {
        // to do with next blog post
    }
}

The above implementation only covers, make a payment, handle failed, canceled the payment. I have only shown basic stuff to get up and running, you are free to update as per your application requirements.

Thanks for reading up on the end!

If you have any feedback, any typo mistake in the post above please feel free to leave your comments below.

If you want to view the overall code on Github follow this link.

For the PayPal Instant Payment Notification support, I will soon come back with a new fresh blog post.

Happy Coding!

]]>
<![CDATA[Laravel 5.4: Login with Username or Email]]> https://sujipthapa.co/blog/laravel-54-login-with-username-or-password cf953127-faa3-4ef6-9781-e460f16b3997 2017-03-12T11:02:00+05:45 Mr. Sujip Thapa I believe that Laravel is now well-known framework among PHP developers. I hope many of you may have already built an application using it. It is really popular due to its powerful features with expressive, beautiful syntax.

From this tutorial, we want to customize the default auth system in our Laravel 5.4 application to allow users to sign in using their username or email address. As of now Laravel framework only supports sign in with email only. After going through this tutorial your application will be able to allow sign in with both username or email, also you can easily extend this concept to any other version of Laravel framework.

 

Before moving to start the tutorial you should be ready with Laravel 5.4 setup on your machine.
Let's begin with creating authentication scaffolding, migrations for User model.

Run below command to generate authentication scaffolding.

php artisan make:auth

If you already have experience with Laravel, then you may know about auth system, which ships with framework out of the box.

If you want to start from scratch, delete the user's table from database/migrations directory and hit the following command to generate new migration file for the user's table.

php artisan make:migration create_users_table --create="users"

Now, we are adding username field to migration file.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('username')->unique();
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

Let's migrate the database and go ahead.

php artisan migrate

Now let's look at LoginController.php which implements AuthenticatesUsers.php trait. which also ships with framework out of the box.

If you look at the trait, which includes several methods only related to authenticating the user into the application. Now its time to deal with our actual requirement.

I am going to override the credentials() methods from AuthenticatesUsers.php trait to LoginController.php

Now, my LoginController.php looks like below.

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
     */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest', ['except' => 'logout']);
    }

    /**
     * Get the needed authorization credentials from the request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    protected function credentials(Request $request)
    {
        $field = filter_var($request->get($this->username()), FILTER_VALIDATE_EMAIL)
            ? $this->username()
            : 'username';

        return [
            $field => $request->get($this->username()),
            'password' => $request->password,
        ];
    }
}

Also, I changed few things in my login.blade.php, the email input field to be the text for username support.

<input type="text" class="form-control" name="email" placeholder="E-Mail / Username" value="{{ old('email') }}" required autofocus>

Okay! now we have a functionality to allow login with username or email.

Last Updated: 10th, September 2017

 

If you would like to view updated new blog post to cover login/register with Laravel v5.5 here.

Conclusion

This tutorial heavily depends on Laravel 5.4, so if you are going to implement this on another version of framework please have a look at the traits used properly to override the credentials array.

Thanks for reading up to the end. If you have any feedback please leave your comments below. Feel free to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[Fixing - PHPUnit Util Deprecated Feature Logger]]> https://sujipthapa.co/blog/fixing-class-phpunit-util-deprecatedfeature-logger 8f954d6b-dddb-4073-9882-e678a564b5b6 2016-11-19T06:03:18+05:45 Mr. Sujip Thapa In this series, i am going to cover a common bug that developers face during running the PHP Unit Tests.

Keep in mind, this error occured while I was using phpunit, version line five.

composer global require "phpunit/phpunit"

Lately, I was working on my open source project on Github, which is an Omnipay based PHP Library for NAB Transact, and stuck with the Class PHPUnit_Util_DeprecatedFeature_Logger PHP Fatal error.

 

This post is to help someone else that tries it – and comes across the same issue like me.

PHP Fatal error: Class PHPUnit_Util_DeprecatedFeature_Logger contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (PHPUnit_Framework_TestListener::addRiskyTest) in …vendor/phpunit/phpunit/PHPUnit/Util/DeprecatedFeature/Logger.php on line 202

My phpunit.xml.dist file was like below.

backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
colors="true"
syntaxCheck="false"> 
./tests/ 
./src

My fix was simple – it took some systematic editing of the phpunit.xml.dist file to figure it out.

The actual fix was simple – the problematical line for me was:

colors = "false"

I’ve said for a long time that “you don’t get paid the big bucks for knowing what to do – it’s for knowing how to fix it when you make the inevitable screw-ups”.

Now, when I run my PHPUnit tests it is working normally.

Thanks for reading up to the end.

Happy Coding!

]]>
<![CDATA[Laravel v4.2.20 , a special release.]]> https://sujipthapa.co/blog/laravel-v4220-a-special-release 8e97311a-f83c-4b1a-8352-befaa7d20d43 2016-10-27T13:45:17+05:45 Mr. Sujip Thapa I new version for Laravel v4.2.20 has been released. Yes!, you are absolutely correct, that new version for the Laravel's back version v4.2 is now released with support for PHP 7.0 and updates to the Symfony components to v2.7.

The v4 line of Laravel is already deprecated, but a lot of developers around the world are still using it and haven’t upgraded to the latest releases. This recent release allows them to move to PHP 7.0 and take new advantage of its speed improvements as well as security fixes without having to upgrade their app's code.

Ideally, those who are still on v4 version have plans to upgrade to v5 but the Laravel team understands that not every business has the luxury of being on the latest and greatest at all times.

 

There will always be an exception in their company to their typical policy of no new features on old versions of applications.

Post Credit: The original post was published on https://laravel-news.com and wanted to write few links to refer my readers.

Conclusion

Thanks for reading this article up to the end. If you have any feedback or the article was really useful to you please leave your comments below. Feel free to share with friends if you like it.

Happy Coding!

]]>
<![CDATA[Hacktoberfest is back, #Hacktoberfest2016]]> https://sujipthapa.co/blog/hacktoberfest-is-back-hacktoberfest2016 df19b099-ce3f-4fea-a8ba-d0ec2c2b9a31 2016-09-29T13:25:00+05:45 Mr. Sujip Thapa Hey Developers !

Its time to celebrate an open source (#Hacktoberfest) in this October by participating with Hacktoberfest 2016. It is a month-long (festival of code) campaign to spread open source resources, which is organized by DigitalOcean and hosted on GitHub.

Rules for developers
To get a Hacktoberfest 2016 t-shirt, you must create four pull requests, starting October 1 - October 31 2016.

Image Courtesy Github

To participate, simply open a pull request and contribute to any open source project. You can fix a bug, add a feature, or even improve some documentation.

 

Don't forget to mention #hacktoberfest hashtag on Twitter, Facebook, or Instagram once you've made your pull requests.

After you create atleast four pull requests by October 31st, you'll get the satisfaction of sharing your code with the world—and a t-shirt, of course.

Last Updated : 26th, Oct 2016

Completed the Hacktoberfest challenge!

Happy Coding !

]]>
<![CDATA[An alternative way for PHP array_column]]> https://sujipthapa.co/blog/an-alternative-way-for-php-array-column f4a76036-1a42-47a5-ab34-766d889a0ac5 2016-09-29T05:45:16+05:45 Mr. Sujip Thapa As a developer i'm pretty much sure, most of the developers use latest version of PHP in their machine, so do I. 

The thing is, sometime with version of PHP on hosting servers, we have to think about the alternative way.

Here with this post I am going to share a pro tip for developers,  I learned when i also got stuck with.

 

Alternative way for to make it working on old version of PHP for method  array_column , this method is available only on (PHP 5 >= 5.5.0, PHP 7) see official doc 

$array = [
   ['developer' => ['name' => 'Sujip Thapa', 'age' => 25, 'expericence' => '+3 years']],
   ['developer' => ['name' => 'Aakash Gurung', 'age' => 25, 'expericence' => '+3 years']],
 ];


In PHP >=5.5 get all developers name only:

$developers = array_column($array, 'name');


In PHP < 5.5 - alternative way to get all developers name only:

$developers = array_map(function ($each) {

 return $each['name'];

 }, $array);


If you are a Laravel developer and using v4.2 you also can do like below, see official doc

$developers = array_fetch($array, 'name');


Output will be same:

$developers = ['Sujip Thapa', 'Aakash Gurung'];


Happy Coding !

]]>
<![CDATA[Git Pro Tip: Show your branch on Linux Ubuntu terminal]]> https://sujipthapa.co/blog/git-pro-tip-show-your-branch-on-linux-ubuntu-terminal 6c4edbe0-ad35-43d8-aeab-1e2e81717f55 2016-09-10T18:15:00+05:45 Mr. Sujip Thapa  If you would like to add branch name to bash prompt then we have to edit the PS1variable(set value of PS1 in~/.bash_profile).

Want to know about PS1 ?

PS1 denotes Prompt String 1. Which is one of the prompt available in Linux/UNIX shell. If we open our terminal, it displays the content defined in PS1 variable in your bash prompt.

Here is an example of how to edit “.bashrc” for Git branch information.

Open up your terminal and type.

nano ~/.bashrc

Note: use sudo command as per you need, otherwise make sure your current user has proper permission to save .bashrc file.

Now, scroll up to last and paste the code snippet provided below.

then press, Ctrl + O to save, Ctrl + X to close the nano edit mode, then restart the terminal.

parse_git_branch() {
     git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\u@\h \[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] $ "

Enjoy !

]]>
<![CDATA[Install Atom Editor on Ubunto 16.04]]> https://sujipthapa.co/blog/install-atom-editor-on-ubunto-1604 ecd84524-9e1f-4fcb-b6c0-79c1d8fcc26e 2016-09-10T18:15:00+05:45 Mr. Sujip Thapa Press Ctrl+Alt +T open up your teminal, please type these commands.

sudo add-apt-repository ppa:webupd8team/atom
sudo apt-get update
sudo apt-get install atom

Enjoy!

]]>
<![CDATA[Setup Mailgun with Digital Ocean]]> https://sujipthapa.co/blog/setup-mailgun-with-digital-ocean 0ae0db81-44f8-4a48-99b2-a3cae2cc43ab 2016-09-10T18:15:00+05:45 Mr. Sujip Thapa One really nice thing is that not having an email server to manage yourself, wanna thank Mailgun for such amazing service.

Few days ago I was trying to set up Mailgun with Digital Ocean , So I planned to write a blog post to share with other developers in the community.

Configure Your Domain

Things are a bit easire if you are setting up against your domain directly.

I would like to show my preview of how to get things configured on Mailgun end.

Here’s what you need to change your specific configuration part.

  • A record for @ should be your droplets’ IP (not mailgun’s)
  • TXT record for mg should be wrapped in quotes
  • TXT record for pic._domainkey.mg should be wrapped in quotes
 

After all under Domain Verification & DNS click on Check DNS Record Now, then if your configuration is good then you shoudl see the screen below.

Below is the Zone file how it looks like, in order to confirm sujipthapa.com.np.np for sending and receiving emails using Mailgun.

Please be sure that 46.101.253.75 is the IP address of your droplet provided by Digital Ocean not provided by mailgun.

Conclusion

Thanks for reading this article up to the end. If you have any feedback or the article was really useful to you please leave your comments below. Feel free to share with friends if you like it.

Enjoy!

]]>
<![CDATA[Laravel 5.1 LTS Cheat Sheet]]> https://sujipthapa.co/blog/laravel-51-lts-cheat-sheet 3b7a72fd-098f-4c12-ad16-a63f9160cd72 2016-09-10T18:15:00+05:45 Mr. Sujip Thapa If you are a Laravel developer you gonna love this article.

Here, I'm going to write about the Laravel v5.1 cheat sheet.

The EST Group has just introduced a new laravel 5.1 LTS version cheat sheet which covers most of the laravel syntax. Which are really useful to all level of laravel developers.

Here is a link for the github project maintained by @Summer, @Aufree, thanks to them for the awesome snippets for quick note that makes really easy while working on code.

 

Also view the different language version available on the github repo.

If you are still working on the back project which was built on Laravel v4.2, make sure you view the previous of cheat sheet written by @JesseObrien 

Happy Coding !

]]>