1. Overview

I need to send direct message to target user to notify user action in my PHP application. My company already has Rocket.Chat messaging system as a Company Messenger. So I decide to use it. Here is a sample.

2. Rocket.Chat Setting

2.1. Give create-d permission to Bot role

To send Direct Message from Bot to user, Bot role has to have create-d permission. Go to “Administration” -> “Permissions” and check create-d permission for bot role.

Rocket.Chat create-d permission

2.2. Create a new bot user

This step is optional if you want to use existing bot user as a sender of this notification.

Go to “Administration” -> “Users” and create a new Bot user and activate.

2.3. Create a new WebHook

Go to “Administration” -> “Integrations” and create a new “Incoming WebHook”. And input each settings like this:

  • Enabled : True
  • Name (Optional) : Your WebHook Name (ex: My WebHook)
  • Post to Channel : Input any default username. This will override. (ex: @zemna)
  • Post as : Input bot user created by Step 2.2 (ex:my-project-bot)
  • Script Enabled : False

Click Save Changes to save.

2.4. Test WebHook

You can see the curl example on page. Copy it and execute from command prompt to test. If you can receive notification message, all settings are correct.

But if you didn’t receive, please check this;

  • WebHook is enabled or not?
  • To send direct message to user, sending user must have create-d permission. You already give permission or not?

2.5. Copy your WebHook URL

You can see WebHook URL on Incoming WebHook page. Copy it to use in your php application.

3. PHP Application Programming

3.0. Prerequisites

We need a mapping betwwen PHP application user and Rocket.Chat user. You have to make like Rocket.Chat Integration page to let user input Rocket.Chat username, or use same username between them.

3.1. Install guzzle using composer

We will use guzzle as a HTTP Client. Install guzzle using composer.

$ composer require guzzlehttp/guzzle

3.2. Sending Direct Message

Write coding like bellow to send direct message to user.

$client = new GuzzleHttp\Client();

$client->post('Paste Your WebHook URL Here', [
  'body' => json_encode([
    'channel' => '@' . $username,
    'username' => 'My PHP Project',
    'text' => 'New notification arrived',
    'attachments' => [[
      'title' => 'Notification Title',
      'title_link' => 'http://www.example.com/123',
      'text' => 'Notification description',
      'color' => '#0000FF'
    ]]
  ])
]);

See Guzzle Documentation for more options.

Rocket.Chat is the best Slack altenative.

If company also has issue management like JIRA and source code repository management system like BitBucket, you can also sending message to Rocket.Chat automatically to easy notify update information about your work progress.

You can see the integration guide from Rocket.Chat documentation.

Rocket.Chat Documentation

How to send JIRA notification to Rocket.Chat

1. Create new Bot user

In Rocket.Chat go to “Administration” -> “Users” and create new bot user(Select user role to Bots). We will use this user as a sender of JIRA notification. Just make a user like ‘jira’ and activite it.

2. Create new integration

  1. In Rocket.Chat go to “Administration” -> “Integrations” and create “New Integration”.
  2. Click “Incoming WebHook”
  3. Select each option like bellow
    • Enabled : True
    • Name (Optional) : JIRA Notification
    • Post to Channel : /Input channel which WekHook will be posted/
    • Post as : /Input username you already created by step 1/
    • Script Enabled : True
  4. Input Script like this gist
