Showing posts with label Web Development. Show all posts
Showing posts with label Web Development. Show all posts

Sunday, July 3, 2022

Mock node-fetch with JEST

Recently I tried my hands on Jest - the popular JavaScript Testing Library. In my application we were using node-fetch to make API calls. I used Jest to mock and test this API calls. Here in this blog I will explain on how to mock node-fetch with Jest. 

Following is my function to call the GET API. 

fetch_get.js

const fetch = require('node-fetch');

module.exports = async () => {

    return await fetch('http://YOUR_GET_URL').then(res => res.json());

};

Now to test this create a test file with name test.fetch_get.js

Step 1: First import the function.

const fetch_get = require('../fetch_get');


Step 2 : Mock the node-fetch with Jest

const fetch = require('node-fetch');
jest.mock('node-fetch', ()=>jest.fn())

Step 3: Create Mock response

const mockedRes = {
  "success": true,
  "data": [
    {
        id: 1,
        text: 'Test Data 1'
    },
    {
        id: 1,
        text: 'Test Data 2'
    }
  ]
};

Step 4: Add Test 

describe("Test get fetch", () => {
    let data;
    it('It should return the data', () => {
      const response = Promise.resolve({
            ok: true,
            status: 200,
            json: () => {
                return mockedRes;
            },
        });
        fetch.mockImplementation(()=> response)
        data = await fetch_get();
        expect(data).toEqual(mockedRes);
    });
});

In above code we have created a rest and mocked the response to return our response when we call .json() method for response. Then we are simply comparing the response. This is a very simple test. Here in stead of comparing data, you may have other tests like checking length of data or checking specific data etc.

Step 5: Run Test

Run the test with npm test or npm run test and you will see the following result.

Test get fetch
    ✓ It should return the data (17 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.313 s, estimated 1 s

Hope this helps you.

Saturday, July 31, 2021

Wait for State Changes with Redux Saga

Hello,

As we know Redux saga allows us to respond to actions outside Redux store. It's kind of middleware library to handle side effects with ease. This side effect could be anything like calling APIs, doing some IO operations or anything. While working with Saga sometime it happens that we also want to detect state changes and do some actions after it. In this blog I am going to explain how you can wait or detect Redux state changes with Saga.

Let's understand with an example. For example there is an API call to get access token and as soon as you have access toke available in state you want to call some other actions like background sync etc. 


function* waitForLoginToken(selector) {

    if (yield select(selector)) return;

    while (true) {

      yield take('*');

      if (yield select(selector)) return;

    }

}

export function* postSignInAction(payload) {

    cost loginTokenSelector = state => state.auth.loginToken;

    yield call(waitForLoginToken, loginTokenSelector);

    yield put({ type: 'BACKGROUND_SYNC' });

}

function* mySaga(){

  yield takeEvery('SIGN_IN', postSignInAction);

}

As you can see in above code, we take every sign in action and call postSignInAction which will wait for state change.

We defined state selector as state.auth.loginToken. Till the time loginToken is not set in state.auth it will wait as we are using while loop to wait for state change. Once we get it in state we call next action. 

This way you can & detect state change with Redux Saga. Hope this helps you.

Saturday, July 17, 2021

React-Draft-Wysiwyg - Find Text Option

Hello,

Recently I was working on POC for rich text editor using React-Draft-Wysiwyg editor. 

There we have a requirement to add find & replace dialog box which should find the matching text from all the blocks and height it with background color. For the POC we added find text functionality. Here in this blog I will explain how to do it. 

For this I have created separate React component and add it as custom option to editor toolbar. Let's understand it step by step. 

If you want to know how to add custom option you can find reference from here.

https://jpuri.github.io/react-draft-wysiwyg/#/docs

1) Create Modal with Text input and Button

Please note I have used grommet UI library to build the POC. So text input and button was imported from Grommet.

<TextInput

    data-cy="find-text"

    autoFocus

    onChange={this.onChangeFindText}

    value={this.state.findText}

