読者です 読者をやめる 読者になる 読者になる

GinでORM(xorm)を使ってみる

Gin API実装

エウレカさんのチュートリアルに沿ってやってみた

Go言語製WAF GinでWebアプリを作ってみる【準備編】 | eureka tech blog

モデル(Model)設計

modelsディレクトリにuser.goを作成する

  • Repository構造体定義
  • Repository構造体に「DBからデータを取得する処理」を定義
  • RepositoryからUser構造体を生成する処理を実装

ソースは以下のとおり

package models
 
import (
    "github.com/go-xorm/xorm"
    _ "github.com/go-sql-driver/mysql"
    "fmt"
)
 
var engine *xorm.Engine
 
 
// init ...
func init() {
    var err error
    engine, err = xorm.NewEngine("mysql", "root:pass@/golang")
    if err != nil {
        panic(err)
    }
}

// User is
type User struct {
    ID       int    `json:"id" xorm:"'id'"`
    Username string `json:"name" xorm:"'nickname'"`
}

// NewUser ...
func NewUser(id int, username string) User {
    return User{
        ID:       id,
        Username: username,
    }
}
 
// UserRepository is
type UserRepository struct {
}
 
// NewUserRepository ...
func NewUserRepository() UserRepository {
    return UserRepository{}
}
 
// GetByID ...
func (m UserRepository) GetByID(id int) *User {
    var user = User{ID: id}
    has, hage := engine.Get(&user)
    if has {
        return &user
    }
 
    return nil
}

xormとは

Go言語のORM ドキュメント

Documentation - xorm: Simple & Powerful ORM Framework for Go Programming Language

1.インストール

$ go get github.com/go-xorm/xorm

2.まずデータベースのためのEngineを作る

engine, err := xorm.NewEngine(driverName, dataSourceName)

user.goのソース上では

engine, err = xorm.NewEngine("mysql", "root:pass@/golang")

driveNameにDB、dataSourceNameにユーザ名:パスワード/ DB名をそれぞれ指定する。

3.ORM Methods(基本)

  • Insert
affected, err := engine.Insert(&struct)
//INSERT INTO struct () values ()
  • 1レコード参照
has, err := engine.Get(&user)
//SELECT * FROM user LIMIT 1
  • 複数レコード参照
sliceOfStructs := new(Struct)
err := engine.Find(sliceOfStructs)
// SELECT * FROM user
  • update文
affected, err := engine.Id(...).Update(&user)
// UPDATE user SET ...
  • delete文
affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ...
  • レコードcount
counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user

他にも複数条件や並び替えなどの指定は以下ドキュメントを参照する github.com/go-xorm/xorm - Go Walker

*Xormはまた、生のSQL実行をサポートしています

results, err := engine.Query("select * from user")

mysqlのドライバーもinstall忘れずに

$ go get github.com/go-sql-driver/mysql

次に

xorm:”‘カラム名'”とアノテーションを入れることで、構造体のフィールドとデータベースのカラムをマッピング

type User struct {
    ID       int    `json:"id" xorm:"'id'"`
    Username string `json:"name" xorm:"'nickname'"`
}

Controller実装

modelのNewUserRepositoryからGetByIDの中身を取得して返すcontrollerを作成

package controllers
 
import (
    "golang/models"
)
 
// User
type User struct {
}
 
// NewUser
func NewUser() User {
    return User{}
}
 
// Get 
func (c User) Get(n int) interface{} {
    repo := models.NewUserRepository()
    user := repo.GetByID(n)
    return user
}

main.go実装

main.goにuser_idユーザー情報を返します。

package main
 
import (
    "golang/controllers"
    "github.com/gin-gonic/gin"
    "reflect"
    "strconv"
)
 
// main ...
func main() {
    router := gin.Default()
    router.GET("/:id", func(c *gin.Context) {
        // Pramを処理する
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            c.JSON(400, err)
            return
        }
        if id <= 0 {
            c.JSON(400, gin.H{"status": "id should be bigger than 0"})
            return
        }
        // データを処理する
        ctrl := controllers.NewUser()
        result := ctrl.Get(id)
        if result == nil || reflect.ValueOf(result).IsNil() {
            c.JSON(404, gin.H{})
            return
        }

        c.JSON(200, result)
    })
    router.Run(":8080")
}

