Sujip Thapa - Blog about Laravel, PHP, Javascript & 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 2024-04-27T09:48:00+05:45 <![CDATA[Laravel Cheat Sheet & Full Reference]]> https://sujipthapa.co/blog/laravel-cheat-sheet-full-reference 12f196f6-1c18-4622-8655-061222d557d7 2024-03-17T14:00:00+05:45 Mr. Sujip Thapa I assume you're landing on this page because, like me, you're a Laravel developer trying to find a full-fledged Laravel Cheat Sheet or a Laravel Developer Cheat Sheet for your shortcut to your daily Laravel development.

Using a cheat sheet isn't limited to beginner or intermediate developers; it's valuable for developers at any level. It serves as a quick reference for Laravel's code syntax and ecosystem features, making it essential for professionals as well.

I've previously covered a few versions of the Laravel Cheat Sheet on this blog, but some of them are already unlisted because they no longer exist now.

We've updated to the new ones as they are updated by the original creators.

What is a Laravel Cheat Sheet?

A Laravel Cheat Sheet is like a quick guidebook that gives you easy access to important info about Laravel. It helps developers by showing examples about every convention of Laravel features like routes, controllers, views, models, commands etc. and common tasks that a Laravel framework can perform in your project.

What are some of the best available cheat sheets?

The Artisan Page, created by James Brooks, a Laravel core team member, serves as a comprehensive cheat sheet for all things related to Laravel Artisan Commands. Recently, the author announced full support for all first-party pages from the Laravel ecosystem.

You can visit the Artisan Page to review both earlier versions and the latest version of Laravel from their navigation menu.
The creator has open-sourced this project on GitHub, making it accessible to everyone, and can also be contributed to it if you have any features, ideas or fixes on it.

Laravel Cheat Sheet, This is another fantastic Laravel cheat sheet which covers everything about Laravel features. However, it does not have any navigation about the supported versions, you will need to have certain knowledge about what sort of things have changed in between the Laravel major version releases.

There is also a get started section, which offers you how to get started on different environments like Windows, Mac, Linux, and basic requirements.

As you go through this cheat sheet, remember to consider the version of Laravel you're using and any changes in parameters or features across different versions. They've included a note stating that it serves as a reference for common commands and features in Laravel.

Also Read: Laravel Cheat Sheet for Verson 4x, 5x

These cheat sheets were primarily created for older Laravel versions like 4.x and 5.x. However, they still cover generic Laravel features that remain relevant, and newer versions have improved upon them. Therefore, they are not considered outdated.

Thank you for reading the article all the way through. If you found it helpful, please consider sharing it with your developer circle.

Stay tuned for more upcoming articles!

 

]]>
<![CDATA[MySQL SELECT Query: Fallback to Another Column for Null or Zero Values]]> https://sujipthapa.co/blog/mysql-select-query-fallback-to-another-column-for-null-or-zero-values d9a5ea62-c88c-4b69-8c2a-86bab66e9ddb 2024-02-20T16:30:00+05:45 Mr. Sujip Thapa Introduction
In Laravel or any PHP project, there are times when you need a SQL query to select a column and check if its value is null or 0. If the value is null or 0, you might want to fallback to a different column from another table. This guide will show you how to achieve this using Laravel Eloquent queries.

As I was working on the report section of my project, I needed to fetch abandoned carts and display their values. The prices were stored in a table called product_prices, which contained various pricing conditions. If a price matched in this table, it was used. Otherwise, the query would fall back to the cart_item.unit_price column. Let's delve into it further below.

This got me thinking—I should share this handy solution with my community through a blog post, and here you are reading about it!

We'll be using three example database tables: cart, cart_item, and product_price. By the end of this guide, you'll understand how to handle null values effectively and provide fallbacks to another column in your Laravel applications.

Prerequisites
Before we begin, make sure you have a basic understanding of Laravel and Eloquent ORM.

Handling Null Values and Fallbacks in Laravel Eloquent Queries

As I mentioned earlier, I needed to fallback to the default price from the cart_item.unit_price when the product price was not found in the product_price table. The product_price table stored conditional prices, but I required the default price. Therefore, if the default price was not available, the fallback price would be displayed.

Now, let me illustrate this concept using the query below and explain it further.

To tackle this, let's break down the provided code snippet:

return CartItem::query()
    ->selectRaw(
        DB::raw(
            "SUM(cart_item.quantity) as total_abandoned,
            COALESCE(
                (SELECT
                    price
                FROM
                    product_price
                WHERE
                    product_price.variant_id = cart_item.variant_id
                    AND product_price.role_id IS NULL
                    AND product_price.is_special = 0
                LIMIT 1),
                cart_item.unit_price
            ) * SUM(cart_item.quantity) AS total_value,
            cart_item.sku,
            CONCAT_WS('', product.name, cart_item.name) as product_name,
            product_variant.title as variant_name,
            CONCAT_WS('||', IFNULL(cart_item.variant_id, 'x'), cart_item.sku) as group_column"
        )
    )
    ->join('cart', 'cart.id', '=', 'cart_item.cart_id')
    ->leftJoin('product', 'cart_item.product_id', '=', 'product.id')
    ->leftJoin('product_variant', 'cart_item.variant_id', '=', 'product_variant.id')
    ->where('cart.is_processed', 0)
    ->whereNotNull('cart_item.sku')
    ->groupBy('group_column')
    ->orderBy('total_abandoned', 'desc');

 

Here's what's happening:

  1. We're using Laravel's Eloquent ORM to construct our query. This allows us to write expressive and readable code.
  2. We're selecting specific columns and performing calculations using the selectRaw() method.
  3. The COALESCE() function is used to handle null values and fallback to another column if necessary. In this case, we're checking if there's a custom price for the product in the product_price table. If not, we fallback to the unit price of the cart item.
  4. We're joining multiple tables (cart, product, and product_variant) to fetch additional information about the cart items.
  5. We're applying certain conditions using the where() method to filter the results. For example, we're only selecting cart items from carts that are not processed (cart.is_processed is 0).
  6. Finally, we're grouping the results by a specific column (group_column) and ordering them by the total quantity of abandoned items in descending order.

This code snippet efficiently handles null values and provides fallbacks, ensuring that our report on abandoned cart items is accurate and informative.

By understanding and implementing concepts like this in our Laravel projects, we can write robust and reliable code that meets our business requirements effectively.

Conclusion
You've now learned how to handle null values and provide fallbacks in Laravel Eloquent queries for the cart, cart_item, and product_price tables. Experiment with these techniques in your own Laravel applications to handle null values effectively and provide appropriate fallbacks when necessary. Happy querying!

 

]]>
<![CDATA[Scroll to Element in JavaScript: A Complete Guide]]> https://sujipthapa.co/blog/scroll-to-element-in-javascript-a-complete-guide c1116bc2-c451-4f0a-a4a0-2b2f0e3cc2e3 2024-02-20T16:00:00+05:45 Mr. Sujip Thapa Introduction
Smooth scrolling to an element on a webpage enhances user experience and navigation. In this tutorial, we'll explore how to achieve this effect using JavaScript, jQuery, Vue.js, and Alpine.js. We'll cover various methods and provide code examples to help you implement smooth scrolling on your website using different frameworks and libraries.

Prerequisites
Before we begin, ensure that you have a basic understanding of HTML, CSS, and the respective framework or library you'll be using (JavaScript, jQuery, Vue.js, or Alpine.js).

HTML Structure
Let's start by defining the HTML structure of our webpage. We'll create a simple layout with a navigation menu and sections that we want to scroll to.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smooth Scrolling Examples</title>
<style>
/* Add your CSS styles here */
</style>
</head>
<body>

<nav>
  <ul>
    <li><a href="#section1">Section 1</a></li>
    <li><a href="#section2">Section 2</a></li>
    <li><a href="#section3">Section 3</a></li>
  </ul>
</nav>

<section id="section1">
  <h2>Section 1</h2>
  <!-- Content for section 1 -->
</section>

<section id="section2">
  <h2>Section 2</h2>
  <!-- Content for section 2 -->
</section>

<section id="section3">
  <h2>Section 3</h2>
  <!-- Content for section 3 -->
</section>

</body>
</html>


JavaScript Example (Vanilla)
Let's implement smooth scrolling using vanilla JavaScript. Add the following JavaScript code to your HTML file:

<script>
document.addEventListener('DOMContentLoaded', function() {
  const links = document.querySelectorAll('nav a');

  links.forEach(link => {
    link.addEventListener('click', function(e) {
      e.preventDefault();
      
      const targetId = this.getAttribute('href').substring(1);
      const targetElement = document.getElementById(targetId);
      
      if (targetElement) {
        const offsetTop = targetElement.offsetTop;
        window.scrollTo({
          top: offsetTop,
          behavior: 'smooth'
        });
      }
    });
  });
});
</script>


jQuery Example
Let's implement smooth scrolling using jQuery. Add the following jQuery code to your HTML file:

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
  $('nav a').click(function(e) {
    e.preventDefault();
    
    const targetId = $(this).attr('href').substring(1);
    const targetOffset = $('#' + targetId).offset().top;
    
    $('html, body').animate({
      scrollTop: targetOffset
    }, 1000);
  });
});
</script>


Vue.js Example
Now, let's implement smooth scrolling using Vue.js. Add the following Vue.js code to your HTML file:

<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
new Vue({
  el: '#app',
  methods: {
    scrollTo(id) {
      const targetOffset = document.getElementById(id).offsetTop;
      window.scrollTo({
        top: targetOffset,
        behavior: 'smooth'
      });
    }
  }
});
</script>


Alpine.js Example
Lastly, let's implement smooth scrolling using Alpine.js. Add the following Alpine.js code to your HTML file:

<script src="https://cdn.jsdelivr.net/npm/alpinejs@2"></script>
<script>
window.alpineInit = function() {
  return {
    scrollTo(id) {
      const targetOffset = document.getElementById(id).offsetTop;
      window.scrollTo({
        top: targetOffset,
        behavior: 'smooth'
      });
    }
  };
}
</script>


Conclusion
You've now learned how to implement smooth scrolling to an element on a webpage using JavaScript, jQuery, Vue.js, and Alpine.js. Experiment with these examples and choose the method that best fits your project's requirements. Smooth scrolling enhances user experience and makes navigation more intuitive for your website visitors.

Happy coding!

]]>
<![CDATA[Leveraging Laravel Eloquent: Retrieving Foreign Key Constraints from the Database]]> https://sujipthapa.co/blog/leveraging-laravel-eloquent-retrieving-foreign-key-constraints-from-the-database 3536e1bc-2256-4ee3-8f4c-ca7b4b83bb01 2024-02-20T15:00:00+05:45 Mr. Sujip Thapa Foreign key constraints play a pivotal role in maintaining data integrity within relational databases. However, accessing information about these constraints programmatically can be challenging. In this guide, we'll explore how to leverage Laravel's Eloquent ORM to effortlessly retrieve foreign key constraints from the database, streamlining your development process and ensuring data consistency.

Understanding Foreign Key Constraints:
Before diving into the implementation details, let's briefly review what foreign key constraints are and why they're essential. Foreign key constraints establish relationships between tables in a relational database, ensuring that data consistency is maintained across related records. By enforcing referential integrity, foreign key constraints prevent orphaned records and maintain the integrity of your data model.

Retrieving Foreign Key Constraints with Laravel Eloquent:
In Laravel, Eloquent provides a powerful and intuitive interface for interacting with the database. We can leverage Eloquent to retrieve information about foreign key constraints from the database schema effortlessly. Here's how to do it:

Step 1: Define the Model:
Start by creating a model for the information_schema.key_column_usage table. Since this table resides in the information_schema database, we need to specify the connection name for this model.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class KeyColumnUsage extends Model
{
    protected $table = 'information_schema.key_column_usage';

    public $timestamps = false;
}

Step 2: Retrieve Foreign Key Constraints:
Once we've defined the model, we can use Eloquent to query the information_schema.key_column_usage table and retrieve foreign key constraints. Here's how to do it:

use App\Models\KeyColumnUsage;

$results = KeyColumnUsage::select('TABLE_NAME as table_name', 'COLUMN_NAME as column_name', 'CONSTRAINT_NAME as constraint_name', 'REFERENCED_TABLE_NAME as referenced_table_name', 'REFERENCED_COLUMN_NAME as referenced_column_name')
    ->where('REFERENCED_TABLE_SCHEMA', DB::getDatabaseName())
    ->whereNotNull('REFERENCED_TABLE_NAME')
    ->get();

This query selects relevant columns from the information_schema.key_column_usage table, filters by the current database schema, and retrieves rows where the REFERENCED_TABLE_NAME column is not null.

In a recent project, I faced a big problem with foreign key constraints when trying to implement force delete functionality. The issue arose because different database engines treated these constraints differently. To solve this problem, I came up with a new plan: I decided to get rid of all the foreign key constraints and keep them only as indexes.

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class ForeignKeyToIndexCommand extends Command
{
    /**
     * @var string
     */
    protected $signature = 'foreign-key-to-index';

    /**
     * @var string
     */
    protected $description = 'Replace Database Foreign Key with Index Key.';

    /**
     * @return void
     */
    public function handle()
    {
        $sql = "SELECT `TABLE_NAME` as `table_name`, `COLUMN_NAME` as `column_name`, `CONSTRAINT_NAME` as `constraint_name`, `REFERENCED_TABLE_NAME` as `referenced_table_name`, `REFERENCED_COLUMN_NAME` as `referenced_column_name` FROM `information_schema`.`key_column_usage` WHERE `REFERENCED_TABLE_SCHEMA` = DATABASE() AND `REFERENCED_TABLE_NAME` IS NOT NULL";

        $results = DB::select($sql);

        if (!sizeof($results)) {
            return $this->info("* No Foreign Key Constraints Found. * \n");
        }

        foreach ($results as $k => $result) {
            Schema::table($result->table_name, function (Blueprint $table) use ($result) {
                if ($this->confirm("* Would you like to Drop Foreign Key [{$result->constraint_name}], and Add Index [y/n] *")) {
                    $table->dropForeign($result->constraint_name);

                    $this->info("* Dropped foreign key constraint: {$result->constraint_name} * \n");
                }

                $index_column = "{$result->table_name}_{$result->column_name}_index";

                $this->info("* Querying INDEX Key {$index_column} * \n");

                $index_stack = [];

                $table_indexes = DB::select("SHOW INDEXES FROM $result->table_name");

                foreach ($table_indexes as $index) {
                    if ($index->Key_name == $index_column) {
                        $index_stack[] = $index_column;
                    }
                }

                if (!sizeof($index_stack)) {
                    $table->index([$result->column_name]);

                    $this->info("* Added INDEX column {$index_column} * \n");
                } else {
                    $this->info("* INDEX column {$index_column} already exists.* \n");
                }
            });
        }
    }
}

Now, updating the console command as Laravel ORM style.

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\KeyColumnUsage;

class ForeignKeyToIndexCommand extends Command
{
    /**
     * @var string
     */
    protected $signature = 'foreign-key-to-index';

    /**
     * @var string
     */
    protected $description = 'Replace Database Foreign Key with Index Key.';

    /**
     * @return void
     */
    public function handle()
    {
        $results = KeyColumnUsage::select(
            'TABLE_NAME as table_name',
            'COLUMN_NAME as column_name',
            'CONSTRAINT_NAME as constraint_name',
            'REFERENCED_TABLE_NAME as referenced_table_name',
            'REFERENCED_COLUMN_NAME as referenced_column_name'
        )
        ->where('REFERENCED_TABLE_SCHEMA', DB::getDatabaseName())
        ->whereNotNull('REFERENCED_TABLE_NAME')
        ->get();

        if (!$results->count()) {
            return $this->info("* No Foreign Key Constraints Found. * \n");
        }

        foreach ($results as $k => $result) {
            Schema::table($result->table_name, function (Blueprint $table) use ($result) {
                if ($this->confirm("* Would you like to Drop Foreign Key [{$result->constraint_name}], and Add Index [y/n] *")) {
                    $table->dropForeign($result->constraint_name);

                    $this->info("* Dropped foreign key constraint: {$result->constraint_name} * \n");
                }

                $index_column = "{$result->table_name}_{$result->column_name}_index";

                $this->info("* Querying INDEX Key {$index_column} * \n");

                $index_stack = [];

                $table_indexes = DB::select("SHOW INDEXES FROM $result->table_name");

                foreach ($table_indexes as $index) {
                    if ($index->Key_name == $index_column) {
                        $index_stack[] = $index_column;
                    }
                }

                if (!sizeof($index_stack)) {
                    $table->index([$result->column_name]);

                    $this->info("* Added INDEX column {$index_column} * \n");
                } else {
                    $this->info("* INDEX column {$index_column} already exists.* \n");
                }
            });
        }
    }
}

Conclusion:
By utilizing the power of Laravel's Eloquent ORM, we can seamlessly retrieve foreign key constraints from the database, empowering us to ensure data integrity and streamline schema management. Whether you're building a new application or maintaining an existing one, leveraging Eloquent for database interactions offers unparalleled flexibility and ease of use.

Wrapping Up:
In this guide, we've explored how to use Laravel's Eloquent ORM to retrieve foreign key constraints from the database schema. By following the steps outlined above, you can effortlessly access vital information about your database structure, enhancing data integrity and simplifying schema management in your Laravel applications.

 

]]>
<![CDATA[Handling Webhook for PayPal REST API on eCommerce Platforms]]> https://sujipthapa.co/blog/handling-webhook-for-paypal-rest-api-on-ecommerce-platforms 93d0d311-8d50-4fe1-bda1-3c0cc97958f3 2023-09-16T17:00:00+05:45 Mr. Sujip Thapa This is the continuation article for the previous article about PayPal REST API integration with PHP apps.

This article assumes that you have completed a full-fledged PayPal integration using the library explained in the previous article.

Please Read: How to Integrate PayPal REST API for Online Payments

Let's begin by adding the route needed to access the web app with a POST request from the PayPal webhook service.

/**
 * PayPal REST API Webhook
 */
Route::post('/checkout/webhook/paypal/rest', [
    'as' => 'checkout.webhook.paypal.rest',
    'name' => 'PayPal Rest Webhook',
    'uses' => 'App\Controllers\Webhook\PayPalRestController@validate',
]);

 

Now, let's set up a controller that can accept the HTTP request from the PayPal webhook service. This controller will handle both the headers data and the payload.

<?php

namespace App\Controllers\Webhook;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Event;

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

    /**
     * @return \Illuminate\Http\Response
     */
    public function validate()
    {
        $data = $this->request->all();

        $headers = $this->request->headers->all();

        $event_type = $data['event_type'] ?? null;

        $verify_payload = [
            'auth_algo' => $this->request->headers->get('PAYPAL-AUTH-ALGO'),
            'cert_url' => $this->request->headers->get('PAYPAL-CERT-URL'),
            'transmission_id' => $this->request->headers->get('PAYPAL-TRANSMISSION-ID'),
            'transmission_sig' => $this->request->headers->get('PAYPAL-TRANSMISSION-SIG'),
            'transmission_time' => $this->request->headers->get('PAYPAL-TRANSMISSION-TIME'),
            'webhook_id' => 'REPLACE-WITH-YOUR-OWN',
            'webhook_event' => $data,
        ];

        $gateway = Omnipay::create('PayPalRest_Rest');

        $gateway->setClientId('xxxxxxxxxxxxxx');
        $gateway->setSecret('xxxxxxxxxxxxxx');
        $gateway->setTestMode(true);

        $response = $gateway->verifyWebhookSignature($verify_payload);

        if ($response->isSuccessful()) {
            // webhook verified by PayPal
            // handle the webhook event as necessary
            
            return \Response::make(null);
        }

        // verification failed.
    }
}

 

It's important to verify the webhook request sent by PayPal to confirm that the PayPal data hasn't been altered and that the request itself originates from PayPal.

Once it's verified, you can proceed to handle the event types that you've subscribed to under the webhook links added to the PayPal dashboard, associated with the credentials of your PayPal apps.

If you are managing webhook records through the API, it's essential to use the corresponding webhook ID for verifying the webhook signatures. For manual handling, you should provide the webhook ID generated in the PayPal dashboard and pass it into the provided codebase above.

Closing

Thank you for reading the article all the way to the end. Please share it within your circle, and continue visiting our website for more articles in the future.

]]>
<![CDATA[How to Integrate PayPal REST API for Online Payments]]> https://sujipthapa.co/blog/how-to-integrate-paypal-rest-api-for-online-payments 117193f2-c49a-4b5d-b1c1-64855ec18290 2023-09-16T15:00:00+05:45 Mr. Sujip Thapa Before diving into the main content of the article below, let's go through the previously created articles on this blog.

We have already created some articles that explain the classic PayPal API known as PayPal Express Checkout, also referred to as NVP/SOAP API. Most of the previous articles were based on the same API version but used different libraries built by developers using PHP.