/>

<Button

    id="find"

    label="Find"

    onClick={this.onFindClick}

/>

2) Handle Find Click Event

onFindClick = () => {

  const search = this.state.findText;

  let contentState = convertToRaw(this.props.editorState.getCurrentContent());

  contentState.blocks.forEach(block => {

    const text = block.text;

    const matches = [...text.matchAll(search)];

    if(matches.length > 0) {

      matches.forEach(match =>{

        block.inlineStyleRanges.push({length: match[0].length,

          offset: match.index,

          style: "bgcolor-rgb(247,218,100)"

        })

      });

    }

  });

  let newEditorState = {

    editorState: EditorState.createWithContent(convertFromRaw({blocks: contentState.blocks,entityMap: {}})),

    contentState: null

  }

  this.props.onChange(EditorState.createWithContent(convertFromRaw({blocks: contentState.blocks,entityMap: {}})));

}

In above code we are getting search text from the state and matching it with texts of all custom blocks. Where ever it matches we are setting inline styles in blocks with yellow color background. and then setting state of editor again with new blocks. 

Hope this helps you.

Sunday, July 4, 2021

Docker MongoDB terminates when it runs out of memory

When you have multiple services running in docker container it's quite possible that you have an issues with certain services when your docker container runs out of memory. MongoDB is one such service. 

On docker container when you have MongoDB running and when it starts storing huge data it starts consuming lots of memory and that's where you have an issue. MongoDB will crash after sometime where isn't much memory left. 

The reason behind this is the IO model of MongoDB, it tries to keep as much data as possible in cache so read and write operations are much faster. But this creates an issue with docker as we have limited memory and lots of services are sharing that. 

Starting from MongoDB 3.2 on words WiredTiger storage engine is the default one for MongoDB and it's recommended. 

There are various advantages of WiredTiger storage engine. For example,

  • Document Level Concurrency
  • Snapshots and Checkpoints
  • Journal
  • Compression
  • Memory Use
One of most useful feature is Memory use. 

With WiredTiger, MongoDB utilizes both the WiredTiger internal cache and the filesystem cache.

You can control it with --wiredTigerCacheSizeGB configuration.

The --wiredTigerCacheSizeGB limits the size of the WiredTiger internal cache. The operating system will use the available free memory for filesystem cache, which allows the compressed MongoDB data files to stay in memory. In addition, the operating system will use any free RAM to buffer file system blocks and file system cache.

With this setting you can enhance memory usage. MongoDB will not use excessive memory and with heavy data usage on docker container MongoDB will not crash on excessive memory usage.

Hope this helps you.

ReactJs Peer to Peer Communication

Recently I evaluated peer to peer communication approach for one of my project so here I am going to share it and how you can use it in your project in case you want to implement peer to peer communication in your project. 

We used library called PeerJS , it's simple peer to peer built on top of webRTC. For this first you have to create a server, which will act as only connection broker. No peer to peer data goes through this server. Let's just create a simple server.

Let's first install peer from npm. 

npm install peer

Now let's create NodeJs script. 

const { PeerServer } = require('peer');

const peerServer = PeerServer({ port: 9000, path: '/server' });

peerServer.on('connection', (client) => { 

console.log(client);

});

That's is now you can run this script through terminal and it will run your server on 9000 port.

Now let's connect to server from our ReactJS component. 

First lets install peerjs npm which is peer client. 

npm install peerjs

We can connect to server in componentDidMount method and add some callbacks function.

import Peer from 'peerjs';

componentDidMount = () => {

        this.peer = new Peer("USERNAME", {

          host: 'localhost',

          port: 9000,

          path: '/server'

        });

        this.peer.on("error", err => {

            console.log("error: ", err)

        })

        this.peer.on("open", id => {

            console.log(id)

        })

        this.peer.on("connection", (con) => {

            console.log("connection opened");

            con.on("data", i => {

                console.log(i)

            });

        })

}