/*jshint  esnext:true*/
const DESC_MAX_LENGTH = 140;
const JIRA_LOGO = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACRElEQVRYhbWXsUscQRTGf4iIyHHIISIWIsHisMgfkNIiBJFwiKQIkipVqpA/wEZEggSxEkmZwiKI5A84REKKkIMQrINYBQmHBDmEHJdNMW+42dk3d3O76wcDu2/e973vZvfN7EF+PAfaMjYL6AzFJFBRYh0gkdEBpryciuQVwjPgFugCu068CvQcAz1g2pnfEc6taOTGL6dIAjxw5nad+FsnvuhxrosYuPbElrz5Rc8Ucu9yfhcxsAncYZZ4fwTeO+HcUcILWgFqOXg1si9vFBrAXB7iEMySfYQZzGCeWxdoAq+Bh8BYjoJjwn0jWrYrqsOIbdIvUQLseTmPgHXgiYx1ibnYU3RuYpyfKMQ/mNWx+KzkfHHmZ4Tj55zGGNhQiAlw5OQ8VeYbzvxRQCNqUxoHLgMCa07eRyd+4sTXAtwrYCLGAJje1URugLrkVIHvMuyLVZccjfsitrhFMyD0k36bTtA/cOZkTuOckaOTFtA7IgEuSG9ONeBHILctWrnwGNO/mvA3zAk4LddaThfTpoXwKiBuVyL0yxPhloLtAUVCY7us4hb7IxQ/KLu4xWFE8cP7Kg6mld4PKH5BvoNrZBMfBphohKnFMAusyvU48ClgoA3M34eBUynwUu6ngK8BE1Gn3ihYccR79Jd5nuyXsx0rZRo498Q7mK8dMDudZuC8rOLLgQI7Ts5xIGe5DANbinCP9AfmEul/SnZslWHgTBFuKnna8a3lpRCzadSVWMiAj6GPIMbAX+/+H9BS8loyN4ibwX9j/jIXDkk+pgAAAABJRU5ErkJggg==';
function stripDesc(str) {
  if (str)
    return str.length > DESC_MAX_LENGTH ? str.slice(0, DESC_MAX_LENGTH - 3) + '...' : str;
  else
    return '';
}

function prepareAttachment({issue, user}, text) {
  let issueType = issue.fields.issuetype;
  let res = {
    author_name: user.displayName
    , author_icon: user.avatarUrls['24x24']
    , thumb_url: issueType.iconUrl
  };
  if (text) {
    text = text.replace(/\{\{(user|issue)\.([^a-z_0-9]+)\}\}/g, (m, type, key) => (type==='user' ? user : issue)[key]);
    res.text = text;
  }
  return res;
}
class Script {
  process_incoming_request({request}) {
    const data = request.content;
    try {
      if (!data.issue || (data.user && data.user.name === 'gitlab')) {
        return;
      }
      let issue = data.issue;
      let baseJiraUrl = issue.self.replace(/\/rest\/.*$/, '');
      let user = data.user;
      let assignedTo = (issue.fields.assigned && issue.fields.assigned.name !== user.name) ? `, assigned to ${issue.fields.assigned.name}` : '';
      let issueSummary = `[${issue.key}](${baseJiraUrl}/browse/${issue.key}) ${issue.fields.summary} _(${issue.fields.priority.name.replace(/^\s*\d*\.\s*/, '')}${assignedTo})_`;
      let message = {
        icon_url: (issue.fields.project && issue.fields.project.avatarUrls && issue.fields.project.avatarUrls['48x48']) || JIRA_LOGO
        , attachments: []
      };

      if (data.webhookEvent === 'jira:issue_created') {
        message.attachments.push(prepareAttachment(data, `*Created* ${issueSummary}:\n${stripDesc(issue.fields.description)}`));
      } else if (data.webhookEvent === 'jira:issue_deleted') {
        message.attachments.push(prepareAttachment(data, `*Deleted* ${issueSummary}`));
      } else if (data.webhookEvent === 'jira:issue_updated') {
        if (data.changelog && data.changelog.items) { // field update
          let logs = [];
          data.changelog.items.forEach((change) => {
            if (!change.field.match('status|resolution|comment|priority') ) {
              return;
            }
            if (change.field==='description') {
              logs.push(`Changed *description* to:\n${stripDesc(change.toString)}`);
            } else {
              logs.push(`*${change.field}* changed from ${change.fromString} to *${change.toString}*`);
            }
          });
          logs.length && message.attachments.push(prepareAttachment(data, `*Updated* ${issueSummary}:\n  - ${logs.join('\n  - ')}`));
        }

        if (data.comment) { // comment update
          let comment = data.comment;
          let action = comment.created !== comment.updated ? 'Updated comment' : 'Commented';
          message.attachments.push(prepareAttachment(data, `*${action}* on ${issueSummary}:\n${stripDesc(comment.body)}`));
        }
      }

      if (message.text || message.attachments.length) {
        return {content:message};
      }
    } catch(e) {
      console.log('jiraevent error', e);
      return {
        error: {
          success: false,
          message: `${e.message || e} ${JSON.stringify(data)}`
        }
      };
    }
  }
}