Follow this link: PayPal Payment Gateway for Business to scroll through those articles.

Let's get started with the PayPal REST API, which we will cover in the article below.

PayPal is a globally recognized payment gateway service that is compatible for businesses of all sizes, whether small or large, to accept online payments. It is known for its security, reliability, and reputation as a trusted payment gateway, making it one of the most trusted in the world.

I recently released a new, framework-agnostic library for PHP to handle the PayPal REST API. You can view it on GitHub and explore the features it offers to meet your needs.

Furthermore, if you require any additional features for the package and have the ability to submit a pull request following the provided coding standards, your contributions are welcome.

# Prerequisites

A good understanding of PHP, some basic knowledge of the Laravel framework, familiarity with Composer, and proficiency in PHP, MySQL databases, as well as having a working environment ready to run the project are prerequisites.

# Composer

{
    "require": {
        "sudiptpa/paypal-rest": "~3.0"
    }
}


To add the library to your project, use the Composer package manager.

Now, you can set up your routes for your application and create a controller where you'll place your application logic for initiating payments.

use Omnipay\Omnipay;

$gateway = Omnipay::create('PayPalRest_Rest');

$gateway->setClientId('xxxxxxxxxxx');
$gateway->setSecret('xxxxxxxxxxx');
$gateway->setTestMode('xxxxxxxxxxx');


You can initiate the library with the above code snippet, and you will now have access to the features within the library.

Now, let's outline the requirements for how the payment flow works:

  • When a user visits the ecommerce website, they can add items to their cart and proceed to the checkout process, where they provide billing and shipping information. Afterward, they proceed to the payment gateway service.
  • The library currently provides an option for direct payment capture. In this method, the user is required to make the payment immediately, and it will be captured instantly if their PayPal account has authorized the payment to be captured.
  • After the user is redirected to PayPal, PayPal accepts data from the website, validates it, and creates an order on their platform. PayPal then returns the order with an ID and links to redirect users to their secure hosted platform for making payments, ensuring customer security.
  • After PayPal authorizes everything as valid, it redirects the user back to the merchant's website with certain parameters. The previously provided order ID must be stored in association with the order, allowing the merchant to reference it when necessary to capture the payment for that specific order.
  • When the user comes back to the website, the system verifies and confirms the successful payment capture.It then:

    - Sends a confirmation email to the customer.
    - Changes the order status to payment successful and ready for fulfillment.
    - Shows a message welcoming the user to review their order.

# Create Order

Here, the process involves creating the order on the PayPal platform, not on the website itself. The assumption is that the order, awaiting payment, has already been established on the website. Now, it's time to direct the user to the PayPal hosted platform to provide their payment details.

Below is the code snippet offered by the library.

<?php
use Omnipay\Common\CreditCard;

...

$payload = [
    'amount' => 20,
    'transactionId' => '1001',
    'transactionReference' => 'INV-1001',
    'currency' => 'AUD',
    'card' => new CreditCard([
        'shippingFirstName' => 'First Name',
        'shippingLastName' => 'Last Name',
        'email' => 'Email Address',
        'shippingPhone' => 'Phone Number',
        'shippingAddress1' => 'Street Address',
        'shippingAddress2' => 'House Number, Apt.',
        'shippingCity' => 'City, Home Town',
        'shippingState' => 'State, Province',
        'shippingPostcode' => 'Postal code',
        'shippingCountry' => 'AU',
    ]),
    'shippingType' => 'SHIPPING',
    'items' => [
        [
            'name' => 'Test Product 1',
            'description' => 'A sample description',
            'quantity' => 1,
            'price' => 20,
            'sku' => 'ITEM-CODE1',
            'category' => 'PHYSICAL_GOODS',
            'reference' => 'ITEM',
        ]
    ],
    'cancelUrl' => 'https://example.com/cancel/url',
    'returnUrl' => 'https://example.com/return/url',
];

$response = $gateway->purchase($payload)->send();

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

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

    // do something else
}

// handle the failure

You can send multiple items, but the item reference indicates whether it relates to shipping, tax, discount, handling, insurance, or the order line item. Please review the enum class to see the supported item references.

# Capture

After returning from PayPal, you need to complete the payment capture process for the order.

$response = $gateway->completePurchase([
    'transactionReference' => $order->paypal_order_id,
])->send();

if ($response && $response->isSuccessful() && $response->isCaptured()) {
    // handle the successful payment
}

if ($response && $response->isFailure()) {
    // show error message
}

 

Furthermore, this library also enables sending tracking information to PayPal for the order using the capture ID it provides after a successful payment capture.

Integrating a webhook is a good practice to automatically validate whether the website order has been marked as paid or refunded. There may be times where the website fails to update the database in real-time when the payment capture occurs. In such cases, a webhook becomes crucial for managing the order status effectively.

Also Read:

Stripe Payment Gateway Integration with Laravel
PayPal Integration – Omnipay PayPal PHP Library
PayPal Instant Payment Notification (IPN) Handling

# Closing

Thank you for following the article up to the end. If you found it interesting, please consider sharing it with others who might also find it worthwhile.

In our next article, we'll explain about webhook verification and handling. We request you to keep visiting our website for more insightful articles like this one in the future.

]]>
<![CDATA[How to Migrate Your Blog to a New Domain]]> https://sujipthapa.co/blog/how-to-migrate-your-blog-to-a-new-domain e70abe83-9a45-4c3a-ae3e-a03a56dbacb4 2023-08-28T00:00:00+05:45 Mr. Sujip Thapa Recently, we migrated this blog from a .co domain to a .com domain. Simultaneously, we also transitioned to Vultr as our hosting provider while making the move to the new domain.

I started this blog in September 2015, as indicated by the billing history from my previous hosting provider.

Why did I change the hosting provider?

I had been using my previous hosting provider from 2015 until August 2023. However, we have now switched to Vultr as our hosting provider.

The sole reason is that I have several other VPS servers with Vultr for my clients. With my previous hosting, I only had one server. I wanted to consolidate them all in one place for easier maintenance and periodic review.

In this blog, I will outline the steps we followed to migrate to a new server along with the new domain.

Moving your established blog to a new domain can be a strategic step to refresh your online presence or align better with your niche. However, executing a seamless migration requires a more advanced approach.

Disclaimer: By using the provided links for Vultr hosting, you grant us a small commission that helps sustain our blog, while also receiving credits to spin up your new server.

Backup

I took a complete backup of my images and database as the initial step with the previous hosting provider. I ensured their safety while proceeding with the following tasks.

Infrastructure

Being familiar with technology, I know how to set up VPS servers with Linux OS. I created a new server, set it up with the latest Ubuntu version, and configured Linux, Nginx, and PHP with necessary modules.

After sorting out the server and SSL setup, I tested everything by pointing them to a new IP using local host changes on my machine.

Vultr provides the option to manage DNS directly from their user dashboard, making it easy to set up simple DNS for the domain hosted on the server.Therefore, we've leveraged DNS management directly within the Vultr dashboard.


Also Read
An Ultimate Guide to Starting a Successful Blog
Get a Free SSL Certificate for Your Website

SSL

We utilize Cloudflare to safeguard our blog against DDoS attacks and benefit from its CDN and DNS services. They also offer free SSL certificates, which is great. While setting up and updating the SSL, I found that the SSL certificate I was using didn't work for the new testing.

So, we had to create new SSL keychains and set them up again.

Migration

Once the server setup was done and ready for migration, we cloned the code from the repository and moved assets, images, the database, and cron jobs.

Using Cloudflare was particularly helpful for DNS. We quickly changed the IP to point to the new server, smoothly transitioning my existing blog to the new domain within minutes.

SEO

Once the migration was complete, we set up a new property in Google Search Console. Additionally, we marked the old property to indicate that the site was moving to a new domain.

Furthermore, to ensure that my Google indexed pages remained consistent, we promptly submitted the new pages for indexing by Google.

To accomplish this, we updated the sitemap link, enabling Google to crawl the pages daily.

The website is up on the new server after the move. If you find any mistakes or have concerns, please let us know using the Contact Us page.

Conclusion

Thanks for checking out our migration process. Stay tuned for more blog posts in the future.

If you like what you read, feel free to share our articles with others.

 

]]>
<![CDATA[Simplifying Laravel Development with Invokable Controllers]]> https://sujipthapa.co/blog/simplifying-laravel-development-with-invokable-controllers 1030b45b-a4fc-40da-bded-a2407723233e 2023-07-24T09:00:00+05:45 Mr. Sujip Thapa Introduction

Laravel, one of the most popular PHP frameworks, is renowned for its simplicity, elegance, and developer-friendly features. Among its array of powerful functionalities, "Invokable Controllers" stands out as an exceptional feature that can significantly enhance the organization and efficiency of your Laravel applications. In this blog post, we will explore the concept of invokable controllers in Laravel, understand their benefits, and learn how to leverage them to streamline your code and improve the overall development experience.

What are Invokable Controllers?

Invokable controllers, also known as "single-action controllers," are a type of controller in Laravel that can be treated as a callable entity. Instead of defining multiple methods for different actions, invokable controllers contain a single __invoke() method. This unique feature allows the controller itself to be invoked as if it were a closure or a function, making it an elegant solution for handling single-action routes.

The Beauty of Simplicity:

One of the main advantages of using invokable controllers is their simplicity. By having a single __invoke() method in the controller, you can streamline your code and avoid clutter caused by multiple action methods. This approach is particularly beneficial for routes that perform a specific action without the need for additional methods.

Creating an Invokable Controller:

Creating an invokable controller in Laravel is straightforward. Let's take a look at an example:

// app/Http/Controllers/MyController.php
namespace App\Http\Controllers;

class MyController
{
    public function __invoke($id)
    {
        // Your controller logic using $id
        // ...
    }
}

 

In this example, we have defined an invokable controller named MyController. The __invoke() method is responsible for handling the logic of the controller and accepting a parameter $id.

Routing with Invokable Controllers:

To use the invokable controller in your routes, simply reference the controller class as the route's action:

// routes/web.php or routes/api.php
use App\Http\Controllers\MyController;

Route::get('/example/{id}', MyController::class);

 

Now, when a request is made to /example/{id}, Laravel will automatically invoke the __invoke() method of the MyController class, passing the {id} as an argument.

Advantages of Invokable Controllers:

  1. Code Organization: Invokable controllers help maintain a cleaner and more organized codebase, especially for single-action routes.

  2. Reusability: With a single-action controller, you can easily reuse the controller logic across different routes, enhancing code modularity.

  3. Readability: Using invokable controllers can make your code more expressive and self-documenting, as it highlights the single purpose of the controller.

  4. Route Simplification: Invokable controllers eliminate the need for additional method definitions in the controller, leading to concise and straightforward route declarations.

Conclusion:

Invokable controllers are a valuable feature in Laravel that brings simplicity and elegance to your application's architecture. By embracing this approach, you can improve code organization, enhance readability, and create modular and maintainable applications. The power of invokable controllers lies in their ability to transform your code into concise, single-action entities, providing a seamless development experience.

Next time you encounter a single-action route in your Laravel project, consider employing an invokable controller to unlock its full potential. Embrace the simplicity and elegance of Laravel's invokable controllers to create cleaner, more maintainable, and efficient code.

Happy coding!

]]>
<![CDATA[Flexibility of Invokable PHP Classes]]> https://sujipthapa.co/blog/flexibility-of-invokable-php-classes 96a44879-d3c0-4c89-ad40-3cb232a9ccc8 2023-07-24T08:00:00+05:45 Mr. Sujip Thapa Introduction

PHP, being a versatile and widely used programming language, offers various features to enhance code flexibility and organization. One such powerful feature is "Invokable PHP Classes" or "Callable Classes." These classes can be invoked as if they were functions, enabling developers to encapsulate behavior and use classes as callable entities. In this blog post, we will delve into the world of invokable PHP classes, exploring their potential applications, and showcasing examples of how they can be utilized to create clean and maintainable code.

1. Dependency Injection:

class MyService
{
    public function __invoke($arg)
    {
        // Service logic using $arg
    }
}

// Register the invokable class in the dependency injection container
$container->register('my_service', MyService::class);

// Resolve and execute the service
$service = $container->get('my_service');
$result = $service($someArgument);

 

2. Middleware:

class AuthenticationMiddleware
{
    public function __invoke($request, $next)
    {
        // Perform authentication logic before passing to the next handler
        // $request = ... modify the request if needed
        $response = $next($request);
        // Perform actions after the request has been handled (if needed)
        return $response;
    }
}

// Usage in a middleware stack
$middlewareStack = [
    new AuthenticationMiddleware(),
    // Other middleware classes
    // ...
    $finalRequestHandler,
];

foreach ($middlewareStack as $middleware) {
    $finalRequestHandler = $middleware($finalRequestHandler);
}

$response = $finalRequestHandler($request);

 

3. Closures with State:

class Counter
{
    private $count = 0;

    public function __invoke()
    {
        return $this->count++;
    }
}

$counter = new Counter();
echo $counter(); // Output: 0
echo $counter(); // Output: 1
echo $counter(); // Output: 2

 

4. Custom Function Wrappers:

class FunctionWrapper
{
    public function __invoke($function, ...$args)
    {
        // Perform actions before calling the function
        // ...
        $result = $function(...$args);
        // Perform actions after calling the function
        // ...
        return $result;
    }
}

$wrapper = new FunctionWrapper();
$wrappedFunction = $wrapper(function ($a, $b) {return $a + $b;}, 3, 5);
echo $wrappedFunction; // Output: 8

 

5. API Routing and Controllers:

class UserController
{
    public function __invoke($request, $response)
    {
        $userId = $request->getParam('id');
        // Fetch user data using $userId
        // ...
        return $response->withJson($userData);
    }
}

// Define API routes
$app->get('/users/{id}', UserController::class);

 

6. Command-Line Tasks:

class ImportDataTask
{
    public function __invoke($options)
    {
        // Read options and perform the data import task
        // ...
    }
}

// Execute the command-line task
$importTask = new ImportDataTask();
$importTask(['file' => 'data.csv', 'format' => 'csv']);

 

7. Event Listeners:

class UserRegisteredListener
{
    public function __invoke($event)
    {
        $user = $event->getUser();
        // Perform actions when a user is registered
        // ...
    }
}

// Register the event listener
$dispatcher->addListener('user.registered', new UserRegisteredListener());

 

8. Custom Filters:

class TrimFilter
{
    public function __invoke($value)
    {
        return trim($value);
    }
}

$trimFilter = new TrimFilter();
$cleanedValue = $trimFilter('  Hello, world!  ');
echo $cleanedValue; // Output: "Hello, world!"


Also Read: Simplifying Laravel Development with Invokable Controllers

Laravel

We share a lot of articles in this blog regarding the Laravel framework. Here, you may have questions about whether any of the Laravel features include invokable PHP classes. An example is provided below.

Yes, Laravel has features that support invokable classes. In fact, Laravel's support for invokable classes is one of its powerful features that can help simplify and organize your code.

In Laravel, you can define a controller as an invokable class, allowing you to use it as a single-action controller with just the __invoke() method. This means you don't need to define multiple methods in your controller for each action; instead, the __invoke() method handles all the logic.

Here's an example of an invokable controller in Laravel:

// app/Http/Controllers/MyController.php
namespace App\Http\Controllers;

class MyController
{
    public function __invoke($id)
    {
        // Your controller logic using $id
        // ...
    }
}


You can then define a route to use this invokable controller in your web.php or api.php routes file:

// routes/web.php or routes/api.php
use App\Http\Controllers\MyController;

Route::get('/my-route/{id}', MyController::class);

 

When a request is made to /my-route/{id}, Laravel will automatically resolve and invoke the __invoke() method of the MyController class, passing the {id} as an argument.

Using invokable controllers in Laravel helps keep your code more concise and organized, especially for simple single-action routes. It's a powerful feature that promotes code reusability and makes your routes and controllers more manageable.

Conclusion

Invokable PHP classes offer a powerful and flexible way to structure code, encapsulate behavior, and create clean and maintainable applications. From dependency injection to middleware, custom filters to event listeners, and more, these classes enable developers to leverage the benefits of object-oriented programming while retaining the convenience of callable entities.

Incorporating invokable PHP classes into your development arsenal empowers you to build well-organized, modular, and easily maintainable codebases. By abstracting away implementation details and focusing on encapsulation, you can craft robust applications that can adapt and evolve over time.

Closing

As you embark on your PHP development journey, remember to explore the potential of invokable PHP classes and harness their power to create elegant and efficient solutions. Whether you're building web applications, APIs, or command-line tools, the use of invokable classes will undoubtedly streamline your development process and help you write code that is easy to read, understand, and extend.

Thank you for reading the article up to the end. If it was worth reading, please share this article on social media within your circle.

Happy coding!

]]>
<![CDATA[An Ultimate Guide to Starting a Successful Blog in 2024]]> https://sujipthapa.co/blog/an-ultimate-guide-to-starting-a-successful-blog-in-2024 33625726-69cb-4b45-b99c-d726fe9a86b7 2023-07-17T15:00:00+05:45 Mr. Sujip Thapa Blogging is all the rage nowadays, offering a fantastic way to share your thoughts and connect with people worldwide. But if you're new to blogging, getting started can feel overwhelming. Don't worry! This article is here to help. We've put together a simple and comprehensive guide to help you launch a successful blog in 2024. Let's dive in and make your blogging dreams a reality!

Discover the essential steps to start a blog successfully, including choosing a niche, selecting a platform, crafting engaging content, and implementing effective strategies for growth and monetization.

Here are 10 essential steps to follow when starting a blog:

  1. Define your niche.
  2. Select a blogging platform.
  3. Design an attractive blog layout.
  4. Get a perfect Domain name, and Hosting.
  5. Craft compelling content.
  6. Implement SEO techniques.
  7. Engage with your audience.
  8. Build a social media presence.
  9. Explore monetization options.
  10. Analyze and track your blog's performance.
  11. Stay up-to-date with industry trends.

With these steps, you'll be on your way to launching a successful blog in no time!

Define your Niche

Defining your niche is a crucial step in starting a successful blog. It involves identifying a specific topic, theme, or area of interest that sets your blog apart from others. By narrowing down your focus, you can position yourself as an expert in that particular field and attract a targeted audience. A well-defined niche helps you create content that resonates with your readers and establishes your credibility. It allows you to explore your passions and delve deeper into subjects that truly inspire you.

For example: In this blog, I write about Laravel and web development, drawing from my personal experience and finding it beneficial in my day-to-day work.

Select a Blogging Platform

Selecting the right blogging platform is crucial for your blog's success. Explore various platforms like WordPress, Blogger, and Squarespace to find one that aligns with your needs and technical expertise. Consider factors such as ease of use, customization options, and scalability to make an informed decision. The right platform will provide a user-friendly interface and a wide range of themes and plugins to enhance your blog's functionality. By choosing the ideal blogging platform, you set the stage for a seamless and efficient blogging experience.

If you're looking to build your own custom blog with a friendly developer, assess your requirements and feel free to reach out to me for assistance.

Design an attractive blog Layout

Crafting an appealing blog layout is key to a successful online presence. Aim for a clean and user-friendly design that facilitates effortless navigation. You can opt for pre-designed themes or enlist a web designer for a unique and customized layout. Remember, an eye-catching design sets the stage for an engaging reader experience.

Simplicity and user-friendly navigation are key factors that attract people to websites. Prioritizing a clean and intuitive user interface can enhance the overall user experience.

Get a perfect Domain name, and Hosting

When it comes to getting a perfect domain name and hosting for your blog, there are several reputable options available. Digital Ocean, Vultr, Kinsta, A2 Hosting, Bluehost, and HostGator are all well-regarded providers in the industry for reliable and efficient services.

Sign up for Digital Ocean using my referral link to get credits for your blog while also supporting me. Start your blogging journey with reliable cloud hosting services from Digital Ocean. Join today!

Digital Ocean and Vultr offer reliable and scalable cloud hosting solutions, ideal for bloggers seeking flexibility and control over their server environment. Kinsta specializes in managed WordPress hosting, providing a hassle-free experience with optimized performance and top-notch security.

A2 Hosting is known for its high-speed hosting and excellent customer support, catering to bloggers who prioritize speed and reliability. Bluehost is a popular choice for beginners, offering affordable shared hosting plans and user-friendly interfaces.

HostGator provides reliable hosting with a range of options suitable for blogs of all sizes.

Consider factors such as pricing, performance, support, and specific requirements of your blog when selecting the most suitable domain name and hosting provider. It's crucial to choose a provider that aligns with your needs and offers the features necessary to support the growth of your blog.

Read: Laravel Envoy to Deploy your Application to Hosting Server
Also Read: How to Deploy Laravel Application to VPS Server

Craft Compelling Content