In above code first function is the error callback function. Second once is when peer connection is opened. Third one is when you receive connection from some other peer and get some data. 

Now let's take an example of how you can connect to other peer and send data. 

const conn = this.peer.connect('REMOTE_PEER');

conn.on('open', () => {

          conn.send('DATA');

});

In above code we are connecting to some remote peer and sending some data to it.

Please note here peer to peer data goes through ICE server which you can setup and assign when you create peer server or else it will use PeerCloud server by default. For the development purpose that's ok but for the production you should create your on TURN or STUN server.

Hope this helps you in setting up 

Saturday, June 26, 2021

How to Create Custom Theme for UI using Grommet V2

 Hello,

In this blog we will understand how you can create custom theme for your using Grommet V2 in your React app.

Using Grommet v2, you can leverage the theme capabilities and define a customTheme as a JS object with your desired colors, margin padding etc. Let's see how we can do this. 

First define your custom theme as JS object. As you can see we have defined some existing grommet color like background, brand. Also we have defined our own color like custom-1, custom-2 etc and then used it in button. 

const customTheme = {

  global: {

    colors: {

      background: "#fff",

      brand: "#000",

      "custom-1": "#aaaaaa",

      "custom-2": "#bbbbbb",

      "custom-3": "#444444"

    }

  },

  button: {

    default: {

      background: "brand",

      color: "white"

    },

    primary: {

      background: "custom-1",

      color: "white"

    },

    secondary: {

      background: "custom-3",

      color: "dark-2"

    }

  }

}

For more information on theme customisation and JSON structure you can visit Official Grommet site

Now you can apply this theme to your application as below.

import { Grommet, Box } from 'grommet';

const DemoGrommet = () => (

  <Grommet theme={customTheme}>

     <Box background="custom-1" >

     </Box>

  </Grommet>

);

export default DemoGrommet;

In case you want to keep default values of Grommet theme and want to override only few things with your theme, you can use deepMerge function provided by grommet v2 utils. 

import { deepMerge } from 'grommet/utils';

import { generate } from 'grommet/themes/base';

const mergedTheme = deepMerge(generate(16), customTheme);

Here generate function generates default theme with 16 pixel font size and then it merge it with your custom theme and provide you updated theme.

Friday, March 19, 2021

How to solve proxy_fcgi:error AH01071: Got error 'Unable to open primary script

 Hello,

Recently I got stuck with strange bug on centOS server. After I updated document root for my primary domain which has Laravel App installed. 

When we try to access app through domain, we got following error. 

No input file specified.

I initially thought it's the permission issue on .htaccess issue. Tried changing permissions and adding some rewrite rules but it didn't work. When I checked apache error logs. I got following error. 

proxy_fcgi:error AH01071: Got error 'Unable to open primary script

I had no idea how to solve this error. So tried few things like

  • Restart apache
  • Restart php-fpm
  • Restart VPS
  • Removed and added account few times.
  • Disabling php-fpm
But nothing worked. I spent almost entire night in solving this issue but no luck. But finally I was able to solve the issue by following steps. It was actually the issue with php fpm, as it was not able to find out root folder for the domain. So here is what you have to do. 

First go to userdata directory 

/var/cpanel/userdata/<USERNAME>

Here you will find one file. 

yourdomain.com.php-fpm.yaml

Open this file with nano or vim editor and add following line at the end of it. 

php_admin_value_doc_root: { name: 'php_admin_value[doc_root]', value: /home/<USERNAME>/public_html/<DOCUMENT_ROOT> }

Save the file and then execute following commands one by one.

/scripts/php_fpm_config --rebuild 

/scripts/restartsrv_apache_php_fpm 

/scripts/restartsrv_httpd

This will rebuild php fpm config and restart php fpm service for apache. Now in the same folder open 

YOURDOMAIN.com 

YOURDOMAIN.com_SSL

Change document root here as well and run following commands.

/scripts/rebuildhttpdconf 

/scripts/restartsrv_httpd

