Vishal Ribdiya

Vishal Ribdiya's Posts

Product Development Head

Top Laravel packages that you need in 2022

What is Laravel?

Laravel is the most popular PHP framework right now to develop web applications, it offers a very easy environment and services for developers.

In this blog, we are going to know about the packages that we must have to use while developing any laravel application.

Best Laravel Packages

Here we are going to see some best and top laravel packages that will help you to optimize your application performance and it's also very useful while doing the development.

IDE Helper

Github: https://github.com/barryvdh/laravel-ide-helper

It's a very helpful package and saves lots of time for the developer.

It will generate the helper file which enables our IDE to provide accurate autocompletion while doing the development.

Laravel Debugbar

Github : https://github.com/barryvdh/laravel-debugbar

This is very helpful when we have to check the page performance, in sense of how many queries are firing on the specific page? , how many models are loading? etc.

We can show the total processing time of the page, and the query results time too. by using that results we can do some refactor to our code and make our application more optimized.

Spatie Medialibrary

Github : https://github.com/spatie/laravel-medialibrary

This package is very useful when we are doing file uploads. also, it allows us to upload files to the s3 (AWS) very easily by changing just the file system driver.

The main functionality it has is it allows us to associate files with the Eloquent models.

Spatie Role Permission

Github : https://github.com/spatie/laravel-permission

It's 2022 and still, lots of developers are using the custom roles/permissions management. they even didn't familiar that this package have capabilities to manage each role/permissions management with a specific Eloquent model too.

We can assign roles or permissions to the user model or even any model. later we can check it via the middleware that this package is providing.

Ziggy

Github : https://github.com/tighten/ziggy

Before using this package you must need to implement the named routes into your laravel application.

Normally people can just provide a hardcoded URL into the JS file while doing the AJAX calls. But with this package, you can use the route we are using in blade files.

This allows us to use the route() helper method in the JS files.

June 09, 20222 minutesVishal RibdiyaVishal Ribdiya
How to use Multi Tenant with Multi Databases Into Any Laravel Application ?

People are quite afraid :), including me :) when it’s about the developing system that works with multi-tenant / multi-database.

In this tutorial, we will implement a multi-tenant system that will create a separate database when the new tenant will create.

Install Package

composer require stancl/tenancy

Then run the following command :

php artisan tenancy:install

Then add the service provider TenancyServiceProvider to your config/app.php file:

/*
 * Application Service Providers...
 */
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\TenancyServiceProvider::class, // <-- here

Setup Tenant Model

namespace App;

use Stancl\Tenancy\Database\Models\Tenant as BaseTenant;
use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Database\Concerns\HasDatabase;
use Stancl\Tenancy\Database\Concerns\HasDomains;

class Tenant extends BaseTenant implements TenantWithDatabase
{
    use HasDatabase, HasDomains;
}

Then, configure the package to use this model in config/tenancy.php:

'tenant_model' => \App\Tenant::class,

Create Migrations For tenant

Create one migration and move that migration file to migrations/tenant. So when we are going to create new tenant this migarations files will run for that new tenant.

You can do the same for seeders. if you want to change the seeder file then you can change it from the config/tenancy.php

Create Actual Tenant

$tenant = Tenant::create([
    'id' => time(),
]);

Result

Now when we run above code it will create new tenant and also create new database with related prefix and given id value.

So it will create following things :

  • New Tenant will be created in main database
  • New tenant database will be created
  • New migrations and seeders will be executed into new tenant database.

Hope that will helps a lot.

June 02, 20221 minuteVishal RibdiyaVishal Ribdiya
How to integrate Authorize Net into Laravel ?

In this tutorial, we are going to see how we can implement the authorized hosted payment gateway by using their UI and components and take payments from users via authorized net using Laravel.

Create HTML form as like below code :

authorize.blade.php

{{ Form::open(array('url' => 'https://test.authorize.net/payment/payment')) }}

Form::hidden('token', '{{$token}}');

Form::submit('Click Me!');

{{ Form::close() }}

You must have to pass $token to form, we will see below how we can generate that token.