To ensure a successful blog, focus on creating high-quality content that captivates and engages your audience. Your articles should be informative, entertaining, and highly relevant to your chosen niche. Consistency is key, so aim to post regularly and consistently to maintain your audience's interest and keep them coming back for more. By delivering valuable content on a consistent basis, you'll establish yourself as a reliable source and foster a dedicated following. Remember, the quality and frequency of your content play a crucial role in driving the success of your blog.

Implement SEO Techniques

Boost your blog's visibility and reach by implementing SEO techniques. Optimize your content with relevant keywords, build quality backlinks, and improve your website's loading speed. By effectively utilizing SEO strategies, you can enhance your blog's search engine rankings and attract organic traffic, expanding your audience and maximizing your online presence.

Engage with your Audience

Engage with your audience to build a strong and loyal community. Respond to comments, participate in discussions, and utilize social media for interaction. By fostering connections and actively engaging with your readers, you create a sense of community and build trust as a relatable blogger.

Build a Social Media Presence

Establish a solid presence on social media platforms such as Facebook, Instagram, Twitter, and LinkedIn to expand your blog's reach. Share your posts, interact with followers, and leverage analytics to optimize your strategies. By actively engaging on social media, you can grow your audience, foster connections, and establish your influence in your niche.

Explore Monetization Options

Google AdSense is a widely used advertising platform that enables you to monetize your blog by displaying relevant ads and earning revenue based on clicks or impressions. Another popular monetization method is affiliate marketing, where you promote products or services on your blog and earn a commission for each sale or referral made through your unique affiliate links. By leveraging these monetization strategies, you can generate income from your blog while providing valuable content to your audience.

You can find various ways to monetize your online presence.

Analyze and Track your Blog's Performance

Analyze your blog's performance with analytics tools like Google Analytics. Gain insights into your traffic, audience, and content engagement to make informed decisions for growth and success.

Stay up-to-date with Industry Trends

Stay in the know with industry trends to keep your blog relevant and engaging. By staying updated on the latest developments, you can provide valuable content that resonates with your audience and maintain a competitive edge in your niche.

In conclusion, by defining your niche, choosing the right platform, creating quality content, promoting effectively, monetizing strategically, tracking metrics, staying updated, and remaining committed, you can launch a successful blog in 2024. Engage your audience and achieve your blogging goals with confidence.

Embrace the journey, stay dedicated, and watch your blog soar to new heights. Best of luck on your path to blogging success!

Thank you for reading! If you found this article helpful, please consider sharing it with others. Let's inspire more bloggers to achieve success!

Happy Blogging!

]]>
<![CDATA[Advanced Query Debugging in Laravel 10: Mastering Efficient Techniques]]> https://sujipthapa.co/blog/advanced-query-debugging-in-laravel-10-mastering-efficient-techniques 6d10a743-d5b2-4390-8be8-71e1e7af6a6d 2023-07-13T17:00:00+05:45 Mr. Sujip Thapa Introduction: Debugging queries is an essential skill for Laravel 10 developers, and utilizing advanced techniques can greatly enhance the process. In this article, we will explore practical examples of query debugging using Laravel's query builder. We will focus on advanced methods, including dd(), dump(), dumpRawSql(), ddRawSql(), and toSql(), to effectively debug complex queries and resolve database-related issues efficiently.

Debugging Query Results with dd():

$query = DB::table('users')
            ->join('orders', 'users.id', '=', 'orders.user_id')
            ->leftJoin('products', 'orders.product_id', '=', 'products.id')
            ->where('users.status', '=', 'active')
            ->where('orders.quantity', '>', 5)
            ->orderBy('orders.created_at', 'desc')
            ->select('users.name', 'orders.total', 'products.name as product_name');

$result = $query->get();
dd($result);


By applying dd() to the query result, you can instantly inspect the query results and gain insights into the retrieved data and query structure.

Inspecting Raw SQL Statement with dumpRawSql():

$query = DB::table('orders')
            ->whereIn('status', ['pending', 'processing'])
            ->orWhere(function ($query) {
                $query->where('total', '>', 1000)
                      ->where('discount', '>', 0);
            })
            ->orderBy('created_at', 'desc')
            ->limit(10);

$query->dumpRawSql();


By using dumpRawSql(), you can examine the raw SQL statement generated by the query, including complex conditions and subqueries.

Inspecting SQL Statement and Bindings with dump():

$name = 'John Doe';

$query = DB::table('users')
            ->where('name', $name)
            ->where('age', '>', 25);

$query->dump();


Using dump(), you can inspect the query builder instance, including the SQL statement and parameter bindings.

Inspecting Raw SQL Statement with ddRawSql():

$query = DB::table('posts')
            ->whereIn('category', ['Technology', 'Science'])
            ->where(function ($query) {
                $query->where('status', '=', 'published')
                      ->orWhere('featured', '=', true);
            })
            ->orderBy('created_at', 'desc')
            ->limit(5);

$query->ddRawSql();


By using ddRawSql(), you can examine the raw SQL statement generated by the query and analyze it more effectively.

Extracting SQL Statement with toSql():

$query = DB::table('posts')
            ->whereIn('category', ['Technology', 'Science'])
            ->where(function ($query) {
                $query->where('status', '=', 'published')
                      ->orWhere('featured', '=', true);
            })
            ->orderBy('created_at', 'desc')
            ->limit(5);

$sql = $query->toSql();
dump($sql);

 

Using toSql(), you can extract the SQL statement from the query and further analyze it without interrupting the code execution.

Conclusion: Advanced query debugging techniques empower Laravel 10 developers to effectively resolve complex database-related issues. By utilizing methods such as dd(), dump(), dumpRawSql(), ddRawSql(), and toSql(), you gain efficient tools to inspect query results, raw SQL statements, parameter bindings, and extracted SQL statements. These methods provide valuable insights during development. It's important to note that while ddRawSql() is helpful for debugging, it should not be used in production environments due to security risks. Incorporating these techniques into your Laravel 10 development workflow will enhance your ability to debug queries, ensuring smooth and optimized database interactions.

Thank you for reading the article up to the end, please share with your circle if you found it worth reading and helpful.

Happy Debugging!

]]>
<![CDATA[Login with Username or Email with Laravel 10: Enhanced User Authentication]]> https://sujipthapa.co/blog/login-with-username-or-email-with-laravel-10-enhanced-user-authentication 594c94d3-c59c-4a5b-b70b-ccfbd05abf14 2023-06-30T14:15:00+05:45 Mr. Sujip Thapa Introduction: Welcome back to my blog series on user authentication with Laravel! In my previous articles, we have covered the topic of login with username or email in different versions of Laravel. Today, I'm excited to bring you an updated approach specifically for Laravel 10. This enhanced functionality allows users to log in using either their username or email address, offering a more personalized login experience. If you're interested in exploring our previous articles on this topic in different Laravel versions, check them out in the "Also Read" section below.

Understanding the Need for Login with Username or Email: In traditional user authentication systems, the login process typically relies on a fixed field, either the email or username. However, as applications evolve, it's essential to provide users with the flexibility to choose their preferred login method. By implementing login with both username and email options, we can create a more user-friendly experience. Let's dive into the implementation details.


Prerequisites: Before getting started with the article below, it is recommended to have a solid understanding of Laravel, PHP and web development concepts. Familiarity with PHP programming, web basics (HTML/CSS/JavaScript), MVC architecture, databases, and basic command-line knowledge will be beneficial for effectively working with the Laravel framework.

Also Read:

Laravel 5.4: Login with Username or Email
Laravel v5.5: Login, Register with Username or Email
Laravel 5.6 Login, Register, Activation with Username or Email Support
Laravel 5.7: Adding Support for Login with Username or Email
Building a Password Less Authentication System with Laravel Signed Routes

Let's get started!

Step 1: Database Preparation To enable login with both username and email, we need to make modifications to our database. Open your migration file and add a new column, username, to the users table. Ensure the username column is unique and indexed.

Schema::table('users', function (Blueprint $table) {
    $table->string('username')->unique()->after('email');
});


Step 2: User Model Configuration Next, update the User model to include the username field in the $fillable array and implement a method to dynamically determine the login field.

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    protected $fillable = [
        'name', 'email', 'password', 'username',
    ];

    public function getLoginField($loginValue)
    {
        return filter_var($loginValue, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
    }

    // ...
}


Step 3: Login Form Update your login form to include a single input field for both the username and email. Modify the login controller to handle the dynamic login field.

<form method="POST" action="{{ route('login') }}">
    @csrf

    <div>
        <label for="login">Username or Email</label>
        <input id="login" type="text" name="login" value="{{ old('login') }}" required autofocus>
    </div>

    <!-- Rest of the form fields -->

    <div>
        <button type="submit">Login</button>
    </div>
</form>


Step 4: Login Controller Modify your login controller to utilize the dynamic login field based on user input. By overriding the username() method, we can dynamically switch between the username and email fields during the authentication process.

use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    // ...

    public function username()
    {
        $loginValue = $this->request->input('login');
        $loginField = $this->guard()->user()->getLoginField($loginValue);

        return $loginField;
    }

    // ...
}

 

Inside the AuthenticatesUsers trait, there is a method called credentials() that is responsible for retrieving the login credentials from the request. In order to support login with both username and email, we need to modify this method to dynamically determine the login field based on the provided input.

Here's an updated version of the credentials() method:

/**
 * Get the login credentials from the request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
protected function credentials(Request $request)
{
    $loginField = $this->getLoginField($request->input($this->username()));
    
    return [$loginField => $request->input($this->username()), 'password' => $request->input('password')];
}


You can easily override the credentials() method in the User model to customize the login process further. By doing so, you can handle any specific logic or validations related to the login credentials.

To override the credentials() method in the User model, follow these steps:

  • Open your User model file (usually located at app/Models/User.php).
  • Add the following code to override the credentials() method:
/**
 * Get the login credentials from the request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
public function credentials(Request $request)
{
    $loginField = $this->getLoginField($request->input($this->username()));

    return [
        $loginField => $request->input($this->username()),
        'password' => $request->input('password'),
        'active' => true, // Customize additional conditions if needed
    ];
}

 

Customize the method as per your requirements. You can add extra conditions to the credentials array, such as checking for an active user or applying any other business logic.

By overriding the credentials() method in the User model, you have the flexibility to tailor the login process according to your application's needs.

Now, Laravel will use the overridden credentials() method from the User model instead of the default implementation.

That's it! With these advanced code snippets and modifications, your login system will now support login with both username and email options. Users can choose their preferred login method, enhancing the overall user experience.

Conclusion: In this article, we explored how to implement login with both username and email options using Laravel 10. By allowing users to choose their preferred login method, we create a more personalized and user-friendly experience. Remember to follow the step-by-step guide and update the necessary code snippets in your application.

I hope this advanced tutorial has been helpful in expanding your understanding of user authentication with Laravel 10. If you have any questions or comments, please feel free to leave them below. Stay tuned for more exciting Laravel tutorials and guides in the future!

References:

Laravel Documentation

 

]]>
<![CDATA[Generate Free SSL Certificates with Let's Encrypt in Laravel: A Step-by-Step Guide]]> https://sujipthapa.co/blog/generate-free-ssl-certificates-with-lets-encrypt-in-laravel-a-step-by-step-guide 0a68cd5a-5976-42a9-b23f-62d480c5028a 2023-06-29T18:00:00+05:45 Mr. Sujip Thapa Introduction: In this tutorial, we will guide you through the process of setting up a simple Laravel website that allows users to generate free SSL certificates using Let's Encrypt, PKIJS, and JSZIP. SSL certificates are essential for securing websites and enabling HTTPS. We will walk you through the steps of installing the necessary composer packages, creating routes and controllers, generating SSL certificates, and providing a download link for users.

Let's get started!

Step 1: Set up a Laravel project First, create a new Laravel project using the Laravel Installer or Composer. Open a command prompt and run the following command:

composer create-project --prefer-dist laravel/laravel your-project-name

 

Step 2: Install required composer packages Next, install the required composer packages by running the following commands in the project's root directory:

composer require letencrypt/letsencrypt
composer require pkijs/pkijs
composer require jszip/jszip


Step 3: Create a route and controller Create a route in the routes/web.php file to handle the SSL certificate generation. Open the file and add the following route definitions:

Route::get('/free-ssl-certificate', [SSLController::class, 'showCertificateForm']);
Route::post('/generate-ssl-certificate', [SSLController::class, 'generateCertificate'])->name('generate.certificate');
Route::get('/download-certificate/{file}', [SSLController::class, 'downloadCertificate'])->name('download.certificate');

 

Next, generate a controller by running the following command in the command prompt:

php artisan make:controller SSLController


Open the generated app/Http/Controllers/SSLController.php file and replace its contents with the provided code.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use PKIjs\CertificationRequest;
use LetEncrypt\Client;
use JSZip\JSZip;
use Illuminate\Support\Facades\Config;

class SSLController extends Controller
{
    public function showCertificateForm()
    {
        return view('ssl.certificate');
    }

    public function generateCertificate(Request $request)
    {
        $domain = $request->input('domain');

        // Perform SSL certificate generation logic using Let's Encrypt, PKIJS, and JSZIP

        // Example code to generate SSL certificate and create a zip file

        // 1. Generate CSR (Certificate Signing Request) using PKIJS
        $privateKey = Config::get('app.private_key');
        $csr = $this->generateCSR($domain, $privateKey);

        // 2. Use Let's Encrypt ACME client to generate the SSL certificate
        $letsEncryptClient = new Client();
        $certificate = $letsEncryptClient->generateCertificate($csr);

        // 3. Create a zip file with the certificate files using JSZIP
        $zip = new JSZip();
        $zip->addFile('certificate.crt', $certificate->getCertificate());
        $zip->addFile('private-key.key', $privateKey);
        $zipContent = $zip->generate();

        // 4. Store the zip file in the public directory
        $zipFileName = 'ssl_certificate.zip';
        file_put_contents(public_path($zipFileName), $zipContent);

        // 5. Redirect to the download page
        return redirect()->route('download.certificate', ['file' => $zipFileName]);
    }

    private function generateCSR($domain, $privateKey)
    {
        $pkijsCSR = new CertificationRequest();

        // Set subject common name (domain)
        $pkijsCSR->subject->typesAndValues[0] = new RelativeDistinguishedNames();
        $pkijsCSR->subject->typesAndValues[0]->type = '2.5.4.3'; // Common Name OID
        $pkijsCSR->subject->typesAndValues[0]->value->valueBlock->value = $domain;

        // Sign the CSR with the private key
        $pkijsCSR->sign(null, $privateKey, 'SHA-256');

        // Return the CSR in PEM format
        return $pkijsCSR->toSchema(true) . "\r\n";
    }

    public function downloadCertificate($file)
    {
        $filePath = public_path($file);

        if (file_exists($filePath)) {
            return response()->download($filePath)->deleteFileAfterSend();
        }

        abort(404);
    }
}


Step 4: Create views Create a new directory called ssl inside the resources/views directory. Inside the ssl directory, create a new file called certificate.blade.php with the following content:

@extends('layouts.app')

@section('title', 'Free SSL Certificate')

@section('content')
    <div class="container">
        <h1>Free SSL Certificate Page</h1>
        <p>Here you can generate your free SSL certificate.</p>

        <form method="POST" action="{{ route('generate.certificate') }}">
            @csrf

            <div class="form-group">
                <label for="domainInput">Enter your domain:</label>
                <input type="text" class="form-control" id="domainInput" name="domain" placeholder="example.com" required>
            </div>

            <button type="submit" class="btn btn-primary">Generate SSL Certificate</button>
        </form>

        @isset($zipFileName)
            <div class="mt-4">
                <h4>Download your SSL certificate:</h4>
                <a href="{{ route('download.certificate', ['file' => $zipFileName]) }}" class="btn btn-success">Download</a>
            </div>
        @endisset
    </div>
@endsection


Step 5: Update the .env file and configuration Open the .env file in the root directory of your Laravel project and add the following line:

PRIVATE_KEY=your_private_key_here

 

Replace your_private_key_here with the actual private key you want to use.

Open the config/app.php file and add the following line to the providers array:

'providers' => [
    // Other providers...
    JSZip\ServiceProvider::class,
],


Step 6: Update the welcome.blade.php file (optional) If you want to add a link to the SSL certificate generation page in the default Laravel welcome page, you can update the resources/views/welcome.blade.php file by adding the following code:

<div class="links">
    <a href="{{ url('/free-ssl-certificate') }}">Generate Free SSL Certificate</a>
</div>

 

That's it! You now have a Laravel website with a free SSL certificate generation feature. Feel free to customize the views, routes, and controllers according to your requirements.

Remember to run php artisan serve or use your preferred web server to access your Laravel application in the browser.

Conclusion

Congratulations! You have successfully implemented a simple Laravel website that allows users to generate free SSL certificates. Users can access the SSL certificate generation page, enter their domain name, and generate the certificate. The generated certificate is then bundled into a zip file that can be downloaded from the website.

By following this tutorial, you have learned how to integrate Let's Encrypt, PKIJS, and JSZIP into your Laravel project for SSL certificate generation. You can further enhance the website by adding additional features such as certificate expiration reminders or certificate management.

Remember to customize the views, routes, and controllers as per your project requirements. Happy coding!

]]>
<![CDATA[Unleashing the Power of Carbon in Laravel: A Comprehensive Guide]]> https://sujipthapa.co/blog/unleashing-the-power-of-carbon-in-laravel-a-comprehensive-guide 6e181679-0911-4acb-b10e-cd7837655c81 2023-06-29T16:30:00+05:45 Mr. Sujip Thapa Introduction: In Laravel, working with dates and times is a common requirement for many applications. Fortunately, the Carbon library provides a powerful and intuitive API that simplifies date manipulation and offers a wide range of features. In this comprehensive guide, we will explore the hidden gems of Carbon and demonstrate how they can elevate your Laravel application. We'll cover 14 essential features and more. Let's dive in!

1. Date Formatting Made Easy

Formatting dates is a breeze with Carbon. You can easily format dates using the format() method, providing you with complete control over the output. For example:

use Carbon\Carbon;

$date = Carbon::now();
$formattedDate = $date->format('Y-m-d H:i:s');
echo $formattedDate; // Output: 2023-06-29 14:30:00

 

2. Handling Timezones with Ease

Carbon simplifies working with different timezones. You can effortlessly switch between timezones using the timezone() method. For example:

use Carbon\Carbon;

$date = Carbon::now();
$date->timezone('Asia/Kathmandu');
echo $date; // Output: 2023-06-29 14:30:00 (Asia/Kathmandu timezone)

 

3. Localization for Multilingual Applications

Localizing date and time outputs is crucial for multilingual applications. Carbon allows you to easily set the desired locale using the setLocale() method. For example:

use Carbon\Carbon;

$date = Carbon::now();
Carbon::setLocale('en');
echo $date->isoFormat('LLLL'); // Output: Tuesday, June 29, 2023 2:30 PM

 

4. Working with Time Intervals

Carbon simplifies adding or subtracting time intervals from a date. You can easily manipulate dates by adding or subtracting minutes, hours, days, months, or years using the add and sub methods. Here's an example:

use Carbon\Carbon;

$date = Carbon::now();
$date->addMinutes(30);
$date->subHours(2);
$date->addMonth();
$date->addYears(2);

 

5. Calculating Date Differences

Carbon provides powerful methods to calculate the difference between two dates. You can determine the difference in minutes, hours, days, or even create custom intervals. Here's an example:

use Carbon\Carbon;

$date1 = Carbon::create(2023, 1, 1);
$date2 = Carbon::create(2023, 1, 2);

$diffInDays = $date1->diffInDays($date2);
$diffInHours = $date1->diffInHours($date2);
$customInterval = $date1->diff($date2)->format('%m months and %d days');

 

6. Advanced Date Manipulation with Carbon Period

Carbon's Period class allows you to work with time periods more intuitively. You can create a period using CarbonPeriod::create() and iterate over the range of dates. Here's an example:

use Carbon\Carbon;
use Carbon\CarbonPeriod;

$start = Carbon::create(2023, 6, 1);
$end = Carbon::create(2023, 6, 30);

$period = CarbonPeriod::create($start, $end);

foreach ($period as $date) {
    echo $date->format('Y-m-d') . "<br>";
}

 

7. Localization of Relative Time

Carbon's diffForHumans() method generates human-readable time differences. By setting the locale, Carbon automatically localizes the output. For example:

use Carbon\Carbon;

$date = Carbon::now()->subHours(2);
Carbon::setLocale('en');
echo $date->diffForHumans(); // Output: 2 hours ago

 

8. Working with Unix Timestamps

Carbon seamlessly integrates with Unix timestamps, allowing you to convert them to Carbon instances and vice versa. For example:

use Carbon\Carbon;

$timestamp = 1625000000; // Example Unix timestamp

$carbonDate = Carbon::createFromTimestamp($timestamp);
echo $carbonDate->format('Y-m-d H:i:s'); // Output: 2021-06-30 12:53:20

$unixTimestamp = $carbonDate->timestamp;
echo $unixTimestamp; // Output: 1625000000

 

9. Leap Year Check

Carbon provides an isLeapYear() method to determine if a given year is a leap year. For example:

use Carbon\Carbon;

$year = 2024;

if (Carbon::isLeapYear($year)) {
    echo "The year {$year} is a leap year.";
} else {
    echo "The year {$year} is not a leap year.";
}

 

10. Comparison with Null-Safe Methods

Carbon offers null-safe methods for comparing dates. You can use methods like equalTo(), notEqualTo(), greaterThan(), and lessThan() to compare dates and handle null values gracefully. Let's look at an example:

use Carbon\Carbon;

$date1 = Carbon::parse('2023-06-30');
$date2 = null;

// Using the equalTo() method to compare dates (including null check)
if (Carbon::equalTo($date1, $date2)) {
    echo "The dates are equal.";
} else {
    echo "The dates are not equal.";
}

 

In the above example, we have two dates: $date1 and $date2, where $date2 is set to null. By using the equalTo() method, Carbon compares the dates while handling the null value gracefully. If both dates are equal, it will output "The dates are equal." Otherwise, it will output "The dates are not equal."

This null-safe comparison approach ensures that your code doesn't encounter errors or unexpected behavior when dealing with null values in date comparisons.

You can use similar null-safe methods like notEqualTo(), greaterThan(), greaterThanOrEqualTo(), lessThan(), and lessThanOrEqualTo() to perform various comparisons while considering null values.

use Carbon\Carbon;

$date1 = Carbon::parse('2023-06-30');
$date2 = null;

// Using the greaterThan() method to compare dates (including null check)
if (Carbon::greaterThan($date1, $date2)) {
    echo "Date 1 is greater than Date 2.";
} else {
    echo "Date 1 is not greater than Date 2.";
}

 

In this example, we use the greaterThan() method to compare $date1 and $date2 while handling the null value. If $date1 is greater than $date2, it will output "Date 1 is greater than Date 2." Otherwise, it will output "Date 1 is not greater than Date 2."

By utilizing these null-safe comparison methods, you can ensure that your date comparisons are reliable and handle null values without causing unexpected errors or inconsistencies in your code.

11. Serialization and Unserialization

You can easily serialize and unserialize Carbon instances using PHP's serialize() and unserialize() functions, allowing you to store and retrieve Carbon objects effortlessly. Let's take a look at a short example that demonstrates how to serialize and unserialize Carbon objects:

use Carbon\Carbon;

$date = Carbon::now();

// Serialize the Carbon object
$serialized = serialize($date);

// Store the serialized data in a file or database

// Retrieve the serialized data
$serializedData = ''; // Assume we fetched the serialized data from storage

// Unserialize the data back into a Carbon object
$carbonDate = unserialize($serializedData);

echo $carbonDate->format('Y-m-d H:i:s');

 

In the example above, we start by creating a Carbon instance with the current date and time using Carbon::now(). We then serialize the $date object using the serialize() function, which converts the object into a string representation that can be stored or transferred.

Once the serialized data is stored (e.g., in a file or database), we can retrieve it when needed. In this example, we assume that we fetched the serialized data into the variable $serializedData. We then use the unserialize() function to convert the serialized data back into a Carbon object.

Finally, we can use the unserialized $carbonDate object to perform various operations. In this case, we format it using the format() method to display the date and time in the desired format.

Serialization and unserialization are particularly useful when you need to persist Carbon objects in storage or transfer them between different parts of your application. By leveraging serialization, you can seamlessly store and retrieve complex date and time data, ensuring consistency and accuracy in your Laravel application.

12. Modifying Date and Time Precision

Carbon allows you to include microseconds in the date/time representation using the micro() method. This provides finer-grained control over the precision of the time information.

use Carbon\Carbon;

$dateTime = Carbon::now();
$dateTimeWithMicroseconds = $dateTime->micro(123456);

echo $dateTimeWithMicroseconds->format('Y-m-d H:i:s.u');

 

In the example above, we create a Carbon instance representing the current date and time. By calling the micro() method with a value on the $dateTime object, we modify the precision of the time by including microseconds. The format() method with the u specifier is used to display the modified date and time with microseconds.

This feature is useful when high precision is required, such as in scientific calculations or when working with systems that rely on microsecond-level accuracy.

By leveraging Carbon's ability to modify the precision of date and time values, you can tailor the output to match the required level of precision in your Laravel application.

13. Customization of Relative Time Thresholds

Carbon allows you to customize the thresholds used for generating relative time strings. This means you can configure phrases like "moments ago," "a few seconds ago," etc., according to your application's requirements.

use Carbon\Carbon;

Carbon::setLocale('en');
Carbon::setToStringFormat('d/m/Y H:i:s');
Carbon::setHumanReadable(Carbon::THRESHOLD_WEEK, '%d days');
Carbon::setHumanReadable(Carbon::THRESHOLD_MONTH, '%d months');
Carbon::setHumanReadable(Carbon::THRESHOLD_YEAR, '%d years');

$date = Carbon::now()->subDays(5);

echo $date->diffForHumans(); // Output: 5 days ago

 

In the example above, we first set the locale to 'en' using setLocale() to ensure the relative time strings are displayed in English.

Next, we set the format for string representation using setToStringFormat(). In this case, we specify the format as 'd/m/Y H:i:s', which will be used when calling $date->tostring().

Then, we customize the thresholds for different time intervals using setHumanReadable(). We set the threshold for a week to display the number of days ('%d days'), for a month to display the number of months ('%d months'), and for a year to display the number of years ('%d years').

Finally, we create a Carbon instance representing a date five days ago. When we call diffForHumans() on $date, it will generate the relative time string using the customized thresholds, resulting in the output "5 days ago."

By customizing the relative time thresholds, you can adapt the language and format of the relative time strings to suit your application's needs, providing a more personalized and user-friendly experience.

14. Fluent Interface for Date Operations

Carbon provides a fluent interface that allows you to chain multiple date operations together. This results in cleaner and more readable code. For example:

use Carbon\Carbon;

$date = Carbon::now()->addDays(2)->subHours(1)->startOfDay();
$date = Carbon::now()->addDays(5)->subHours(5)->endOfDay();
$date = Carbon::now()->addYears(1)->startOfYear();
$date = Carbon::now()->addYears(1)->endOfYear();

 

Conclusion

Carbon is an indispensable library for handling date and time-related operations in Laravel. Its extensive feature set, including date formatting, timezone handling, localization, time calculations, and hidden features, empowers developers to work with dates efficiently. By leveraging Carbon's capabilities, you can streamline your code, improve user experience, and handle complex date scenarios effortlessly. Start integrating Carbon into your Laravel applications and unlock its true potential.

Remember to consult the official Carbon documentation for more details and explore its vast array of features.

Thank you for reading until the end! If you found this article valuable, we would appreciate it if you could share it with others.

Happy Coding!



]]>
<![CDATA[Simplify Your Code with Single Action Controllers in Laravel]]> https://sujipthapa.co/blog/simplify-your-code-with-single-action-controllers-in-laravel 72093677-6b6b-4667-8347-5bb458f543b1 2023-06-29T15:27:00+05:45 Mr. Sujip Thapa In the Laravel framework, controllers play a crucial role in organizing and handling the logic of your web application. Typically, a controller contains multiple actions, each responsible for handling a specific HTTP request. However, there are cases where a controller may only need to handle a single action, leading to the concept of Single Action Controllers.

Single Action Controllers provide a streamlined approach to organizing your application's code by dedicating a controller class to a single action. This architectural pattern promotes separation of concerns, improves code readability, and simplifies the maintenance of your Laravel application. In this article, we will delve into an advanced example of Single Action Controllers in Laravel to demonstrate their practical usage.

Prerequisites

Before we proceed, make sure you have a working Laravel installation along with its dependencies. You can refer to the official Laravel documentation for installation instructions.

Understanding Single Action Controllers

Traditionally, a Laravel controller consists of multiple methods, each representing a specific action that handles an incoming request. However, in certain scenarios, a controller might only require a single action. For instance, consider a simple API endpoint that fetches user details. In such cases, creating a dedicated controller with just one method becomes more intuitive and cleaner.

Setting Up the Project

Let's begin by setting up a new Laravel project. Open your terminal and navigate to the desired directory. Run the following command to create a new

laravel new single-action-example


Once the project is created, navigate to its directory:

cd single-action-example

 

Creating the Single Action Controller

To begin, generate a new Single Action Controller class using the make:controller Artisan command. Open your terminal and run the following command:

php artisan make:controller ContactFormController --invokable

 

This will create the ContactFormController class in the app/Http/Controllers directory.

Next, open the ContactFormController.php file and replace the code with the following:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;

class ContactFormController extends Controller
{
    public function __invoke(Request $request)
    {
        // Validate the form data
        $validatedData = $request->validate([
            'name' => 'required',
            'email' => 'required|email',
            'message' => 'required',
        ]);

        // Send an email with the form data
        Mail::to('[email protected]')->send(new \App\Mail\ContactFormMail($validatedData));

        return response()->json(['message' => 'Contact form submitted successfully']);
    }
}

 

In this example, the __invoke method handles the form submission. It first validates the form data using Laravel's validation rules. If the data is valid, it sends an email with the form data using the Mail facade. Finally, it returns a JSON response to indicate the successful submission.

Registering the Route

Now, let's define a route to access our Single Action Controller. Open the routes/web.php file and add the following route definition:

use App\Http\Controllers\ContactFormController;

Route::post('/contact', ContactFormController::class);

 

In this example, we're using the post method to define a route that matches the /contact URI pattern. When this route is accessed, it will invoke the ContactFormController class.

Testing the Single Action Controller

To test the contact form submission, you can create a simple HTML form in your view. Here's an example of how you can create a form in a Blade template:

<form action="/contact" method="POST">
    @csrf
    <label for="name">Name:</label>
    <input type="text" name="name" id="name">

    <label for="email">Email:</label>
    <input type="email" name="email" id="email">

    <label for="message">Message:</label>
    <textarea name="message" id="message"></textarea>

    <button type="submit">Submit</button>
</form>

 

Make sure to include the CSRF token by adding @csrf within the form. This is necessary to protect your application from cross-site request forgery attacks.

When the form is submitted, the data will be sent to the /contact route, which will invoke the ContactFormController and handle the form submission.

Remember to handle the email sending logic appropriately in your application. In this example, we used the Mail facade to send the email, assuming you have configured Laravel's mail settings correctly.

Conclusion

Single Action Controllers in Laravel provide a clean and focused approach to handling specific actions within your application. By dedicating a controller class to a single action, you can improve code organization, readability, and maintainability. Whether it's fetching user details or processing a contact form submission, Single Action Controllers offer a flexible and efficient way to structure your Laravel application.

Thank you for reading until the end! If you found this article valuable, we would appreciate it if you could share it with others.

 
]]>
<![CDATA[Stripe Payment Gateway Integration with Laravel]]> https://sujipthapa.co/blog/stripe-payment-gateway-integration-with-laravel c43f30fe-2a3d-418a-a4fe-11f93396273d 2022-04-23T19:35:00+05:45 Mr. Sujip Thapa Before through this blog, I've covered some of the popular payment gateways like PayPal, SecurePay, eSewa, etc.

This article is about the Stripe payment gateway to handle one-time payments with express checkout.

Introduction

Stripe is a payment service provider dealing with online payment processing for businesses around the globe.

Stripe offers different APIs for payment processing, which can be for one-time or subscription-based and recurring payments, and many more.

In this article, we're writing about the steps involved in integrating the express checkout.

Prerequisites

This article is for developers having an experience of mid-level or above with Laravel and PHP, as we won't cover the basics.

So we assume you already have experience with the below points.

  • Having good knowledge of PHP and Laravel, third-party APIs.
  • Understand composer and use packages and libraries with the composer in the Laravel projects.

Setup

We assume you already have a Laravel project installed in your machine, and you are ready to start the stripe integration.

Install official stripe-php-sdk from packagist with composer using the below command.

composer require stripe/stripe-php

Before diving into the code, go to the stripe dashboard and grab the API credentials needed for the integration.

The red border boxes shown in the above picture have the place to reveal the Publishable key and Secret key from the stripe dashboard under the Developers tab.

As of crafting this article, Laravel is at 9.* version.

You can then store the API credentials, depending on how you manage the API credentials via database or put them on the .env file.

Here we are going to use the .env file for this article.

STRIPE_PUBLISHABLE_KEY=YOUR-KEY-HERE
STRIPE_SECRET_KEY=YOUR-SECRET-KEY-HERE

Coding

Let's set up routes in Laravel where the user can access the order and continue through the payment flow.

<?php

use App\Http\Controllers\Payments\StripeController;
use Illuminate\Support\Facades\Route;

Route::get('/checkout/overview', [StripeController::class, 'overview'])
    ->name('checkout.overview');
Route::post('/checkout/payment/{order}/stripe', [StripeController::class, 'payment'])
    ->name('checkout.payment');
Route::get('/checkout/payment/{order}/approved', [StripeController::class, 'approved'])
    ->name('checkout.approved');
Route::get('/checkout/payment/{order}/cancelled', [StripeController::class, 'cancelled'])
    ->name('checkout.cancelled');

Now, we need a controller for handling the incoming request and responses from the route.

<?php
namespace App\Http\Controllers\Payments;

use App\Models\Order;
use App\Payments\Stripe;
use Illuminate\Routing\Controller;

class StripeController extends Controller
{
    public function overview()
    {
        // implement your own order overview here

        $order = Order::query()
            ->whereHas('items')
            ->with(['items'])
            ->paymentPending()
            ->first();

        return view('stripe', ['order' => $order]);
    }

    /**
     * @param $uuid
     */
    public function payment($uuid)
    {
        // implement your own order here

        $order = Order::paymentPending()
            ->where('uuid', $uuid)
            ->firstOrFail();

        return Stripe::initialize($order);
    }

    /**
     * @param $uuid
     */
    public function approved($uuid)
    {
        $order = Order::query()
            ->where('uuid', $uuid)
            ->firstOrFail();

        return Stripe::captured($order);
    }

    /**
     * @param $uuid
     */
    public function cancelled($uuid)
    {
        // render your cancelled page here

        return redirect()->route('checkout.overview')
            ->with('error', 'You have cancelled the payment. Therefore, the order has not been placed yet.');
    }
}

To interact with the database, we need a model. Here we have only created a single Order.php model and used the order items as static values to simplify the integration.

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->increments('id');
            $table->uuid();
            $table->string('transaction_id')->nullable();
            $table->float('amount')->unsigned()->nullable();
            $table->integer('payment_status')->unsigned()->default(0);
            $table->timestamps();
            $table->softDeletes();
        });
    }

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

You have to deal with the dynamic order items based on your application architecture.

<?php

namespace App\Models;

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

class Order extends Model
{
    use HasFactory;

    const PAYMENT_COMPLETED = 1;
    const PAYMENT_PENDING = 0;

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

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

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

    /**
     * @return \App\Models\OrderItem
     */
    public function items()
    {
        return $this->hasMany(OrderItem::class);
    }

    /**
     * @param $query
     * @return \Illuminate\Database\Query\Builder
     */
    public function scopePaymentPending($query)
    {
        return $query->where("{$this->table}.payment_status", self::PAYMENT_PENDING);
    }
}
<?php

namespace App\Models;

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

class OrderItem extends Model
{
    use HasFactory;

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

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

    /**
     * @var array
     */
    protected $fillable = ['uuid', 'order_id', 'name', 'sku', 'quantity', 'description', 'amount'];

    /**
     * @return \App\Models\Order
     */
    public function order()
    {
        return $this->belongsTo(Order::class);
    }
}

We've got a simple page to display a continue to the payment form.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com"></script>
<title>Payment</title>
</head>
<body>
<section class="container mx-auto mt-4 mb-4">
    <form action="{{ route('checkout.payment', [$order->uuid]) }}" method="POST">
        @csrf

        <button type="submit" class="bg-slate-900 text-white text-sm font-semibold h-10 px-6 rounded-md sm:w-auto">Continue to Payment</button>
    </form>
</section>
</body>
</html>

For an e-commerce website, this page needs to be an order overview with order items and payment gateways choices for users.

Finally, we have a dedicated class created to handle the initialization of stripe checkout.

<?php
namespace App\Payments;

use Exception;
use Stripe\Checkout\Session;
use Stripe\Stripe as Checkout;

class Stripe
{
    /**
     * @param $order
     */
    public static function initialize($order)
    {
        $static = new static;

        $checkout = Checkout::setApiKey(env('STRIPE_SECRET_KEY'));

        try {
            $session = Session::create([
                'client_reference_id' => $order->uuid,
                'billing_address_collection' => 'required',
                'line_items' => $static->toLineItems($order),
                'mode' => 'payment',
                'success_url' => $static->toReturnUrl($order),
                'cancel_url' => $static->toCancelUrl($order),
            ]);
        } catch (Exception $e) {
            return redirect()->route('checkout.overview')
                ->with('error', 'Unknown error occurred, please try again later.');
        }

        $order->update(['transaction_id' => $session->id]);

        if (filter_var($session->url, FILTER_VALIDATE_URL)) {
            return redirect()->to($session->url, 303);
        }

        return redirect()->route('checkout.overview')
            ->with('error', 'Unknown error occurred, please try again later.');
    }

    /**
     * @param $order
     */
    public static function captured($order)
    {
        $checkout = Checkout::setApiKey(env('STRIPE_SECRET_KEY'));

        try {
            $session = Session::retrieve($order->transaction_id);
        } catch (Exception $e) {
            return redirect()->route('checkout.overview')
                ->with('error', 'The payment was not successful, please retry again.');
        }

        if ($approved = $session && $session->payment_status == 'paid') {
            $order->update(['payment_status' => $order::PAYMENT_COMPLETED]);

            // take user to order placed page with message

            return redirect()->route('checkout.overview')
                ->with('success', 'Thank you for placing an order, the payment was captured successfully.');
        }

        return redirect()->route('checkout.overview')
            ->with('error', 'The payment was not successful, please retry again.');
    }

    /**
     * @param $order
     * @return array
     */
    public function toLineItems($order)
    {
        $stack = [];

        $static = new static;

        foreach ($order->items as $item) {
            $stack[] = [
                'price_data' => [
                    'currency' => 'USD',
                    'product_data' => [
                        'name' => $item->name,
                    ],
                    'unit_amount' => $static->toCentAmount($item->amount),
                ],
                'quantity' => $item->quantity,
            ];
        }

        return $stack;
    }

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

    /**
     * @param $order
     */
    public function toReturnUrl($order)
    {
        return route('checkout.approved', $order->uuid);
    }

    /**
     * @param $amount
     */
    public function toCentAmount($amount)
    {
        return (int) ($amount * 100);

        return number_format($amount, 2, '.', '');
    }
}


The request parameters are validated, and if everything is accurate, the request will be auto redirected to the payment gateway service to handle the actual payment.

After the payment, the user gets redirected back to the merchant website. The response received needs to be validated again to make sure the transaction was fully captured or not.

Webhook

We highly recommend handling the payment events via webhook. The webhook is a legit way to verify the payment status of an order.

The webhook is a far more secure way to verify the payment status of an order because it happens behind the scene on your server, as no user interaction is involved.

Conclusion

Thanks for following the article up to the end. I hope it was helpful to you.

Feel free to share this article on social media if you wish to share it in your circle.

]]>
<![CDATA[How to Get a Free SSL Certificate for Your Website]]> https://sujipthapa.co/blog/how-to-get-a-free-ssl-certificate-for-your-website d489e43b-cda8-4a86-b63e-6e48d86c6fad 2022-03-20T00:00:00+05:45 Mr. Sujip Thapa Having is an SSL certificate is becoming important nowadays to gain trust from the customer. Already, trusted browsers like Google Chrome have started to show Not Secure on the URL bar if the website does not have an SSL certificate installed on the server for the website.

For websites accepting an online payment from their customer by selling some product, having an SSL certificate is mandatory to protect their e-commerce transaction data.

Getting an SSL certificate in the past used to cost money.
However, some providers now offer it free to make the internet a safe place.

If it is for your blog or making a small business website, almost likely everyone wants to keep the server and website costs lower.

You get it free of cost. Why not use it and secure the data on your website to make the internet world safer for everyone?

How to get a free SSL certificate?

The answer is simple. There are multiple ways you can get a free SSL certificate in 2022.

We will take you through the step-by-step guide here to show you how you can get it and install it on your server.

Prerequisites

I usually write down the article for Laravel developers in this blog. Laravel framework requires a server that can be easily accessible through an SSH connection.