This will rebuild apache config file and restart apache. 

This is it and now if you visit your domain, you will not have the issue of file missing. 

Hope this saves your time

Saturday, September 5, 2020

Create Secure Web Socket (WSS) with Ratchet PHP

Hello,

Recently I was working on PHP project where we I was create secure web socket which is accessible with wss:// protocol. For this I struggled for couple of hours so here in blog I am going to explain how to do that so it can save your time. 

Earlier version was not supporting WSS but later it introduced React socket server which allows SSL connection. So here are steps you need to follow. 

First add all the required classed in your php file.


use Ratchet\Server\IoServer;

use Ratchet\Http\HttpServer;

use Ratchet\WebSocket\WsServer;

use MyApp\Socket;


In above example Socket is my class file which has all listeners. After this add auto load file.

require dirname( __FILE__ ) . '/vendor/autoload.php';

Next we will create our socket app. 

$app = new \Ratchet\Http\HttpServer(
    new \Ratchet\WebSocket\WsServer(
        new \MyApp\Socket()
    )
);

Now next step is to create React Secure server.

$loop = \React\EventLoop\Factory::create();
$webSock = new \React\Socket\Server('0.0.0.0:8080', $loop);

We are using 0.0.0.0  so this socket can be connected from anywhere. 

Now lets create secure server by adding path to certificate and key

$webSock = new \React\Socket\SecureServer($webSock, $loop, [
    'local_cert' => 'CRT_PATH', 
    'local_pk'=> 'KEY_PATH', 
    'allow_self_signed' => true, 
    'verify_peer' => false
]);

Now finally we will run our server.

$webSock = new \Ratchet\Server\IoServer($app, $webSock, $loop);
$webSock->run();

That's it. It will start your server to which you can connect with WSS protocol. To test you can use WebSocket ECHO test.


Hope this helps you.

Wednesday, August 21, 2019

NodeJs MySQL Observer

Hello,

In this blog we are going to learn how to use NodeJs to observe changes in MySql databases. This is useful when you want to track MySQL changes and based on that want to send some events to frontends or want to do any other actions.

For this first of all you have to enable binary logging in you database. Binary logging is very much useful for real time MySQL replication. In Amazon RDS, it's by default available and you can switch on it from configurations. For your local database if you are using MAMP, you can do following trick.

Create a file with name my.cnf and add following content to it.

[mysqld]
server-id = 1
default-storage-engine = InnoDB
log-bin=bin.log
log-bin-index=bin-log.index
max_binlog_size=100M
expire_logs_days = 10
binlog_format=row
socket=mysql.sock

Add this file to conf folder of your MAMP directory and restart MySQL server. This will enable binary logging in your database.

Now to observe this changes we use npm package called zongji . Install it with NPM.

Add following code to your NodeJs script.

var ZongJi = require('zongji');
var _underScore = require('underscore');

var zongji = new ZongJi({
    user : 'YOUR_USERNAME',
    password : "YOUR_PASSWORD",
    database: 'YOUR_DATABASE',
    socketPath : '/Applications/MAMP/tmp/mysql/mysql.sock'
});

Now add event on binlog.

zongji.on('binlog', function(evt) {

});

This event is triggered whenever there is a change in any of your database tables.

Inside this event you can have logic of checking new rows, updates rows, deleted rows.
zongji.on('binlog', function(evt) {
if (evt.getEventName() === 'writerows' || evt.getEventName() === 'updaterows' || evt.getEventName() === 'deleterows') {
var database = evt.tableMap[evt.tableId].parentSchema; 
        var table =  evt.tableMap[evt.tableId].tableName; 
        var columns = evt.tableMap[evt.tableId].columns; 
        _underScore.each(evt.rows, function(row) {
        });
}
});

At last start the process and pass the events you want to watch.
zongji.start({
  includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows']
});

Tuesday, August 20, 2019

ReactJs Material UI Table Infinite Scroll

Hello,