ここまででhttp://localhost8080/:idを指定すると DBに入っている情報を参照してデータを返してくれることが分かりました。

ここまではチュートリアルなので次回はviewなどを作り簡易なサイトを作成できればと思います。

github.com

Mysqlをローカルに入れてみる(Mac)

Mysqlインストール

前回の記事でGoのwebフレームワークを使ってAPIを実装するにあたりローカルにMysqlを用意してMVCを実装してみる。

以下コマンドでインストール

$ brew install mysql

mysqlのバージョンを確認する

$ brew info mysql
mysql: stable 5.7.14 (bottled)
Open source relational database management system
https://dev.mysql.com/doc/refman/5.7/en/
Conflicts with: mariadb, mariadb-connector-c, mysql-cluster, mysql-connector-c, percona-server
/usr/local/Cellar/mysql/5.7.14 (13,467 files, 447M) *
  Poured from bottle on 2016-09-02 at 00:12:25

サーバーを開始してみる

$ mysql.server start
Starting MySQL
. SUCCESS! 

mysqlにログインしてみる

$ mysql -uroot
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.7.14  Homebrew

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

こんな感じでログインできたことがわかります。

次にパスワードを設定してみる

Mysqlでのパスワード設定

ここで少しつまづく

$ mysql_secure_installation

ここでpaswordの強度を聞かれる - Low - Medium - Strong

何も確認せずとりあえずMediumを使って半角英数字小文字と数字で設定すると 以下エラーが出る

ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

MySQL 5.7.8以降のrpmパッケージではvalidate_passwordプラグインがデフォルトで有効になっているらしい

mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| validate_password_dictionary_file    |        |
| validate_password_length             | 8      |
| validate_password_mixed_case_count   | 1      |
| validate_password_number_count       | 1      |
| validate_password_policy             | MEDIUM |
| validate_password_special_char_count | 1      |
+--------------------------------------+--------+

どうやら特殊文字と半角大文字が一文字ずつ必要だったらしい

今回はローカルでしか使わないのでセキュリティレベルを落としてパスワード作成

mysql>  SET GLOBAL validate_password_policy=LOW;
Query OK, 0 rows affected (0.00 sec)

そしてもう一度以下コマンドで設定していくとパスワードを設定することができます。

$ mysql_secure_installation

ログインしてみる

$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 5.7.14 Homebrew

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

無事ログインできました。

おまけ(mysqlセキュリティポリシー)

  • LOW ポリシーは、パスワードの長さのみテストします。パスワードは少なくとも 8 文字の長さでなければなりません。

  • MEDIUM ポリシーは、パスワードが最低 1 つの数値文字を含み、1 つの小文字および大文字を含み、1 つの特殊文字 (英数字以外) を含む必要があるという条件を追加します。

  • STRONG ポリシーは、パスワードの 4 文字以上の部分文字列が、(辞書ファイルが指定された場合に) 辞書ファイル内の単語と一致してはならないという条件を追加します。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 6.1.2.6 パスワード検証プラグイン

Gin入門編やってみた(Mac)

Ginとは

Go言語のWebFrameworkの一つです。

参考

エウレカさんのチュートリアルを途中まで再現してみました。 APIですこしつまづいているのでAPI実装は次回の記事で

Go言語製WAF GinでWebアプリを作ってみる【準備編】 | eureka tech blog

Goのインストール

$ brew install go

macでは一発でインストールできますがwindowsではすこし手間がかかりますが簡単にインストールできます。

goのバージョンを確認

$ go version
go version go1.6.2 darwin/amd64

Ginインストール

Goの触れる環境を作成したら次にGinをインストールしてみる。 まずはGOPATHとGOROOTの設定

export GOPATH="$HOME/repos/gohome"
export GOROOT="/usr/local/opt/go/libexec"
export GOBIN="$GOPATH/bin"
export PATH="$GOBIN:$GOROOT/bin:$PATH"

設定が反映されているか確認してみる

$ go env
GOARCH="amd64"
GOBIN="/bin"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/home/code/golang"
GORACE=""
GOROOT="/usr/local/opt/go/libexec"
GOTOOLDIR="/usr/local/opt/go/libexec/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT="1"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"

ここまでできたらGinをインストールしてみる

$ go get github.com/gin-gonic/gin

これでGinがインストールされます。

HelloWorld