Press Save Changes button and copy Webhook URL.

3. Create a WebHook on JIRA

Go to JIRA as administrator and goto “System” -> “WebHooks” and click “Create a WebHook”. Input like this:

  • Name : Name of webhook
  • Status : Enabled
  • URL : Paste webhook url you copied from step 5.
  • Event : Check events which you want to send notification message

All done. Your JIRA notifications will be sent to Rocket.Chat.

How to send BitBucket notification to Rocket.Chat

1. Create new Bot User

Just like the step 1 of JIRA integration, create a new bot user like ‘bitbucket’.

2. Create new integration

Same as step 2 of JIRA integration, but use this script to Script section.

Press Save Changes button and copy `Webhook URL’.

3. Create a WebHook on BitBucket

BitBucket doesn’t have WebHook function basically, But they provide it as add-on.

Got to BitBucket as administrator and goto “Administration” -> “Find new add-ons”. Search Bitbucket Web Post Hooks Plugin and install it.

BitBucket have to setting hook per repository. Click repository and go to “Settings” -> “Hooks” and click “Post-Receive WebHooks”.

Paste WebHook URL you copied by step 2.

All done. BitBucket notifications will be sent to Rocket.Chat.

Laravel provides unique validation rule for check unique value on a given database table.

Ref : https://laravel.com/docs/5.1/validation#rule-unique

Syntax

unique:table,column,except,idColumn,whereColumn1,whereValue1,whereColumn2,whereValue2,…

Ex1. Validate unique value in given database table

'username' => 'unique:users,username'

Ex2. Validate unique value except given ID

In mostly used when update existing record.

'username' => 'unique:users,username,'.$user->id

Ex3. Gives id column name if primary key of table isn’t ‘id’.

'username' => 'unique:users,username,'.$user->id.',user_id'

Ex4. Adding additional where clauses for more complex condition

If company has tag list and want to check unique value,

'name' => 'unique:tags,name,NULL,id,company_id,'.$company_id

If company has hierarchical folder list and want to check unique value per each node,

'name' => 'unique:folders,name,NULL,id,company_id,'.$company_id.',parent_id,'.$parent_id

If you want to give user can add new options to select tag in runtime, you can use Select2 jQuery library to do it.

Final result is like this:

Select2 Dropdown

1. Enable tags option

$('select').select2({
  tags: true
});

2. Handle createTag function to add extra properties

$('select').select2({
  tags: true,
  createTag: function (params) {
    var term = $.trim(params.term);

    if (term === '') {
      return null;
    }

    return {
      id: term,
      text: term,
      newTag: true // add additional perameters
    }
  }
});

3. Update templateResult function to display “(new)” text inside of item

$('select').select2({
  tags: true,
  createTag: function (params) {
    var term = $.trim(params.term);

    if (term === '') {
      return null;
    }

    return {
      id: term,
      text: term,
      newTag: true // add additional perameters
    }
  },
  templateResult: function(data) {
    var $result = $("<span></span>");

    $result.text(data.text);

    if (data.newTag) {
      $result.append(" <em>(new)</em>");
    }

    return $result;
  }
});

References

When insert a new row to pivot table, we can use attach() method like this:

$company = Company::find(1);
$company->users()->attach(1);

But this will not update created_at and updated_at timestamps column in pivot table.

To update timestamps, use withTimestamps() method on the relationship method.

class Company extends Model
{
  public function users()
  {
    return $this->belongsToMany(User::class)->withTimestamps();
  }
}