Recently in one of my ReactJs project, I faced a challenge in implementing infinite scroll in Material UI table. In this blog I am going to mention trick I have used.

First of all I was really surprised to see that Material UI table does not have infinite scroll function out of the box. It's very much needed. Sometimes something can not be achieved with frameworks, can be achieved via basics of JavaScript. In this I have done something similar.

I tried adding on scroll events on Table, Table Body but it didn't work. I also tried adding refs to body and then bind the Scroll event but that also did not work. After struggling for couple of hours, I finally decided to it with Pure JavaScript.

Step 1 : Wrap material UI table inside the container with fixed height and set overflow = scroll to container.


import styled from "styled-components";

export const Table = props => (
  <TableWrapper id={props.id}>
    <MuiTable {...props}/>
  </TableWrapper>

);

const TableWrapper = styled.div`
  max-height: 500px;
  overflow: scroll;

  ::-webkit-scrollbar {
    width: 3px;
    height: 3px;
  }
`;

As you can see I created a wrapper of table and set max height to it. You can make it dynamic as well depending on window height.

Step 2: Import Table to your component

import {
  Table

 } from './components/Table';

return (
          <>         
             <Table id={"tableID"}/>
          </>
        );

Step 3: Bind scroll event to wrapper

let me = this;
document.getElementById('tableID').onscroll = function(event){
   if(this.scrollTop == (this.scrollHeight - this.clientHeight)){
         //User reached bottom of table after scroll
         //Logic to call web services to get next set of data
   }
};

Hope this helps you.

Monday, August 19, 2019

Accessing Data From Redis Using NodeJs

Hello,

When you are working with business applications, it's sometimes need to cache the data. At this point Redis can be very useful, it can be used as database or cache database. You can store any kind of data like strings, JSON objects etc. in Redis.

Problem we face while working with NodeJs and Redis, get data operation from Redis is Asynchronous operations so it gives you callback and your code execution will continue. This may create a problem when you want to handle it in Synchronous way. For example you may have loops inside that you are trying to access data from Redis.

In this blog I am going to explain how you can have Synchronous operations. In nutshell we got to promisify the redis module.

There is a library called bluebird, that can be used for this. Lets go step by step.

Step 1

Install bluebird and redis in your NodeJs app.

npm install bluebird
npm install redis

Step 2

Import it in NodeJs app.

var redis = require('redis');
var bluebird = require("bluebird");

Step 3

Promisify Redis.

bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);

Step 4

Connect to Redis client.

var client = redis.createClient();
    client.on('connect', function() {
    console.log('Redis client connected');
});

Step 5

Use Async version of get function to get data.

client.getAsync("MY_KEY").then(function(res) {
      //Access Data
});

This is how you can have Sync operations with Redis. 

Wednesday, August 14, 2019

NodeJs , MySql Wait For Query Result

Hello,

One major issue we face while working with NodeJs and MySql is waiting for query result. When you have nested queries or you have to use for loop inside query result and wait for it's output. Because NodeJs MySql uses async function to give you query result and by the time you get the result the script finish execution.

In this blog I am going to mention one of the method I have used in one of my project. There can be various other ways for it.

First of all we have to import async plugin in your node app and add it to your script.

var async = require('async');

Now, lets assume here is your first query.

const result = connection.query('SELECT * FROM TABLE1', async function(err, rows, fields) {

});

Now this query result you have to do loop and have more queries.

let resultRows = Array();
const result = connection.query('SELECT * FROM TABLE1', async function(err, rows, fields) {
     async.each(rows, function (row, callback) {
          connection.query('SELECT * FROM TABLE 2', function(err, innerRow){
                 resultRows.push(innerRow);
                 callback(null);
          });
     }, async function () {
            //This is the final function which get executed when loop is done.
            const response = {
                statusCode: 200,
                headers: {
                "Access-Control-Allow-Origin" : "*" // Required for CORS support to work
              },
                body: JSON.stringify({
                  success: true,
                  data: resultRows
                })
              };
              callback(null, response);
    });
});