ブラウザで確認してみる 1.まずは開発用のディレクトリを作成し main.goファイルを作成する。

package main
 
import (
    "github.com/gin-gonic/gin"
)
func main() {
    r := gin.Default()
 
    r.GET("/", func(c *gin.Context) {
        c.String(200, "Hello world")
    })
 
    r.GET("/hoge", func(c *gin.Context) {
        c.String(200, "fuga")
    })
    r.Run(":8080")
}

サーバー起動

$ go run main.go
[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] GET    /hoge                     --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2016/09/01 - 22:42:19 | 200 |      94.169µs | ::1 |   GET     /
[GIN] 2016/09/01 - 22:42:27 | 404 |       1.202µs | ::1 |   GET     /fuga
[GIN] 2016/09/01 - 22:42:38 | 200 |       6.408µs | ::1 |   GET     /hoge

localhost:8080にアクセスすると f:id:suga-tech3:20160904175557p:plain

localhost:8080/hogeでアクセスするとfugaがでてきます。

サーバにアクセスするごとにコマンドラインでGETリクエストがきていることがわかります。

次回はつまづいているAPIを実装してみようと思います。

1週間Go言語を勉強して登壇してみた vol.2

Go言語 × SlackAPI

この記事ではGo言語とslackAPIを用いてメッセージを送ってみようと思う

Slackとは

主にビジネス向けのチャットツールを使っている Slackは2013年8月のリリース後、わずか2年半でDAU200万を突破、2016年5月時点ではDAU300万を突破した、アメリカ発のサービスである。

背景

このままではLTでGoの文法紹介になってしまうため 何かものを作ろうと思った。

台風が最近多く天気が不安なことがあるのでlivedoorの天気予報RSSを利用して明日の天気と気温を通知してくれるサービスを作ってみた。

SlackAPI(Incomming WebHooks)を使用する(LT登壇まであと3日)

  • Incomming WebHooksとは?

=Slackが発行するURLへPOSTするとSlackにメッセージが投稿される簡単にメッセージを送ることができるAPIである。

Incoming Webhooks | Slack

上記からTokenを取得する。

Goを使ってslackに通知してみた(LT登壇まであと2日)

http.clientを使ってGETリクエストを送信してみた。

実際のソースは以下である

package main

import (
        "bytes"
        "net/http"
        "net/url"
        "fmt"
)

var(
        token  string = ""//Token追記
        apiUrl string = "https://slack.com/api/chat.postMessage"
)


func main() {
        data := url.Values{}
        data.Set("token",token)
        data.Add("channel","#general")
        data.Add("username","testBot")
        data.Add("text", "test")

        client := &http.Client{}
        r, _ := http.NewRequest("POST",  fmt.Sprintf("%s",apiUrl), bytes.NewBufferString(data.Encode()))
        r.Header.Add("Content-Type", "application/x-www-form-urlencoded")

        resp, _ := client.Do(r)
        fmt.Println(resp.Status)
}

実行するとこれだけでslackにメッセージを送ることができます。

Goでslackに天気予報通知してみた(LT登壇まであと1日)

登壇まであと残すところあと1日

以下のlivedoorRSSを用いて東京の天気予報を取得する。

http://weather.livedoor.com/forecast/rss/area/130010.xml

package main

//packageをimport
import (
        "bytes"
        "net/http"
        "net/url"
        "fmt"
        "encoding/xml"
        "io/ioutil"
        "log"
)
//変数宣言
var(
        //SlackAPIトークン
        token  string = ""
        //SlackApiのメッセージリクエストURL
        apiUrl string = "https://slack.com/api/chat.postMessage"
        //livedoor天気予報のRSS(東京)
        FEED_URL string = "http://weather.livedoor.com/forecast/rss/area/130010.xml"

)

//構造体
type WeatherHack struct {
    Title string `xml:"channel>title"`
    Description []string `xml:"channel>item>description"`
}


