diceline-chartmagnifiermouse-upquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

How to add dynamic routes with VUE router

Dynamic routes are an important part. Let's consider we have a blog (for example) which has 10 posts, each of them with a unique URL. Creating a new component for each blog post isn't the best practice, so here intervenes a special feature of VUE: dynamic routes.

First, let's create a Post component which will serve as a template for displaying each unique blog post:

<template>
	<div>
		<h1>Welcome to Post page</h1>
		<p>This is the page for post number {{ $route.params.id }}</p>
	</div>
</template>

Now, we need to update the router/index.js with our new route. *Don't forget to include the import for the Post component.

Below, we will use :id parameter as a dynamic value in the route:

{
	path: '/post/:id',
	name: 'Post',
	component: Post
}

Nicely done, you have now dynamic routes for your blog.

Visual Studio Code Snippets

VS Code offers the possibility to create personalized code snippets. These Code snippets can be configured for a specific user, globally, for a certain project or programming language.

For example, if a user wants to avoid the repetitive writing of the basic "console.log({})", after configuring the snippets, it will be enough to write a prefix like: "cl" or "log" and VSC will autocomplete the code snippet. The braces {} will also show the variable name before the result.

In order to achieve this, we can run the command "Preferences: Configure User Snippets" in VSC and a popup will appear showing the types of of snippets to configure.

If we wish to configure a snippet as in the 'console.log' example above, we need to choose to modify the "javascript.json" file with the following content:

{
    "Print to console": {
        "prefix": "cl",
        "body": [
	    "console.log({$1});",
	    "$2"
	],
        "description": "Log output to console"
	}
}

As you can see, a certain snippet will have a prefix under it will be accessed, a body (the code that will be inserted) and the description of the snippet.

This is a powerful feature of VSC and can avoid repetitive code writing that can be quite tedious after a while.

More info about this feature of VSC can be found here:

https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets

SQLite vs MySQL - foreign key checks

Sqlite does not check for foreign key integrity when creating tables, only when inserting records. However, MySQL does check in both cases.

To make this more clear, let's take a look at a simple example: given two tables - todos and users - created in this exact order:

 Schema::create('todos', function (Blueprint $table) {
      $table->id();
      $table->string('title');
      $table->unsignedBigInteger('user_id');
      $table->foreign('user_id')
          ->references('id')
          ->on('users');
  });
 Schema::create('users', function (Blueprint $table) {
      $table->id();
      $table->string('username');
      $table->string('password');
  });

If you run your migrations using the sqlite driver, everything works just fine.

However, if you run your migrations using the mysql driver, you get the following error:

SQLSTATE[HY000]: General error: 1005 Can't create table `[your-app]`.`todos` 
(errno: 150 "Foreign key constraint is incorrectly formed") 
(SQL: alter table `todos` add constraint `todos_user_id_foreign`
 foreign key (`user_id`) references `users` (`id`))

Folder aliases using webpack

In order to define a folder alias using webpack, all you have to do is write the following in webpack.config.js:

const path = require('path');

module.exports = {
  ...
  resolve: {
    alias: {
      '@': path.resolve('src'),
      images: path.resolve('assets/images'),
    },
  },
};

Now , if you want to import something from assets/images, all you need do to is

import image from 'images/[path]'

regardless of the current directory.

If the location of any of the folders defined with aliases ever changes, you no longer need to update all the imports - changing the path in webpack.config.js will take care of everything.

Extracting closures to their own classes

Laravel has a lot of functions that accept a closure as a callback. But sometimes the callback function is just too large to be done inline - so our becomes very unreadable.

Let's take a look at an example of using such a function:

class MyScope
{
    public function apply(Builder $query, Model $model)
    {
        return $query->whereHas(
            'relationship',
            function (Builder $query) {
                //really long function
            }
        );
    }
}

If you are used to writing Javascript you might first attempt something like this in order to refactor:

class MyScope
{

    public function callback(Builder $query)
    {
       //really long method
    }

    public function apply(Builder $query, Model $model)
    {
        return $query->whereHas(
            'relationship',
            $this->callback
        );
    }
}

Here we extracted the callback to a method and we attempt to pass it as a parameter instead of writing the closure inline.

Unfortunately, this would not work and we would get the following error:

Undefined property: App\Scopes\MyScope::$callback