As you can see in above code, after first query is executed we are using async each for the loop.
In second query result function we are returning null callback. Once the loop is executed and all the null call backs are return it will call the final function where you will get your result rows. There you can process the result.

Tuesday, August 13, 2019

ReactJs - Convert JSON Data using loadash

Hello,

Finally now I am going to post few blogs on React and ReactJs. In this blog we are going to learn how we can modify, manipulate or edit JSON data received from API according to your need.

This will be very useful when your backend developer refused to change API response. So not to worry, let it be as it is and convert data on your side before you use it.

For this we are going to use Lodash library, so first of all add it your ReactJs project using npm and import it in your class file.

import _ from "lodash";

Now create function for converting data.

const convertData = (data, key) =>{
  let result = _.chain(data)
    .get(key)
    .map(d => ({
      modified_key1: _.get(d, "original_key1"),
      modified_key2: _.get(d, "original_key2"),
      modified_key3: _.get(d, "original_key3"),
      modified_key4: _.get(d, "original_key4")
    }))
    .value();
    return result;
}

Now call this function in your API response.

let modifiedData = convertData(response.body, "data");

Here data is the root key of your JSON response. Change it according to your JSON response. That's it now you have modified data according to your requirements.

Friday, April 20, 2018

Laravel ValidationException Handling

This is the creepiest thing I have ever fixed in Laravel. I can't even imagine that in Laravel we can have such creepy problem. But anyways other than this I really like Laravel framework.

First let me explain the problem. In our Laravel 5.5 application we have added exception handling in app/Exceptions/Handler.php file

public function render($request, Exception $exception)
{
    Redirect::to('admin/errorPage')->send();
}

Now the problem was in case of form validations it was throwing ValidationException so in case of returning back to form it always took me to the error page and I am not able to see what are the validation issues. Now that was really strange. So there were two options . First, I have to remove server side validations so it does not throw ValidationException and we shall do all client side validations. Or find out some other way to handle this.

So after 3 to 4 hours of struggle of going though framework code and documentation and online help I finally figure out the solution. Here are steps to add it.

1) Step 1 : Add  ValidationException in dontReport field in app/Exceptions/Handler.php

protected $dontReport = [
   //
   \Illuminate\Validation\ValidationException::class
];

2) Step 2 : Update the render method declaration

public function render($request, Exception $exception)
{
    if($this->shouldReport($exception)){
       Redirect::to('admin/errorPage')->send();
    }else{
       return $this->convertValidationExceptionToResponse($exception, $request);
    }
}

So here first we are checking if the current exception to be reported or not by checking shouldReport function

If not to be reported then we are using convertValidationExceptionToResponse method of super class to generate the response and send it back.

Please note that this solution will only work

Sunday, February 25, 2018

Scroll Two Divs Together

Recently in one of my project there was a requirement to maintain scroll positions of two divs. In UI there are two divs side by side. If user scrolls on left DIV then right div should also be scrolled and if user scrolls on right DIV then left div should also be scrolled. So here is how to do this.

var leftPart = document.getElementById('leftPart');
var rightPart = document.getElementById('rightPart');

Now lets add scroll event on both.

leftPart.onscroll = function() {
    rightPart.scrollTop = this.scrollTop;
}

rightPart.onscroll = function() {
    leftPart.scrollTop = this.scrollTop;
}

That's it now both should scroll no but wait it makes your UI unresponsive because there is a deadlock. While right one is scrolling, it will also scroll left one and at the same time it will also fire event and will try  to scroll right one again and there is DEADLOCK.........

So to prevent it, we have to add flags to check if one is already scrolling then hold the event.

so lets have two flags.

var isSyncingLeftScroll = false;
var isSyncingRightScroll = false;

and use it in scroll event.

leftPart.onscroll = function() {
  if (!isSyncingLeftScroll) {
    isSyncingRightScroll = true;
    rightPart.scrollTop = this.scrollTop;
  }
  isSyncingLeftScroll = false;
}