func main() {
        //getWeather関数にRSSのURLを渡す
        wh, err := getWeather(FEED_URL)

        //error処理
        if err != nil {
            log.Fatalf("Log: %v", err)
            return
        }

        //URLに生成するデータを追加
        data := url.Values{}
        data.Set("token",token)
        data.Add("channel","#general")
        data.Add("username","天気予報Bot")
        data.Add("icon_url","http:~~~.jpg")
        data.Add("text", wh.Description[2])
        fmt.Println(data)

        //http.clientを使用してGETリクエスト送信
        client := &http.Client{}
        r, _ := http.NewRequest("POST",  fmt.Sprintf("%s",apiUrl), bytes.NewBufferString(data.Encode()))
        r.Header.Add("Content-Type", "application/x-www-form-urlencoded")

        resp, _ := client.Do(r)
        fmt.Println(resp.Status)
}

//天気情報を取得する
func getWeather(feed string) (p *WeatherHack, err error) {

        //http.Getメソッドを利用してGETリクエストを送る
        res, err := http.Get(feed)
        if err != nil {
            return nil, err
        }

        b, err := ioutil.ReadAll(res.Body)
        if err != nil {
            return nil, err
        }
        wh := new(WeatherHack)
        err = xml.Unmarshal(b, &wh)

        return wh, err
}

上記をターミナルにて以下コマンドを実行する

go run ファイル名.go

実行ができたら 200 OKと返ってくる

slackに通知が来ているので確認してみると 以下のように天気が通知されていることがわかります。

f:id:suga-tech3:20160827175318p:plain

無事本田翼さんから天気予報が届いたことが確認できました。

Goをしてみたまとめ

  • コンパイル型を使ったことがそんなに多くないので型宣言を忘れがちになった

  • 1週間でここまでしたがまだまだメソッドなど多く奥が深い

  • GETリクエスト以外にもPOSTだけで5通りもの方法がある

  • エラーがわかりやすいため学習が比較的スムーズにできた

Goを一週間してきたことをまとめてみましたが まだまだここからがスタートなので次は並列処理、ポインタの理解をより深めてマスターして記事を公開していきます。

1週間Go言語を勉強して登壇してみた vol.1

Go言語

「Go」は2009年にGoogleによって発表されたコンパイラ型のプログラミング言語である。

YouTubeDropboxUberなどでもサービスのクリティカルな部分にGo言語を採用されている界隈で有名な言語となっている 特にGo言語は処理速度がとても速いため、APIのバックエンドやバッチ処理など「パフォーマンスがネックとなる部分」に採用されることが多いです。Androidの開発にも使える。

Goを始めた理由

  • もともとGoに興味があったがしていなかった。
  • 社外の人との合同LT会(勉強会)で登壇することに
  • LTまでの1週間の間に新言語をどれだけ習得できるのかやってみたかった(5日間は別仕事してるので仕事を終えてからです)

GoTour始めてみた(LT登壇まであと7日)

先輩エンジニアの人に勧めていただいたGoTourを3日で終わらようと決意

A Tour of Go

f:id:suga-tech3:20160827163406p:plain

phpと違うなと感じたこと(LT登壇まであと6日)

GoTourを文法を一通り終わらせてbasicまでやってみて気づいたことを書いてみる。

  • コンパイル型であること
  • 並列処理ができること
  • 変数名の 後ろ に型名を書く
  • 変数に暗黙的な型宣言ができる etc.(k := 3)
  • Goはポインタを扱うことができる
  • defer ステートメントという呼び出し元の関数の終わり(returnする)まで遅延させるものがあること。
  • 特有の構造体を持っていること

goにwhile文がなくfor文でループすることができる。

  • 標準のfor文
func main() {
        for i := 0; i < 10; i++ {
        }
}
  • whileのfor文での記述
func main() {
        for sum < 1000 {
        }
}

  • 並列処理とは
func main() {
        go func() {
        //このブロックは並列して実行される
        }()
        //このブロックは上の記述と同時に実行される
}

go特有の構造体

Goはクラスや継承の機能がないが以下のこれらを用いることでオブジェクト指向プログラミングが可能になる。

  • メソッドの定義
  • 構造体埋め込み

構造体の定義と初期化 - Goの場合、大文字から始まる名前 (関数名、型名、フィールド名) は、他のパッケージからアクセス可能になる

//構造体定義
type User struct {
  name string
  age  int
}
 
//User構造体にアクセス
var u User
u.name = "taro"             
u.age = 30                  
fmt.Printf("name:%s", u.name)
  • Goの構造体は以下のような初期化関数を作るのが一般的である
func newUser(name string, age int) *User {
    u := new(User)
    u.name = name
    u.age = age
    return u
}
 
