Hướng dẫn sử dụng Google Cloud Messaging - Server

GOOGLE CLOUD MESSAGING

Đợt này đang chán không có hứng lắm nhưng hôm nay mình vẫn sẽ viết 1 tut về sử dụng Google Cloud Messaging(GCM) alt.

Thực sự thì sử dụng GCM bằng PHP thì có vẻ hơi thừa vì có khá nhiều lib được người khác viết sẵn rồi và mình chỉ việc sử dụng. Nhưng vì cái này cũng khá là đơn giản và thực sự thì các lib cũng chỉ đáp ứng được phần cơ bản nhất của GCM thôi. Nên nếu muốn tùy chỉnh lại cách gửi message thì bạn cần phải chỉnh sửa lại lib hoặc phải viết thêm class mới (mà theo mình là không nên).

alt

Giới thiệu qua một chút GCM là một công cụ của Google nhằm hỗ trợ bạn có thể gửi tin nhắn đến nhiều thiết bị cùng lúc thông qua cloud của Google.

Chuẩn bị

Trước để để bắt đầu có thể bắt tay vào code để sử dụng được GCM thì bạn phải đăng ký với Google để tạo một project. Bạn vào link https://console.developers.google.com để đăng ký. Sau đó nhớ chọn Google API để enable Google Cloud Messaging lên nhé. Tiếp đến các bạn vào API > Credentials chọn create server key. Sau khi tạo key xong bạn sẽ được cấp một Server API key (key có dạng như này nhé :AIzaSyDAFD7m0eXKNg6xMXyzO7fRV9XtYNrGBIY) dùng để bạn kết nối và gửi các message lên google cloud để google giúp bạn gửi các message này đến các client.

Sau khi có được Server API key bạn có thể bắt đầu.

Bắt đầu

Ở đây mình hơi lười code PHP thuần nên mình sẽ hướng dẫn các bạn làm trên Laravel 5 (cụ thể 5.2 là phiên bản laravel mình đang sử dụng cho bài tut này).

Bước 1

Cài laravel 5.2 -> xem trên trang chủ có hướng dẫn chi tiết rồi nhé laravel.com

Bước 2

Tạo 1 project mới -> cũng như trên nhé

alt

Bước 3

Tạo cơ sở dữ liệu -> bạn tạo kiểu gì cho phù hợp với dự án của bạn là được

alt

Nhưng ít nhất phải có 1 bảng để lưu registration_id là đây là các id của các client đã đăng ký. Phải có các id này bạn mới có thể gửi được các message một cách chính xác nhất. P/S: Thật sự thì lúc trước mình không cần registration_id mình vẫn gửi message đến tất cả các device của mình được nhưng không hiểu sao đến khi mình viết bài tut này thì nó lại không hoạt động được nữa.

alt

Tìm hiểu các kiểu nhưng thấy ở đâu cũng dùng nên thôi có cũng tốt vì thông qua registration_id bạn có thể tùy chỉnh gửi message đến cho ai mình mong muốn chứ không phải send all nữa

alt

Tạo DB thì mình sẽ tạo gồm có 2 bảng là messages và devices

Bảng messages sẽ như sau. Bạn chạy

php artisan make:migration create_messages_table

Sau đó bạn vào file vừa được tạo trong database/migrations/2016_03_09_124125_create_messages_table.php và sửa lại thành như sau

P/S:(for noob) mấy cái số phía trước là được tự tạo ra chứ không phải do tự mình viết nên cái số phía trước bạn không cần quan tâm để ý cái tên thấy đúng như trong lệnh trước là được.

alt

<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMessagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->string('content');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('messages');
    }
}

Bây giờ đến bảng devices bạn cũng làm tương tự như trên. Bạn chạy

php artisan make:migration create_devices_table

Sau đó bạn vào file vừa được tạo trong database/migrations/2016_03_09_124125_create_devices_table.php và sửa lại

<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateDevicesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('devices', function (Blueprint $table) {
            $table->increments('id');
            $table->string('registration_id');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('devices');
    }
}