rightPart.onscroll = function() {
  if (!isSyncingRightScroll) {
    isSyncingLeftScroll = true;
    leftPart.scrollTop = this.scrollTop;
  }
  isSyncingRightScroll = false;
}

That's it and now you will have smooth scrolling effect on both the DIVs. Hope this helps you.

Sunday, February 4, 2018

Some Tips and Tricks of Working With Bootstrap Select2 Control

Hello,

Since I am a web developer, I used to work  a lot with Bootstrap Select 2 Control. Here I am going to share some quick tips and tricks of working with Select 2 controls.

1) Dynamically create Select2 control with JavaScript

When we are working on dynamic websites, sometimes we also have to create select2 controls dynamically. Most of the developers faces issues here. Because select2 control is created by JavaScript on document ready. So if you want create it dynamically, first append basic html of control inside and the by using JavaScript init it.

Something like this.

$('.select2').select2();

Sometimes it takes time to get reflect in DOM so in this case you may have to give some timeout.

setTimeout(function(){
      $('.select2').select2();
},500);

2) Open Select2 dropdown on focus.

This is one more UX experience. Most of the web users are used to work with TAB. So in your form if you have select2 control and you come on it via tab. It should open dropdown.

Here is how we can do it.

$(document).on('focus', '.select2', function() {
if($(this).prev().val() == ''){
  $(this).siblings('select').select2('open');
}
});

3) Open Select2 dropdown with down arrow key

This is one more UX experience. Most of the web users are used to work with up and down arrow while working with dropdown. So in case of select2 they are expecting the same result.

$(document).on('keydown', '.select2', function(event) {
    if(event.keyCode == 40){
    $(this).siblings('select').select2('open');
    }
});

4) Keep focus on Select2 after selecting item and dropdown is closed.

In new version of Select2 there is a bug that after you select an item and dropdown is closed. It will lost focus. So to keep focus on the control, use following code.

$('select').on(
  'select2:close',
  function () {
  $(this).focus();
  }
  );
},1000);

Hope this tips helps you in your development with Select2.

Wednesday, November 8, 2017

Laravel Query Builder GroupBy Syntax error or access violation

Hello,

Recently I forked a Github project and was updating it. I faced strange issue in using GroupBy in query builder. I was trying to query model as follow.

$projects = ProjectReviews::groupBy('project_id')->orderBy('created_at','DESC')->get();

Basically I wanted to get recently reviewed projects and show dates. But in above query I was getting error Syntax error or access violation project_reviews.id isn't in group by.

That was really strange issue as that's the primary key of table and it should not be part of group by. If you run that query directly in PHP MyAdmin it was working fine. So I was not sure about this. Finally after spending couple of hours I was able to find out the problem.

It's because of strict config settings of database. If you look into config/database.php file there is strict config there.

        'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],

You have to set to false to make GroupBy working.

Following is mentioned in MySql Documentation for this config.

Strict mode controls how MySQL handles invalid or missing values in data-change statements such as INSERT or UPDATE. A value can be invalid for several reasons. For example, it might have the wrong data type for the column, or it might be out of range. A value is missing when a new row to be inserted does not contain a value for a non-NULL column that has no explicit DEFAULT clause in its definition. (For a NULL column, NULL is inserted if the value is missing.) Strict mode also affects DDL statements such as CREATE TABLE.
So basically it secures your database operations. However in Laravel it's not working properly. So I was not sure if it's laravel bug. But I had to disable it make this query working.

Disabling it wouldn't make your web app unsecured if you handle all validations in your controllers and follow best practices like Laravel already does.

However I still recommend to be careful, while using this.

Thursday, September 7, 2017

jQuery Submit Form with Ajax

Hello,

In this blog we are going to take a look at how we can submit for with Ajax using jQuery.

First of all add jQuery file in your head section of HTML page.

<script src="jquery.min.js"></script>

Now we will have following form.