I use Digital Ocean for almost all of my Laravel applications. So I heavily recommend Laravel developers to use it as it is pretty good for the Laravel framework to host with them.

  • Have a VPS server like Digital Ocean, Linode, Vultr, etc.
  • The example I will use it with the Nginx server.
  • A Cloudflare account setup.

So, if you already have a site and want an SSL certificate to be installed and configured, then you are at the right place here.

Step 1 - Generating an Origin CA TLS Certificate from Cloudflare

Cloudflare offers a free TLS certificate signed by them to install on your Nginx server.

The primary use of this TLS certificate is to establish a secure connection between your server and Cloudflare's servers.

Log in to your Cloudflare account in any secure web browser. Go to select your particular domain. Then, navigate to the SSL/TLS section on the dashboard.

Right there, navigate to the Origin Server tab, and click on the Create Certificate button.

Keep the selected option Generate private key and CSR with Cloudflare by default.

Now, click Create button, and on the next page, you will see the Origin Certificate and Private key.

You need to copy the generated content on those two particular keys and save it on your server.

Due to security reasons, the Private key will not show again, copy both of the keys to your server and make sure you keep the backup before you decide to click Ok.

We will take the /etc/ssl directory to keep the origin certificate and the private key files on the server.

The folder already exists on the server. Go to copy the key on the Origin Certificate and save it to /etc/ssl/cert.pem

sudo nano /etc/ssl/cert.pem


Similarly, save the private key.

sudo nano /etc/ssl/key.pem

Note: Be sure to avoid blank lines when copying the keys for the relevant certificates.

Now that you copied the key and certificate files to your server, you need to update the Nginx configuration to use them.

Step 2 - Pointing the Origin CA Certificate to Nginx

You need to update the Nginx configuration for your site to use the origin certificate and private key to secure the connection between Cloudflare’s servers and your server.

Next, you need to make sure the UFW allows HTTPS traffic.

You need to Enable Nginx Full, which will open both port 80 (HTTP) and port 443 (HTTPS)

sudo ufw allow 'Nginx Full'


Now reload UFW

sudo ufw reload


Now, you need to check the new rules are allowed and that UFW is active.

sudo ufw status

The output looks like below.

Output
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)           ALLOW       Anywhere (v6)


Now you are ready to adjust your Nginx server block. Nginx creates a default server block during installation. Remove it if it still exists, as you’ve already configured a custom server block for your domain.

sudo nano /etc/nginx/sites-available/site_domain

The basic configuration on the file should look like this.

server {
        listen 80;
        listen [::]:80;

        root /var/www/site_domain/html;
        index index.html index.htm index.nginx-debian.html;

        server_name site_domain www.site_domain;

        location / {
                try_files $uri $uri/ =404;
        }
}

Here, the job is to modify the Nginx configuration file to follow the following instructions below.

  • Listen to port 80 and redirect all requests to use HTTPS.
  • Listen to port 443 and communicate with the certificate files added in the previous stage.

The file modification looks like below.

server {
    listen 80;
    listen [::]:80;
    server_name site_domain www.site_domain;
    return 302 https://$server_name$request_uri;
}

server {

    # SSL configuration

    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate         /etc/ssl/cert.pem;
    ssl_certificate_key     /etc/ssl/key.pem;

    server_name site_domain www.site_domain;

    root /var/www/site_domain/html;
    index index.html index.htm index.nginx-debian.html;


    location / {
            try_files $uri $uri/ =404;
    }
}

Finally, save the file and exit from the edit mode.

Now, test the Nginx configuration syntax to avoid errors.

sudo nginx -t

After all, when there are no syntax errors. You can reload the Nginx configuration to enable the changes on the server.

sudo systemctl restart nginx

Enable up Full (strict) mode on the SSL/TLs section on the dashboard.

Go to the Overview tab under SSL/TLS section on the dashboard.

By setting this up, Cloudflare always encrypts the connection between your server and their servers.

Finally, visit your website to validate the setup is working as intended.

The browser should report that the site is secure.

Step 3 - Set up Authenticated Origin Pulls

We need to set up Authenticated Origin Pulls to validate the origin server taking the Cloudflare rather than others.

We are validating the certificate from Cloudflare here.

Download the Cloudflare certificate signed by a CA from Cloudflare’s documentation.

Create this certificate in the path /etc/ssl/cloudflare.crt and save, avoid the blank lines on the file.

Again, update your Nginx configuration to use TLS Authenticated Origin Pulls to validate the requests coming to the server.

Open the configuration file for your domain.

sudo nano /etc/nginx/sites-available/site_domain

Add the ssl_client_certificate and ssl_verify_client directives as shown below and save the file.

. . .

server {

    # SSL configuration

    ...
    
    ssl_client_certificate /etc/ssl/cloudflare.crt;
    ssl_verify_client on;

    . . .

Verify the Nginx configuration and reload the server to enable the changes.

sudo nginx -t

sudo systemctl restart nginx

Finally, go to SSL/TLS section in the Cloudflare dashboard, open the Origin Server tab and toggle the Authenticated Origin Pulls option.

Visit your domain to validate your changes and to test the changes toggle between the Authenticated Origin Pulls option in the Cloudflare dashboard.

Conclusion

Thanks for reading up to the end. We hope this article helped to secure another website to the internet world.

Happy Coding!

]]>
<![CDATA[Manage multiple PHP versions in Linux: 2022 edition]]> https://sujipthapa.co/blog/manage-multiple-php-versions-in-linux-2022-edition 9c61e7ef-90d4-4b6e-bf94-1dfc97609dbf 2022-03-12T00:00:00+05:45 Mr. Sujip Thapa Do you also have the below question in your mind?

How to manage multiple PHP versions in your local development environment?

If you are having trouble managing multiple PHP versions in your local development environment, then you're at the right place to get the best solution for your issue.

If you are over Linux like me, it is possible to install multiple PHP versions to run one version at a time with your web server.

You may need to switch between them as per your requirement with your project that supports the particular PHP version.

Assume that you have installed multiple PHP versions (let's take a few, PHP 5.6, 7.0, 7.2, 7.4, 8.0, and 8.1).

Also, keep in mind that you can only set a single version as a default one to run with your web server (Nginx or Apache).

It is a challenge to become a modern web developer, as one should stay updated with new technologies and have to play with them every day.

Besides that, if a developer handles packages, frameworks, and libraries and their client's projects, they need to have a supported setup to apply new updates and fixes for their works.

Let's dive into the actual workflow on how you can manage multiple PHP versions in your machine.

Open up your favorite code editor and copy and paste the snippet below.

phpswap () {
    if [[ -z "$1" ]]; then
        echo "You did not specify a version (5.6, 7.0, 8.0, 8.1)"
    elif [[ "$1" == "5.6" ]]; then
        echo "Switching to PHP 5.6"
        sudo ln -sf /usr/bin/php5.6 /etc/alternatives/php
        
        sudo a2dismod php7.0
        sudo a2dismod php8.0
        sudo a2dismod php8.1
        
        sudo a2enmod php5.6
        
        sudo systemctl restart apache2

    elif [[ "$1" == "7.0" ]]; then
        echo "Switching to PHP 7.0"    
        sudo ln -sf /usr/bin/php7.0 /etc/alternatives/php
        
        sudo a2dismod php5.6
        sudo a2dismod php8.0
        sudo a2dismod php8.1
        
        sudo a2enmod php7.0

        sudo systemctl restart apache2

    elif [[ "$1" == "8.0" ]]; then
        echo "Switching to PHP 8.0"
        sudo ln -sf /usr/bin/php8.0 /etc/alternatives/php
        sudo a2dismod php5.6
        sudo a2dismod php7.0
        sudo a2dismod php8.1

        sudo a2enmod php8.0
        sudo systemctl restart apache2

    elif [[ "$1" == "8.1" ]]; then
        echo "Switching to PHP 8.1"
        sudo ln -sf /usr/bin/php8.1 /etc/alternatives/php
        sudo a2dismod php5.6
        sudo a2dismod php7.0
        sudo a2dismod php8.0

        sudo a2enmod php8.1
        sudo systemctl restart apache2
    else
        echo "You did not specify a version (5.6, 7.0, 8.0, 8.1)"
    fi
}

The script has support for multiple versions of PHP and has the way to run it with a single command and easy to remember bash function that you can run on your terminal whenever you need it.

Apply the relevant changes to the actual version you want to use in your environment.

After all, you need to apply it to the run time of your system somewhere.

Open up your terminal and hit the below command.

nano ~/.bashrc

Once the edit mode appears on your screen, paste the script somewhere in the file and save it.

Log off your system and login in again, or reboot if necessary, then you are ready to go with it.

Usage

Open up your terminal and check your current php version with php -v

After showing the current version, you can hit phpswap, and it alerts you with options you have set up previously with the bash function.

In my case, it shows like this below.

➜ phpswap
You did not specify a version (7.0, 8.0, 8.1)

Then, hit phpswap 8.1, and it does everything else for you, and after it finishes running, it's ready with another version activated.

Easy right?

Conclusion

Thanks for reading this post up to the end.

If the article was helpful to you, share it with your circle to help spread the good content.

If you have any feedback feel free to submit the contact us form, and I will get your message right into my inbox.

Happy Coding!

]]>
<![CDATA[Laravel Jetstream: Quick Review]]> https://sujipthapa.co/blog/laravel-jetstream-quick-review d6f48ab2-eb68-4f8b-b828-9c1c162212f1 2020-09-05T21:40:00+05:45 Mr. Sujip Thapa The official team from Laravel has announced the next major version, Laravel 8.0, to release on the 8th of September 2020.

Along with this significant release, few first-party packages, including Laravel Jetstream, Fortify are also releasing to join the Laravel ecosystem.

The author of Laravel initially announced Laravel Jetstream at Laracon Online 2020 with a complete walk-through featuring the new goodies for Laravel 8.

The packages releasing will also be the next free and open-source packages by the Laravel team.

What is Jetstream?

Laravel Jetstream is a brand new application scaffolding package, which comes with some convenient features, and gives a perfect start point for your next Laravel 8 project.

In short, Jetstream includes features like login, registration, email verification, 2FA, session management, API support, and teams support.

The past versions of Laravel stood with the Bootstrap framework for a long time. As of now, with the extensive popularity of Tailwind CSS, Jetstream is also designed using Tailwind CSS, which is a utility-first CSS framework.

Jetstream offers two different flavors to choose between Livewire, Inertia, which allows developers to use their preferred ones.

Read: How to Deploy Laravel Application to VPS Server

Usage

$ php artisan jetstream:install livewire

// or

$ php artisan jetstream:install inertia

For teams feature, add --teams option on the artisan command.

$ php artisan jetstream:install livewire --teams

Once you install your Jetstream stack, remember to compile your frontend assets using the command below.

npm install && npm run dev

Through, Laravel Installer

The team has recently released the v4 version of the Laravel installer to support Jetstream's installation via the installer. Make sure you upgrade your composer package if you have it already installed.

$ laravel new your-next-project-name --jet

Features Overview

You will be able to see this beautifully designed dashboard view once you set it up correctly.

Laravel Jetstream Dashboard

User Profile

Jetstream comes with a profile edit feature for the user, and the forms fields are necessary standard fields, including profile photo for the user registration, so you are always free to customize those files for your use case.

Security

Jetstream brings features like change password, enable 2FA, revoke authenticated browser sessions under the account. These are the regular features that most do not need customization. However, if you would like to modify, you could change the relevant code in action classes under the app/Actions/Fortify directory.

Email Verification

Jetstream out of the box comes with an email verification feature. If you decide to enable this feature, once a user registers for your project, they will be taken to a screen to click the verification link sent to their email used during the registration.

To enable this feature, scroll to features array within the fortify.php config file, as you have full control to edit the file.

Account Deletion

Jetstream introduces an action panel that allows the user to delete their account entirely. You have full control over the code to customize for the backend logic under App\Actions\Jetstream classes.

API Support

Jetstream comes with first-party integration with Laravel Sanctum for API support. If you wonder how the Laravel Sanctum works, follow the official docs for full information.

Inside Jetstream, with Sanctum's use, each user can generate multiple API tokens for their account.

These tokens are used in API call to authorize the access to the application via the API call.

You must enable this feature as well under the jetstream.php config file.

'features' => [
     Feature::api(),
],

Teams

Jetstream packs a great feature as teams, as many people are going to use it in their next project. It gives a great way to manage a team, create a team, switch between the team, even manage roles, permission to the member, etc.

The team feature also needs to be enabled via the jetstream.php config file.

'features' => [
    Feature::teams(),
],

To read in-depth about the teams feature, and use case and customization, follow the official documentation.

While using any of the two choices from Livewire or Inertia, you can still utilize many packed features with your entire application.

Those features include components, models, forms, validation helpers. If your application context matches to use them, you are free to take them and never write your own again. To publish those files, use the below command.

$ php artisan vendor:publish --tag=jetstream-views

Closing

In the Laracon Online talk, Taylor mentioned, Laravel team has almost offered the non-billing part of Laravel Spark as Laravel Jetstream, which is entirely free for everyone now.

Conclusion

Thanks for staying up to the end of the article. If you think it is worth reading, share this article in your circle and follow me on Twitter for more updates.

]]>
<![CDATA[How to Deploy Laravel Application to VPS Server]]> https://sujipthapa.co/blog/how-to-deploy-laravel-application-to-vps-server 18c05ef6-7be4-44c4-865c-971842edcfa2 2020-08-30T16:58:00+05:45 Mr. Sujip Thapa In the previous article, I wrote about how we can utilize an Envoy package, and gave a walk through to the Envoy use cases, and explained who it makes a lot easier on deploying your next Laravel application to the remote server.

Throughout this article, you are going to learn how you can deploy your laravel apps, with the precise use of Laravel Envoy, including how you can efficiently deploy your updates once you make changes in your version control platforms like Github, Bitbucket.

Read: Laravel Envoy to Deploy your Application to Hosting Server

Also, this article will cover a few more other ideas about the deployment process for Laravel application on different platforms.

Let's get started step by step about the deployment on how you can start deploying your Laravel projects now using the latest Laravel version.

Prerequisites

Before you start, you need to have a deployment-ready server with everything up and running to launch your next Laravel app, as we are not going to deal with any server set up stuff here.

However, you will need to make sure your server meets the requirements based on the Laravel version you are using.

I regularly follow the Digital Ocean technical articles to study about server set up things, as they have an outstanding community with great content.

Set up SSH

Now, the initial step is to generate an SSH key on your server and add it back to VCS platforms like Github, Bitbucket, or whatever you use.

I primarily use Linux Ubuntu flavor to host my apps, so this article takes reference based on the Linux Ubuntu but also would work for most of the Linux flavors.

Access your server via console using SSH connection and follow the command below.

$ ssh-keygen -t rsa -b 4096 -C "[email protected]"

Copy that SSH key based on where you save it.

On Linux, you can cat the contents like below.

$ cat<~/.ssh/id_rsa.pub

After adding it to the version control platform, you can test your SSH connection like below.

$ ssh -T [email protected]

If you are a beginner and need more assistance, I suggest you read this article by Bitbucket, which has in-depth information on it.

Set up Composer

For those who want to set up composer, follow the steps below.

$ cd ~
$ mkdir bin
$ cd bin
$ curl -sS https://getcomposer.org/installer | php

Set up Envoy

// Envoy.blade.php

@setup
    $branch = isset($branch) ? $branch : "master";
    $serverUser = 'deployer';
    $rootDirectory = '~/home/' . $serverUser;

    $server = $serverUser . '@server_ip';
@endsetup

@servers(['production' => $server])

@task('clone', ['on' => 'production'])
    echo '>> cd {{ $rootDirectory }}'
    cd {{ $rootDirectory }}

    echo '>> mkdir {{ $rootDirectory }}/project'
    mkdir {{ $rootDirectory }}/project

    echo '>> chmod 755 {{ $rootDirectory }}/project'
    chmod 755 {{ $rootDirectory }}/project

    echo '>> cd {{ $rootDirectory }}/project'
    cd {{ $rootDirectory }}/project

    echo '<< git clone [email protected]:username/project.git deploy'
    git clone [email protected]:username/project.git deploy
@endtask

@task('environment', ['on' => 'production'])
    echo '>> cd {{ $rootDirectory }}/project/deploy'
    cd {{ $rootDirectory }}/project/deploy

    echo '<< cp .env.example .env'
    cp .env.example .env

    echo '>> SSH to your server, paste your valid .env credentials & save them. Then run envoy run post-deploy'
@endtask

@task('composer-install', ['on' => 'production'])
    echo '>> cd {{ $rootDirectory }}/project/deploy'
    cd {{ $rootDirectory }}/project/deploy

    echo '<< /home/{{ $serverUser }}/bin/composer.phar install --prefer-dist --no-scripts --no-dev -q -o'

    /home/{{ $serverUser }}/bin/composer.phar install --prefer-dist --no-scripts --no-dev -q -o
@endtask

@task('composer-update', ['on' => 'production'])
    echo '>> cd {{ $rootDirectory }}/project/deploy'
    cd {{ $rootDirectory }}/project/deploy

    echo '<< /home/{{ $serverUser }}/bin/composer.phar dump -o && php artisan optimize'

    /home/{{ $serverUser }}/bin/composer.phar dump -o && php artisan optimize

@endtask


@task('migrate', ['on' => 'production'])
    echo '>> cd {{ $rootDirectory }}/project/deploy'
    cd {{ $rootDirectory }}/project/deploy

    php artisan migrate --force;
@endtask

@task('symlink', ['on' => 'production'])
    echo '<< ln -s /home/{{ $serverUser }}/project/deploy/public /var/www/html'

    ln -s /home/{{ $serverUser }}/project/deploy/public /var/www/html
@endtask

@task('deploy-changes', ['on' => 'production'])
    echo '>> cd {{ $rootDirectory }}/project/deploy'
    cd {{ $rootDirectory }}/project/deploy

    echo '>> git checkout {{ $branch }}'
    git checkout {{ $branch }}

    echo '<< git pull --rebase'
    git pull --rebase
@endtask

@story('deploy', ['on' => 'production'])
    setup
    environment
@endstory

@story('post-deploy', ['on' => 'production'])
    composer-install
    composer-update
    migrate
    symlink
@endstory

@story('update')
    deploy-changes
    composer-update
    migrate
@endstory

Deploy

You will only need to run this task when you launch your application for the initial phase on the remote server.

$ envoy run deploy

The initial task finishes with a message telling about preparing the .env file.

Set up .env

Prepare the .env configuration with the necessary credentials for your application to run on the server.

SSH to the server, copy & paste to the server where .env file lies, and save it.

Final Deploy

Once you save the server .env file with correct credentials, then run another command.

$ envoy run post-deploy

After the first successful deploy, you cannot run the same process to deploy your future updates on the repository. Now, your future job will be only pulling new changes or installing more new packages if needed.

$ envoy run update

Finishing

It is a reasonable question to come in your mind, this deployment script is likely to break the application, and it could take your application to go through downtime either on any of the deployment.

I agree with the above statement, as it is not a zero-downtime deployment solution, as the article was focused generally on the way of only deploying your code to the server.

As being a developer, having some server related knowledge is almost necessary for every day to work with fun on any application, no matter the size and infrastructure required to scale it.

People these days talk about zero-downtime deployment and have seen a lot of articles about it, and there are commercial platforms available to handle the concern.

If you want to deploy your code to your server without worrying about downtime, choose the service like Laravel Envoyer, which is a commercial product from the Laravel ecosystem.

Even more, you do not want to worry about managing servers and want to go hassle-free with auto-scaling your apps, use Forge, and Vapor a serverless platform.

Using those managed services, you need to pay for both your server cost as well as the service cost every month, using those platforms helps your team to focus more on the work rather than suffering from deployments and servers.

Also, as a free solution for zero-downtime deployment strategy, there is an open-source package built by Papertank.

Tip: People ask a lot of questions about how to deploy Laravel to shared hosting frequently. But, my answer to this is, Laravel works great on VPS service providers like Digital Ocean, Vultr Linode, AWS, or any Custom VPS, etc.

Conclusion

Thanks for being up to the end of the article. Please share this article to your circle, if you enjoyed reading it.

Follow me on Twitter for more updates or visit the blog more often to view new articles at a later date.

]]>
<![CDATA[Laravel Envoy to Deploy your Application to Hosting Server]]> https://sujipthapa.co/blog/laravel-envoy-to-deploy-your-application-to-hosting-server 89a47491-4b3f-4271-aed2-9c69206056cd 2020-08-29T16:30:10+05:45 Mr. Sujip Thapa Laravel Envoy is an open-source package to efficiently deploy your apps to the server or even run tasks in the local environment. It is one of the most helpful tools in the Laravel ecosystem that every developer utilize every day. This tool provides a precise, minimal syntax for defining any tasks you run on your remote servers.