var user *User = newUser("taro",30)

急いで学ぶGo lang#5 構造体 | Developers.IO

Goで何を作ろうか考える(LT登壇まであと4日)

長くなってきたので次の記事へ続く...。

CodeigniterでmodelとDBを使って見る

Codeigniterで基本のMVCしてみる

前回はCodeigniterのcontrollerとviewの基本を使ってみた 今回はmodelとDBを用いたMVCの流れを一連でやってみました。

  • まず以下のdatabaseファイルに情報を入力する
    • application/config/database.php
$db['default'] = array(
  'dsn' => '',
  'hostname' => 'localhost',
  'username' => 'name',
  'password' => 'password',
  'database' => 'database_name',
  'dbdriver' => 'mysqli',
  'dbprefix' => '',
  'pconnect' => FALSE,
  'db_debug' => (ENVIRONMENT !== 'production'),
  'cache_on' => FALSE,
  'cachedir' => '',
  'char_set' => 'utf8',
  'dbcollat' => 'utf8_general_ci',
  'swap_pre' => '',
  'encrypt' => FALSE,
  'compress' => FALSE,
  'stricton' => FALSE,
  'failover' => array(),
  'save_queries' => TRUE
);

Modelの書き方

  • 上記の情報を入れたらデータベースに接続可能であるかmodelでチェックしてみる。
    • application/models/Model.php
class Model extends CI_Model {

    function __construct(){
      parent::__construct();
      $this->load->database();
    }

    function getData($page){
      $query = $this->db->get("poem");

      return $query->result();
    }
  }

まずapplication/models配下にModel.phpを作成する。 コンストラクタ内でデータベースに接続できているか確認する。

getData関数を作成し$queryを作成する この$this->db->get()でpoemテーブルの中身を全件取得することができる。

Controllerにてmodelデータ読み込み

  • controllerにてAfter.phpに以下のソースにする
    • application/controllers/After.php
class After extends CI_Controller {

  public function index()
  {
    $this->load->model("Model");
    $data['result'] = $this->Model->getData("poem");

    $this->load->view("after", $data);
  }
}

Model.phpを読み込んでgetData関数にてpoemテーブルの情報を配列に格納し それをafter.phpのviewに値を渡す。

view側では以下のようにデータを取得する。

viewにmodelで取得したデータを表示させる

  • viewにてafter.phpに以下のソースにする
    • application/views/after.php
<body>
<?php foreach ($result as $results): ?>
  <p><?php echo $results->poem; ?></p>
<?php endforeach; ?>
</body>

$resultをforeachで回して取得したデータがstdClass型なので アロー記号でカラムを指定して中身を表示させると 以下のようにデータが取得できていることがわかります。

f:id:suga-tech3:20160814021129p:plain

Codeigniterの基本動作をしていきましたが難しくなくコードが書きやすく分かりやすいです。次は何かしらサイトを作っていけたらと思います。

Codeigniter初めてのcontrollerデータ渡し

Route

ルーティングでは一旦default_controllerを使う - application/config/route.php

$route['default_controller'] = after; 

このafterがControllerのクラスになる

Controller

クラスを作っていきたいが afterクラスを作成するがここがポイント クラスのphpファイルには先頭を大文字で設定する - After.php

class After extends CI_Controller {
    public function index()
    {
        $data['message'] = 'Hello world!';
        $this->load->view('after', $data );
        }
}

View

viewsディレクトリにafter.phpを追加する

<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?><!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>CodeIgniter</title>
</head>
<body>
<p><?php echo $message;?></p>
</body>
</html>

$data['message']で渡されたデータをview側で展開すると$messageでデータを取得することが可能となる

表示をみる

URLにアクセス http://example.com/index.php/before すると Hello world!が表示されていることが確認できます。

  • URLにindex.phpが含まれている

codeigniterの設定ではindex.phpを経由して表示させることからURLにも反映してしまう。 このURLを取り除きたい場合は.htaccessファイルをpublic_html配下にアップロードする。

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteCond $1 !^(index\.php|images|robots\.txt)
    RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>

上記ファイルを配置するだけで以下URLで表示を確認することができる。 http://example.com/before

まとめ

index.phpのURL排除に少しつまずいたものの簡単に確認することができました。 次回はDBを用いた記事を書きたいと思います。