AuthorizeController.php

 public function onboard() {

    $token = $this->getAnAcceptPaymentPage();

    return view('authorize', compact('token'));
 }

 public function getAnAcceptPaymentPage()
 {
    $merchantAuthentication = new AnetAPI\MerchantAuthenticationType();
    $merchantAuthentication->setName(config('payments.authorize.login_id'));
    $merchantAuthentication->setTransactionKey(config('payments.authorize.transaction_key'));

    $refId = 'ref' . time();

    $transactionRequestType = new AnetAPI\TransactionRequestType();
    $transactionRequestType->setTransactionType("authCaptureTransaction");
    $transactionRequestType->setAmount("2050"); 

    $setting1 = new AnetAPI\SettingType();
    $setting1->setSettingName("hostedPaymentButtonOptions");
    $setting1->setSettingValue("{\"text\": \"Pay\"}");

    $setting2 = new AnetAPI\SettingType();
    $setting2->setSettingName("hostedPaymentOrderOptions");
    $setting2->setSettingValue("{\"show\": false}");

    $setting3 = new AnetAPI\SettingType();
    $setting3->setSettingName("hostedPaymentReturnOptions");
    $setting3->setSettingValue(
        "{\"url\": \"http://127.0.0.1:8000/authorize-success?refID\".$refID, \"cancelUrl\": \"http://127.0.0.1:8000/authorize-cancel\", \"showReceipt\": true}"
    );

    // Build transaction request
    $request = new AnetAPI\GetHostedPaymentPageRequest();
    $request->setMerchantAuthentication($merchantAuthentication);
    $request->setRefId($refId);
    $request->setTransactionRequest($transactionRequestType);

    $request->addToHostedPaymentSettings($setting1);
    $request->addToHostedPaymentSettings($setting2);
    $request->addToHostedPaymentSettings($setting3);

    $controller = new AnetController\GetHostedPaymentPageController($request);
    $response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::SANDBOX);

    if (($response != null) && ($response->getMessages()->getResultCode() == "Ok")) {

    } else {
        echo "ERROR :  Failed to get hosted payment page token\n";
        $errorMessages = $response->getMessages()->getMessage();
        echo "RESPONSE : " . $errorMessages[0]->getCode() . "  " .$errorMessages[0]->getText() . "\n";
    }
    return $response->getToken();

}

Now create routes into web.php as specified below.

web.php

Route::get('authorize-onboard', [\App\Http\Controllers\AuthorizePaymentController::class, 'onboard'])->name('authorize.init');

Route::get('authorize-success', [\App\Http\Controllers\AuthorizePaymentController::class, 'success']);

How it's going to work ?? (flow)

So initially we will call the route that contains that authorization form and also contains the payment information.

Here we are generating token before, generally, it should be generated from the payment screen.

The token will contains the payment information so while generating it make sure you are passing all the details properly.

Now when you submit the form it will redirect you to the authorized checkout page from where users can do payments and again redirect to the success screen.

Once Payment is done successfully you will be redirected to the success route URL with the RefID which is basically the transaction ID, and you can perform related actions on success action.

Hope it will help.

April 16, 20222 minutesVishal RibdiyaVishal Ribdiya
How to check Laravel logs with UI Interface ?

Debugging the most important thing that developers always need while developing things.

If it's about local environments then we can easily check our logs by putting logs to local but when it's about live environments it's a time-consuming process.

We have to go to the files and open/download those files to local and then we are able to check live logs.

Here we are going to one package that will provide us the UI interface and we can easily check all our logs there.

We can also clear / delete our logs files from there. its better to use daily logs so we can trace logs easily.

Let's see how we can integrate that package to our existing laravel application.

Installation

composer require rap2hpoutre/laravel-log-viewer

Add Service Provider to config/app.php in providers section

Rap2hpoutre\LaravelLogViewer\LaravelLogViewerServiceProvider::class,

Access Logs UI By adding a new route

Route::get('logs', [\Rap2hpoutre\LaravelLogViewer\LogViewerController::class, 'index']);

That's it and you can see all the logs thereby accessing the given route.

That will saves lots of debugging time, hope that will help you :)

February 25, 20221 minuteVishal RibdiyaVishal Ribdiya
How to use laravel routes with Javascript / JQuery ?

Generally, we can use laravel routes into its blade files, but what do we have to do when we want to use routes in javascript? is that possible to use laravel routes into javascript?

Yes, now you can use laravel routes into laravel, thanks to Tighten/Ziggi package.