By making the use of Blade style syntax, you can quickly set up tasks for deployment actions, run inbuilt Artisan commands, and many more. Envoy currently has support for Mac, Linux only.

Similar to any other composer packages you install with your Laravel application, Laravel Envoy also follows the same process.

If you don’t already have it installed, then follow the below simple command.

$ composer global require laravel/envoy

After a successful installation, now your turn is to start to set up your tasks needed to deploy your application. Test your set up like below, shows the screen like below with more other information.

$ envoy -v

Laravel Envoy 2.3.1

You can initialize the Envoy.blade.php file by using the command below.

$ envoy init [email protected]

Adding Tasks

Before starting to write any tasks, define a new file Envoy.blade.php in the root of your project directory.

@servers(['web' => '[email protected]'])

@task('deploy')
    cd /path/to/site
    git pull origin master
@endtask

As you can see above, the @servers directive in a single line, referencing the array of servers to point around when you deploy the application.

The @task directive keeps the bash script to run on your server in the deployment process. For the initialization of variables necessary for the deployment script, you can also use the @setup directive to assign variables based on your application structure.

If your project logic requires to verify certain things, and that requires to include any PHP files in between the deployment process, you could always use the @include directive at the top of your Envoy.blade.php file.

@include('vendor/autoload.php')

@setup
    $branch = isset($branch) ? $branch : "main";
    $env = isset($env) ? $env : "production";
    $account = isset($account) ? $account : "sujipthapa";
@endsetup

@servers(['local' => '127.0.0.1', 'production' => '192.168.1.1'])

Define Variables

As per your requirement, Laravel Envoy also supports passing variables with values to your tasks within the command line.

$ envoy run deploy --branch=main --account=sujipthapa

Adding Stories

Adding @stories directive helps you to group up your small tasks into a flow of actions, which gives you a way to maintain the sequence to execute scripts on your server in your deployment process.

For instance, you may add a story label as a post-deploy, which groups your tasks like git pull, composer install, migration, etc.

Notify

You may instantly send a notification to different channels like Telegram, Discord, Slack right inside from the Envoy setup file.

It just requires to define your simple setup for that process like below.

@finished
    @slack('slack-webhoo-url', '#deploy')
@endfinished

Overview

As from the above in-depth run down, below can you can try to understand the Envoy set up with few useful directives and usual tasks defined for your to as quick overview.

@setup
    $branch = isset($branch) ? $branch : "main";
    $env = isset($env) ? $env : "production";
    $account = isset($account) ? $account : "sujipthapa";
@endsetup

@servers(['local' => '127.0.0.1', 'production' => '192.168.1.1'])

@task('cd_server_path', ['on' => 'production'])

    echo ">> cd /home/{{ $account }}/project"

    cd /home/{{ $account }}/project

@endtask


@task('git_commands')

    echo ">> git checkout {{ $branch }} ..."

    git checkout {{ $branch }}

    echo ">> git pull origin {{ $branch }} --force ..."

    git pull origin {{ $branch }} --force

@endtask

@task('run_composer')

    echo ">> composer depencencies..."

    composer install --no-interaction --quiet --no-dev --prefer-dist --optimize-autoloader

@endtask

@task('run_migrate')
    php artisan migrate --env={{ $env }} --force --no-interaction
@endtask

@story('deploy')
    cd_server_path
    git_commands
    run_composer
    run_migrate
@endstory

Finally, your turn is to run the deploy command.

$ envoy run deploy

I suggest you follow the official Laravel Envoy docs as more options and new features roll out every single day, as it is an open-source project, so many people contribute and use the ecosystem.

Conclusion

Thanks for reaching up to the end of the article. Please share this article to your circle, if you loved reading it.

Follow me on Twitter for more updates or visit the blog more often to view new articles at a later date.

]]>
<![CDATA[Hacktoberfest 2020 — help growing open source projects]]> https://sujipthapa.co/blog/hacktoberfest-2020-help-growing-open-source-projects 85b039d3-781d-496e-bbec-7bc1ad76afc2 2020-08-23T13:14:32+05:45 Mr. Sujip Thapa Hacktoberfest is landing this October 2020 again like in the previous years, which is a month-long celebration of contribution to open-source projects maintained by developers around the world.

Hacktoberfest is a great event, which encourages participation in the open-source community to contribute, as the open-source community is growing bigger every year.

If you have never known about Hactoberfest before or never contributed before, be the one to complete the 2020 challenge and earn a limited-edition T-shirt this year.

I started my Hactoberfest participation since 2016 and trying to celebrate this event every year on the open-source contribution. I've contributed to a lot of projects in the past, and try to give more whenever possible.

Read : Hacktoberfest 2016, Hacktoberfest 2018

From the beginning of my developer carrier in 2013, there has been tremendous growth in the open-source community, with several popular open-source projects landing every day. This growing community is becoming a great motivation to newbies to learn new things and talent developers to become great creators of their platform to work full or part-time with the passion-filled in them.

Since the launch of the github sponsors platform, developers who are maintaining open-source projects can now get direct funding support from the community, businesses relying on the project would show interest in supporting them. So, developers receiving such support from the platform encourages them to work with full passion and generate more new ideas everyday.

Hacktoberfest is open to everyone in the global community. This event helps in growing yourself and the community, as you get a chance to learn new things, and open-source projects get better with your contribution.

 So, better try and contribute weather, you are a beginner in development or a long-time contributor, your support helps to grow the community.

Read the guidelines that match your position (maintainer, contributor, company) in the official Hacktoberfest website.

I would recommend you to read the open-source contribution guidelines before you start contributing to any project.

Thanks to Digital Ocean for organizing this great event every year!

Happy Contributing!

]]>
<![CDATA[eSewa Payment Gateway Integration with Laravel]]> https://sujipthapa.co/blog/esewa-payment-gateway-integration-with-laravel ed28d640-1cb5-4508-94dc-a9d5f3826431 2019-03-16T15:17:00+05:45 Mr. Sujip Thapa In this article, I will show you how to integrate eSewa API v1.0 with the Laravel framework. eSewa is Nepal's first popular online payment gateway helping businesses to accept their payments online.

As like a few other payment gateways integration with PHP, I've recently worked on a new package for eSewa, which is a used in the majority of eCommerce platform in Nepal. I got a chance to work for the client to accept their payment online.

I have first explored on google to know if anyone has worked to create a better PHP package for the gateway. I found there is no any good package for the payment gateway. So decided to create a new reusable library with unit testing. I always try to share the real-time example and solution for the working developers through this blog.

Below, I'll cover complete steps needed to integrate eSewa API using the package built on top of Omnipay library.

Before diving into the code, let me first share my experience on how to grab API keys for development, production environment.

While I went through their API docs, there are no test credentials given in there. eSewa API credentials are available to the merchant after contacting the eSewa office via phone call or email support.

Now, let's jump into the code after you have grabbed the API credentials to make API calls by using the package.

Coding

The library is framework agnostic, can be used in any custom project, not only with Laravel. Here, we're going to integrate it on top of the Laravel framework. If you're a Laravel developer, hopefully, you will benefit from it to save your time.

Also, read:
Omnipay PayPal Integration
PayPal Instant Payment Notification

Include the package with your project with a composer command.

composer require league/omnipay sudiptpa/omnipay-esewa

Routes

Route::get('/checkout/payment/esewa', [
    'name' => 'eSewa Checkout Payment',
    'as' => 'checkout.payment.esewa',
    'uses' => 'EsewaController@checkout',
]);

Route::post('/checkout/payment/{order}/esewa/process', [
    'name' => 'eSewa Checkout Payment',
    'as' => 'checkout.payment.esewa.process',
    'uses' => 'EsewaController@payment',
]);

Route::get('/checkout/payment/{order}/esewa/completed', [
    'name' => 'eSewa Payment Completed',
    'as' => 'checkout.payment.esewa.completed',
    'uses' => 'EsewaController@completed',
]);

Route::get('/checkout/payment/{order}/failed', [
    'name' => 'eSewa Payment Failed',
    'as' => 'checkout.payment.esewa.failed',
    'uses' => 'EsewaController@failed',
]);

Model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

/**
 * Class Order
 * @package App
 */
class Order extends Model
{
    const PAYMENT_COMPLETED = 1;
    const PAYMENT_PENDING = 0;

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

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

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

Esewa

<?php

namespace App;

use Exception;
use Omnipay\Omnipay;

/**
 * Class Esewa
 * @package App
 */
class Esewa
{
    /**
     * @return \SecureGateway
     */
    public function gateway()
    {
        $gateway = Omnipay::create('Esewa_Secure');

        $gateway->setMerchantCode(config('services.esewa.merchant'));
        $gateway->setTestMode(config('services.esewa.sandbox'));

        return $gateway;
    }

    /**
     * @param array $parameters
     * @return $response
     */
    public function purchase(array $parameters)
    {
        try {
            $response = $this->gateway()
                ->purchase($parameters)
                ->send();
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }

        return $response;
    }

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

        return $response;
    }

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

    /**
     * @param $order
     */
    public function getFailedUrl($order)
    {
        return route('checkout.payment.esewa.failed', $order->id);
    }

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

Controller

<?php

namespace App\Http\Controllers;

use App\Esewa;
use App\Order;
use Exception;
use Illuminate\Http\Request;

/**
 * Class EsewaController
 * @package App\Http\Controllers
 */
class EsewaController extends Controller
{
    /**
     * @param Request $request
     */
    public function checkout(Request $request)
    {
        $order = Order::findOrFail(mt_rand(1, 20));

        return view('esewa.checkout', compact('order'));
    }

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

        $gateway = with(new Esewa);

        try {
            $response = $gateway->purchase([
                'amount' => $gateway->formatAmount($order->amount),
                'totalAmount' => $gateway->formatAmount($order->amount),
                'productCode' => 'ABAC2098',
                'failedUrl' => $gateway->getFailedUrl($order),
                'returnUrl' => $gateway->getReturnUrl($order),
            ], $request);

        } catch (Exception $e) {
            $order->update(['payment_status' => Order::PAYMENT_PENDING]);

            return redirect()
                ->route('checkout.payment.esewa.failed', [$order->id])
                ->with('message', sprintf("Your payment failed with error: %s", $e->getMessage()));
        }

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

        return redirect()->back()->with([
            'message' => "We're unable to process your payment at the moment, please try again !",
        ]);
    }

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

        $gateway = with(new Esewa);

        $response = $gateway->verifyPayment([
            'amount' => $gateway->formatAmount($order->amount),
            'referenceNumber' => $request->get('refId'),
            'productCode' => $request->get('oid'),
        ], $request);

        if ($response->isSuccessful()) {
            $order->update([
                'transaction_id' => $request->get('refId'),
                'payment_status' => Order::PAYMENT_COMPLETED,
            ]);

            return redirect()->route('checkout.payment.esewa')->with([
                'message' => 'Thank you for your shopping, Your recent payment was successful.',
            ]);
        }

        return redirect()->route('checkout.payment.esewa')->with([
            'message' => 'Thank you for your shopping, However, the payment has been declined.',
        ]);
    }

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

        return view('esewa.checkout', compact('order'));
    }
}

HTML

@extends('default')

@section('content')
    <div class="title m-b-md">
        eSewa Checkout
    </div>

    <div class="links">

        @if($message = session('message'))
            <p>
                {{ $message }}
            </p>
        @endif

        <p>
            <strong>QuietComfort® 25 Acoustic Noise Cancelling® headphones — Apple devices</strong>
        </p>

        <br>

        <form method="POST" action="{{ route('checkout.payment.esewa.process', $order->id) }}">

            @csrf

            <button class="btn btn-primary" type="submit">
                ${{ $order->amount }} Pay with eSewa
            </button>
        </form>
    </div>
@stop

Code

The sample code is available on github example repository.

Conclusion

Thanks for following up this article up to the end. If you have any feedback for me, feel free to leave your comment. If you think you have found a bug, please reach us via email or create an issue on github. Do not forget to share if you think worthreading and sharing this article.

Happy Coding!

]]>
<![CDATA[Laravel 5.7: Adding Support for Login with Username or Email]]> https://sujipthapa.co/blog/laravel-57-adding-support-for-login-with-username-or-email 577722fb-dc4c-46f5-a20b-f37742194e38 2018-10-17T00:00:00+05:45 Mr. Sujip Thapa I've already published a few articles on Laravel Login to support username or email. In this article, I'm going to cover the process of adding the username or email support with Laravel 5.7 default authentication system.

The process below helps you to understand the following things.

  • Adds support for email verification which is supported by default in the framework now.
  • Adds support for register with the username, which is not available on a default authentication system.
  • Adds support for login with either username or email.

The reason behind writing this article is peoples, preferring my previous articles about Laravel login and register with username or email support for earlier versions of Laravel.

Compatibility

If you are starting your fresh Laravel 5.7 project, and want to add the support for username or email for login and register, you could follow the steps from the beginning.

For any existing project just upgraded to Laravel 5.7, it is, of course, useful to follow the article to customize your authentication system.

Customization

Let's begin with the registration part to add support for username or email login with Laravel 5.7.

Register

For a fresh Laravel 5.7 setup, begin with generating the default auth scaffolding with an artisan console command, which ships with the framework out of the box.

Routes

// no email verification
Auth::routes();

// after adding, email verification
Auth::routes(['verify' => true]);


I would like to recommend you to follow the earlier article [Email Verification after Registration with Laravel 5.7] side by side with this article to implement email verification with Laravel 5.7 authentication system.

Database

For existing project add a new migration file using an artisan command to add a new field to the users' table, and for the new fresh project, you can easily edit the migration file generated with auth scaffolding and add a single line below.

$table->string('username')->unique();

To fill value in the database for username field, we've to add a key username to the $fillable property under app/User.php database model.

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

View

The user registration form also requires a little bit of customization to add the username field.

<div class="form-group row">
    <label for="username" class="col-md-4 col-form-label text-md-right">{{ __('Username') }}</label>

    <div class="col-md-6">
        <input id="username" type="text" class="form-control{{ $errors->has('username') ? ' is-invalid' : '' }}" name="username" value="{{ old('username') }}" required>

        @if ($errors->has('username'))
            <span class="invalid-feedback" role="alert">
                <strong>{{ $errors->first('username') }}</strong>
            </span>
        @endif
    </div>
</div>

Controller

The default auth system ships with a controller class, which handles the necessary methods for the registration system. We've to edit a few methods under that class to add the username to the database.

Login

We've already added the necessary routes, email verification steps linked with another article. For the login system to support username or email we need to override a few methods on the login controller.

The login form requires a very little change on the input type field from email to text.

<input id="email" type="text" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required autofocus>

The controller class for login also ships with the framework and uses an AuthenticatesUsers trait, which handles the specific methods necessary for the default auth system.