chạy lệnh sau để thực hiện migrate vào cơ sở dữ liệu

php artisan migrate

Vậy là xong bước 3 tạo database. copy paste copy paste quá là nhanh

alt

Bước 4 Tạo trang để quản lý và gửi message

Có 3 phần chúng ta cần phải làm ở đây Phần 1 tạo controller Vào app/Http/Controllers/ Tạo 1 file mới có tên là MessageController.php rồi thêm vào đoạn mã sau

<?php
namespace App\Http\Controllers;

use Illuminate\Support\Facades\Session;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Message;
use App\Device;
use DB;

class MessageController extends Controller
{
    /**
     * Hiển thị toàn bộ các message
     */
    public function index()
    {
        $messages = Message::all();

        return view('messages.index')->withMessages($messages);
    }

    /**
     * Tạo thêm một message mới
     */
    public function create()
    {
        return view('messages.create');
    }

    /**
     * Lưu message mới tạo vào database
     */
    public function store(Request $request)
    {
        $input = $request->all();

        $this->validate($request, [
            'title' => 'required',
            'content' => 'required',
        ]);

        Message::create($input);

        Session::flash('flash_message', 'Message successfully added!');

        return redirect()->back();
    }

    /**
     * Sửa message
     */
    public function edit($id)
    {
        $message = Message::findOrFail($id);

        return view('messages.edit')->withMessage($message);
    }

    /**
     * Lưu message được sửa vào database
     */
    public function update($id, Request $request)
    {
        $message = Message::findOrFail($id);

        $this->validate($request, [
            'title' => 'required',
            'content' => 'required',
        ]);

        $input = $request->all();
        $message->fill($input)->save();

        Session::flash('flash_message', 'Message successfully added!');

        return redirect()->back();
    }

    /**
     * Gửi message
     * $message_id là id của message trong database
     * $registration_id là id của device đã đăng ký với google và gửi về cho server lưu lại mà bạn muốn gửi message đến
     * $application_id là server id mà bạn đã tạo ở lúc chuẩn bị.
     * Chỗ này mà copy paste copy paste là không chạy đâu :))))
     */
    public function send($message_id, $registration_ids)
    {
        $message = Message::findOrFail($message_id);
        $data = array('message' => array('title' => $message->title, 'content' => $message->content));
        $applicationId = "application_id của bạn nhé ";
        $url = 'https://gcm-http.googleapis.com/gcm/send';

        $postData = array(
                    'registration_ids' => $registration_ids,
                    'data'             => $data,
                    );
        $headers = array(
                        'Authorization: key=' . $applicationId,
                        'Content-Type: application/json;charset=UTF-8;',
                    );

        // Initialize curl handle
        $ch = curl_init();
        // Set URL to GCM endpoint
        curl_setopt( $ch, CURLOPT_URL, $url );
        // Set request method to POST
        curl_setopt( $ch, CURLOPT_POST, true );
        // Set our custom headers
        curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
        // Get the response back as string instead of printing it
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
        // Set JSON post data
        curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $postData ) );

        // Actually send the push
        $result = curl_exec( $ch );

        // Error handling
        if ( curl_errno( $ch ) )
        {
            echo 'GCM error: ' . curl_error( $ch );
        }
        // Close curl handle
        curl_close( $ch );
        // Debug GCM response

        echo $result;
    }

    public function destroy($id)
    {
        $message = Message::findOrFail($id);

        $message->delete();

        Session::flash('flash_message', 'Task successfully deleted!');

        $messages = Message::all();

        return view('messages.index')->withMessages($messages);
    }

    /**
     * Nhận registration_id từ client và lưu vào database
     * cái này là client đăng ký với google và trả về cho mình chứ không phải là lung tung đâu nhé
     */
    public function addRegistration($reg_id)
    {
        header('Content-Type: application/json;charset=UTF-8;');

        $device = Device::where('registration_id', '=', $reg_id);

        if ($device)
        {
            return json_encode(array('code'=>'200'));
        }
        else
        {
            $device = Device::create(array('registration_id'=>$reg_id));
            return json_encode(array('code'=>'200'));
        }
    }

    /**
     * Cái này là send message cho tất cả các device đã đăng ký.
     */
    public function sendAll($message_id)
    {
        $message = Message::findOrFail($message_id);
        $registration_ids = DB::table('devices')->lists('registration_id');
        $data = array('message' => array('title' => $message->title, 'content' => $message->content));
        $applicationId = "application_id của bạn nhé ";
        $url = 'https://gcm-http.googleapis.com/gcm/send';

        $postData = array(
                    'registration_ids' => $registration_ids,
                    'data'             => $data,
                    );
        $headers = array(
                        'Authorization: key=' . $applicationId,
                        'Content-Type: application/json;charset=UTF-8;',
                    );

        // Initialize curl handle
        $ch = curl_init();
        // Set URL to GCM endpoint
        curl_setopt( $ch, CURLOPT_URL, $url );
        // Set request method to POST
        curl_setopt( $ch, CURLOPT_POST, true );
        // Set our custom headers
        curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
        // Get the response back as string instead of printing it
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
        // Set JSON post data
        curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $postData ) );

        // Actually send the push
        $result = curl_exec( $ch );

        // Error handling
        if ( curl_errno( $ch ) )
        {
            echo 'GCM error: ' . curl_error( $ch );
        }

        // Close curl handle
        curl_close( $ch );

        // Debug GCM response
        echo $result;
    }
}

