Thursday, November 30, 2023

Tuesday, October 31, 2023

Vue js Lazy loading translations

 //i18n-setup.js

import Vue from 'vue'

import VueI18n from 'vue-i18n'

import messages from '@/lang/en'

import axios from 'axios'


Vue.use(VueI18n)


export const i18n = new VueI18n({

  locale: 'en', // set locale

  fallbackLocale: 'en',

  messages // set locale messages

})


const loadedLanguages = ['en'] // our default language that is preloaded


function setI18nLanguage (lang) {

  i18n.locale = lang

  axios.defaults.headers.common['Accept-Language'] = lang

  document.querySelector('html').setAttribute('lang', lang)

  return lang

}


export function loadLanguageAsync (lang) {

  if (i18n.locale !== lang) {

    if (!loadedLanguages.includes(lang)) {

      return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}`).then(msgs => {

        i18n.setLocaleMessage(lang, msgs.default)

        loadedLanguages.push(lang)

        return setI18nLanguage(lang)

      })

    }

    return Promise.resolve(setI18nLanguage(lang))

  }

  return Promise.resolve(lang)

}

Javascript Export and Import explained

 https://javascript.info/import-export

Sunday, July 2, 2023

Create KPI for user

 // database/migrations/<timestamp>_create_cycles_table.php

use Illuminate\Database\Migrations\Migration;

use Illuminate\Database\Schema\Blueprint;

use Illuminate\Support\Facades\Schema;


class CreateCyclesTable extends Migration

{

    public function up()

    {

        Schema::create('cycles', function (Blueprint $table) {

            $table->id();

            $table->date('start_date');

            $table->date('end_date');

            $table->timestamps();

        });

    }


    public function down()

    {

        Schema::dropIfExists('cycles');

    }

}

// App/Models/Medrep.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Medrep extends Model
{
    protected $fillable = ['name'];

    public function visits()
    {
        return $this->hasMany(Visit::class);
    }

    public function getVisitsWithinCycle($cycleId)
    {
        $cycle = Cycle::findOrFail($cycleId);
        $startDate = $cycle->start_date;
        $endDate = $cycle->end_date;

        return $this->visits()
            ->whereBetween('visit_date', [$startDate, $endDate])
            ->count();
    }
}

// App/Models/Medrep.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Medrep extends Model
{
    protected $fillable = ['name'];

    public function visits()
    {
        return $this->hasMany(Visit::class);
    }

    public function updateKPI($cycleId, $minimumVisits, $maximumVisits)
    {
        $visits = $this->getVisitsWithinCycle($cycleId);
        $kpi = '';

        if ($visits < $minimumVisits) {
            $kpi = 'Below Target';
        } elseif ($visits >= $minimumVisits && $visits <= $maximumVisits) {
            $kpi = 'On Target';
        } else {
            $kpi = 'Exceeded Target';
        }

        $this->update(['kpi' => $kpi]);
    }
}

<!-- resources/views/medreps.blade.php -->
@foreach($medreps as $medrep)
    <div>
        <h3>{{ $medrep->name }}</h3>
        <p>KPI: {{ $medrep->kpi }}</p>
    </div>
@endforeach

// app/Console/Kernel.php
use App\Models\Medrep;

protected function schedule(Schedule $schedule)
{
    $schedule->call(function () {
        // Get the current cycle or create a new cycle
        $currentCycle = Cycle::whereDate('end_date', '>=', now())->first();

        if (!$currentCycle) {
            $currentCycle = Cycle::create([
                'start_date' => now(),
                'end_date' => now()->addDays(15), // Assuming each cycle is 15 days
            ]);
        }

        // Calculate and update the KPI for each medrep within the current cycle
        $medreps = Medrep::all();

        foreach ($medreps as $medrep) {
            $medrep->updateKPI($currentCycle->id, 5, 10); // Assuming minimum visits = 5 and maximum visits = 10
        }
    })->dailyAt('00:00');
}

// app/Console/Kernel.php
use App\Models\Medrep;

protected function schedule(Schedule $schedule)
{
    $schedule->call(function () {
        $today = now();
        $dayOfMonth = $today->day;

        // Calculate the start and end dates for the current cycle
        $startOfMonth = $today->startOfMonth();
        $endOfMonth = $today->endOfMonth();

        $startCycle = $dayOfMonth <= 15 ? $startOfMonth : $startOfMonth->addDays(15);
        $endCycle = $dayOfMonth <= 15 ? $startCycle->copy()->addDays(14) : $endOfMonth;

        // Create the cycle if it doesn't exist
        $currentCycle = Cycle::where('start_date', $startCycle)
            ->where('end_date', $endCycle)
            ->first();

        if (!$currentCycle) {
            $currentCycle = Cycle::create([
                'start_date' => $startCycle,
                'end_date' => $endCycle,
            ]);
        }

        // Calculate and update the KPI for each medrep within the current cycle
        $medreps = Medrep::all();

        foreach ($medreps as $medrep) {
            $medrep->updateKPI($currentCycle->id, 5, 10); // Assuming minimum visits = 5 and maximum visits = 10
        }
    })->dailyAt('00:00');
}




Saturday, June 3, 2023

Laravel Polymorphic Many-To-Many: Get All Related Records

In Laravel's many-to-many polymorphic relations, there is a situation where you can't get ALL records of different models by their "parent" record. Let me explain, and show the potential solution.

Scenario: you have multiple Models that each may have multiple tags.

Example Tags: "eloquent", "vue", "livewire"

And then each Post, Video, and Course may have many tags.

Our Task: get all records (Posts + Videos + Courses) by a specific tag.

Unfortunately, there's nothing like $tag->taggables()->get(). You will see the solution for this below, but let's go step-by-step.

Here's the DB schema for this:

tags

    id - integer

    name - string

    ...

 

posts

    id - integer

    post_title - string

    ...

 

videos

    id - integer

    video_title - string

    ...

 

courses

    id - integer

    course_title - string

    ...

 

taggables

    tag_id - `foreignId('tags')->constrained()`

    taggable_id - integer (ID of post or video or course)

    taggable_type - string (Model name, like "App\Models\Post")

The DB table taggables deserves its migration to be shown, it looks like this:

Schema::create('taggables', function (Blueprint $table) {
$table->foreignId('tag_id')->constrained();
$table->morphs('taggable');
});

Here's what the data in that DB table would look like:


Then, in the Eloquent Models, you have this code.

app/Models/Post.php

class Post extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}

}

Similarly, the Models of Course and Video will have the same identical tags() method with morphToMany().

And then, if needed, the Tag model has multiple morphedByMany() relations.

app/Models/Tag.php

class Tag extends Model
{
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
 
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
 
public function courses()
{
return $this->morphedByMany(Course::class, 'taggable');
}

}

Now, how to query data. How to get the entries by Tag?

Unfortunately, there's no way to run a single query, like $tag->taggables()->get();, because there's no single Model structure for different Post/Video/Course, they all have different fields, so how you can group them together?

Well, the trick is to run three different queries, but then combine the results into an identical structure and merge them together into one Collection. From there, you can paginate or transform that collection however you want.

$tag = Tag::find(1);
$posts = $tag->posts()->get()->map(fn($post) => [
'id' => $post->id,
'title' => $post->post_title
]);
 
$videos = $tag->videos()->get()->map(fn($video) => [
'id' => $video->id,
'title' => $video->video_title
]);
 
$courses = $tag->courses()->get()->map(fn($course) => [
'id' => $course->id,
'title' => $course->course_title
]);
 
$results = collect()->merge($courses)->merge($posts)->merge($videos);



This code will return this structure, if there is a Post/Video for the tag but no Course:

Illuminate\Support\Collection {#2198
all: [
[
"id" => 1,
"title" => "Post about Eloquent",
],
[
"id" => 1,
"title" => "Video comparing Vue and Livewire",
],
],
}