To apply the new changes we need, we have to override the methods under the controller class for the login system.

    // LoginController.php

    /**
     * Get the needed authorization credentials from the request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    protected function credentials(Request $request)
    {
        $field = $this->field($request);

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

    /**
     * Determine if the request field is email or username.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string
     */
    public function field(Request $request)
    {
        $email = $this->username();

        return filter_var($request->get($email), FILTER_VALIDATE_EMAIL) ? $email : 'username';
    }

    /**
     * Validate the user login request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function validateLogin(Request $request)
    {
        $field = $this->field($request);

        $messages = ["{$this->username()}.exists" => 'The account you are trying to login is not registered or it has been disabled.'];

        $this->validate($request, [
            $this->username() => "required|exists:users,{$field}",
            'password' => 'required',
        ], $messages);
    }

Code

The full source code for the all above customization is available on the github repository.

Conclusion

Thanks for following up my regular articles, and reading up to the end. If you have any feedback for me, feel free to leave your comment. Do not forget to share if you think worth sharing, and reading my articles.

Happy Coding!

]]>
<![CDATA[Hacktoberfest 2018 — win a T-shirt just by contributing to open source projects.]]> https://sujipthapa.co/blog/hacktoberfest-2018-win-a-t-shirt-just-by-contributing-to-open-source-projects 4986ddb1-ce8b-45fe-995f-d33207679b4b 2018-09-05T10:40:00+05:45 Mr. Sujip Thapa Hacktoberfest a month-long celebration of open source software is happening again. I started my Hacktoberfest challenge journey in 2016 and following the years to contribute and get the cool T-shirt.

I'm writing my second blog post about the Hacktoberfest challenge, but the competition is the third edition for me in real, by regularly winning the T-shirts from this great event.

The Hacktoberfest 2018 is the 5th edition, which was continuously organized by two giant tech companies @DigitalOcean, @Github in the previous years but this year the newest partner @Twilio also joining to run this challenge for the developers all over the world.

I'm super excited this time as well by looking at the excellent design of the Hacktober 2018 T-shirt.

Who can participate?
It is open to everyone in our global community!

How can I participate?
Open a minimum of 5 pull requests on Github, by choosing your favorite projects. If you need the contribution guide, follow this official contribution guide provided by Github.

When to participate?
You can sign up at the official website of Hacktoberfest 2018 anytime between October 1 and October 31.

The organizers are the world famous technology companies providing following great services to their customers.

Digital Ocean, Inc - An American based cloud infrastructure service provider, serving customers over the world with more than 13 data centers across different countries.

GitHub, Inc - A web-based code hosting service for version control using Git. It is a widely known source code repository platform for different types of applications.

Twilio - A cloud communications platform as a service company based in San Francisco, California. Twilio allows developers to programmatically make and receive phone calls, send and receive SMS.

Since last year, the Hacktoberfest fan created a platform to check if you completed the Hacktoberfest challenge to receive the T-shirt or not by viewing the pull request and activity.

Credit: The above preview image credit goes to Hacktoberfest 2018.

Thanks for reading the article up to the end, if you are interested and need help in participating in this challenge, please feel free to leave your comment below. I will look back and try to help as much as possible.

Happy Coding!

]]>
<![CDATA[Email Verification after Registration with Laravel 5.7]]> https://sujipthapa.co/blog/email-verification-after-registration-with-laravel-57 6176c3b6-78d4-45c9-b693-b68e2e362801 2018-09-01T13:16:17+05:45 Mr. Sujip Thapa Nowadays most of the modern applications force their users to verify the ownership by sending an activation email after they complete the account registration. Moving ahead, with the release of Laravel 5.7 the user email verification is shipping with the framework out of the box. People were creating the custom feature to implement this before the version 5.7.
This feature now makes easier to implement email verification on each application people build by using the Laravel framework.

In this article, we're implementing the complete email verification process in depth. We're going to achieve the verification feature by using the standard framework code without doing any modification.

Let's start the coding together.

Auth Scaffolding

Let's start with publishing the default auth scaffoldings by using the artisan command.

After publishing the necessary files, we don't have to worry about the routes, views, controllers required for authentication, as they ship with the framework, but you can always customize them if needed.

php artisan make:auth

Database

The new class is introduced in the framework to implement the email verification system.

So, now the App\User model should implement the Illuminate\Contracts\Auth\MustVerifyEmail contract.

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements MustVerifyEmail
{
    // ...
}

We need to add one more field email_verified_at to the user's table to store the verified timestamp when a user clicks the activation link sent to their email address.

    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });

Routing

A new controller Auth\VerificationController class is introduced to handle the necessary logic to send an email verification link to the user's email address.

Also, register the verification routes by passing the parameter like below.

// before
Auth::routes();

// after
Auth::routes(['verify' => true]);

Middleware

A new middleware Illuminate\Auth\Middleware\EnsureEmailIsVerified class is introduced to protect the routes from being accessed by the unauthorized users.

You can have a look at the Kernel.php to see the registered middleware.

    protected $routeMiddleware = [
        ...
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

Also, you may want to protect other routes, like profile, the dashboard to non-activated users by attaching the middleware on routes.

// example
Route::get('/home', 'HomeController@index')
    ->name('home')
    ->middleware('verified');

Route::get('/member/profile', function () {
    // verified users only
})->middleware('verified');

Views

The necessary views for login, register, and email verification are created inside resources/views/auth directory when you publish the auth scaffoldings.

After Verification

After the successful verification, the application redirects a user to the /home page by default.

You are free to customize the redirect location by defining a redirectTo method or property on the VerificationController.php.

protected $redirectTo = '/dashboard';

Also,

If you want to control the page redirection when the user interacts with login, register pages, you want them to go back to the previous page.

Read: Redirect to Previous URL After Logging In

Conclusion

Thanks for reading the article up to the end, please let me know through comments if you have any issue on the integration.

Happy Coding!

]]>
<![CDATA[What's New Coming to Laravel 5.7 Release]]> https://sujipthapa.co/blog/whats-new-coming-to-laravel-57-release 86e89f5e-5943-4ae6-8aa1-018040ea28ff 2018-08-17T10:15:15+05:45 Mr. Sujip Thapa Laravel a most popular PHP framework, which is actively supported and contributed open source project, which is reaching to its next release 5.7 in August 2018.

The release will receive bug fixes until February 2019 and security fixes until August 2019.

This release continues to improvements for the previous version 5.6, and also includes some exciting new features.

In this blog post, I'm listing some cool features that are already announced by the Laravel team and taking some reference from the github repository.

I'll keep on updating this post up to the final release is announced from the official team, as new pull requests are still coming from the community contributors.

1. Laravel Nova

Laravel Nova%2C beautifully designed admin panel package for Laravel.

The most awaited laravel package that was announced by Taylor during Laracon US 2018, and which is a beautiful admin panel package for Laravel application. It is a code-driven admin panel for your new or existing laravel project. The previous version of Laravel still supports Nova, as it is a simple composer package. To know in depth about it follow the official documentation, also go through the introduction article on the medium by Taylor.

Laravel Nova is officially released now on Aug 22, 2018, the initial release v1.0.* (Orion) is now available to purchase from the official website.

2. Email Verification

The framework introducing optional email verification feature with this release. To utilize this feature, you have to add the email_verified_at timestamp column to the user's table migration that ships with the framework.

To advise newly joined users to verify their email, the User model should implement the MustVerifyEmail interface.

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements MustVerifyEmail
{
    // ...
}

After implementing the interface, the newly registered members will receive an email containing a signed verification link. Once the user clicks this link, the application will automatically update the database and redirect the user to the intended location.

This new feature also introduced a verified middleware. If you wish to protect your application's routes from only verified members, it can be attached to the application routes.

'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,


3. Guest User Gates / Policies

The previous version of Laravel used to return false if an unauthenticated user wants to access the application. However, with the new version (5.7) it will now allow declaring an "optional" type-hint or supplying a null default value to permit the guest user to pass through the authorization checks.

Gate::define('update-product', function (?User $user, Product $product) {
    // ...
});


4. Symfony Dump Server

The Symfony's dump-server is coming to Laravel 5.7. It is a command via package built by a Laravel community member Marcel Pociot.

This feature will be a lot useful to debug an error on your application without interrupting the application runtime.

Laravel 5.7 Dump Server

The command runs in the background and collects data transmitted from the application and shows output through the console mode.

php artisan dump-server

// Output to the HTML file.
php artisan dump-server --format=html > report.html

This package is open sourced by the author, contribute if you have any idea for it.

5. URL Generator & Callable Syntax

To generate the URLs to controller actions, Laravel now supports callable array syntax, instead of strings only.

Previously,

$url = action('BlogController@index');

$url = action('BlogController@view', ['id' => 1]);

Now,

use App\Http\Controllers\HomeController;

$url = action([BlogController::class, 'index']);

$url = action([BlogController::class, 'view'], ['id' => 1]);

6. Paginator Links

You can now control the number of pagination links on each side of the paginated URLs design. By default, there will be three links created on each side of the paginator links. The newly introduced method onEachSide() allows to control them with Laravel 5.7.

{{ $pages->onEachSide(5)->links() }}

7. Read / Write Streams method in Filesystem

Laravel 5.7 introduces new methods for the Filesystem integration.

    Storage::disk('s3')->writeStream(
        'destination-file.zip',
        Storage::disk('local')->readStream('source-file.zip')
    );

8. Notification Localization
You can now assign locale for the notifications to send other than the current language.

The Illuminate\Notifications\Notification class adds new locale method to assign the desired language.

$user->notify((new NewUser($user))->locale('np'));

You could also utilize the facade to set localization for multiple notification entries.

    Notification::locale('np')
        ->send($subscribers, new WeeklyNewsletter($newsletter));

9. Improved Error Messages

You can now better track your errors messages with your Laravel application. As of Laravel 5.7, you will get a cleaner concise message saying that the method doesn't exist on the specific model.

Improved Error Messages

10. Resources Directory Changes

A little bit changes to the resources directory coming with the new release.

This change was announced by Taylor with a tweet, as assets directly will go away and js, sass, lang, views coming out into the resources directory.

Laravel 5.7 resources directory changes.

When you upgrade your application to 5.7, you don’t need to reconstruct the resources/asset directory according to the newer directory structure. The older structure will still work.

11. Testing Artisan Commands

The first employee of Laravel (Mohamed Said) recently contributed a great feature in the framework to test artisan commands. He announced via his tweet about this feature, and which is also documented in official documentation already. Now, with this addition, framework now provides a simple API for testing console applications that ask for user input.

class InstallCommandTest extends TestCase
{
    public function testInstallTest()
    {
        $this->artisan('app:setup', [
            'name' => 'Setup New Project'
        ])
            ->expectsQuestion('Are you sure you want to start installation ?', 'Yes')
            ->expectsOutput('Initializing...')
            ->expectsQuestion('Please select your preferred version', 'v2.5')
            ->expectsOutput('Installing...')
            ->expectsQuestion('Do you want to run composer dump -o ?', 'Yes')
            ->expectsOutput('Generating Optimized Autoload Files...')
            ->assertExitCode(0);
    }
}

Conclusion

Thanks for reading this article up to the end, please follow this article in later dates to know more new features coming to the 5.7 release.

Happy Coding!

]]>
<![CDATA[Building a Password Less Authentication System with Laravel Signed Routes]]> https://sujipthapa.co/blog/building-a-password-less-authentication-system-with-laravel-signed-routes fdd631b4-7d56-4667-84f8-43b4ed8c5d6f 2018-08-12T14:22:18+05:45 Mr. Sujip Thapa I've seen many moderns apps are offering passwordless authentication in their platform. A lot of social media, email platforms are also offering the login with their API system by providing limited data necessary for user registration.

A lot of modern webs, mobile apps using social login to give a great user experience while using their platforms.

Today in this blog post, I'm explaining the process of customizing to use own passwordless authentication system with the Laravel framework.

Let's start together.

This article will utilize features from Laravel 5.6 version.

laravel new passwordless-auth

cd passwordless-auth

php artisan make:auth

After publishing the default Laravel auth scaffoldings, we now need to remove unnecessary files listed below.

ResetPasswordController.php
ForgotPasswordController.php

passwords/email.blade.php
passwords/reset.blade.php

The register, login pages come up with password fields, as we're building passwordless auth, so we have to tweak on those files.

login.blade.php
register.blade.php

Note: It is better to give some instructions on each pages describing how our passwordless authentication system works.

Now, adding auth routes needed for this integration.

We're not going to use the default routes which ship with the framework for the auth system.

We'll care the naming convention of routes, but we have to avoid unnecessary ones coming with the framework which are not necessary for this integration.

Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
Route::post('login/attempt', 'Auth\LoginController@attempt')->name('login.attempt');
Route::get('login/{token}/validate', 'Auth\LoginController@login')
    ->name('login.token.validate')
    ->middleware('signed');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
Route::post('register', 'Auth\RegisterController@register');

Let's start with registering a user without a password to log in.

Before writing any codes for login, register process, let me give you some idea on what things need to update.

Signed Route Middleware
The middleware prevents the user to misuse the expired URLs by checking the signature validity.

    protected $routeMiddleware = [
        ...
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
    ];​

The new feature signed route was introduced to the framework with version 5.6.

Handle InvalidSignatureException
We need to handle the exception for invalid signature, if a user tries to login with the expired signed URL show message to them to generate new URL.

    use Illuminate\Routing\Exceptions\InvalidSignatureException;

    public function render($request, Exception $exception)
    {
        if (is_a($exception, InvalidSignatureException::class)) {
            return response()->view('_signature-expired');
        }

        return parent::render($request, $exception);
    }
@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">
                        <h2>{{ __('Error') }}</h2>
                    </div>
                    <div class="card-body">
                        <div class="alert alert-danger text-center text-muted">
                            The signature seems to be expired, please try generating a new one and try again.
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Remove Password Fields
We're not using the password field anymore, so remove from the migration file and User model.

After all, these make sure you run the migration to generate database tables.
Register

<?php

namespace App\Http\Controllers\Auth;

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

class RegisterController extends Controller
{
    use RegistersUsers;

    /**
     * @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',
            'email' => 'required|string|email|max:255|unique:users',
        ]);
    }

    /**
     * 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'],
            'email' => $data['email'],
        ]);
    }

    /**
     * Handle a registration request for the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function register(Request $request)
    {
        $this->validator($request->all())->validate();

        event(new Registered($user = $this->create($request->all())));

        return redirect()->route('login')
            ->with(['success' => 'Success! your account is registered.']);
    }
}

The main updates in this file are, remove password form validation, create methods.

Another major update is to prevent a user from logging in automatically into the application after registration inside the register method.

Tip: I've seen BuySell Ads, a popular advertising platform using this kind of system as only admin adds the user.

Login

I've added a new trait for the Login process to keep the overrides a little bit cleaner.

Previously with the framework's default auth, there is a trait called AuthenticatesUsers.

Now, with this integration, we will be importing new trait which itself imports the existing AuthenticatesUsers.

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Auth\Traits;
use App\Http\Controllers\Controller;

class LoginController extends Controller
{
    use Traits\PasswordLessAuth;

    /**
     * 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');
    }
}

Also, the Login Throttling feature will still work.

<?php

namespace App\Http\Controllers\Auth\Traits;

use App\LoginAttempt;
use App\Notifications\NewLoginAttempt;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;

trait PasswordLessAuth
{
    use AuthenticatesUsers;

    /**
     * Validate the user login request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function validateLogin(Request $request)
    {
        $messages = ['exists' => trans('auth.exists')];

        $this->validate($request, [
            $this->username() => 'required|email|exists:users',
        ], $messages);
    }

    /**
     * Handle a login attempt request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function attempt(Request $request)
    {
        $this->incrementLoginAttempts($request);

        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        $this->validateLogin($request);

        if ($this->createLoginAttempt($request)) {
            return $this->sendAttemptResponse($request);
        }

        return $this->sendFailedLoginResponse($request);
    }

    /**
     * Handle a login request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function login($token, Request $request)
    {
        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        if ($this->attemptLogin($token, $request)) {
            return $this->sendLoginResponse($request);
        }

        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

    /**
     * Attempt to log the user into the application.
     *
     * @param string $token
     * @param \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function attemptLogin($token, Request $request)
    {
        $user = LoginAttempt::userFromToken($token);

        if (is_object($user)) {
            return $this->guard()->login($user);
        }
    }

    /**
     * Attempt to log the user into the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \App\LoginAttempt
     */
    protected function createLoginAttempt(Request $request)
    {
        $authorize = LoginAttempt::create([
            'email' => $request->input($this->username()),
            'token' => str_random(40) . time(),
        ]);

        $authorize->notify(new NewLoginAttempt($authorize));

        return $authorize;
    }

    /**
     * @param $request
     */
    public function sendAttemptResponse($request)
    {
        return \View::make('auth._link-sent');
    }
}

To customize the validation message for the user exists rule, open up resources/lang/en/auth.php and update like below.

<?php

return [

    ...
    'exists' => 'The provided email address does not match our records.'
];

Let me give some run down about the login feature and new methods added to the PasswordLessAuth.php trait.

  • The method validateLogin() now, only validates email field and if that exists in the database.
  • The new attempt() method is responsible for sending an email with a signed URL after validating the request.
  • The new createLoginAttempt(), is being called via attempt() method, which creates a new login attempt which will be validated later while user clicks on the link sent via email.
  • After creating a login attempt, we display a message to the user with sendAttemptResponse() method.

Let's create a model, migration and notification class for the login attempt process.

<?php

namespace App;

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

class LoginAttempt extends Model
{
    use Notifiable;

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

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'email', 'token',
    ];

    /**
     * @return mixed
     */
    public function user()
    {
        return $this->hasOne(User::class, 'email', 'email');
    }

    /**
     * @param $token
     */
    public static function userFromToken($token)
    {
        $query = self::where('token', $token)
            ->where('created_at', '>', Carbon::parse('-15 minutes'))
            ->first();

        return $query->user ?? null;
    }
}

Add, those fields by creating a new migration file like below.

    Schema::create('login_attempts', function (Blueprint $table) {
        $table->increments('id');
        $table->string('email')->index();
        $table->string('token')->index();
        $table->timestamps();
    });

To send the login link in an email, we have a new notification class.

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\URL;

class NewLoginAttempt extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($attempt)
    {
        $this->attempt = $attempt;
    }

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

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return with(new MailMessage)
            ->from(env('ADMIN_MAIL_ADDRESS'))
            ->subject('Login Your Account')
            ->greeting("Hello {$this->attempt->user->name}!")
            ->line('Please click the button below to get access to the application, which will be valid only for 15 minutes.')
            ->action('Login to your account', URL::temporarySignedRoute('login.token.validate', now()->addMinutes(15), [$this->attempt->token]))
            ->line('Thank you for using our application!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

After a user makes a valid attempt to login into the application, we display a message to the user with a view file like below.

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">
                        <h2>{{ __('Success') }}</h2>
                    </div>
                    <div class="card-body">
                        <div class="alert text-center text-muted">
                            Please check your email for a login link, which will be valid for next 15 minutes only.
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <a class="btn btn-link" href="{{ route('login') }}">
                                    {{ __('Get Another Link') }}
                                </a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Conclusion

The full source code now freely available on github, you are free to modify as per your application logic.

Also Read: Laravel 5.6 Login, Register, Activation with Username or Email Support

Thanks for reading this article up to the end, please don't forget to give your feedback in the comment section below.

Happy Coding!

]]>
<![CDATA[Testing Packages on Composer with Specific Commit Before Creating New Tag]]> https://sujipthapa.co/blog/testing-packages-on-composer-with-specific-commit-before-creating-new-tag f416d66e-3cad-41f3-a6ec-11f5b47ddca1 2018-07-21T08:18:18+05:45 Mr. Sujip Thapa Composer, which is a modern developer tool for dependency management for PHP. It is used to declare the dependencies your project depends on it and it manages them very well for you. It is incredibly flexible to manage the dependencies in various stage of your project.

Let's suppose you're releasing a new package, but before announcing a new major stable version, you want to test it thoroughly in the development branch.

Now, to use the specific git commit before publishing a new tag, composer offers a way to include the commit hash value directly via the command line or via the require block in the composer.json file.

composer require vendor/package:dev-master#0fcf728

or

    "require": {
        "vendor/package": "dev-master#0fcf728"
    }

The composer command above will check out your package version to that specific commit, so you can test the code properly before finally releasing a stable version.

Also, sometime you may want to install a specific released version of the package.

composer require vendor/package:version

// Example 
composer require sudiptpa/paypal-ipn:1.0.x-dev

Also Read: Speed up your Composer Install or Update Process

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

If it is worth reading, let other people know about it by sharing this post.

Happy Coding!

]]>
<![CDATA[Laravel 5.6 Login, Register, Activation with Username or Email Support]]> https://sujipthapa.co/blog/laravel-56-register-login-activation-with-username-or-email-support e3e9db5e-d8de-4922-907b-f6b166af7c2c 2018-06-10T00:00:00+05:45 Mr. Sujip Thapa This is another interesting blog post about Laravel 5.6 login, register with username or email support. I've already published two articles about customizing the laravel authentication process. In this article, I will be helping you to build following things below, with Laravel v5.6.

  • Adding username support on default register, login process.
  • Activating user after the registration process is completed via email.
  • Disabling default login after register, only allowing to log in for the activated user.
  • Using Laravel notification to send email to the user about registration.

To follow this article you should have existing or fresh laravel setup with version 5.6, as you will be seeing some codes samples only supporting this particular version and they may not be compatible with other older versions.

The regular laravel installation comes without auth scaffolding with the recent releases, to generate the default auth scaffolding it ships with an artisan command out of the box.

If you haven't already generated them, use the below artisan command to populate them.

php artisan make:auth

Let's start with the additional routes for authentication under routes/web.php

Auth::routes();

Route::get('activate/{token}', 'Auth\RegisterController@activate')
    ->name('activate');

To add username support on register process, let's add few new fields in the migration file.

<?php

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

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->string('token');
            $table->integer('active')->default(0);
            $table->rememberToken();
            $table->timestamps();
        });
    }

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

We've added three new fields (username, token, active), to store unique username, activation token, active field to main active flag for users.

Now, updating model app/User.php with new fields.

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;

    const ACTIVE = 1;
    const INACTIVE = 0;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'username', 'email', 'password', 'token', 'active',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

Register

Adding username field for a registration form inside resources/views/auth/register.blade.php

<div class="form-group row">
    <label for="username" class="col-md-4 col-form-label text-md-right">{{ __('Username') }}</label>

    <div class="col-md-6">
        <input id="username" type="text" class="form-control{{ $errors->has('username') ? ' is-invalid' : '' }}" name="username" value="{{ old('username') }}" required>

        @if ($errors->has('username'))
            <span class="invalid-feedback">
                <strong>{{ $errors->first('username') }}</strong>
            </span>
        @endif
    </div>
</div>

To view, the full source code for the registration form read the article up to end.

To make the username support coming through the registration form, we need to modify few methods inside the controller.

    // App\Http\Controllers\Auth\RegisterController.php
    
    /**
     * 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)
    {
        $user = User::create([
            'name' => $data['name'],
            'username' => $data['username'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
            'token' => str_random(40) . time(),
        ]);

        $user->notify(new UserActivate($user));

        return $user;
    }

    /**
     * Handle a registration request for the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function register(Request $request)
    {
        $this->validator($request->all())->validate();

        event(new Registered($user = $this->create($request->all())));

        return redirect()->route('login')
            ->with(['success' => 'Congratulations! your account is registered, you will shortly receive an email to activate your account.']);
    }

    /**
     * @param $token
     */
    public function activate($token = null)
    {
        $user = User::where('token', $token)->first();

        if (empty($user)) {
            return redirect()->to('/')
                ->with(['error' => 'Your activation code is either expired or invalid.']);
        }

        $user->update(['token' => null, 'active' => User::ACTIVE]);

        return redirect()->route('login')
            ->with(['success' => 'Congratulations! your account is now activated.']);
    }

Let's point out the updates made in the above controller.

  • We've added a validation rule for username field inside the validator() method.
  • We've updated create() method to call a notification class to send an activation email to the user after registration is completed.
  • Overriding register() method originally called from the RegistersUsers trait to protect from being auto-login to non-activated users.
  • Also created new method activate() to allow activation to the new users.

We've added a new notification class, UserActivate.php to send an activation link to the users while they register their new account.

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class UserActivate extends Notification
{
    use Queueable;

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

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

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->from(env('ADMIN_MAIL_ADDRESS'))
            ->subject('Activate Account!')
            ->greeting(sprintf('Hi, %s', $this->user->name))
            ->line('We just noticed that you created a new account. You will need to activate your account to sign in into this account.')
            ->action('Activate', route('activate', [$this->user->token]))
            ->line('Thank you for using our application!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

The customization for registration process is finally done with above changes, now we will be looking at login process below.

Login

We've updated the file login.blade.php to add support for email or username. To make that addition we've changed the input type email to be text to support both username or email but the input type name remains same.

<div class="form-group row">
    <label for="email" class="col-sm-4 col-form-label text-md-right">{{ __('E-Mail / Username') }}</label>

    <div class="col-md-6">
        <input id="email" type="text" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required autofocus>

        @if ($errors->has('email'))
            <span class="invalid-feedback">
                <strong>{{ $errors->first('email') }}</strong>
            </span>
        @endif
    </div>
</div>

Now, we're adding another method to the controller.

    // App\Http\Controllers\Auth\LoginController.php

    /**
     * Get the needed authorization credentials from the request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    protected function credentials(Request $request)
    {
        $field = $this->field($request);

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

    /**
     * Determine if the request field is email or username.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string
     */
    public function field(Request $request)
    {
        $email = $this->username();

        return filter_var($request->get($email), FILTER_VALIDATE_EMAIL) ? $email : 'username';
    }

    /**
     * Validate the user login request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function validateLogin(Request $request)
    {
        $field = $this->field($request);

        $messages = ["{$this->username()}.exists" => 'The account you are trying to login is not activated or it has been disabled.'];

        $this->validate($request, [
            $this->username() => "required|exists:users,{$field},active," . User::ACTIVE,
            'password' => 'required',
        ], $messages);
    }

Let's point out what has changed inside the controller above.

  • The method credentials(), originally called from AuthenticatesUsers trait which collects credential to attempt the authentication is now overridden to the controller.
  • The field username or email is determined inside the credentials() method.
  • Added active field in the authentication process.

After updating the above files the login process also now supports username or email.

The full source code is freely available on github, you are free to modify and use it for your application.

Thanks for reading this article up to the end, please don't forget to give your feedback in the comment section below.

Happy Coding!

]]>
<![CDATA[SecurePay Integration Guide for Omnipay v3 PHP Library with Laravel]]> https://sujipthapa.co/blog/securepay-integration-guide-for-omnipay-v3-php-library-with-laravel 51d273ef-a3da-4b71-818d-5f952e36beb9 2018-05-30T20:02:17+05:45 Mr. Sujip Thapa I've already covered few Omnipay integration guides with my previous blog posts. In this post, I'm going to explain in detail about SecurePay, a leading payment solution for businesses of different sizes over Australia. I've recently contributed to the v3 release of the omnipay-securepay open source library for PHP on github.

By using this package (omnipay/securepay) we can implement different gateways listed below.

  • SecurePay_DirectPost (DirectPost v2 Integration)
  • SecurePay_SecureXML (Secure XML API Integration)

We're going to integrate SecurePay DirectPost v2 in this article below.

  • The customer submits the payment information directly to the Direct Post payment service from the website.
  • The payment gateway validates and redirects back to the website to fulfill the order.

Before starting the coding let's install the package via composer.

We're going to integrate this package with Laravel v5.6 for this article, use the command below to install the package.

composer require league/omnipay omnipay/securepay

Let's add some new routes for this integration under app/routes/web.php

Route::get('/checkout/payment', [
    'name' => 'Payment',
    'as' => 'checkout.payment',
    'uses' => 'PaymentController@checkout',
]);

Route::post('/checkout/payment/{order}/process', [
    'name' => 'Payment',
    'as' => 'checkout.payment.process',
    'uses' => 'PaymentController@payment',
]);

Route::get('/checkout/payment/{order}/completed', [
    'name' => 'Payment Completed',
    'as' => 'checkout.payment.completed',
    'uses' => 'PaymentController@completed',
]);

Route::get('/checkout/payment/{order}/failed', [
    'name' => 'Payment Failed',
    'as' => 'checkout.payment.failed',
    'uses' => 'PaymentController@failed',
]);

Adding new model toapp/Order.php handle database operations to store orders coming through the website.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    const PAYMENT_COMPLETED = 1;
    const PAYMENT_PENDING = 0;
    /**
     * @var string
     */
    protected $table = 'orders';

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

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

This model has really basic stuff to show the process while building this integration guide, you are free to customize as per the requirement of your application.

A new database table is needed to store the orders. Let's create a table orderswith a console command.

php artisan make:migration create_table_orders --create=orders
<?php

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

class CreateOrdersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->increments('id');
            $table->string('transaction_id')->nullable();
            $table->float('amount')->unsigned()->nullable();
            $table->integer('payment_status')->unsigned()->default(0);
            $table->timestamps();
            $table->softDeletes();
        });
    }

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