In this tutorial, we will learn how we can use laravel routes into javascript, so let's get started.

Install the package

composer require tightenco/ziggy

Update your main layout file

Add the @routes Blade directive to your main layout (before your application's JavaScript), and the route() helper function will now be available globally!

E.g (app.blade.php)

... ... @routes .. ..

Usage

// routes/web.php

Route::get('users', fn (Request $request) => /* ... */)->name('users.index');

// app.js

route('users.index'); // 'https://url.test/users'

So this is how its works, so simple right :)

You can get more information about this package from here

Kepp connected to us to get the latest laravel information.

January 03, 20223 minutesVishal RibdiyaVishal Ribdiya
How to use laravel multi tenant (stancl/tenancy) with single DB ?

Nowadays multi-tenant applications are more useful than single-tenant applications. We can use multi-tenant with multiple databases or single databases as per our need. But it's better to use a single DB with a multi-tenant when you have a small application.

In this tutorial, we are going to use multi-tenant with a single database.

We will implement multi-tenant with single DB by using the following package: https://github.com/archtechx/tenancy

Assuming you already have Laravel 8 repo setup. Now please follow the given steps to implement multi-tenancy with a single DB.

Package Installation

Run following commands :

  1. composer require stancl/tenancy

  2. php artisan tenancy:install

  3. php artisan migrate

Add following service provider to config/app.php

App\Providers\TenancyServiceProvider::class

Create Custom Model

Now create modal named MultiTenant into app\Models

MultiTenant.php

 SavingTenant::class,
        'saved'    => TenantSaved::class,
        'creating' => CreatingTenant::class,
        //        'created' => TenantCreated::class,
        'updating' => UpdatingTenant::class,
        'updated'  => TenantUpdated::class,
        'deleting' => DeletingTenant::class,
        'deleted'  => TenantDeleted::class,
    ];
}

Update Tenancy Configuration

As we have added custom model we also need to define that model into config/tenancy.php

Please change tenant_model value to our custom model.

'tenant_model' => \App\Models\MultiTenant::class,

Add Resolver

To use multi tenant with single DB we also need to add our customer resolver, that will be used into Middlewares that we will create ahead.

Create MultiTenantResolver into app\Resolvers

App\Resolvers\MultiTenantResolver.php

find(Auth::user()->tenant_id)) {
            return $tenant;
        }

        throw new TenantCouldNotBeIdentifiedByPathException($id);
    }

    public function getArgsForTenant(Tenant $tenant): array
    {
        return [
            [$tenant->id],
        ];
    }
}

Add Middleware

We will create our custom middleware that will set the current tenant into cache, and that will used by package to fire default query where('tenant_id', "tenant id we have set into middleware")

App\Http\Middleware\MultiTenantMiddleware.php