One valid approach would be to have our method return the function we need instead:

class MyScope
{

    public function callback()
    {
       return function (Builder $query) {
          //really long function
       }

    }

    public function apply(Builder $query, Model $model)
    {
        return $query->whereHas(
            'relationship',
            $this->callback()
        );
    }
}

This would do just fine, but we can take this one step further by making use of a powerful PHP feature - invokable classes:

class InvokableClass
{
    //this method will automatically be called and
    //passed the query parameter when the time comes
    public function __invoke($query)
    {
        //really long method
    }
}

This approach is very beneficial because it also allows us to break up the long method into smaller, more understandable pieces (because an invokable class is still a class and we can take full advantage of it) so we are not just sweeping the unreadable code under the rug.

We can now use our freshly defined invokable class in the following way:

class MyScope
{
    public function apply(Builder $query, Model $model)
    {
        return $query->whereHas(
            'relationship',
            \Closure::fromCallable(new InvokableClass())
        );
    }
}

Please notice that we called Closure::fromCallable() first - this is necessary because we have to convert our class to a closure to avoid getting an error such as:

Argument 2 passed to Illuminate\Database\Eloquent\Builder::whereHas() must be an instance of Closure or null, instance of App\InvokableClass given

Conditionally loading packages based on environment.

Based on the environment of the project (production or development), there is a way to load packages conditionally. We may want to load packages conditionally to keep an easier to run development server, for instance.

In order to achieve this, we may use the following technique, if the used packages do not already feature a parameter to be used accordingly to the environment.

For instance we may provide an APP_ENVIRONMENT parameter in the .env file of the project. Based on the value of the parameter, we may load or not a package.

.env

APP_ENVIRONMENT=development

nuxt.config.js

let modules = {
  "@nuxtjs/axios",
}

if (process.env.APP_ENVIRONMENT !== "development")
  modules.push("@nuxtjs/component-cache")

Vue + TypeScript App architecture with autonomous service layer implementation

Goal: having an autonomous, central layer for the different services defined in their own modules that will provide all registered services through a custom hook

Step 1: in a new TypeScript file, import all needed services from their modules

import { Service1 } from '@/location1/Service1'
import { Service2 } from '@/location2/Service2'
import { Service3 } from '@/location3/Service3'

Step 2: define the object containing the services, so that we will be able to retrieve the services from it. New services will be imported and registered in this object. After that, extract its type for further need.

const services = {
   service1: Service1,
   service2: Service2
   service3: Service3,
};

type AvailableServices = typeof services

Step 3: define a ServiceProvider class that will internally hold the services object

class ServiceProvider {
  constructor(protected services: AvailableServices) {}

  public provide( serviceName: keyof AvailableServices)
  :InstanceType<AvailableServices[keyof AvailableServices]> {
    return new this.services[serviceName]();
  }
}

Note 1: constructor automatically initialising protected class member "services", no explicit initialiser needed

Note 2: the generic return type of the provide method. It creates the returned type instance based on the constructor function of the service returned

Step 4: create a service provider instance

const serviceProvider = new ServiceProvider(services);

Step 5: define the magic hook that we will be calling from all components for making use of the above defined logic

export function useService( serviceName: keyof AvailableServices,)
:InstanceType<AvailableServices[keyof AvailableServices]> {
  return serviceProvider.provide(serviceName);
}

How to use it inside components:

import { useService } from '@/itsLocation';

const service1 = useService('service1');

Nice to have as a future implementation: each service as a singleton

Customize web page using query parameters with help of $route.query

VueJS offers the possibility to access query parameters so that you can customise a certain's page content based on the given parameters.

For instance, we have the following method and the following query parameters:

https://*link*?message=Custom+Message
    getQuery() {
      if (this.$route.query.message) {
        this.queryMessage = this.$route.query.message
      }
    },

This method will set a variable according to the query parameter "message", that can be further used in the content of the page. For example in a heading:

    <h1>{{ queryMessage }}</h1>

Also, by using the functionality of $router combined with "v-if", the user can render components conditionally.

    getQuery() {
      if (this.$route.query.icon) {
        this.queryIcon = this.$route.query.icon === 'true'
      }
    },
    <svg v-if="queryIcon">

Therefore, when parsing a query containing:

?icon=true

only then, the svg will be rendered.