# How does Laravel Telescope work?

### Introduction

ဒီ article မှာတော့ Laravel ရဲ့ First Party Package တစ်ခုဖြစ်တဲ့ [Laravel Telescope](https://laravel.com/docs/10.x/telescope) ရဲ့အလုပ်လုပ်ပုံကို overview ကြည့်ကြမှာဖြစ်ပါတယ်။ Telescope မတိုင်ခင်က Laravel Development တွေ အတွက် Debugging Tools ([Debugbar](https://github.com/barryvdh/laravel-debugbar), [PHP Clockwork](https://github.com/itsgoingd/clockwork) and so on.) တွေကပြန့်ကြဲနေပြီးတော့ တစ်ခုချင်းစီမှာလည်း အားသာချက် အားနည်းချက်တွေနဲ့ ရှိနေပါတယ်။ ဒါကို Laravel Ecosystem ကသူတွေအတွက်ပိုလည်း အဆင်ပြေအောင် Laravel အနေနဲ့ First Party Package ဖြစ်တဲ့ Laravel Telescope ကိုထုတ်ပြီးတော့ Development ကော Production ကော အဆင်ပြေအောင် လုပ်ပေးလိုက်ပုံပါပဲ။

***ဒီအချိန်မှာ Telescope က အကောင်းဆုံး tools တစ်ခုဖြစ်လာတာကလည်း Laravel Team ကိုယ်တိုင်က ရှိပြီးသား Built-in events တွေ အပြင် Telescope အတွက်လိုအပ်မယ့် Events တွေကိုပါ Framework Level ကနေ fire လုပ်ပေးလိုက်လို့ပါပဲ။ ဒါကတော့ First Party Package တွေရဲ့အားသာချက်ပါ။ Framework Level ကိုပါပြင်ပြီးလိုအပ်တာအကုန် ထောက်ပံ့ပေးထားတော့ တစ်ခြား Third Party Package တွေထက်ကို ပိုပြီးပြည့်စုံကောင်းမွန်ဖို့ အခွင့်အလမ်းများပါတယ်။***

### Event-Driven Architecture

Telescope ကို Event-Driven Architecture ကိုသုံးပြီးတော့ တည်ဆောက်ထားပါတယ်။ ဆိုလိုတာက Request Life-Cycle အတွင်းမှာ Laravel Framework ကနေ လိုအပ်တဲ့ Events တွေ Fire လုပ်ပေးမယ်။ Telescope က အဲ့ Events တွေကို Listen လုပ်ပြီးတော့ လိုအပ်သလို Record ယူမယ်၊ သင့်တော်သလို Viewer နဲ့ပြန်ပြပေးမယ် အဲ့တာပါပဲ။

**တစ်ကယ်လို့  code diving အပိုင်းကိုပဲကြည့်ချင်တယ်ဆိုရင်** [**Let's dive into the code**](https://kalaung.org/how-does-laravel-telescope-work#heading-lets-dive-into-the-code) **ကိုတန်းသွားလို့ရပါတယ်။**

### **Available Watchers**

Telescope ကိုသုံးပြီး Watch လုပ်လို့ရနိုင်မယ့်ဟာတွေကတော့ အောက်မှာပြထားပါတယ်။ Reference - [Available Watchers](https://laravel.com/docs/10.x/telescope#available-watchers)

[Batch Watcher](https://laravel.com/docs/10.x/telescope#batch-watcher) − Batched Jobs တွေကို Queue လုပ်ထားတဲ့ records တွေကို watch လုပ်ဖ်ို့သုံးနိုင်ပါတယ်။

[Cache Watcher](https://laravel.com/docs/10.x/telescope#cache-watcher) - Cache ထဲကို interact လုပ်သမျှကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Command Watcher](https://laravel.com/docs/10.x/telescope#command-watcher) - Command တွေ Run တဲ့အခါ arguments, options, output တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Dump Watcher](https://laravel.com/docs/10.x/telescope#dump-watcher) - dump() ဆိုတဲ့ global function ကိုသုံးပြီး ထုတ်ထားသမျှကို watch လုပ်ဖို့အတွက်သုံးနိုင်ပါတယ်။

[Event Watcher](https://laravel.com/docs/10.x/telescope#event-watcher) - App ထဲမှာ fire/dispatch လုပ်သွားတဲ့ events တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Exception Watcher](https://laravel.com/docs/10.x/telescope#exception-watcher) - Reportable Exceptions တွေတက်တဲ့အခါ watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Gate Watcher](https://laravel.com/docs/10.x/telescope#gate-watcher) - Gate တွေကနေဖြတ်သွားတဲ့ data တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[HTTP Client Watcher](https://laravel.com/docs/10.x/telescope#http-client-watcher) - [Laravel HTTP Client](https://laravel.com/docs/10.x/http-client) ကိုသုံးပြီးတော့ တစ်ခြား application တွေကိုလှမ်းခေါ်တဲ့ outgoing requests တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။ ဒီနေရာမှာ သတိထားဖို့လိုတာက [Laravel HTTP Client](https://laravel.com/docs/10.x/http-client) ကိုမသုံးပဲ သူ့အောက်က [guzzlehttp/guzzle](https://docs.guzzlephp.org/en/stable/) ကိုတိုက်ရိုက်သွားသုံးမယ်ဆိုရင်တော့ Event Fire လုပ်မပေးတော့တာမလို့ Telescope ရဲ့ HTTP Client အပိုင်းမှာ entries တွေကိုတွေ့ရတော့မှာ မဟုတ်ပါဘူး။

[Job Watcher](https://laravel.com/docs/10.x/telescope#job-watcher) - Job တွေကို watch လုပ်ဖို့အတွက်သုံးနိုင်ပါတယ်။

[Log Watcher](https://laravel.com/docs/10.x/telescope#log-watcher) − Log ထုတ်ထားတာတွေကိုကြည့်ဖို့သုံးနိုင်ပါတယ် (အရင်က တစ်ခြား Package တွေထည့်ပြီးတော့ကြည့်ခဲ့ရတာပါ။ အခုတော့ Telescope တစ်ခုနဲ့တင်အကုန်ပြီးပါတယ်။)

[Mail Watcher](https://laravel.com/docs/10.x/telescope#mail-watcher) - App ကပို့တဲ့ mails တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Model Watcher](https://laravel.com/docs/10.x/telescope#model-watcher) - Model Events တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Notification Watcher](https://laravel.com/docs/10.x/telescope#notification-watcher) - Notifications တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Query Watcher](https://laravel.com/docs/10.x/telescope#query-watcher) - SQL QUERY တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။ တော်တော် လူသုံးများတဲ့ watcher တစ်ခုဖြစ်ပါတယ်။

[Redis Watcher](https://laravel.com/docs/10.x/telescope#redis-watcher) - Redis Command တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Request Watcher](https://laravel.com/docs/10.x/telescope#request-watcher) - Request တစ်ခုလုံးရဲ့ information တွေကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[Schedule Watcher](https://laravel.com/docs/10.x/telescope#schedule-watcher) - Scheduler ကို watch လုပ်ဖို့သုံးနိုင်ပါတယ်။

[View Watcher](https://laravel.com/docs/10.x/telescope#view-watcher) - Blade နဲ့ပတ်သတ်တဲ့ information တွေကိုကြည့်ဖို့သုံးနိုင်ပါတယ်။

### **Let's dive into the code**

Source Diving အပိုင်းမှာတော့ လိုအပ်တဲ့အပိုင်းတွေကိုပဲ ဖြတ်ထုတ်ပြီးတော့ ရှင်းပြသွားမှာဖြစ်ပါတယ်။ ဒီအခြေခံကိုအသုံးချပြီးတော့ ကိုယ်တိုင် ဆက်ပြီးတော့ dive လုပ်နိုင်ပါတယ်။

အရင်ဆုံး Laravel Package တစ်ခုရဲ့ entry point ဖြစ်တဲ့ Service Provider ကိုကြည့်ကြရအောင်။

[TelescopeServiceProvider.php](https://github.com/laravel/telescope/blob/4.x/src/TelescopeServiceProvider.php)

```php
<?php

namespace Laravel\Telescope;

use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Laravel\Telescope\Contracts\ClearableRepository;
use Laravel\Telescope\Contracts\EntriesRepository;
use Laravel\Telescope\Contracts\PrunableRepository;
use Laravel\Telescope\Storage\DatabaseEntriesRepository;

class TelescopeServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->registerCommands();
        $this->registerPublishing();

        if (! config('telescope.enabled')) {
            return;
        }

        Route::middlewareGroup('telescope', config('telescope.middleware', []));

        $this->registerRoutes();
        $this->registerMigrations();

        Telescope::start($this->app);
        Telescope::listenForStorageOpportunities($this->app);

        $this->loadViewsFrom(
            __DIR__.'/../resources/views', 'telescope'
        );
    }

    // Code to be ignored in this article

}
```

TelescopeServiceProvider ရဲ့ boot method ထဲမှာဆိုရင် commands၊ publishables တွေကို အရင် register လုပ်ပါတယ်။ ပြီးတော့ telescope  ကို Enable လုပ်ထားလားစစ်ပါတယ်။ မလုပ်ထားဘူးဆိုရင် telescope နဲ့ပါတ်သတ်တာတွေကို ရပ်လိုက်မှာဖြစ်ပြီးတော့ လုပ်ထားတယ် ဆိုရင် telescope နဲ့ပါတ်သတ်တာတွေကို ဆက်လုပ်မှာပါ။ ဒီ option ကိုတော့ [Telescope config](https://github.com/laravel/telescope/blob/4.x/config/telescope.php) ကနေ control လုပ်နိုင်မှာပါ။ ကျန်တာတွေကတော့ routes၊ migrations၊ views တွေကိုဆက်ပြီးတော့ register လုပ်ပါတယ်။ ဒီနေရာမှာ အသားပေးချင်တာကတော့ ***Telescope::start($this-&gt;app)*** ပဲဖြစ်ပါတယ်။

[Telescope.php](https://github.com/laravel/telescope/blob/4.x/src/Telescope.php)

```php
<?php

namespace Laravel\Telescope;

use Closure;
use Exception;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Log\Events\MessageLogged;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Illuminate\Support\Testing\Fakes\EventFake;
use Laravel\Telescope\Contracts\EntriesRepository;
use Laravel\Telescope\Contracts\TerminableRepository;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Throwable;

class Telescope
{
    use AuthorizesRequests,
        ExtractsMailableTags,
        ListensForStorageOpportunities,
        RegistersWatchers;


    // Code to be ignored in this article
    
    public static function start($app)
    {
        if (! config('telescope.enabled')) {
            return;
        }

        static::registerWatchers($app);

        static::registerMailableTagExtractor();

        if (! static::runningWithinOctane($app) &&
            (static::runningApprovedArtisanCommand($app) ||
            static::handlingApprovedRequest($app))
        ) {
            static::startRecording($loadMonitoredTags = false);
        }

    // Code to be ignored in this article
    }
```

start method မှာဆိုရင် ထပ်ပြီးတော့  telescope ကို enable လုပ်ထားလားဆိုတာစစ်ပါတယ်။ အဲ့နောက်မှာတော့ အပေါ်မှာပြောထားတဲ့ watchers တွေကို စပိးတော့ register လုပ်ပါတယ်။ အဲ့တာပြီးရင် telescope entries လို့ခေါ်တဲ့ records တွေကို စသိမ်းသင့်မသိမ်းသင့်ဆိုတာဆုံးဖြတ်ပါတယ်။ ဒီနေရာမှာစိတ်ဝင်စားဖို့ကောင်းတာက အဲ့ဆုံးဖြတ်တဲ့ working flow ဖြစ်ပါတယ်။

တစ်ကယ်လို့ [Laravel Octane](https://laravel.com/docs/10.x/octane) ကိုမသုံးထားရင် (and) approved artisan command (or) approved request တွေဆိုရင်သိမ်းမယ်လို့ဆုံးဖြတ်ထားပါတယ်။

```php
    protected static function runningApprovedArtisanCommand($app)
    {
        return $app->runningInConsole() && ! in_array(
            $_SERVER['argv'][1] ?? null,
            array_merge([
                // 'migrate',
                'migrate:rollback',
                'migrate:fresh',
                // 'migrate:refresh',
                'migrate:reset',
                'migrate:install',
                'package:discover',
                'queue:listen',
                'queue:work',
                'horizon',
                'horizon:work',
                'horizon:supervisor',
            ], config('telescope.ignoreCommands', []), config('telescope.ignore_commands', []))
        );
    }
```

App ကိုလည်း console ကနေ run နေတယ်၊ [Telescope config](https://github.com/laravel/telescope/blob/4.x/config/telescope.php) ရဲ့ ignore\_commands list ထဲမှာလက်ရှိ run နေတဲ့ command ကမပါဘူး ဆိုရင် approvedArtisanCommand (သိမ်းသင့်တဲ့ command) လို့သတ်မှတ်ပါတယ်။

```php
    protected static function handlingApprovedRequest($app)
    {
        if ($app->runningInConsole()) {
            return false;
        }

        return static::requestIsToApprovedDomain($app['request']) &&
            static::requestIsToApprovedUri($app['request']);
    }

    protected static function requestIsToApprovedDomain($request): bool
    {
        return is_null(config('telescope.domain')) ||
            config('telescope.domain') !== $request->getHost();
    }

    protected static function requestIsToApprovedUri($request)
    {
        if (! empty($only = config('telescope.only_paths', []))) {
            return $request->is($only);
        }

        return ! $request->is(
            collect([
                'telescope-api*',
                'vendor/telescope*',
                (config('horizon.path') ?? 'horizon').'*',
                'vendor/horizon*',
            ])
            ->merge(config('telescope.ignore_paths', []))
            ->unless(is_null(config('telescope.path')), function ($paths) {
                return $paths->prepend(config('telescope.path').'*');
            })
            ->all()
        );
    }
```

approved request ဟုတ်မဟုတ်ကိုဆုံးဖြတ်တဲ့နေရာမှာ Telescope Domain ကနေဝင်လာတဲ့ request ဟုတ်မဟုတ်ကို စစ်ပါတယ်။ နောက်တစ်ခုကတော့ ​Application Logic နဲ့မဆိုင်တဲ့ Tools (Telescope, Horizon) စတာတွေကို request လုပ်တာလားဆိုတာကိုစစ်ပါတယ်။ သဘောကတော့ Application Logic နဲ့မဆိုင်တာတွေဝင်လာရင်််entries တွေကိုမသိမ်းတော့ပဲ ကျော်လိုက်မယ်လို့ဆိုလိုတာဖြစ်ပါတယ်။ မဟုတ်ရင် မလိုတဲ့ entries တွေကို database ထဲသိမ်းမိပြီးတော့ အသုံးမဝင်ဖြစ်မှာစိုးလို့ဖြစ်ပါတယ်။ သေချာတည်ဆောက်ထားတာကိုဒီနေရာမှာမြင်တွေ့ရပါတယ်။

ဘာတွေသိမ်းမယ်၊ ဘယ်လိုအလုပ်လုပ်မယ်ဆိုတာသိပြီဆိုတော့ အခု registerWatchers ကိုပြန်သွားရအောင်။

```php
    protected static function registerWatchers($app)
    {
        foreach (config('telescope.watchers') as $key => $watcher) {
            if (is_string($key) && $watcher === false) {
                continue;
            }

            if (is_array($watcher) && ! ($watcher['enabled'] ?? true)) {
                continue;
            }

            $watcher = $app->make(is_string($key) ? $key : $watcher, [
                'options' => is_array($watcher) ? $watcher : [],
            ]);

            static::$watchers[] = get_class($watcher);

            $watcher->register($app);
        }
    }
```

[Telescope config](https://github.com/laravel/telescope/blob/4.x/config/telescope.php) ရဲ့ watchers စာရင်းကိုဖတ်ပါတယ်။ ပြီးတော့ enable လုပ်ထားတာတွေကိုပဲယူပြီးတော့ သက်ဆိုင်ရာ watcher ရဲ့ register method  ကိုလှမ်းခေါ်ပေးပါတယ်။ ဒီနေရာမှာ watchers တိုင်းကိုလိုက် dive မပြတော့ပဲနဲ့ [Log Watcher](https://github.com/laravel/telescope/blob/4.x/src/Watchers/LogWatcher.php) ကိုပဲ dive ပြပါမယ်။ ကျန်တာတွေကိုတော့ [ဒီနေရာ](https://github.com/laravel/telescope/tree/4.x/src/Watchers) မှာဆက်ဖတ်ကြည့်လို့ရပါတယ်။

```php
<?php

namespace Laravel\Telescope\Watchers;

use Illuminate\Log\Events\MessageLogged;
use Illuminate\Support\Arr;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Psr\Log\LogLevel;
use Throwable;

class LogWatcher extends Watcher
{
    /**
     * The available log level priorities.
     */
    private const PRIORITIES = [
        LogLevel::DEBUG => 100,
        LogLevel::INFO => 200,
        LogLevel::NOTICE => 250,
        LogLevel::WARNING => 300,
        LogLevel::ERROR => 400,
        LogLevel::CRITICAL => 500,
        LogLevel::ALERT => 550,
        LogLevel::EMERGENCY => 600,
    ];

    /**
     * Register the watcher.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function register($app)
    {
        $app['events']->listen(MessageLogged::class, [$this, 'recordLog']);
    }

    /**
     * Record a message was logged.
     *
     * @param  \Illuminate\Log\Events\MessageLogged  $event
     * @return void
     */
    public function recordLog(MessageLogged $event)
    {
        if (! Telescope::isRecording() || $this->shouldIgnore($event)) {
            return;
        }

        Telescope::recordLog(
            IncomingEntry::make([
                'level' => $event->level,
                'message' => (string) $event->message,
                'context' => Arr::except($event->context, ['telescope']),
            ])->tags($this->tags($event))
        );
    }

    /**
     * Extract tags from the given event.
     *
     * @param  \Illuminate\Log\Events\MessageLogged  $event
     * @return array
     */
    private function tags($event)
    {
        return $event->context['telescope'] ?? [];
    }

    /**
     * Determine if the event should be ignored.
     *
     * @param  mixed  $event
     * @return bool
     */
    private function shouldIgnore($event)
    {
        if (isset($event->context['exception']) && $event->context['exception'] instanceof Throwable) {
            return true;
        }

        $minimumTelescopeLogLevel = static::PRIORITIES[$this->options['level'] ?? 'debug']
                ?? static::PRIORITIES[LogLevel::DEBUG];

        $eventLogLevel = static::PRIORITIES[$event->level]
                ?? static::PRIORITIES[LogLevel::DEBUG];

        return $eventLogLevel < $minimumTelescopeLogLevel;
    }
}
```

registerWatchers ကနေတစ်ဆင့် watcher တွေရဲ့ register method ကိုလှမ်းခေါ်တယ်ဆိုတာကို အရှေ့မှာပြပြီးဖြစ်ပါတယ်။ အခု အဲ့ register ကနေဆက်ကြည့်ရအောင်။ Laravel မှာ Log ထုတ်ရင် App ထဲကနေ Illuminate\\Log\\Events\\MessageLogged::class ဆိုတဲ့ event ကို fire/dispatch ပေးပါတယ်။  အဲ့ event ကို register method မှာ listen လုပ်နေပြီး အဲ့ event လာတာနဲ့ recordLog ဆိုတဲ့ method ကိုခေါ်ပေးပါတယ်။ ***ဒီနေရာမှာ Event-Driven Architecture ဆိုတာကိုပြန်မြင်ဖို့လိုလာပါတယ်။ Telescope package ရဲ့ watchers တိုင်းက အဲ့ architecture ကိုသုံးထားတယ်ဆိုတာ watcher တိုင်းရဲ့ register method ကိုလိုက်ကြည့်နိုင်ပါတယ်။***

recordLog မှာတော့ Telescope က recording  လုပ်နေတယ်ဆိုတာရယ်၊ event  က ignore မလုပ်သင့်ဘူးဆိုတာရယ်သေချာရင် database ထဲကို စမှတ်ပါတယ်။ ပြီးရင်တော့ frontend မှာ telescope dashboard ဆောက်ပြီးတော့ လိုအပ်သလို data ကိုပြလိုက်တာပဲဖြစ်ပါတယ်။

### Extending a custom watcher

Watcher တစ်ခုရဲ့အလုပ်လုပ်ပုံကိုသိပြီဆိုတော့ တစ်ကယ်လို့သူ့မှာမပါတဲ့ watcher ကို ကိုယ်တိုင် implement လုပ်ချင်ရင်လည်းလွယ်သွားပါပြီ။ Custom Watcher File တစ်ခုဆောက်ပြီး [Laravel\\Telescope\\Watchers\\Watcher](https://github.com/laravel/telescope/blob/4.x/src/Watchers/Watcher.php) ကို extends လုပ်ပြီးတော့ ကိုယ်လိုချင်တဲ့ event ကို register method  မှာဖမ်း၊ Telescope config ကို publish လုပ်ပြီးတော့ Custom Watcher File ကို telescope ရဲ့ watchers section မှာ register ပြီးတော့ enable လုပ်ပေးလိုက်တာနဲ့ telescope က handle လုပ်ပေးသွားမှာပါ။ ဒီနေရာမှာ ကိုယ့် custom watcher အတွက်လိုအပ်တဲ့ dashboard component (view) ကိုတော့ ကိုယ်တိုင်တည်ဆောက်ပြီး api endpoints အနည်းငယ်ကိုလည်း ထုတ်ပေးဖို့လိုပါလိမ့်မယ်။ Laravel က ထုတ်ပေးတဲ့ event list ကို ***php artisan event:list*** ဆိုတာကို run ပြီးတော့ ကြည့်လို့ရပါတယ်။ ကိုယ်တိုင် custom event fire/dispatch လုပ်ပြီး ပြန် listen လုပ်ချင်တယ်ဆိုလည်းရပါတယ်။

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1688366654128/98bb3703-f3c5-4c57-9268-7b3f3eae66bc.png align="center")

### Extra

Larave Telescope  ကို  production မှာသုံးမယ်ဆိုရင်တော့ တစ်ခုသတိထားဖို့လိုပါတယ်။ သူက ​entries  တွေကို database ထဲမှာသိမ်းတာမလို့ ကြာလာတာနဲ့ အမျှ database size ကြီးလာပါလိမ့်မယ်။ အဲ့အတွက် [prune command](https://laravel.com/docs/10.x/telescope#data-pruning) ကို built-in ထောက်ပံ့ပေးထားပါတယ်။ ကိုယ့် Application ရဲ့ requirements ပေါ်မူတည်ပြီးလိုအပ်သလို configure ဖို့လိုအပ်ပါလိမ့်မယ်။

### Complete Event List

ဟိုးအရင် Laravel version အဟောင်းတွေမှာတော့ Framework + Plugins တွေက fire လုပ်ပေးတဲ့ event စာရင်းကို ပြဖို့ command ပေးမထားပေမယ့် နောက်ပိုင်း version တွေမှာတော့ Laravel က command တစ်ခုကိုထုတ်ပေးထားပါတယ်။

```bash
php artisan event:list
```

### Conclusion

Telescope ရဲ့အလုပ်လုပ်ပုံကို  code ကော logic ပါသိပြီဆိုတော့ လိုအပ်သလို  အသုံးချ၊ ပြုပြင်နိုင်မယ်လို့ထင်ပါတယ်။ တစ်ခြား package တွေ framework တွေကိုလည်း ကိုယ်တိုင် dive ပြီး အသင့်တော်ဆုံးကိုရွေးနိုင်၊ ပြုပြင်အသုံးချနိုင်မယ်လို့မျှော်လင့်ပါတယ်။

Nay Thu Khant

Solution Architect @ [**onenex.co**](http://onenex.co)