tenancy = $tenancy;
        $this->resolver = $resolver;
    }

    /**
     * Handle an incoming request.
     *
     * @param  Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $tenant = Auth::user()->tenant_id;

        return $this->initializeTenancy(
            $request, $next, $tenant
        );
    }
}

Also, don't forget to add middleware alias into App\Http\kernel.php

 protected $routeMiddleware = [
        ..............
        'multi_tenant' => MultiTenantMiddleware::class,
];

Now we will apply this multi_tenant middleware to our routes.

Add Trait to tenant-specific models

We have to add BelongsToTenant trait to all of our tenant-specific models.

Say if we want to add tenant_id into the users table then we must have to add BelongsToTenant to the app\Models\User model.

That trait will by default add following query everytime when we will try to fetch records or update records.

Where('tenant_id', 'tenant id will taken from cache')

Add tenant_id to tenant-specific migrations

As we have added the tenant trait, we must have to add tenant_id into tenant-specific migrations as specified below.

public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            ...........................

            $table->string('tenant_id');

            $table->foreign('tenant_id')
                ->references('id')
                ->on('tenants')
                ->onUpdate('cascade')
                ->onDelete('cascade');

            $table->timestamps();
        });

Update TenancyServiceProvider

Replace the App\Providers\TenactServiceProvider by the following code.

 [],
            Events\TenantCreated::class => [
                JobPipeline::make([
                    Jobs\CreateDatabase::class,
                    Jobs\MigrateDatabase::class,
                    // Jobs\SeedDatabase::class,

                    // Your own jobs to prepare the tenant.
                    // Provision API keys, create S3 buckets, anything you want!

                ])->send(function (Events\TenantCreated $event) {
                    return $event->tenant;
                })->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
            ],
            Events\SavingTenant::class => [],
            Events\TenantSaved::class => [],
            Events\UpdatingTenant::class => [],
            Events\TenantUpdated::class => [],
            Events\DeletingTenant::class => [],
            Events\TenantDeleted::class => [
                JobPipeline::make([
                    Jobs\DeleteDatabase::class,
                ])->send(function (Events\TenantDeleted $event) {
                    return $event->tenant;
                })->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production.
            ],

            // Domain events
            Events\CreatingDomain::class => [],
            Events\DomainCreated::class => [],
            Events\SavingDomain::class => [],
            Events\DomainSaved::class => [],
            Events\UpdatingDomain::class => [],
            Events\DomainUpdated::class => [],
            Events\DeletingDomain::class => [],
            Events\DomainDeleted::class => [],

            // Database events
            Events\DatabaseCreated::class => [],
            Events\DatabaseMigrated::class => [],
            Events\DatabaseSeeded::class => [],
            Events\DatabaseRolledBack::class => [],
            Events\DatabaseDeleted::class => [],

            // Tenancy events
            Events\InitializingTenancy::class => [],
            Events\TenancyInitialized::class => [
//                Listeners\BootstrapTenancy::class,
            ],

            Events\EndingTenancy::class => [],
            Events\TenancyEnded::class => [
                Listeners\RevertToCentralContext::class,
            ],

            Events\BootstrappingTenancy::class => [],
            Events\TenancyBootstrapped::class => [],
            Events\RevertingToCentralContext::class => [],
            Events\RevertedToCentralContext::class => [],

            // Resource syncing
            Events\SyncedResourceSaved::class => [
                Listeners\UpdateSyncedResource::class,
            ],

            // Fired only when a synced resource is changed in a different DB than the origin DB (to avoid infinite loops)
            Events\SyncedResourceChangedInForeignDatabase::class => [],
        ];
    }

    public function register()
    {
        //
    }

    public function boot()
    {
        $this->bootEvents();
//        $this->mapRoutes();

        $this->makeTenancyMiddlewareHighestPriority();
    }

    protected function bootEvents()
    {
        foreach ($this->events() as $event => $listeners) {
            foreach (array_unique($listeners) as $listener) {
                if ($listener instanceof JobPipeline) {
                    $listener = $listener->toListener();
                }

                Event::listen($event, $listener);
            }
        }
    }

    protected function mapRoutes()
    {
        if (file_exists(base_path('routes/tenant.php'))) {
            Route::namespace(static::$controllerNamespace)
                ->group(base_path('routes/tenant.php'));
        }
    }

    protected function makeTenancyMiddlewareHighestPriority()
    {
        $tenancyMiddleware = [
            // Even higher priority than the initialization middleware
            Middleware\PreventAccessFromCentralDomains::class,

            Middleware\InitializeTenancyByDomain::class,
            Middleware\InitializeTenancyBySubdomain::class,
            Middleware\InitializeTenancyByDomainOrSubdomain::class,
            Middleware\InitializeTenancyByPath::class,
            Middleware\InitializeTenancyByRequestData::class,
        ];

        foreach (array_reverse($tenancyMiddleware) as $middleware) {
            $this->app[\Illuminate\Contracts\Http\Kernel::class]->prependToMiddlewarePriority($middleware);
        }
    }
}

Create / Fetch Tenant

Now we have to create a tenant and give that tenant_id to related users.

each user contains their specific tenant_id.

Use the following code to create a tenant :

 $tenant1 = \App\Models\MultiTenant::create([
     'name' => 'Tenant 1'
 ]);

 $tenant2 = \App\Models\MultiTenant::create([
     'name' => 'Tenant 2'
  ]);

That will create tenant into tenants table and values will be stored into data column as a son.

$tenant1 = App\Models\MultiTenant::where('data->name', 'Tenant 1')->first();

$tenant2 = App\Models\MultiTenant::where('data->name', 'Tenant 2')->first();

$tenant1User = User::where('id', 'user id here')->update(['tenant_id' => $tenant1->id]);

$tenant2User = User::where('id', 'user id here')->update(['tenant_id' => $tenant2->id]);

Now we have 2 tenants with 2 separate users who contain separate tenant ids.

Add Middleware to Routes

Now do login with User 1 and try to fetch all users from the database, it will return users of logged-in users' tenants only.

As we have the BelongToTenant trait into the User model.

Route::group(['middleware' => ['auth', 'multi_tenant']], function () { Route::get('users', function() {

 // only tenant-1 users will be returned because we are setting the logged-in user tenant into the cache from `multi_tenant`middleware.
 $allUsers = User::all();
});

});

You can use the same for other models too.

Hope this helps you.

August 14, 20212 minutesVishal RibdiyaVishal Ribdiya
How to generate pre-signed URL from s3 bucket ?

People nowadays are becoming more intelligent, so better to protect our application's content/data from those who are calling themself hackers.

One of the best examples is the data URLs from AWS buckets. it's not a good idea to store sensitive data into a public AWS Bucket, as the URL is accessible by the people.

Of Course, you can store profile avatars and others data to the public bucket's that not contains any confidential information. so that's fine.

But when it's about confidential information like PAN CARD Details, AADHAR Card Details, Bank Informations we Must Recommend using AWS Protected Bucket.

In this tutorial, we are going to show that how we can prevent that kind of case, Or how we can integrate AWS Protected Bucket in our Laravel Application.

The following code will help you to generate a pre-signed AWS URL that will prevent our data, that URL is non-guessable and it will expire within some minutes/hours specified by us.

So let's start with some code :

$s3 = \Storage::disk(config('filesystems.s3_protected_disk'));
$client = $s3->getDriver()->getAdapter()->getClient();
$expiry = "+1 minutes";
$command = $client->getCommand('GetObject', [
  'Bucket' => \Config::get('filesystems.disks. s3_protected_disk.bucket'),
      'Key'    => 'Path to your file',
    ]);
$request = $client->createPresignedRequest($command, $expiry);
    return (string) $request->getUrl();

So here we have created an s3 instance and it's stored on the $s3 variable, we have specified the expiry time as 1 minute so the given URL for data will be expired within a minute.

Also, we have to specify the bucket name and path to our protected file to generate AWS pre-signed URL.

It will return the pre-signed URL and its looks like as the following URL.

https://pre-signed.s3.au-west-2.amazonaws.com/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxx%2F20180210%2Feu-west-2%2Fs3%2Faws4_request&X-Amz-Date=20210210T171315Z&X-Amz-Expires=60&X-Amz-Signature=xxxxxxxx&X-Amz-SignedHeaders=host

Hope this helps.

July 16, 20212 minutesVishal RibdiyaVishal Ribdiya
How to develop package into Laravel ?

In our daily life, we are going through lots of packages, and some of us don't know how to build our own package into Laravel.

We are going to perform the core steps to create your own package in laravel. In this tutorial we are going to build a zoom package, so we will perform steps related to it.

Setup Fresh Laravel Repo

Setup fresh laravel repo, and then create directories within it.

for e.g Create infyomlabs/zoom-api directory into the root.

2021-01-23-600c1615b8dc5

Now create src directory into zoom-api

Run Composer init Into src Directory

After hitting composer init it will ask for some information from you, as you can see below image I have entered some of the information. you can just hit enter if you do not want to add other information.

2021-01-23-600c1880b29e5

Add your config file (Optional)

Create a directory config into the src directory and add your config.php file there from where you can manage your env variables.

2021-01-23-600c195f8a4e3

Add Service Provider

Create your service provider from where you can do lots of actions. like you can publish config/routes/ migrations files from there. Here we are publishing the zoom config file.

2021-01-23-600c1aaf5a24d

Add your class (Which contains all functions)

Here we have added a Zoom class which will contain all zoom functions.

2021-01-23-600c1b713c011

Update Composer.json

2021-01-23-600c1cbe97ce6

Finally, Test it in your existing project

Put the following code to your main composer.json (in your project's root). and hit composer update

  "repositories": [
        {
            "type": "path",
            "url": "infyomlabs/zoom-api",
            "options": {
                "symlink": true
            }
        }
    ],
    "license": "MIT",
    "require": {
        "infyomlabs/zoom-api": "dev-develop"
    },
January 27, 20211 minuteVishal RibdiyaVishal Ribdiya