Xong phần controller giờ ta tiến đến route. Vào app/Http/routes.php

Sửa thành

<?php

Route::get('/', function () {
    return view('welcome');
});

Route::get('/registration/{reg_id}', '[email protected]');

$router->get('/message/send/{message_id}', ['uses' => '[email protected]', 'as' => 'message.send']);

$router->resource('message', 'MessageController');

Cuối cùng ta sẽ tạo view hiển thị cho phần quản lý và gửi message vào resources/views/ thực hiện tạo file home.blade.php với nội dung như sau

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-default">
                <div class="panel-heading">Dashboard</div>
            </div>
        </div>
    </div>
</div>
@endsection

Tiếp đó tạo folder messages và thêm lần lượt 4 file với nội dung như sau

create.blade.php

@extends('layouts.app')

@section('content')

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel">
            <h1>Create Message </h1>
<p class="lead">Create Message below. <a href="{{ route('message.index') }}">Go back to all messages.</a></p>
<hr>
            @if($errors->any())
			    <div class="alert alert-danger">
			        @foreach($errors->all() as $error)
			            <p>{{ $error }}</p>
			        @endforeach
			    </div>
			@endif
{!! Form::open(array('route' => 'gcm.store')) !!}

				<div class="form-group">
    {!! Form::label('title', 'Title:', ['class' => 'control-label']) !!}
    {!! Form::text('title', null, ['class' => 'form-control']) !!}
				</div>

				<div class="form-group">
    {!! Form::label('content', 'Content:', ['class' => 'control-label']) !!}
    {!! Form::textarea('content', null, ['class' => 'form-control', 'style' => 'height:60px']) !!}
				</div>

{!! Form::submit('Create New Message', ['class' => 'btn btn-primary']) !!}

{!! Form::close() !!}
			</div>
		</div>
	</div>
</div>
@endsection

edit.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel">
            <h1>Edit Message </h1>
<p class="lead">Edit Message below. <a href="{{ route('gcm.index') }}">Go back to all messages.</a></p>
<hr>
            @if($errors->any())
			    <div class="alert alert-danger">
			        @foreach($errors->all() as $error)
			            <p>{{ $error }}</p>
			        @endforeach
			    </div>
			@endif
{!! Form::model($message, [
    'method' => 'PATCH',
    'route' => ['message.update', $message->id]
]) !!}

<div class="form-group">
    {!! Form::label('title', 'Title:', ['class' => 'control-label']) !!}
    {!! Form::text('title', null, ['class' => 'form-control']) !!}
</div>