<form action="">
      <input id="text" autocomplete="off" placeholder="Type here" />
</form>

If you want to submit this form with Ajax using jQuery, first you have to do is add submit event handler.

$(function () {
$('form').submit(function(){
var formData = JSON.stringify($('form').serializeArray());
$.ajax({
type: "POST",
url: "YOUR_API_URL",
data: formData,
success: function(data){

},
failure: function(errMsg) {
}
});
return false;
    });
});

So first we have added submit event handler and used return false so it does refresh the whole page and just use the Ajax request.  Inside event handler we are using HTML 5 FormData to get form data first and then using Ajax request to send data to server in APIs.

Hope this helps you.

Monday, August 28, 2017

Using Google Translation API For Indian Language with PHP

Google translation API works good for Indian languages, if we type in proper language. For example, if you pass following in API. 

कैसे हो

Then it will translate properly to 

how are you

But if you pass it in english, like this.

"KAISE HO"

Then it will send the same text back but it only detects language. So it does not work properly. So what shall the possible solution for it. 

Here is how I worked out in one of my old project.

First I used Google input tools API to convert the input to regional language. 

So steps if First

KAISE HO is converted to

कैसे हो

and then pass it in translation API to convert it to English. Here is the code for the same.

$translatedText = "KAISE HO";
$detectedSourceLanguage = "hi";

$url ='https://www.google.com/inputtools/request?text='.urlencode($translatedText).'&ime=transliteration_en_'.urlencode($detectedSourceLanguage);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_PROXYPORT,3128);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
$response = curl_exec($ch);
$output = json_decode($response);
$resultText = '';

if($output[0] == 'SUCCESS'){
if(isset($output[1])){
if(isset($output[1][0])){
if(isset($output[1][0][1])){
$resultText = $output[1][0][1][0];
}
}
}
}

if($resultText != ''){
$url ='https://translation.googleapis.com/language/translate/v2';
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,
"q=".urlencode($resultText)."&target=en&key=YOUR_API");

curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_PROXYPORT,3128);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
$response = curl_exec($ch);
$output = json_decode($response);
$resultText = $output->data->translations[0]->translatedText;
}

Hope this helps you in proper translation.

Monday, July 3, 2017

Magento 1.9 Newsletter Unsubscribe Link Not Working

Yes.... still we are working on Magento 1.9 as Magento 2.0 is bit difficult to work with and there were some bugs in it. So recently we created magento store with Magento 1.9 where we faced an issue of adding unsubscribe link in subscription success email and other newsletter.

Subscriber object is shared with all the newsletter email templates. So to add unsubscribe link you just have to add following code.

<a href="{{var subscriber.getUnsubscriptionLink() }}"> Unsubscribe </a>

 But somehow this was not working. So after checking system log we found following error.

getUnsubscribeUrl function not found for Mage_Newsletter_Helper_Data class.

Now this was a strange issue as this is the basic functionality of Magento 1.9 and it is not working. So I checked the Mage_Newsletter_Helper_Data class in app/code/core/Mage/Newsletter/Helper folder and there was no such function but the function name was different.

Following is the function found in data.php

public function getUnsubscribeLink($subscriber)
    {
        return Mage::getModel('core/url')
            ->setStore($subscriber->getStoreId())
            ->getUrl('newsletter/subscriber/unsubscribe', array(
                'id'     => $subscriber->getId(),
                'code'   => $subscriber->getCode(),
                '_nosid' => true
            ));
    }

And in app/code/core/Mage/Newsletter/Model/Subscriber.php file following is the function.

public function getUnsubscriptionLink() {
        return Mage::helper('newsletter')->getUnsubscribeUrl($this);
    }

Which was used in template. So that was the issue. So to solve this issue replace above function with following function in app/code/core/Mage/Newsletter/Model/Subscriber.php file.

public function getUnsubscriptionLink() {
        return Mage::helper('newsletter')->getUnsubscribeLink($this);
    }

And now it should work. Hope this helps you.