Let's add a new classapp/SecurePay.php which interacts with library classes and framework classes to interact with API services.

<?php

namespace App;

use Exception;
use Illuminate\Http\Request;
use Omnipay\Common\CreditCard;
use Omnipay\Common\Exception\InvalidCreditCardException;
use Omnipay\Omnipay;

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

        $gateway->setMerchantId(config('services.securepay.merchant_id'));
        $gateway->setTransactionPassword(config('services.securepay.password'));
        $gateway->setTestMode(config('services.securepay.sandbox'));

        return $gateway;
    }

    /**
     * @param array $parameters
     * @param Request $request
     */
    public function card(array $parameters, Request $request)
    {
        return [
            'card' => new CreditCard([
                'firstName' => $request->get('first_name'),
                'lastName' => $request->get('last_name'),
                'number' => $request->get('card_number'),
                'expiryMonth' => $request->get('expiry_month'),
                'expiryYear' => $request->get('expiry_year'),
                'cvv' => $request->get('cvc'),
            ]),
        ];
    }

    /**
     * @param array $parameters
     * @param Request $request
     * @return mixed
     */
    public function purchase(array $parameters, Request $request)
    {
        $parameters = array_merge($parameters, $this->card($parameters, $request));

        try {
            $response = $this->gateway()
                ->purchase($parameters)
                ->send();

        } catch (InvalidCreditCardException $e) {
            throw new Exception($e->getMessage());
        }

        return $response;
    }

    /**
     * @param array $parameters
     * @param Request $request
     * @return mixed
     */
    public function complete(array $parameters, Request $request)
    {
        $parameters = array_merge($parameters, $this->card($parameters, $request));

        $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('checkout.payment.failed', $order->id);
    }

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

To store the API credentials required to connect with API service, you can use config/services.phpwhich ships with the framework out of the box.

    'securepay' => [
        'merchant_id' => env('SECUREPAY_MERCHANTID'),
        'password' => env('SECUREPAY_PASSWORD'),
        'sandbox' => env('SECUREPAY_SANDBOX', true)
    ],

Now, we need a controller class to handle and process all incoming requests into the application. Let's add a class PaymentController.php

<?php

namespace App\Http\Controllers;

use App\Order;
use App\SecurePay;
use Exception;
use Illuminate\Http\Request;

/**
 * Class PaymentController
 * @package App\Http\Controllers
 */
class PaymentController extends Controller
{
    /**
     * @param Request $request
     */
    public function checkout(Request $request)
    {
        $order = Order::findOrFail(mt_rand(1, 140));

        // you application logic goes here
        // the above order is just for example.

        return view('checkout.payment', compact('order'));
    }

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

        $gateway = new SecurePay;

        try {
            $response = $gateway->purchase([
                'amount' => $gateway->formatAmount($order->amount),
                'transactionId' => $order->id,
                'currency' => 'USD',
                'cancelUrl' => $gateway->getCancelUrl($order),
                'returnUrl' => $gateway->getReturnUrl($order),
            ], $request);
        } catch (Exception $e) {
            $order->update(['payment_status' => Order::PAYMENT_PENDING]);

            return redirect()
                ->route('checkout.payment.failed', [$order->id])
                ->with('message', sprintf("Your payment failed with error: %s", $e->getMessage()));
        }

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

        return redirect()->back()->with([
            'message' => "We're unable to process your payment at the moment, please try again !",
        ]);
    }

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

        $gateway = new SecurePay;

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

        if ($response->isSuccessful()) {
            $order->update([
                'transaction_id' => $response->getTransactionReference(),
                'payment_status' => Order::PAYMENT_COMPLETED,
            ]);

            return redirect()->route('checkout.payment')->with([
                'message' => 'Payment Successful, Thank you for your order !',
            ]);
        }

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

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

        return view('checkout.payment', compact('order'));
    }
}

To submit the payment information by the customer we need a checkout page, for this example, I have implemented a nice view with the code snippet below.

<div class="card-details">
  <h3 class="title">Credit Card Details</h3>
  <span>@csrf</span>
  <div class="row">
    <div class="form-group col-sm-8">
      <label for="card-holder">Card Holder</label>
      <div class="input-group expiration-date">
          <input type="text" class="form-control" placeholder="Fist Name" name="first_name" aria-label="Fist Name" aria-describedby="basic-addon1">
          <span class="date-separator">/</span>
          <input type="text" class="form-control" placeholder="Last Name" name="last_name" aria-label="Last Name" aria-describedby="basic-addon1">
      </div>
    </div>
    <div class="form-group col-sm-4">
      <label for="">Expiration Date</label>
      <div class="input-group expiration-date">
        <input type="text" class="form-control" placeholder="MM" aria-label="MM" name="expiry_month" aria-describedby="basic-addon1">
        <span class="date-separator">/</span>
        <input type="text" class="form-control" placeholder="YY" aria-label="YY" name="expiry_year" aria-describedby="basic-addon1">
      </div>
    </div>
    <div class="form-group col-sm-8">
      <label for="card-number">Card Number</label>
      <input id="card-number" type="text" class="form-control" placeholder="Card Number" name="card_number" aria-label="Card Holder" aria-describedby="basic-addon1">
    </div>
    <div class="form-group col-sm-4">
      <label for="cvc">CVC</label>
      <input id="cvc" type="text" class="form-control" placeholder="CVC" aria-label="CVC" name="cvc" aria-describedby="basic-addon1">
    </div>
    <div class="form-group col-sm-12">
      <button type="submit" class="btn btn-primary btn-block">
          <i class="fa fa-credit-card" aria-hidden="true"></i>
          Pay with Credit Card
      </button>
    </div>
  </div>
</div>

To view the full HTML code click here.

The preview looks like below.

SecurePay Integration, Checkout

To view the full source code written while crafting this blog post, visit the github repository and grab the code.

Thanks for reading up to the end, I hope you enjoyed reading this article. If you have any feedback on this article feel free to leave your comment below.

Happy Coding!

]]>
<![CDATA[PayPal Integration – Omnipay PayPal PHP Library v3.0 with Laravel]]> https://sujipthapa.co/blog/paypal-integration-omnipay-paypal-php-library-v30-with-laravel e23bd3c6-3ed4-4b6b-993e-4561be3b45c3 2018-05-20T05:12:00+05:45 Mr. Sujip Thapa This is my third blog post about PayPal Integration with Omnipay payment processing library. In this article, I will mainly cover about the recent release v3.0, which is finally available for developers around the world. I'm sure many developers around the world were eagerly waiting for this release.

In my previous article I explained about how to Integrate Omnipay v2.* with Laravel framework.

In the new release v3.0, the team focused mainly on separating the HTTP Client, to be independent with Guzzle. This release now supports symfony 3,4 components to run with all Laravel v5.* versions. This is a most awaited release, developers were not able to use, as it was not fully compatible with latest symfony components.

To know about the breaking changes and new additions to the package, view the full release note from their official doc and github repository.

The original omnipay/omnipay package has been changed as league/omnipay

By using this package (omnipay/paypal) we can implement different gateways listed below.

  • PayPal_Express (PayPal Express Checkout)
  • PayPal_ExpressInContext (PayPal Express In-Context Checkout)
  • PayPal_Pro (PayPal Website Payments Pro)
  • PayPal_Rest (Paypal Rest API)

We're going to integrate PayPal Express Checkout in this article below.

Before starting the coding let's install the package via composer.

composer require league/omnipay omnipay/paypal

Let's add new routes for the PayPal integration under app/routes/web.php

Route::get('/paypal/{order?}', [
    'name' => 'PayPal Express Checkout',
    'as' => 'order.paypal',
    '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',
]);

I mainly write named routes all over my applications, having name routes are easy to create URLs from anywhere within the project with a simple helper route(), I have mentioned before about this mechanism with my previous articles as well.

Adding required models for this implementation.

app/Order.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    const PAYMENT_COMPLETED = 1;
    const PAYMENT_PENDING = 0;

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

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

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

The order model only has few basic fields implemented just for the simple example, you are free to customize with your required fields in your application.

Adding new database table orders, you need to create a migration file with a console command.

php artisan make:migration create_table_orders --create=orders
<?php

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

class CreateOrdersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->increments('id');
            $table->string('transaction_id')->nullable();
            $table->float('amount')->unsigned()->nullable();
            $table->integer('payment_status')->unsigned()->default(0);
            $table->timestamps();
            $table->softDeletes();
        });
    }

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

Adding new helper class for generating gateway object and routes to interact with PayPal API.

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('services.paypal.username'));
        $gateway->setPassword(config('services.paypal.password'));
        $gateway->setSignature(config('services.paypal.signature'));
        $gateway->setTestMode(config('services.paypal.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('services.paypal.sandbox') ? "sandbox" : "live";

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

Laravel ships with a config file config/services.php to store credentials to be used with third-party API services. For this implementation add following credentials under the same file like below.

    'paypal' => [
        'username' => env('PAYPAL_USERNAME'),
        'password' => env('PAYPAL_PASSWORD'),
        'signature' => env('PAYPAL_SIGNATURE'),
        'sandbox' => env('PAYPAL_SANDBOX'),
    ],

To handle and maintain the request coming into the application we need to pass information to the controller. We are adding new 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 = Order::findOrFail(mt_rand(1, 140));

        // the above order is just for example.

        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(),
                'payment_status' => Order::PAYMENT_COMPLETED,
            ]);

            return redirect()->route('order.paypal', 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('order.paypal', encrypt($order_id))->with([
            'message' => 'You have cancelled your recent PayPal payment !',
        ]);
    }

        /**
     * @param $order_id
     * @param $env
     * @param Request $request
     */
    public function webhook($order_id, $env, Request $request)
    {
        // to do with new release of sudiptpa/paypal-ipn v3.0 (under development)
    }
}

Finally, adding a simple view to submitting a form to pay the order amount with PayPal, the form redirects the user to PayPal hosted page to enter the user account details for the security reason.

app/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
                <div class="row">
                    <div class="col">
                        <img src="{{ asset('images/paypal.png') }}" class="img-responsive gateway__img">
                    </div>
                    <div class="col">
                        <img src="{{ asset('images/laravel.png') }}" class="img-responsive gateway__img">
                    </div>
                </div>
                <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, 140))]) }}">
                    {{ 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

The preview of the above view looks like below, I just made for this example.

PayPal Integration with PHP

For the above real-time example, I tried to cover only the mechanism to handle the payment request, failed payment, canceled a payment. You are free to customize it and manage as per your application architecture. The above example was created with Laravel v5.6 version but it works for all v5.* versions, that's the benefit of the new release for omnipay/paypal latest version v3.0.

To view the full source codes, view few commits (65e4b5a, 75a9585, c469e59, ab0b4db b24fa97) on github example repository.

Thanks for reading this article up on the end, please leave your feedback below in the comment section.

Happy Coding!

]]>
<![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 Last Updated: 22nd Sep, 2020

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.

Also, view the last updated code here with another pull request in 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 use Insomnia, to design, and test APIs.

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.

Last Updated: Jan 7, 2021

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!

Last Updated - May 24, 2018 : The process I have explained above also works for new version v3.0 of Facebook Graph API

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/ipstack


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\Ipstack\Ipstack;

/**
 * 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 Ipstack(
            $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 The freegeoip.net service is discontinued, so please refer to the new package to get the same service sudiptpa/ipstack

I've abandoned the legacy package sudiptpa/geoip as the service provider discontinued on July 1st, 2018.

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.

Javascript Code Preview

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 little 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 your Laravel application is running with v5.5 and higher version, this package also has support for package 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' => env('PAYPAL_USERNAME'),
        'password' => env('PAYPAL_PASSWORD'),
        'signature' => env('PAYPAL_SIGNATURE'),
        'sandbox' => env('PAYPAL_SANDBOX')
    ],
];

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, please follow the link here to read my another blog post.

Note: The team from Omnipay recently released the new version v3.0 with support for symfony 3,4 components. Also, I published a new fresh blog post to explain the new changes in v3.0 with complete integration guide. 

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[Linux: How to show current git branch in Bash prompt]]> https://sujipthapa.co/blog/linux-how-to-show-current-git-branch-in-bash-prompt 6c4edbe0-ad35-43d8-aeab-1e2e81717f55 2016-09-10T18:15:00+05:45 Mr. Sujip Thapa The bash prompt does not show the current active git branch on the project root.

So adding a way to display the active git branch can help developers avoid the branching problems and code conflict with other developers some time.

In this post, you will get a chance to learn how you can show your active git branch on your Linux terminal on the bash prompt.

Open up your terminal and hit cd ~ to stay on the root. Open up the .bashrc file from there.

Example:

nano ~/.bashrc

Scroll to the place where you want to paste the content.

Pickup the below snippet and paste into the .bashrc file and save it.

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\] $ "

Then, save the file and exit from the edit mode.

After the update, you need to restart the terminal or source, and it should start working for you.

Happy Coding!

I hope this was helpful for you, and feel free to share it with other developers around you.

Feel free to share the article on social media to share the knowledge among the developers in your circle.

Last Updated: April 26, 2022

 

 

]]>
<![CDATA[Laravel Cheat Sheet]]> https://sujipthapa.co/blog/laravel-cheat-sheet 3b7a72fd-098f-4c12-ad16-a63f9160cd72 2016-09-10T18:15:00+05:45 Mr. Sujip Thapa Are you a PHP developer, working using the Lararvel framework?

If your answer is Yes, you are at the right place; here in this post, we are writing about the Laravel cheat sheet to quickly pick up the Laravel syntax for different features available in the Laravel ecosystem.

The cheat sheet guide is a complete reference for different versions of the framework.

The cheat sheet guide supports responsive design, so can be viewed in any devices.

Contribute to the project on github - https://github.com/summerblue/laravel5-cheatsheet

Open the cheat sheet - https://summerblue.github.io/laravel5-cheatsheet

Laravel Cheat Sheet - https://learninglaravel.net/cheatsheet/

Laravel Artisan Cheatsheet by @jbrooksuk a developer at Laravel.

https://artisan.page

Last Updated: 19th Mar, 2022

Happy Coding!

]]>
<![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 big panic thing is managing and having to set up the email server of own. It requires a lot of technical things, resources, pricing, security prerequisites and many more. A lot of modern, secure, reliable mail service providers are up in the tech market these days.

Here, in this blog post, we are going to set up a renowned mail service provider to work your project with low hassle. The mail service provider I'm setting up is Mailgun.

I use Digital Ocean as a primary hosting partner for the Laravel projects I work for my clients. We are going to setup Mailgun with Digital Ocean platform to send emails from the web application. In the previous years, Mailgun used to work without doing any configuration side of things to Digital Ocean end. In the following years, they want us the verify the ownership to use their service with any third party application.

Before following this article, I will assume you have following services available to you if you would like to implement the step by step guide.

Prerequisites

  • Register for Digital Ocean, create a droplet, install the platform you want to run your application.
  • Register your account with Mailgun

Before starting let's understand some benefits of using Mailgun.

  • Exciting thing up to now is, it gives you 10,000 emails free every month, no payment hassle for really small to medium-sized companies.
  • Reliable & Powerful APIs that facilitates to send, receive, track your emails seamlessly.
    Trusted by the tech giants in the industry, so kind of no compromise in security side.
  • If your business needs more than the free emails provided by the service provider, you can choose their simple pricing method as per your requirement. The pricing is like pay as you go, as they give you interface to calculate the price based on your needs for your monthly volume of emails you want.

There are a lot of competitors to this service providing the same quality of email services. I am using Mailgun for long with my projects, so due to its simplicity and reliability, I am considering to use it furthermore for now.

We are not going through any large server setup in this article. We are just going to look at the Networking part under the Digital Ocean account to manage a few DNS configuration to make emails stuff work with Mailgun.

Mailgun
After your successful registration with Mailgun service, the first step is to go to the Domains tab. For the fresh new account, you will see one record saying sandbox domain, which allows testing the development phase emails of your application before going to production.

Now, our concern is to setup emails environment for the production application.

Add Mailgun DomainGo through the steps below to add a new domain.

  • Click on the Add New Domain button and enter your production domain name in which you are going to use this Mailgun email service.
  • After adding your production domain, it will appear in the list under the Domains tab.
  • Click on the domain name from the list to view the email service configurations to be added to Digital Ocean service.
  • After clicking your domain from the list, you will see a page with a lot of details, and configuration that can be changed or managed to make the email service up and running.

Review the following headings on the page based on your need according to your application context.

  • Domain Information
  • Domain Settings
  • Tracking Settings
  • Security Settings For Outgoing Mail
  • IP Management
  • Domain Verification & DNS

In this article, we are going to look more in-depth about Domain Verification & DNS.

Before you add a few DNS configuration to the hosting partner end, the Mailgun shows the status of your domain as Unverified.

Note: I am taking Digital Ocean as my hosting partner, but not only Digital Ocean, this article works for any hosting provider which gives you access to manage DNS settings from their user panel based on what type of plan you choose from them.

After completing the above few things in the Mailgun end, we now have to log in into the hosting partner, for me its Digital Ocean.

Digital Ocean

After logging into digital Ocean dashboard, navigate to the Networking tab. It shows you the list of domains attached with droplets you own for your applications.

If you are going to manage the new domain, add it to the list, or select the existing one from the list.

Digital Ocean DNS PanelWe have to add a few DNS records like TXT, MX, CNAME to verify the Mailgun domain form the Digital Ocean DNS management panel.

Add following records under the Networking > yourdomain.com.

  • A record should always point to your droplet's IP Address, not the IP Address given by previous Mailgun configuration page.
  • While adding TXT record given by Mailgun, wrap them in double quotes before saving in Digital Ocean end.
  • TXT, MX, CNAME should be the correct value from Mailgun.

Mailgin Domain VerificationAfter all setup, you have to lastly go to Mailgun, under Domain Verification & DNS click on Check DNS Record Now, to verify the domain configuration page.

Usage

If you are using Mailgun with Laravel, you have to update your .env file or config/mail.php to use mailgun driver, and config/services.php

'mailgun' => [
    'domain' => 'your-mailgun-domain',
    'secret' => 'your-mailgun-api-key',
],


To get the API Key, select the domain from the Domains list, and you will see it on that page.

Testing

To test your production emails, now perform some email sending functionality, the emails log will appear in the Mailgun dashboard as processed, delivered, dropped and so on.

Conclusion

Thanks for reading this article up to the end. If you have any queries on setting up Mailgun, feel free to ask via comment. I will try to reply or even help for other hosting partners including Digital Ocean.

Last Updated: 7th, October 2018

Happy Coding!

]]>