<div class="form-group">
    {!! Form::label('content', 'content:', ['class' => 'control-label']) !!}
    {!! Form::textarea('content', null, ['class' => 'form-control']) !!}
</div>

{!! Form::submit('Update Task', ['class' => 'btn btn-primary']) !!}

{!! Form::close() !!}
			</div>
		</div>
	</div>
</div>

@endsection

index.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
	<h3>Google Cloud Message</h3>
	<a href="{{ route('message.create') }}" class="btn btn-primary">Add New Message</a>
	<hr>
	<table class="table table-bordered">
	<tr>
		<td>#</td>
    	<td>Title</td>
    	<td>Content</td>
        <td>Edit</td>
        <td>Send</td>
        <td>Delete</td>
	</tr>
	<?php $i = 1; ?>
@foreach($messages as $message)
	<tr>
		<td>{{$i++}}</td>
    	<td>{{ $message->title }}</td>
    	<td>{{ $message->content }}</td>
        <td><a href="{{ route('message.edit', $message->id) }}" class="btn btn-primary">Edit</a></td>
        <td><a href="{{ route('message.send', $message->id) }}" class="btn btn-primary">Send</a></td>
        <td>
        	{!! Form::open([
	            'method' => 'DELETE',
	            'route' => ['gcm.destroy', $message->id]
	        ]) !!}
	            {!! Form::submit('Delete', ['class' => 'btn btn-danger']) !!}
	        {!! Form::close() !!}
        </td>
	</tr>
@endforeach
    </table>
</div>

@endsection

Định nghĩa layout để viết code dùng chung vào 1 file. Tạo folder layouts Tạo file app.blade.php với nội dung

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Laravel</title>

    <!-- Fonts -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css" rel='stylesheet' type='text/css'>
    <link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700" rel='stylesheet' type='text/css'>

    <!-- Styles -->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
    {{-- <link href="{{ elixir('css/app.css') }}" rel="stylesheet"> --}}

    <style>
        body {
            font-family: 'Lato';
        }

        .fa-btn {
            margin-right: 6px;
        }
    </style>
</head>
<body id="app-layout">
    <nav class="navbar navbar-default">
        <div class="container">
            <div class="navbar-header">

                <!-- Collapsed Hamburger -->
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
                    <span class="sr-only">Toggle Navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
            </div>

            <div class="collapse navbar-collapse" id="app-navbar-collapse">
                <!-- Left Side Of Navbar -->
                <ul class="nav navbar-nav">
                    <li>
                        <a href="{{ route('message.index') }}" class="panel-heading">Message</a>
                    </li>
                </ul>

            </div>
        </div>
    </nav>

    @if(Session::has('flash_message'))
        <div class="alert alert-success col-md-6 col-md-offset-3">
            {{Session::get('flash_message')}}
        </div>
    @endif

    @yield('content')

    <!-- JavaScripts -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
    {{-- <script src="{{ elixir('js/app.js') }}"></script> --}}
</body>
</html>

Đó như vậy là đã xong 90% code ở server bạn chỉ cần tự chỉnh lại theo ý muốn của mình thôi, không thì để demo thì code của mình cũng chạy luôn được, nhưng sẽ gửi message cho tất cả các client đã đăng ký và gửi về registration_id. Chúng ta sẽ nhận registration_id thông qua URL params là

Route::get('/registration/{reg_id}', '[email protected]');

nghĩa là khi client gửi registration_id về cho bạn sẽ thông qua link này. Vd như : localhost/registration/c5reg75tb9e8nmcr2980928v502 Còn làm sao để client nhận được registration_id từ google thì bài này của mình sẽ không hướng dẫn nhưng cũng đơn giản thôi ý mà

alt

THE END

P/S: Publish rồi mà quên mất

Trong laravel bản 5.2 không như bản 4. Nó không tự động add form vào để có thể sử dụng như mình ở phía trên các bạn tự tìm hiểu để add vào nhé không thì chuyển sang viết form html bình thường cũng được.

Mình sài "laravelcollective/html":"5.2.*" này nhé alt


All Rights Reserved