20200112のPHPに関する記事は18件です。

【try-catchは難しくなかった】cakePHPとPHPの理解を深めるためフォームの書き方を復習してみた

cakePHP2.x

view/Users/create.ctp
$this->Form->create('モデル名','オプション');
$this->Form->input('フィールド','オプション')
$this->Form->end('ボタン名')
Controller/UsersController.php
function create {
 if($this->request->is('method属性'){
   $this->モデル名->save($this->request->data);
 }
} 

覚えておく事

モデル等の命名について

テーブル名は「-s」をつけるが
ただモデル名は「-s」をつけない
 ※Controller,Viewは「-s」をつける

(createの)オプション~inputのオプションは別~

array('type'=>'','action'=>'')

typeはHTMLのmethod属性を指定するもの
└'get'か'post'を指定する
└PUTやDELETEはvalue属性なのでここに入らない

 actionはaction属性を指定するもの
  └「.」で編集しているファイルのディレクトリーがはいる

⇒連想配列を書かないとmethodに'post'が入り'action'には自ファイルのアドレスがはいる

is()

 リクエストタイプを判別する

  if($this->request->is('post')){
    //postの場合の処理}

上記の文書の時は、postのリクエストがあった場合の処理をさせている

PHP7.x

form.php
<form method="POST" action="<?php print($_SERVER['PHP_SELF']) ?>">
<input type="text" name="name">
<input type="text" name="content">
<input type="submit" name="btn1" value="投稿する">
</form>

<?php
 @$name = $_POST['name'];
 @$content = htmlspecialchars($_POST['content']);


    try{
        $pdo = new PDO('mysql:host=localhost;dbname=study;charset=utf8','root','1234');
        $sql = "INSERT INTO bull(
            name,content
        ) VALUES (
            '$name','$content'
        )";
        $res = $pdo->query($sql);
    } catch(PDOException $e) {
        echo $e->getMessage();
        die();
    }
?>

DBの読み込み

$PDO = new PDO($pdo = new PDO($dsn, $username, $password);

PDOクラスをインスタンス化をして、DBに接続できる様にする。
dsn ⇨ DBに接続する文字列(DBによって書き方が異なる)
username ⇨接続するDBのユーザー名
password ⇨接続するDBのパスワード

try-catch

何らかの理由でDBに接続できなかった時にPDOクラスはPDOException例外を発生するみたいです。
その例外によってスクリプト全体が停止しまわない様に例外対処のために「try-catch-finally」が使われるみたいです。

try{
 //例外が発生する可能性のあるコード
}catch(発生するかもしれない例外の種類 例外を受け取る変数名){
 //例外発生時の処理
}finally{
 //例外に関わらず実行するコード
}

catch(PDOException $e)

発生したPDOException例外に対して$eに例外を受け取らせています。
変数$eから例外メッセージを受け取るために

$e->getMessage();

と記載しています

INSERT INTO

INSERT INTO テーブル名(列名1, ...) VALUES(列値1, ...)

テーブル名の後ろにカラム名を入れて、VALUESの後ろに代入する値を入れる。

エラー制御演算子(@について)

あまり使わない方がいいみたいですがエラーが出て先に進めなかったので利用しました

@エラーを表示させたくないコード

htmlspecialchars

入力した内容の一部をエンコードしてくれる事で悪意のあるスクリプトを不実行にしてくれる。
例えば

とフォームに入力すると

&lt;script&gt;alert(1)&lt;/script&gt;

とエンコードされDBに保存される。

一番不安に思ったのがそのままDB内容を表示させようとするといけないんじゃね?デコードされるんじゃない?と思いきやサイト表記は 

となる。

理由としてはブラウザがHTMLエンティティ化というものをしてくれるみたい

補足
htmlspecialchars_decodeの使い道
 ⇒HTMLエンティティ化したものはブラウザの機能で変換されるが、コード内で使用する際は変換されないのでデコードが必要になるのでDBなどいじる時は必要になる

参考サイト

【CakePHP】CakeRequest
┗$this->requestのメソッドが色々書いているので参考になる

PHPでデータベースに接続するときのまとめ
 ┗DB周りについて詳しく書いてる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エンジニア研修

概要

今回弟がエンジニアデビューするということで、macbookAir(gold)を買ってあげました。
これから9か月間、私の方で研修をおこない、それなりのエンジニアになってもらおうかと思います。

そこで弟のように、エンジニアを始めたばかりの人の参考になるように、「研修内容」、「弟が躓いた箇所」や「参考文献など」を記事にしていこうかと思います。
普段私は自分のブログサイトで発信していましたが、誰も見てくれないので、qiitaに書いていくことにしました。
https://campbel.love/が私のサイトです。たまには見てくださいな(笑)

弟はそこまで忍耐力もなく、また、ほとんど初心者なので「挫折をしないカリキュラム」、「スムーズな理解」に重点を置くつもりです。まさにゆとり!
なので、少し遠回りをしてしまうかもしれません。
例えば、Dockerを用いてもらいたいけど、初学者の弟には難しいのでMampから始めてもらう。みたいな。(最終的にはdockerにしてもらいますが。)

偉そうにカリキュラムは組みましたが、私自身も勉強途中で全部を理解はしていません。弟と一緒に勉強して知識を高めていきたいと思っています。
*もし、こんなやり方の方がいい、これは間違っているなどありましたら、コメントをお願いいたします。弟と私のために。

私は文章を書くのがすこぶる苦手なので、わかりづらい事や誤字脱字もあるかと思いますが、どうぞよろしくお願い致します。

予定のカリキュラム

文法の理解などは基本はdotinstallをやってもらいます。(有料なので私が支払うことになりそう。。。)
わからないことがあれば随時きくように。

1ヶ月~2ヶ月
・php、html、css、js、mysqlを理解する。(jsから初めてもらおうかなと)
・mampの設定
・macの操作、mampになれる。

3ヶ月〜5ヶ月
・jqueryもしくはvueをつかう。vueが使えるようになったら最高。ちなみに私は使用したことがない。
・gitを使えるようにする。
・local環境下でいいのでlaravelでサイトを作れるようにする。
「掲示板的なやつ」もしくは「画像投稿サイト」でいい。
デザインパターンはしっかり行う。
リレーション、トランザクションもしっかり行こなう。
ログイン認証もしっかり行う
時間があればrest api作成も行う。
・hostsの理解など

ここらへんで一区切り。。。

6ヶ月〜7ヶ月
・cicdを使う(cercleciでいいかと)
・awsを使う
・laravelのテスト仕様書も書けるようにする。

8ヶ月
・localとawsでdockerを使う。

9ヶ月
kubernetesを使用する??これは未定。。。

これでかなりレベルの高いエンジニアであると僕は思うが、参考にしているエンジニアチャンネルでは、これが最低ラインだと言っていました。。。(kubernetesは除く)
エンジニアの敷居が上がってきているのかしら?٩( ᐛ )و
しかし、専門学生が2年通うよりも全然最強だと思います!!
ちなみに私はこれらのことを、もちろんこれだけでなく他の勉強もしながらですが、数年かけました。それを弟は9ヶ月で行うため、厳しい道のりになるとおもわれます。弟よ!がんばれ〜!!( ^ω^ )

私の課題として、「awsでdockerを使用する」、「kubernetesを使用する」はまだやったことがないので、できるようにしておきます!

なぜmacなのか

一言で言うと使っている人が多いから。
他には、サーバーではlinuxを使用することが多いが、macはwinよりもlinuxに近いからとか、bashが使えるからとか、かっこいいから。

賛否両論だと思いますが、とにかくmacを買うことをお勧めします。
高いなと思うかもしれませんが、分割での購入も可能で、2年の分割だと利子はなんと0円です。
あと学割があって最大22000円引きで購入が可能。2020年2月からは新学期キャンペーンで3万ほどのヘッドフォンがついてきますよ!お得です!
参考

1日目はマックの設定

1 appleidの作成、osが最新かどうかのチェック
2 アプリのダウンロード(詳細は以下)
3 chromeの拡張のダウンロード(詳細は以下)

macにダウンロードするアプリ

ブラウザ
「こんなにいらないやろ」と思うかもしれないが、”エンジニア”には必要。(アプリ欄を見てこれらがないと笑われてしまう。)
chrome
firefox
opera

開発に必要なアプリ
サイバーダッグ: サーバーのファイルをいじれる
iterm2: ターミナルの拡張版
sequel pro: mysqlをいじる
vs code: エディタ(高級なメモ帳)
postman: apiを可視化
xcode: 主にアイフォンのアプリを作成するのに使用。アイフォンのアプリを作成しなくとも必要。
mamp: mac、apache、mysql、phpの略。windowsだとxamp。パソコン内に仮想のサーバーを立てることができる。
mampでの開発は時代遅れのゴミで、本来ならば(というよりできるエンジニアは)Dockerを使うべきだが設定はmampの方が断然楽なので、初め(6ヶ月くらい)はmampで対応する。
composer: phpのライブラリ環境コマンド。これは必須。なくても開発はできるが、使用していないのはゴミエンジニアである。

チャットアプリ
line: line
chatwork: よく使われるチャットツール
slack: よく使われるチャットツール

その他便利なアプリ
app cleaner: アプリをきれいに削除できる
cd to: フォルダから直で移動できる。
clipy: 有料。コピーしたものの履歴が残る。数百円なので絶対に入れた方がいいと思われるアプリの一つ。使ったら世界が変わったように感じた。弟にも買ってあげた。

これらはまだ使用しないと思われるので、まだインストールしなくていい
brew cask
git
anyenv: phpしか使わないならば、いらない。python、node.jsをやる場合は入れた方がいい。

chromeにダウンロードする拡張の機能

開発はおもにchromeで行うため、chromeの設定はとても大事です。
ダウンロード先はここ
https://chrome.google.com/webstore/category/extensions?hl=ja

開発に必要なアプリ
Clear Cache: キャッシュの削除
EditThisCookie: クッキーの編集
Page load time: ページが表示されるまでの時間計測
Quick Javascript Switcher: jsのオンオフ切替。1クリックでできるため、めちゃめちゃ便利です。

あると便利なアプリ
Wappalyzer: 自分が閲覧したサイトがどの技術で作られているか確認ができる。
Google のクロム ™ のための時計: chromeに時計を表示させる。
ブックマークサイドバー: ブックマークがマウスオーバーで開ける。お勧めの設定は「マウスを左に持っていき右クリックで表示させる。」

個人的にお勧めなアプリ
Douga Getter: 動画のダウンロード
ストリームレコーダー: 動画のダウンロード

とりあえず1日目はここまで。お疲れ様です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VBAフォーム、DB接続クラス化入門①

【はじめに】

医療事務から30代未経験でSIerへ転職して1年経過したため(保守運用)、この一年勉強して自分がググる際に、この情報もっとあったらいいなと思ったことを備忘録として登録します。
配属しいている現場はVB6を使用したレガシーな現場である為、書き方が古かったりするかもしれないが、その点はまだ無知な点が多い為、ご了承ください。
熟練者から見たらまだまだ、冗長化されている部分もあるかと思いますが、あくまで入門者向けの方に参考していただければと思います。

自宅でも勉強構築できる、なおかつ互換性のあるVBAにてCRUDができる機能を実装します。
そもそもプログラミング自体ほぼ皆無だった状態からのため、レガシーだろうが、何だろうが、まずはプログラミングについて理解を深める意味で初めてQiitaに投稿します。
今回はDB接続~テーブルに登録してあるデータをSELECTして取り出したデータをリストに表示するところまでを備忘録として記載します。

【環境】

  • OS:Windows8.1
  • Excel 2013
  • エディタ:VBE
  • ローカル開発環境:xampp(過去にPHPのCRUDを勉強するためにxamppを用いてDB構築したため利用)

【主な機能について】

DB登録されたID、UserName、Email、登録年月日をテキストボックスに入力して、データ表示ボタンをクリックすると、リストボックスに登録データを表示する。
※1.登録年月日の入力は必須
※2.各バリデーションチェックのソースコードを載せると長くなるため、記載しておりません。

フォームのイメージは以下
メイン画面(現状データ検索ボタンしかないが、データ作成、更新、削除を追記予定)
frmMain
Main.PNG

データ検索ボタンをクリックすると以下のフォームを表示
テキストボックスのオブジェクト名は以下の通り

  • ID⇒ID_txt.Text
  • UserName⇒UserName_txt.Text
  • Email⇒Email_txt.Text
  • 登録年月日(from)⇒InsDate_from_txt.Text
  • 登録年月日(to)⇒InsDate_to_txt.Text

frmSearch
frmイメージ.PNG

チェック項目

  ID   UserName     Email     登録年月日(from)     登録年月日(to)    
入力 任意 任意 任意 必須 必須
最大文字数 11 25 255 8 8

※DB設計時、細かく設定しておらず、Emailの最大文字数は255のままになっている
改めて適切なchar数に設定したいと思う

【Create Table】

DBへ登録するための、テーブル作成
データベース名:bbs
テーブル名:test_user_data_hed
※データは適当にINSERTして登録してもらえればと思います。


test_user_data_hed.sql
CREATE TABLE bbs.user_data_hed (
    ID        INT(11) AUTO_INCREMENT NOT NULL PRIMARY KEY,
    USER_NAME VARCHAR(25) NOT NULL,
    EMAIL     VARCHAR(35) NOT NULL,
    STATUS    INT(1) NOT NULL,
    INS_DATE  VARCHAR(14) NOT NULL,
    UPD_DATE  VARCHAR(14) NOT NULL      
);

/*
STATUS = 1:通常、9:論理削除
*/

【ソースコード】


frmMain
'--------------------------------------------------------------------
'データ検索フォーム表示
'--------------------------------------------------------------------
Private Sub frmTableSearch_Click()
    frmSearch.Show
End Sub
frmSearch
Option Explicit
'--------------------------------------------------------------------
'変数宣言
'--------------------------------------------------------------------
    Private lstrId             As String
    Private lstrUser_name      As String
    Private lstrEmail          As String
    Private lstrStatus         As String
    Private lstrInsDatefrom    As String
    Private lstrInsDateTo      As String

'--------------------------------------------------------------------
'機能:bbsへDB接続
'test_user_data_hedに登録されているテーブルデータをリストへ表示するためのトリガー
'--------------------------------------------------------------------
Public Sub select_cmd_Click()
    Dim dbconnect   As mysql_connect
    Dim ladoRs      As ADODB.Recordset   'ローカルレコードセット変数
    Dim ConnectFlg  As Boolean           '接続成功: True   接続失敗:False
    Dim lstrSQL1    As String            'ローカルsql格納変数
    Dim lintroop    As Integer           'ループ変数
    '例外処理⇒以下の記述でエラーが発生した場合にはエラーメメッセージ表示
On Error GoTo ErrHandler_End

    lstrId = ID_txt.Text
    lstrUser_name = UserName_txt.Text
    lstrEmail = Email_txt.Text
    lstrInsDatefrom = InsDate_from_txt.Text
    lstrInsDateTo = InsDate_to_txt.Text

    'mysqlクラスへ接続 Mysql_connectクラスのdbconnectをインスタンス化
    Set dbconnect = New mysql_connect
    'Mysqlクラスのmysql_connectメソッド内で接続処理を行い、接続成功の場合Trueを返す
    ConnectFlg = dbconnect.mysql_connect

    'selectすべきSQLを記述
    lstrSQL1 = ""
    lstrSQL1 = "SELECT ID"
    lstrSQL1 = lstrSQL1 & ",USER_NAME"                        
    lstrSQL1 = lstrSQL1 & ",EMAIL"
    lstrSQL1 = lstrSQL1 & ",STATUS"
    lstrSQL1 = lstrSQL1 & ",INS_DATE"
    lstrSQL1 = lstrSQL1 & ",UPD_DATE"
    lstrSQL1 = lstrSQL1 & " FROM bbs.test_user_data_hed"
    '論理削除は表示しない
    lstrSQL1 = lstrSQL1 & " WHERE STATUS <> '9'"

    'ID入力がある場合は、
    If Trim(lstrId) <> vbNullString Then
        lstrSQL1 = lstrSQL1 & " AND ID ='" & lstrId & "' "
    End If

    'USER_NAME入力がある場合は、
    If Trim(lstrUser_name) <> vbNullString Then
        lstrSQL1 = lstrSQL1 & "AND USER_NAME ='" & lstrUser_name & "' "
    End If

    'EMAIL入力がある場合は、
    If Trim(lstrEmail) <> vbNullString Then
        lstrSQL1 = lstrSQL1 & "AND EMAIL ='" & lstrEmail & "' "
    End If

    lstrSQL1 = lstrSQL1 & " AND INS_DATE >='" & lstrInsDatefrom & "000000" & "' "
    lstrSQL1 = lstrSQL1 & " AND INS_DATE <='" & lstrInsDateTo & "235959" & "' "

    'DB接続状態である場合に、Mysqlクラスのmysql_selectメソッドへアクセスし、上記で記述したSQLとリストオブジェクトを引数として渡す。
    If ConnectFlg = True Then
      Set ladoRs = dbconnect.mysql_select(lstrSQL1, select_data_List)      
    End If
    'インスタンス解放(確保していたメモリ領域を0にする)    
    Set dbconnect = Nothing
    Set ladoRs = Nothing

Exit Sub

ErrHandler_End:
    Set dbconnect = Nothing
    Set ladoRs = Nothing
    Call MsgBox ("DB接続エラー" & vbCrLf & _
                "ErrNo:" & Err.Number & vbCrLf & _
                "Err内容:" & Err.Description _
               , vbExclamation, "Error:Sub dbconnect")
End Sub

Private Sub Exit_cmd_Click()
    Unload frmSearch
    Load frmMain
End Sub

Private Sub UserForm_Initialize()
    ID_txt.Text = vbNullString
    UserName_txt.Text = vbNullString
    Email_txt.Text = vbNullString
    InsDate_from_txt.Text = vbNullString
    InsDate_to_txt.Text = vbNullString
End Sub

Mysql_connect.cls
Option Explicit
 Private adoCon     As ADODB.Connection      'データベースアクセスインターフェースオブジェクト(ActiveX Date Objects)
 Private adoRs      As ADODB.Recordset       'レコードセット変数 Executeで実行したSQLで取得したレコードをセット
 Private adoCmd     As ADODB.Command         'SQL実行コマンドオブジェクト
 Private Const DSN = "ExcelDB"               'DataSourceName
 Private Const Database = "bbs"              '接続先DB
'--------------------------------------------------------------------
'DB接続class
'mysqlへの接続
'【引数】なし
'mysql_connect          True:接続成功 False:接続失敗
'--------------------------------------------------------------------
Public Function mysql_connect() As Boolean
    'adoコネクション作成(データベース接続オブジェクト)
    Set adoCon = New ADODB.Connection

On Error GoTo ErrHandler

    'サーバから取得したカーソルをクライアント側に置いて作業する。
    adoCon.CursorLocation = adUseClient
    'DB接続、ODBCを使用してMysqlへ接続
    'Mysql接続(接続文字列 ⇒ DSN)
    adoCon.Open DSN
    '接続成功
    mysql_connect = True
    'エクセル画面の左下、ステータスバーにDB接続成功した場合は、以下の文字列を表示
    Application.StatusBar = "DB接続成功!! " & "接続先:" & Database
    Debug.Print "接続先:" & "bbs"


Exit Function

ErrHandler:
        '接続失敗
        mysql_connect = False
        Set adoCon = Nothing
        Application.StatusBar = "DB接続失敗!! 調査を依頼してください。"
        Call MsgBox("DB接続時エラー", vbExclamation, "Error:Function mysql_connect")


End Function


'--------------------------------------------------------------------
'【機能】sql文実行(SELECT)
'【引数】 lstrSQL
'        select_date_List
'【戻り値】adoRs 問い合わせしたSQLのレコードを返す
'--------------------------------------------------------------------
Public Function mysql_select(ByVal vstrSQL1 As String _
                           , ByVal vobjList As MSForms.ListBox) As ADODB.Recordset

    Dim lintloopRcdCnt   As Integer        'レコード取得ループ変数
    Dim lRecordCnt       As Long           'レコードカウント数取得変数
'On Error GoTo 以下で予期せぬエラーが発生した場合、ErrHandlerのラベルまで飛び、エラー発生時の処理およびメッセージを表示してFunctionを抜ける
On Error GoTo ErrHandler

    'DBアクセスのタイムアウト時間(時間切れ)を10分に設定
    adoCon.CommandTimeout = 60*10
    Set adoCmd = New ADODB.Command        'SQLコマンドオブジェクトインスタンス化 
    adoCmd.ActiveConnection = adoCon      'コマンドオブジェクトのActiveConnectionプロパティを使用して、現在開いているDBと関連付け
    adoCmd.CommandText = vstrSQL1         '実行するSQLを格納 コマンドテキストにSQLステートメントを定義する(文字列にストアドプロシージャの文字列を設定するとストアドを実行する)
    Set adoRs = New ADODB.Recordset       'レコードセットオブジェクトインスタンス化

    'クエリーの結果を格納するRecordsetオブジェクト変数
    'コマンドテキストに格納されたSQLをExecuteメソッドを実行し、adoRsへ格納する
    Set adoRs = adoCmd.Execute
    Debug.Print vstrSQL1

    'SQLで問い合わせて取得したデータをリストボックスへ表示
    With vobjList
            .Clear                       'リストの項目を初期化するためクリア
            .ColumnCount = 3             'リストに表示するカラム数
            .ColumnWidths = "30;60;100"  'リストに表示する幅

   'adoRsへ格納したSQLステートメントをループさせてリストへ取り出す処理   
    Do Until adoRs.EOF
         .AddItem ""
         'レコードセットの列数を取得して、ループ
         For lintloopRcdCnt = 0 To adoRs.Fields.Count - 1
         'レコードの値が存在する場合は、レコードの値をリストへ表示する
             If Not IsNull(adoRs(lintloopRcdCnt).Value) Then
                 'adoRsのレコードデータをリスト(行、列)をループして追加(Listの先頭インデックスは「0」スタートのため、-1をする⇒(List(0),レコード列の
                 'カウント数)に対して、レコードの値を代入していっている)
                 .List(.ListCount - 1, lintloopRcdCnt) = adoRs(lintloopRcdCnt).Value
             End If
         Next
         '1行目のレコード取り出しが完了したら、次のレコードへ
         adoRs.MoveNext
    Loop
    End With

   'レコードカウント数を取得し、0件だった場合はエラーメッセージ
    lRecordCnt = adoRs.RecordCount
    If lRecordCnt = 0 Then
        Call MsgBox("該当のデータは存在しません。", vbQuestion)
        Exit Function
    End If
    'レコードセットを返す
    Set mysql_select = adoRs

    Set adoCmd = Nothing
    Set adoRs = Nothing
    'Connectionオブジェクトを閉じて、それに関連するRecordsetオブジェクトも閉じる
    adoCon.Close
    '閉じただけでは確保しているメモリは解放されない為、Nothingで解放する。
    Set adoCon = Nothing

Exit Function
ErrHandler:
        'レコード取得時に予期せぬエラー
        Set adoCmd = Nothing
        Set adoRs = Nothing
        adoCon.Close
        Set adoCon = Nothing
        Call MsgBox("DBレコード取得時エラー" & vbCrLf & _
                          "ErrNo:" & Err.Number & vbCrLf & _
                          "Err内容:" & Err.Description _
                         , vbExclamation, "Error:Function mysql_select ")


End Function

以上がMysqlへ接続するためのソースになるが、ソースとその説明を別々に記載するとわかりづらいかと思い、すべてコメントに記述してみた。

【参考資料】


ODBCによるMysql接続は上記サイトを参考に、コントロールパネル⇒システムとセキュリティ⇒管理ツールの順で進み、ODBCデータソース(32ビット)にて、ユーザーDSNを追加し、

  • Data Source Name: xxxxxx
  • TCP/IP: localhost or 127.0.0.1
  • Port: 3306
  • User: xxxxxx
  • Password: xxxxxx
  • Database: xxxxxx

上記を設定後、テストボタンをクリックして、「Connection Successful」が出れば接続テストは完了です。
PHP超入門はクラスを学ぶ上で大変参考にさせていただいた記事です。
PHP言語に限らず他の言語でも参考になるものと思っています。

こちらの記事も大変参考にさせていただいた記事です。クラス、Propertyの使い方など(この点については使いどころも部分がまだまだ自分も未熟のため、本投稿のソースはPropertyを使用していませんが…)実際にデバック操作することで理解を深められる内容となっていました。
どこを分割するか、どうやって分割するか、についてのコツがわかりやすくまとまっています。
今でも何度も読み返すことの多いブログです。

オブジェクト指向について未だに理解が不十分であるが、そもそもを理解するために、良書といわれている上記本を参考し、クラスをインスタンス化した場合のメモリの確保などについて学んだ本。
再度読み返し、理解を深めたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laragonで簡単に開発環境を構築する

皆さん、学習するとき、開発をするとき、ローカルで確認を行うときの環境構築はどうされていますか?
恐らくMacを所有している方が多くDockerを使われている方が多いと思います。

自分が現在、所有しているはHPのSPECTRE X360 13、Windows 10 Homeです。Windows 10 Homeなのです。
ラップトップと言っても非力というほどではありませんがVagrantを立ち上げている間、かなりファンが回り気になります。
それに決して速いとは言えません。さらに現時点ではまだWSL2とDocker for WSLは正式リリースされていません。

ではどうするか?
コメント 2020-01-01 215211.png
https://laragon.org/

Laragonです。
Windows向けのAMP環境構築ソフトで
PHP、Node.js、Python、Java、Go、Rubyをサポートし、
ApacheとNginxのどちらを使用するかを切り替えることができます。
では早速、インストールを行っていきましょう。

1. インストール

コメント 2020-01-01 215427.png
https://github.com/leokhoa/laragon/releases

Githubから最新版を落としてきましょう。
環境構築の経験がない、初学者の方はlaragon-full.exeを選んでいきましょう。
以後はこれをベースに進めます。

インストール先は拘りがなければわかりやすいようデフォルトのC:\Laragonにしておきます。
Laragon 2020-01-12 164745.png

VSCode等を使用する際は"Sublime Text & Terminalを追加する"のチェックが外れた状態にしておきます。
Laragon 2020-01-12 164703.png
ちなみにNotepad++、WinSCP、Putty、HeidiSQL Portable等がインストールされるのでこの1回のインストールだけで開発が出来るようになります。

2. 起動

では起動してみましょう。
Laragon 2020-01-12 165644.png
このスクリーンショットでPHP7.4になっているのは自身で後から追加ができるためです。
"ウェブ"を押すとホスト名を"localhost"ポート番号を8080で設定しているので
"http://localhost:8080/"
の形でブラウザが開きます。
"データベース"でHeidiSQL
"ターミナル"でCmderを起動します。
"ルート"は設定されたルートフォルダを表示します。"ウェブ"で表示される場所も同じものになります。
設定は右上にの歯車マークから表示できる設定で行います。

3. バージョンの追加

今回はPHPを例に紹介します。
https://windows.php.net/download/
から使用するものをダウンロードしてきます。
フォルダを解凍後、
C:\laragon\bin\php\へ追加します。
PythonであればC:\laragon\bin\python\のようになります。
Laragon 2020-01-12 170749.png
追加後に使用するバージョンを左上のメニューから選択します。
Laragon 2020-01-12 171603.png
ここから起動時に使用する言語やバージョン等を切り替えることが出来ます。

4.ガンガン使いましょう!

Macには現時点で対応していませんし、万能とまではいかないまでも
軽く使いやすいので空いた時間でサクッと作業を行うのに役立ちます。
OSがWindows 10 Homeで開発環境で悩んでいる方に特にオススメです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでLoggerをいい感じにする

ログって結構大事ですよね。
手軽にちゃんと情報がだせるLoggerを定義しておいて、いい開発・運用環境作りたいです。

この記事では、ElasticSearch等に突っ込んでログを検索可能にすることを前提に、いい感じにログ出力することを目指します。

環境

Laravel 5.8

やりたいこと

Laravelのログをカスタマイズして、いい感じにする。

いい感じとは

  • Laravel上で生じたイベントを記録し、問題が生じた際に、原因の究明と対応を迅速に行えるようにする。

この辺はログ指針を立てることで、目的を明文化します。

(参考)
ログ出力指針の書き方 - White Box技術部
https://seri.hatenablog.com/entry/2018/10/20/172656

どうするか

この記事ではログ出力にフォーカスします

ロギングに関しては、二つの段階があると思います。
1. ログ出力
2. ログ検索

ここでは、ログ出力に際して、実装者が考えることを最小限にし、ログ出力のたびに毎度考えることを減らすための方策を書きます。検索に関しては、ElasticSearchに突っ込む想定だけしておいて、詳細についてはまた別の機会に書きます。

何を出力するか

そのためには、
- アプリケーションの操作者
- いつそのイベントが起きたか
- どこでイベントが起きたか
- 何が起きたか
- そのイベントはそれくらい重大か/すぐに対応が必要か

を適切に伝える必要があります。

そして、これを、なるべく実装者が余計なことを考えないで実装するようにしたい。

理想は、以下くらい書いたら必要なこと全部わかるくらいにしたい。

\Log::info('Fail to get user own posts.');

すると、以下の内容は、自動で出力したいなってところです。

  • 操作者情報
  • いつイベントが起きたか
  • どこで起きたか

まとめ

操作者情報といつ・どこで起きたかに関して、ロガーに実装者が情報を渡さなくてもよしなに出力される。

ことを目標にして、Laravelの実装を以下で進めようと思います。

やること

以下の二つのログ出力を実装します。

  • イベントログ

実装者によって、Loggerを使って出力するように実装するログです。
ログインの実装をしていればログインのログを、
APIとのやり取りではパラメータの内容などを残すこともあるでしょう。

datetime:2020-01-08 14:48:23    channel:local   level_name:INFO message:fugafuga     uid:e86d557c07  file:/var/www/app/Http/Controllers/HomeController.php   line:30 class:App\Http\Controllers\HomeController       function:index
  • アクションログ

リクエストごとに生成されるログです。
ここに、ユーザーID、セッションID、リクエストIDを付与する事で、これらの関連づけをします。

datetime:2020-01-08 14:48:23    channel:local   level_name:INFO message:action_log      user_id:0       session_id:zZPV5egG7L2FXAWKR6nDHvs2JgNL80Fm6HfmtdCn     uid:e86d5  url:/   ip:172.25.12.11   http_method:GET server:localhost   referrer:http://localhost/fuga

diagram-5236162916520731523.png

リクエストと各イベントの関連づけをしておけば、エラーログが検出された時にその前後の操作が追えます。

実装

共通

  • LTSVフォーマットライブラリ導入

解析の際に、LTSV形式だと簡単になりそうなのでLTSVで出力します。

Laravelのロガーで使われているMonologでLTSVを使うには、以下のライブラリが良さそうなので、これを入れます。

https://packagist.org/packages/hikaeme/monolog-ltsv-formatter

※Laravel5だとMonologのバージョンが1系で、Laravel6はバージョン2です。それに伴って、上記のライブラリもバージョンが1→2に変わっていますので、それぞれの環境に合わせて使うといいと思います。

イベントログ

ログのカスタマイズについては以下が参考になります。

Monologチャンネルの上級カスタマイズ | ログ 5.8 Laravel
https://readouble.com/laravel/5.8/ja/logging.html#customizing-monolog-for-channels

Laravelのログにファイル名やメソッド名を出力する
https://qiita.com/iakio/items/7b8539db928d2b419921

Laravelでログのフォーマット変更する方法 - ノーバグノーライフ
https://blog.nobug-nolife.com/entry/20181202/1543762300

Seldaek/monolog
https://github.com/Seldaek/monolog

  • ログの設定を追加

Laravel5.8だと、config/logging.php でログのチャンネルを設定できます。

デフォルトだとstack チャネルを使っていて、stackチャネルはdailyチャネルにその詳細が定義されてます。今回は、日付ごとにファイルを分けて出力されるようにしてOKなので、dailyチャネルのまま、フォーマットを変えていきます。

config/logging.php
   'daily' => [
       'driver' => 'daily',
       'path' => storage_path('logs/laravel.log'),
+      'tap' => ['App\Logging\CustomizeFormatter'],
       'level' => 'debug',
       'days' => 14,
   ],
  • フォーマッタを実装
app/Logging/CustomizeFormatter.php
<?php

namespace App\Logging;
use Hikaeme\Monolog\Formatter\LtsvFormatter;
use Monolog\Logger;
use Monolog\Processor\IntrospectionProcessor;

class CustomizeFormatter
{
    public function __invoke($logger)
    {
        $uidprocessor = app()->make('Monolog\Processor\UidProcessor');
        $introprocessor = new IntrospectionProcessor(Logger::DEBUG, [
            'Monolog\\',
            'Illuminate\\',
        ]);
        $ltsvformatter = new LtsvFormatter(
            'Y-m-d H:i:s',
            [
                'datetime' => 'datetime',
                'channel' => 'channel',
                'level_name' => 'level_name',
                'message' => 'message',
            ]
        );
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter($ltsvformatter);
            $handler->pushProcessor($introprocessor);
            $handler->pushProcessor($uidprocessor);
        }
    }
}
    public function register()
    {
+        $this->app->singleton(UidProcessor::class, function ($app) {
+            return new UidProcessor(10);
+        });

    }

リクエストごとにuidを一意にするために、UidProcessorはsingletonとして登録しています。そうじゃないと、ログごとにuidが変わってしまいます。

アクションログ

  • ログ設定を追加
config/logging.php
+        'action' => [
+            'driver' => 'daily',
+            'path' => storage_path('logs/laravel.log'),
+            'tap' => ['App\Logging\ActionLogFormatter'],
+            'level' => 'debug',
+            'days' => 14,
+        ],
  • フォーマッタ実装

イベントログと共通のuidと、リクエスト情報が出力されるようにします。

app/Logging/ActionLogFormatter.php
<?php

namespace App\Logging;
use Hikaeme\Monolog\Formatter\LtsvFormatter;
use Monolog\Processor\WebProcessor;

class ActionLogFormatter
{
    public function __construct()
    {
    }
    public function __invoke($logger)
    {
        $uidprocessor = app()->make('Monolog\Processor\UidProcessor');
        $webprocessor = new WebProcessor();
        $ltsvformatter = new LtsvFormatter(
            'Y-m-d H:i:s',
            [
                'datetime' => 'datetime',
                'channel' => 'channel',
                'level_name' => 'level_name',
                'message' => 'message',
            ]
        );
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter($ltsvformatter);
            $handler->pushProcessor($webprocessor);
            $handler->pushProcessor($uidprocessor);
        }
    }
}
  • ミドルウェアに登録

ミドルウェアに登録し、アクションログを取得したいルーティングに設定します。

app/Http/Kernel.php
+        'action_log' => \App\Http\Middleware\ActionLogMiddleware::class,
route/web.php
Route::group(['middleware' => 'action_log'], function () {
   ...対象にしたいroute
});
app/Http/Middleware/ActionLogMiddleware.php
<?php

namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;

class ActionLogMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $user_id = Auth::id() ?? 0;  // 今回はLaravelのAuthの仕組みを使っているので、そこから取得されるidをuser_idとして使います。
        $session_id = session()->getId();
        \Log::channel('action')->info('action_log', [
            'user_id' => $user_id,
            'session_id' => $session_id,
        ]);
        $response = $next($request);
        return $response;
    }
}

おまけ

以上の設定をすると、ログがLTSVで出力されるようになるのですが、stack traceの改行も半角スペースに置換されるので、デバッグの際にちょっと見づらいです。

そこで、デフォルトのログ設定をlocalチャネルとしておき、開発環境ではそちらを使うようにします。

config/logging.php
+       'local' => [
+           'driver' => 'daily',
+           'path' => storage_path('logs/laravel.log'),
+           'level' => 'debug',
+           'days' => 14,
+       ],

ローカルの.envは以下に書き換えます。

.env
- LOG_CHANNEL=stack
+ LOG_CHANNEL=local

まとめ・課題

とりあえずこれで操作を追うところまではできるようになったのではないでしょうか。おそらくまだ課題はある(ログイン前とログイン後の紐付け、POSTデータの取得とか)ので、その辺はこれから改善していきます。

検索のための仕組みもまた改めて書けたらいいなという感じです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでログフォーマットをいい感じにする

ログって結構大事ですよね。
手軽にちゃんと情報がだせるLoggerを定義しておいて、いい開発・運用環境作りたいです。

この記事では、ElasticSearch等に突っ込んでログを検索可能にすることを前提に、いい感じにログ出力することを目指します。

環境

Laravel 5.8

やりたいこと

Laravelのログをカスタマイズして、いい感じにする。

いい感じとは

  • Laravel上で生じたイベントを記録し、問題が生じた際に、原因の究明と対応を迅速に行えるようにする。

この辺はログ指針を立てることで、目的を明文化します。

(参考)
ログ出力指針の書き方 - White Box技術部
https://seri.hatenablog.com/entry/2018/10/20/172656

どうするか

この記事ではログ出力にフォーカスします

ロギングに関しては、二つの段階があると思います。
1. ログ出力
2. ログ検索

ここでは、ログ出力に際して、実装者が考えることを最小限にし、ログ出力のたびに毎度考えることを減らすための方策を書きます。検索に関しては、ElasticSearchに突っ込む想定だけしておいて、詳細についてはまた別の機会に書きます。

何を出力するか

そのためには、
- アプリケーションの操作者
- いつそのイベントが起きたか
- どこでイベントが起きたか
- 何が起きたか
- そのイベントはそれくらい重大か/すぐに対応が必要か

を適切に伝える必要があります。

そして、これを、なるべく実装者が余計なことを考えないで実装するようにしたい。

理想は、以下くらい書いたら必要なこと全部わかるくらいにしたい。

\Log::info('Fail to get user own posts.');

すると、以下の内容は、自動で出力したいなってところです。

  • 操作者情報
  • いつイベントが起きたか
  • どこで起きたか

まとめ

操作者情報といつ・どこで起きたかに関して、ロガーに実装者が情報を渡さなくてもよしなに出力される。

ことを目標にして、Laravelの実装を以下で進めようと思います。

やること

以下の二つのログ出力を実装する事で

  • イベントログ

実装者によって、Loggerを使って出力するように実装するログです。
ログインの実装をしていればログインのログを、
APIとのやり取りではパラメータの内容などを残すこともあるでしょう。

datetime:2020-01-08 14:48:23    channel:local   level_name:INFO message:fugafuga     uid:e86d557c07  file:/var/www/app/Http/Controllers/HomeController.php   line:30 class:App\Http\Controllers\HomeController       function:index
  • アクションログ

リクエストごとに生成されるログです。
ここに、ユーザーID、セッションID、リクエストIDを付与する事で、これらの関連づけをします。

datetime:2020-01-08 14:48:23    channel:local   level_name:INFO message:action_log      user_id:0       session_id:zZPV5egG7L2FXAWKR6nDHvs2JgNL80Fm6HfmtdCn     uid:e86d5  url:/   ip:172.25.12.11   http_method:GET server:localhost   referrer:http://localhost/fuga

diagram-5236162916520731523.png

リクエストと各イベントの関連づけをしておけば、エラーログが検出された時にその前後の操作が追えます。

実装

共通

  • LTSVフォーマットライブラリ導入

解析の際に、LTSV形式だと簡単になりそうなのでLTSVで出力します。

Laravelのロガーで使われているMonologでLTSVを使うには、以下のライブラリが良さそうなので、これを入れます。

https://packagist.org/packages/hikaeme/monolog-ltsv-formatter

※Laravel5だとMonologのバージョンが1系で、Laravel6はバージョン2です。それに伴って、上記のライブラリもバージョンが1→2に変わっていますので、それぞれの環境に合わせて使うといいと思います。

イベントログ

ログのカスタマイズについては以下が参考になります。

Monologチャンネルの上級カスタマイズ | ログ 5.8 Laravel
https://readouble.com/laravel/5.8/ja/logging.html#customizing-monolog-for-channels

Laravelのログにファイル名やメソッド名を出力する
https://qiita.com/iakio/items/7b8539db928d2b419921

Laravelでログのフォーマット変更する方法 - ノーバグノーライフ
https://blog.nobug-nolife.com/entry/20181202/1543762300

Seldaek/monolog
https://github.com/Seldaek/monolog

  • ログの設定を追加

Laravel5.8だと、config/logging.php でログのチャンネルを設定できます。

デフォルトだとstack チャネルを使っていて、stackチャネルはdailyチャネルにその詳細が定義されてます。今回は、日付ごとにファイルを分けて出力されるようにしてOKなので、dailyチャネルのまま、フォーマットを変えていきます。

config/logging.php
   'daily' => [
       'driver' => 'daily',
       'path' => storage_path('logs/laravel.log'),
+      'tap' => ['App\Logging\CustomizeFormatter'],
       'level' => 'debug',
       'days' => 14,
   ],
  • フォーマッタを実装
app/Logging/CustomizeFormatter.php
<?php

namespace App\Logging;
use Hikaeme\Monolog\Formatter\LtsvFormatter;
use Monolog\Logger;
use Monolog\Processor\IntrospectionProcessor;

class CustomizeFormatter
{
    public function __invoke($logger)
    {
        $uidprocessor = app()->make('Monolog\Processor\UidProcessor');
        $introprocessor = new IntrospectionProcessor(Logger::DEBUG, [
            'Monolog\\',
            'Illuminate\\',
        ]);
        $ltsvformatter = new LtsvFormatter(
            'Y-m-d H:i:s',
            [
                'datetime' => 'datetime',
                'channel' => 'channel',
                'level_name' => 'level_name',
                'message' => 'message',
            ]
        );
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter($ltsvformatter);
            $handler->pushProcessor($introprocessor);
            $handler->pushProcessor($uidprocessor);
        }
    }
}
    public function register()
    {
+        $this->app->singleton(UidProcessor::class, function ($app) {
+            return new UidProcessor(10);
+        });

    }

リクエストごとにuidを一意にするために、UidProcessorはsingletonとして登録しています。そうじゃないと、ログごとにuidが変わってしまいます。

アクションログ

  • ログ設定を追加
config/logging.php
+        'action' => [
+            'driver' => 'daily',
+            'path' => storage_path('logs/laravel.log'),
+            'tap' => ['App\Logging\ActionLogFormatter'],
+            'level' => 'debug',
+            'days' => 14,
+        ],
  • フォーマッタ実装

イベントログと共通のuidと、リクエスト情報が出力されるようにします。

app/Logging/ActionLogFormatter.php
<?php

namespace App\Logging;
use Hikaeme\Monolog\Formatter\LtsvFormatter;
use Monolog\Processor\WebProcessor;

class ActionLogFormatter
{
    public function __construct()
    {
    }
    public function __invoke($logger)
    {
        $uidprocessor = app()->make('Monolog\Processor\UidProcessor');
        $webprocessor = new WebProcessor();
        $ltsvformatter = new LtsvFormatter(
            'Y-m-d H:i:s',
            [
                'datetime' => 'datetime',
                'channel' => 'channel',
                'level_name' => 'level_name',
                'message' => 'message',
            ]
        );
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter($ltsvformatter);
            $handler->pushProcessor($webprocessor);
            $handler->pushProcessor($uidprocessor);
        }
    }
}
  • ミドルウェアに登録

ミドルウェアに登録し、アクションログを取得したいルーティングに設定します。

app/Http/Kernel.php
+        'action_log' => \App\Http\Middleware\ActionLogMiddleware::class,
route/web.php
Route::group(['middleware' => 'action_log'], function () {
   ...対象にしたいroute
});
app/Http/Middleware/ActionLogMiddleware.php
<?php

namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;

class ActionLogMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $user_id = Auth::id() ?? 0;  // 今回はLaravelのAuthの仕組みを使っているので、そこから取得されるidをuser_idとして使います。
        $session_id = session()->getId();
        \Log::channel('action')->info('action_log', [
            'user_id' => $user_id,
            'session_id' => $session_id,
        ]);
        $response = $next($request);
        return $response;
    }
}

おまけ

以上の設定をすると、ログがLTSVで出力されるようになるのですが、stack traceの改行も半角スペースに置換されるので、デバッグの際にちょっと見づらいです。

そこで、デフォルトのログ設定をlocalチャネルとしておき、開発環境ではそちらを使うようにします。

config/logging.php
+       'local' => [
+           'driver' => 'daily',
+           'path' => storage_path('logs/laravel.log'),
+           'level' => 'debug',
+           'days' => 14,
+       ],

ローカルの.envは以下に書き換えます。

.env
- LOG_CHANNEL=stack
+ LOG_CHANNEL=local

まとめ・課題

とりあえずこれで操作を追うところまではできるようになったのではないでしょうか。おそらくまだ課題はある(ログイン前とログイン後の紐付け、POSTデータの取得とか)ので、その辺はこれから改善していきます。

検索のための仕組みもまた改めて書けたらいいなという感じです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

database接続の手書き入力について

databaseの手書き接続まとめ

環境
xampp
mysqlとApacheが必要です。

なぜこの記事を書こうと思ったのか?
主に3つあります。

1.databaseのPDO形式が多く手書き入力のサイト記事が全くないこと。

2.Qiitaにいつもお世話になっているから

3.いつでもアクセスできるように記事として残しておきたい

登録するためのdatabase

まずここで登録するdataを確認

database名:bowring
table:player
id: auto_increment,int型
name:varchar型

database接続:基本構文(INSERT,UPDATE,DELETEで有効)
  //mysqli_connectはホスト名,ユーザー名,パスワード,database名の順で記述すること
  $cn = mysqli_connect("localhost", "root" , "" , "bowling" ); 
  //utf8は言語を設定:utf-8という記述は×
  mysqli_set_charset($cn,'utf8');
  //sql文記述
  $sql = "INSERT INTO player (name) VALUES ('hogehoge');";
  //queryで実行するという意味
  mysqli_query($cn,$sql);
  mysqli_close($cn);

VALUESの後に注目
varchar型を用いる場合はシングルクォーテーションを含み、記述すること。
私はこの処理に2時間悩んでいました(泣)

varchar,int型での注意
$id = 1;
$name = 'hogehoge';
//db接続
 $cn = mysqli_connect("localhost", "root" , "" , "bowling" );
  mysqli_set_charset($cn,'utf8');
  $sql = "INSERT INTO player (id,name) VALUES ($id,'$name');";
  mysqli_query($cn,$sql);
  mysqli_close($cn);

SELECT文で全件取り出す。

SELECT文
  $cn = mysqli_connect("localhost", "root" , "" , "bowling" );
  mysqli_set_charset($cn,'utf8');
  //プレイヤーテーブルから全件取り出し
  $sql = "SELECT * FROM player";
  //実行して$resultの中に入れる
  $result = mysqli_query($cn,$sql);
  //空配列
  $player_list = [];
  //表示結果(フィールド)をひとつづつループで回し、$player_listに代入する
  while($row = mysqli_fetch_assoc($result)){
    $player_list[] = $row;
  }
  mysqli_close($cn);

//全件表示;配列で表示するため、foreachで回す
foreach($player_list as $player_lists){
  echo $player_lists['id'].$player_lists['name']."<br>";
}

もっと楽にできるfunction処理
皆さん、functionをご存じでしょうか?
知らない人もいるとは思いますのでこちらの記事を参考にどうぞ!!!
https://techacademy.jp/magazine/4925

簡単にご説明すると関数を呼び出すだけで何百行という処理を一行で書くことが可能になります。

database接続:基本構文(INSERT,UPDATE,DELETEで有効)
function set_player($sql,$host , $db_user , $db_pass , $db){
  $cn = mysqli_connect($host , $db_user , $db_pass , $db );
  mysqli_set_charset($cn,'utf8');
  mysqli_query($cn,$sql);
  mysqli_close($cn);
}
//ここにsql文と接続所を記述するだけで呼び出し可能:
set_player("INSERT INTO player(id) VALUES(1)","localhost" , "root","","bowling");
##SELECT文で全件取り出す。
function get_player($host , $db_user , $db_pass , $db ){
  $sql = "SELECT * FROM player;
  $cn = mysqli_connect($host , $db_user , $db_pass , $db );
  mysqli_set_charset($cn,'utf8');
  $result = mysqli_query($cn,$sql);
  $player_list = [];
  while($row = mysqli_fetch_assoc($result)){
    $player_list[] = $row;
  }
  mysqli_close($cn);
  //returnで返すことによってより広範囲で可能
  return $player_list;
}
//
$player_list = get_player("localhost" , "root","","bowling");

//全件表示;配列で表示するため、foreachで回す
foreach($player_list as $player_lists){
  echo $player_lists['id'].$player_lists['name']."<br>";
}

SQLインジェクション対策についての手書き入力
また、文字の種類にも注意すること

i 対応する変数の型は integer です。
d 対応する変数の型は double です。
s 対応する変数の型は string です。
b 対応する変数の型は blob で、複数のパケットに分割して送信されます。

SQLインジェクション対策
function set_player($host , $db_user , $db_pass , $db , $name){
  $cn = mysqli_connect($host , $db_user , $db_pass , $db );
  mysqli_set_charset($cn,'utf8');
  $stmt = mysqli_prepare($cn,"INSERT INTO player (name) VALUES (?);");
  $sql = mysqli_stmt_bind_param($stmt,'s',$name);
  mysqli_stmt_execute($stmt);
  mysqli_stmt_close($stmt);
  mysqli_close($cn);
}

//set_player("localhost" , "root","","bowling","hogehoge");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文字列→塩基配列の相互変換ツールをつくってみた(アプリ版)

はじめに

この記事で公開したアプリの中身についてです。

この記事(どこまでショボいアプリがAppleの審査に通るのか試してみた)をみてわりと機能が少なくてもアプリ公開できるのか!と思い正月休みにアプリをつくってみました。

以前作ったこれ(文字列→塩基配列の相互変換ツールをつくってみた(PHP))をアプリにしてリリースしました。

リリースしたアプリ

つくったアプリは有料です。(目指せ!!トータルダウンロード数25!!!)

  • Mac, iOS: ¥120
  • Android : ¥100

Macアプリ

ターゲット:MacOS Catalina以降

DNA変換

iOSアプリ

ターゲット:iOS13以降

DNA変換

Androidアプリ

ターゲット:Android6.0以降

DNA変換

Web版

こんなアプリに金払いたくねぇよって人はぜひWeb版をどうぞ

http://adventam10.php.xdomain.jp/dna/index.php

アプリ概要

機能は極小で文字列⇔塩基配列を相互変換し、Twitterに投稿できるアプリです。(一応英語版もつくりました)

文字列->塩基配列 塩基配列->文字列
ios_dna_1 ios_dna_2

Macアプリ

Macアプリは一発で審査が通ったのでiOSと比べると機能が少ないです。

機能

  • 文字列⇔塩基配列を相互変換
  • Twitterに投稿機能
  • 塩基配列の他アプリへの共有機能
  • 塩基配列のテキストファイルへの書き出し機能
  • 塩基配列のペーストボードへコピー機能

iOSアプリ

iOSアプリは4回リジェクトされたので他と比べると機能が多いです。

機能

  • 文字列⇔塩基配列を相互変換
  • Twitterに投稿機能
  • 塩基配列の他アプリへの共有機能
  • 塩基配列のテキストファイルへの書き出し機能
  • 塩基配列のペーストボードへコピー機能
  • 塩基配列の履歴機能(10件まで)
  • 音声入力機能

1回目の審査では共有機能が使えないけど?バグじゃね?って理由でリジェクトされたのですが、2回目の審査で下記が追加されました:scream:

Guideline 4.2 - Design - Minimum Functionality

履歴機能追加 -> リジェクト、音声入力機能追加 -> 通過:tada:

音声入力機能追加後にアプリをアップしようとすると下記のようなメールが来ました

ITMS-90683: Missing Purpose String in Info.plist - Your app's code references one or more APIs that access sensitive user data. The app's Info.plist file should contain a NSSpeechRecognitionUsageDescription key with a user-facing purpose string explaining clearly and completely why your app needs the data. Starting Spring 2019, all apps submitted to the App Store that access user data are required to include a purpose string. If you're using external libraries or SDKs, they may reference APIs that require a purpose string. While your app might not use these APIs, a purpose string is still required. You can contact the developer of the library or SDK and request they release a version of their code that doesn't contain the APIs. Learn more (https://developer.apple.com/documentation/uikit/core_app/protecting_the_user_s_privacy).

ローカライズ対応していたので Info.plist にキーは追加せずに InfoPlist.strings に下記のように記述していたのですがそれではダメなようです。

"NSSpeechRecognitionUsageDescription" = "音声入力するために必要です";
"NSMicrophoneUsageDescription" = "音声入力するために必要です";

Info.plist にもキー追加して同じように記述してやると通りました。Info.plist にも記載するとこちらの記載が優先されて InfoPlist.string の文字が表示されないと思ったのですがそうでもないようです。(ちゃんとローカライズされてました。)

Androidアプリ

Androidアプリはあんまさわったことがなかったので、最小構成です。(がんばってiOSアプリを追従するようにします!)

機能

  • 文字列⇔塩基配列を相互変換
  • Twitterに投稿機能

Web版

Webもほぼさわったことないので、最小構成です。(一応レスポンシブ対応はしてます。)

機能

  • 文字列⇔塩基配列を相互変換
  • Twitterに投稿機能

アプリのコードについて

このアプリのきもは文字列⇔塩基配列なのですがそこのコードについてです。
ソースは全部 GitHub で公開してます。

方法

変換方法は間に16進数をかませてやってます。

最初は4進数に変換してそれぞれ [0, 1, 2, 3] -> [A, T, C, G] のように変換していたのですが、以前コメントで 0⇔AA みたいに2文字ずつやれば16進数でいけるよと教えていただきました:heart_eyes:

文字列->塩基配列

  1. 文字列 -> 2進数に変換
  2. 2進数 -> 16進数に変換
  3. 16進数 -> 塩基配列に変換(ATCG)

塩基配列->文字列

  1. 塩基配列 -> 2文字ずつに分割
  2. 分割文字列 -> 16進数に変換
  3. 16進数 -> 2進数に変換
  4. 2進数 -> 文字列に変換

変換コード

もっといい方法があればぜひ教えて下さい!!

swift

swift では String の Extension で文字列から16進数への変換、16進数から2進数への変換、文字列を2文字ずつ分割する変数とメソッドをつくりました。(swift が一番めんどくさい感じになってしまいました...:cry:

StringExtensions.swift
public extension String {
    // 16進数->2進数への変換
    var hexadecimal: Data? {
        var data = Data(capacity: count / 2)
        let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
        regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in
            let byteString = (self as NSString).substring(with: match!.range)
            let num = UInt8(byteString, radix: 16)!
            data.append(num)
        }
        guard data.count > 0 else { return nil }
        return data
    }

    // 文字列->16進数の変換
    var hex: String {
        let data = self.data(using: .utf8)!
        return data.map { String(format: "%02X", $0)}.joined()
    }

    // 指定文字数で文字を分割する
    func splitInto(_ length: Int) -> [String] {
        var str = self
        for i in 0 ..< (str.count - 1) / max(length, 1) {
            str.insert(",", at: str.index(str.startIndex, offsetBy: (i + 1) * max(length, 1) + i))
        }
        return str.components(separatedBy: ",")
    }
}
private let dnaHexValues: [String: String] =
        ["AA": "0", "AT": "1", "AC": "2", "AG": "3",
         "TA": "4", "TT": "5", "TC": "6", "TG": "7",
         "CA": "8", "CT": "9", "CC": "a", "CG": "b",
         "GA": "c", "GT": "d", "GC": "e", "GG": "f"]

// 文字列->塩基配列
func convertToDNA(_ text: String?) -> Result<String, DNAConvertError> {
        if isEmptyText(text) {
            return .failure(.empty)
        }
        var result = text!.hex.lowercased()
        dnaHexValues.forEach { dna, hex in
            result = result.replacingOccurrences(of: hex, with: dna)
        }
        return .success(result)
    }

// 塩基配列->文字列
func convertToLanguage(_ text: String?) -> Result<String, DNAConvertError> {
        if isEmptyText(text) {
            return .failure(.empty)
        }
        if isInvalidDNA(text) {
            return .failure(.invalid)
        }
        let hex = text!.splitInto(2).compactMap { dnaHexValues[$0] }.joined()
        if hex.isEmpty {
            return .failure(.invalid)
        }
        if let data = hex.hexadecimal,
            let result = String(data: data, encoding: .utf8) {
            return .success(result)
        }
        return .failure(.invalid)
    }

kotlin

kotlinが一番スッキリした感じにかけました。

val dnaHexValues = mapOf(
        "AA" to "0", "AT" to "1", "AC" to "2", "AG" to "3",
        "TA" to "4", "TT" to "5", "TC" to "6", "TG" to "7",
        "CA" to "8", "CT" to "9", "CC" to "a", "CG" to "b",
        "GA" to "c", "GT" to "d", "GC" to "e", "GG" to "f"
    )

// 文字列->塩基配列
fun convertToDNA(text: String?): String? {
        if (text.isNullOrEmpty()) {
            return null
        }
        val hex = text.toByteArray().map { b -> String.format("%02X", b) }.joinToString("")
        var result = hex.toLowerCase()
        dnaHexValues.forEach { (k, v) -> result = result.replace(v, k) }
        return result
    }

// 塩基配列->文字列
fun convertToLanguage(text: String?): String? {
        if (text.isNullOrEmpty()) {
            return null
        }
        if (isInvalidDNA(text)) {
            return null
        }
        var index = 0
        val strings: MutableList<String> = mutableListOf()
        while (index < text.length) {
            strings.add(text.substring(index, index+2))
            index += 2
        }
        val hex = strings.map { n -> dnaHexValues[n] }.joinToString("").toUpperCase()
        val result = ByteArray(hex.length / 2) { hex.substring(it * 2, it * 2 + 2).toInt(16).toByte() }
        return String(result)
    }

PHP

PHPは変換のときにバックスラッシュいれないといけなくてなんか冗長な感じになりました。

// 文字列->塩基配列
function convertToDNA($text){
  $hex = bin2hex($text);
  $nucleotideArray = array("AA", "AT", "AC", "AG", "TA", "TT", "TC", "TG", "CA", "CT", "CC", "CG", "GA", "GT", "GC", "GG");
  $hexArray = array("/0/", "/1/", "/2/", "/3/", "/4/", "/5/", "/6/", "/7/", "/8/", "/9/", "/a/", "/b/", "/c/", "/d/", "/e/", "/f/");
  $result = preg_replace($hexArray, $nucleotideArray, $hex);
  return $result;
}

// 塩基配列->文字列
function convertToLanguage($text){
  $strArray = str_split($text, 2);
  $resultArray = array_map("dnaDecode", $strArray);
  $hex = implode("", $resultArray);
  return hex2bin($hex);
}

function dnaDecode($nucleotide){
  $nucleotideArray = array("/AA/", "/AT/", "/AC/", "/AG/", "/TA/", "/TT/", "/TC/", "/TG/", "/CA/", "/CT/", "/CC/", "/CG/", "/GA/", "/GT/", "/GC/", "/GG/");
  $hexArray = array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f");
  $result = preg_replace($nucleotideArray, $hexArray, $nucleotide);
  return $result;
}

さいごに

変換後の塩基配列をどうにか圧縮したいのですが、そうすると圧縮した印が必要になったり...(TATAボックスでも付けるか:thinking:

圧縮するにしてもswift, kotlin, PHPで方法は揃える必要があるし...悩みは尽きないです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

何でもランキングにしてツイートできる「Cappps(キャップス)」を公開した【個人開発】

ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。

リリースしたもの

Cappps 何でもランキングにしてみよう!
ランキングを作ってツイートできるサービスです。
作ったランキングをシェアすることもできます。

見た目

top.png

ランキング作成画面

create.png

作った理由

食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
        ↓
いっそのことwebサービスにしちゃおう。

っていう流れでできました。

どうでも良い機能

  • 視聴回数

投稿者のモチベアップにつながるかな と思ってつけました。

  • ジャンル分け

これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。

作りたいもの

お問い合わせ

流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。

アクセスランキング

これも作りたいけど、インデックスしてないのでつらそうです。

使った言語

フロントエンド

  • JavaScript

ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。

  • CSS

エラーメッセージがないときの消去を疑似要素:emptyでやりました。

サーバーサイド

  • PHP

こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。

一応ジャンル分けもできるようになってます。

感想

最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。

こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。

今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。

宣伝

好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発Webサービス】何でもランキングにしてツイートできる「Cappps(キャップス)」をリリースしました

ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。

リリースしたもの

Cappps 何でもランキングにしてみよう!
ランキングを作ってツイートできるサービスです。
作ったランキングをシェアすることもできます。

見た目

top.png

ランキング作成画面

create.png

作った理由

食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
        ↓
いっそのことwebサービスにしちゃおう。

っていう流れでできました。

どうでも良い機能

  • 視聴回数

投稿者のモチベアップにつながるかな と思ってつけました。

  • ジャンル分け

これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。

作りたいもの

お問い合わせ

流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。

アクセスランキング

これも作りたいけど、インデックスしてないのでつらそうです。

使った言語

フロントエンド

  • JavaScript

ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。

  • CSS

エラーメッセージがないときの消去を疑似要素:emptyでやりました。

サーバーサイド

  • PHP

こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。

一応ジャンル分けもできるようになってます。

感想

最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。

こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。

今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。

宣伝

好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps

下のようなランキングを作れます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発Webサービス】何でもランキングにしてツイートできる「Cappps(キャップス)」をリリース

ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。

リリースしたもの

Cappps 何でもランキングにしてみよう!
ランキングを作ってツイートできるサービスです。
作ったランキングをシェアすることもできます。

見た目

top.png

ランキング作成画面

create.png

作った理由

食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
        ↓
いっそのことwebサービスにしちゃおう。

っていう流れでできました。

どうでも良い機能

  • 視聴回数

投稿者のモチベアップにつながるかな と思ってつけました。

  • ジャンル分け

これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。

作りたいもの

お問い合わせ

流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。

アクセスランキング

これも作りたいけど、インデックスしてないのでつらそうです。

使った言語

フロントエンド

  • JavaScript

ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。

  • CSS

エラーメッセージがないときの消去を疑似要素:emptyでやりました。

サーバーサイド

  • PHP

こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。

一応ジャンル分けもできるようになってます。

感想

最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。

こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。

今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。

宣伝

好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps

下のようなランキングを作れます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発サービス】たった6時間で作った、なんでもランキングにしたがるお前ら向けのサービス「Cappps(キャップス)」をリリースした

ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。

リリースしたもの

Cappps 何でもランキングにしてみよう!
ランキングを作ってツイートできるサービスです。
作ったランキングをシェアすることもできます。

見た目

top.png

ランキング作成画面

create.png

作った理由

食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
        ↓
いっそのことwebサービスにしちゃおう。

っていう流れでできました。

作った機能

投稿できる機能(当たり前)

データベースを使わず、テキストファイル(CSVなど)で保存しました。
htmlspecialcharsでエスケープされる<を区切り文字に使いました。

Twitterカード画像の自動生成

Twitterカード画像を自動生成するシステムを作りました。
PHPの、imagecreatefrompng()で画像を取り込んで、文字を付けただけですが...

どうでも良い機能

  • 視聴回数

投稿者のモチベアップにつながるかな と思ってつけました。

  • ジャンル分け

これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。

作りたいもの

お問い合わせ

流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。

アクセスランキング

これも作りたいけど、インデックスしてないのでつらそうです。

使った言語

フロントエンド

  • JavaScript

ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。

  • CSS

エラーメッセージがないときの消去を疑似要素:emptyでやりました。

サーバーサイド

  • PHP

こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。

一応ジャンル分けもできるようになってます。

感想

最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。

こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。

今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。

宣伝

好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps

下のようなランキングを作れます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発サービス】爆速開発!たった6時間で作った、なんでもランキングにしたがるお前ら向けのサービス「Cappps(キャップス)」をリリースした

ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。

リリースしたもの

見た目

top.png

ランキング作成画面

create.png

作った理由

食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
        ↓
いっそのことwebサービスにしちゃおう。

っていう流れでできました。

作った機能

投稿できる機能(当たり前)

データベースを使わず、テキストファイル(CSVなど)で保存しました。
htmlspecialcharsでエスケープされる<を区切り文字に使いました。

Twitterカード画像の自動生成

Twitterカード画像を自動生成するシステムを作りました。
PHPの、imagecreatefrompng()で画像を取り込んで、文字を付けただけですが...

どうでも良い機能

  • 視聴回数

投稿者のモチベアップにつながるかな と思ってつけました。

  • ジャンル分け

これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。

作りたいもの

お問い合わせ

流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。

アクセスランキング

これも作りたいけど、インデックスしてないのでつらそうです。

使った言語

フロントエンド

  • JavaScript

ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。

  • CSS

エラーメッセージがないときの消去を疑似要素:emptyでやりました。

サーバーサイド

  • PHP

こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。

一応ジャンル分けもできるようになってます。

感想

最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。

こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。

今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。

宣伝

好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps

下のようなランキングを作れます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発サービス】たった6時間で作った、なんでもランキングにしたがる君たち向けのサービス「Cappps(キャップス)」をリリースした

ランキングを作ってTwitterでツイートできるサービス「Cappps」を作ったので、公開してみました。
どうにか飽きずにリリースまで持っていけました。
今まで作っては飽き作っては飽きで、完成できなかったので、リリースできてよかったです。

リリースしたもの

見た目

top.png

ランキング作成画面

create.png

作った理由

食べ物のおすすめランキングサイトを作っていました。
でも、いちいちHTML書くの嫌だからシステムにしちゃおう。
        ↓
いっそのことwebサービスにしちゃおう。

っていう流れでできました。

作った機能

投稿できる機能(当たり前)

データベースを使わず、テキストファイル(CSVなど)で保存しました。
htmlspecialcharsでエスケープされる<を区切り文字に使いました。

Twitterカード画像の自動生成

Twitterカード画像を自動生成するシステムを作りました。
PHPの、imagecreatefrompng()で画像を取り込んで、文字を付けただけですが...

どうでも良い機能

  • 視聴回数

投稿者のモチベアップにつながるかな と思ってつけました。

  • ジャンル分け

これは完全に暇だったからです。
セレクトボックスから選んでジャンルをつけられます。

作りたいもの

お問い合わせ

流石に作ったほうが良いですね。
匿名のほうが問い合わせやすいですし。

アクセスランキング

これも作りたいけど、インデックスしてないのでつらそうです。

使った言語

フロントエンド

  • JavaScript

ライブラリ使ってない素のJSです。
あと対してJSは使ってないです。
ランキング表示部だけ、慣れてるJavaScriptで書きました。

  • CSS

エラーメッセージがないときの消去を疑似要素:emptyでやりました。

サーバーサイド

  • PHP

こちらも素のPHPです。
データベースがよくわからないので、テキストファイルに書き出して保存していました。

一応ジャンル分けもできるようになってます。

感想

最低限の機能だけでも案外ものになるなと思いました。
なんならまだTwitterカードの動作が不安ですけれど...
投稿する というところが動いた時点でリリースしました。

こんくらい雑でも動けばいいです。なんなら動かなくてもいいです。

今、アプリでもサービスでも完成して作り込んでいる方は、とりあえず最初のリリースをしてみてください。
あなたも、ユーザーも、WinWinになるでしょう。

もしこの記事がよかったらぜひいいねボタンを押してください。

宣伝

好きなゲームでも、嫌いな授業でも、何でも良いです。
ぜひ、ランキングを作ってみてください。
Cappps

下のようなランキングを作れます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのバージョンを確認するコマンド

php artisan -V

もしくは

php artisan --version
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dockerでbrowser-syncを導入して令和のWordPress環境に

gengou_happyou_reiwa_kakageru.png

概要

2017年ぐらいまではほぼWPに食わせてもらっていたというぐらいにWP案件をやりまくっていたものの、そこからSPAなどを触るようになりブラウザシンクやローカル構築に甘やかされまくった結果、WPの仕事が辛くて仕方なくなる

クリアライン

・ブラウザシンクを入れてローカルのthemeを変更したらブラウザシンクでブラウザをオートリロードさせて開発スピードをアップしたい

問題解決に向けて

docker-comopseを導入する
docker-compose.yml

version: "3"

services:
    db:
        image: mysql:5.7
        volumes:
            - ./db_data:/var/lib/mysql
            - ./localhost.sql:/docker-entrypoint-initdb.d/install_wordpress.sql
        environment:
            MYSQL_ROOT_PASSWORD: wordpress
            MYSQL_DATABASE: main_db
            MYSQL_USER: wordpress
            MYSQL_PASSWORD: wordpress

    wordpress:
        image: wordpress:latest
        depends_on:
            - db
        ports:
            - "8000:80"
        environment:
            WORDPRESS_DB_HOST: db:3306
            WORDPRESS_DB_NAME: main_db
            WORDPRESS_DB_PASSWORD: wordpress
            WORDPRESS_TABLE_PREFIX: wp_
        volumes:
            - ./themes:/var/www/html/wp-content/themes
            - ./plugins:/var/www/html/wp-content/plugins
            - ./languages:/var/www/html/wp-content/languages

    phpmyadmin:
        image: phpmyadmin/phpmyadmin
        environment:
            - PMA_ARBITRARY=1
            - PMA_HOST=db:3306
            - PMA_USER=wordpress
            - PMA_PASSWORD=wordpress
        depends_on:
            - db
        ports:
            - 8080:80
        volumes:
            - /sessions

    bs-wordpress:
        image: ustwo/browser-sync
        command: start --proxy "wordpress:80" --files "themes/**/*.php,themes/**/*.css"
        volumes:
            - ./themes:/source/themes
        depends_on:
            - wordpress
        ports:
            - "3000:3000"
            - "3001:3001"

docker-compose up後に

bs-wordpress_1  | [Browsersync] Proxying: http://wordpress:80
bs-wordpress_1  | [Browsersync] Access URLs:
bs-wordpress_1  |  -----------------------------------
bs-wordpress_1  |        Local: http://localhost:3000
bs-wordpress_1  |     External: http://172.21.0.5:3000
bs-wordpress_1  |  -----------------------------------
bs-wordpress_1  |           UI: http://localhost:3001
bs-wordpress_1  |  UI External: http://172.21.0.5:3001
bs-wordpress_1  |  -----------------------------------

というメッセージが出てきてlocalhost:3000でブラウザシンクが立ち上がり
themes配下のphpファイルとcssをを更新するとブラウザシンクが走りリロードされます。

何をやっているのか

Dockerオートデプロイ (1).png

ついでに

普段のWP環境はこれに
・localhost.sqlを利用してサーバーのDBをローカルにpullする
・WP Offload Mediaを利用して画像ファイルの管理をすべてS3にぶん投げてローカルとサーバーのズレを最小限にする
・AWS code deployを利用して特定のgitブランチにアップすると自動でサーバーにデプロイを行う
ということを組み合わせて行いPRベースでの開発を徹底しています。
そのあたりもまた機会があれば記事にしたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dockerでブラウザシンクを導入して令和のWordPress環境に

gengou_happyou_reiwa_kakageru.png

概要

2017年ぐらいまではほぼWPに食わせてもらっていたというぐらいにWP案件をやりまくっていたものの、そこからPWAなどを触るようになりブラウザシンクやローカル構築に甘やかされまくった結果、WPの仕事が辛くて仕方なくなる

クリアライン

・ブラウザシンクを入れてローカルのthemeを変更したらブラウザシンクでブラウザをオートリロードさせて開発スピードをアップしたい

問題解決に向けて

docker-comopseを導入する
docker-compose.yml

version: "3"

services:
    db:
        image: mysql:5.7
        volumes:
            - ./db_data:/var/lib/mysql
            - ./localhost.sql:/docker-entrypoint-initdb.d/install_wordpress.sql
        environment:
            MYSQL_ROOT_PASSWORD: wordpress
            MYSQL_DATABASE: main_db
            MYSQL_USER: wordpress
            MYSQL_PASSWORD: wordpress

    wordpress:
        image: wordpress:latest
        depends_on:
            - db
        ports:
            - "8000:80"
        environment:
            WORDPRESS_DB_HOST: db:3306
            WORDPRESS_DB_NAME: main_db
            WORDPRESS_DB_PASSWORD: wordpress
            WORDPRESS_TABLE_PREFIX: wp_
        volumes:
            - ./themes:/var/www/html/wp-content/themes
            - ./plugins:/var/www/html/wp-content/plugins
            - ./languages:/var/www/html/wp-content/languages

    phpmyadmin:
        image: phpmyadmin/phpmyadmin
        environment:
            - PMA_ARBITRARY=1
            - PMA_HOST=db:3306
            - PMA_USER=wordpress
            - PMA_PASSWORD=wordpress
        depends_on:
            - db
        ports:
            - 8080:80
        volumes:
            - /sessions

    bs-wordpress:
        image: ustwo/browser-sync
        command: start --proxy "wordpress:80" --files "themes/**/*.php,themes/**/*.css"
        volumes:
            - ./themes:/source/themes
        depends_on:
            - wordpress
        ports:
            - "3000:3000"
            - "3001:3001"

docker-compose up後に

bs-wordpress_1  | [Browsersync] Proxying: http://wordpress:80
bs-wordpress_1  | [Browsersync] Access URLs:
bs-wordpress_1  |  -----------------------------------
bs-wordpress_1  |        Local: http://localhost:3000
bs-wordpress_1  |     External: http://172.21.0.5:3000
bs-wordpress_1  |  -----------------------------------
bs-wordpress_1  |           UI: http://localhost:3001
bs-wordpress_1  |  UI External: http://172.21.0.5:3001
bs-wordpress_1  |  -----------------------------------

というメッセージが出てきてlocalhost:3000でブラウザシンクが立ち上がり
themes配下のphpファイルとcssをを更新するとブラウザシンクが走りリロードされます。

何をやっているのか

Dockerオートデプロイ (1).png

ついでに

普段のWP環境はこれに
・localhost.sqlを利用してサーバーのDBをローカルにpullする
・WP Offload Mediaを利用して画像ファイルの管理をすべてS3にぶん投げてローカルとサーバーのズレを最小限にする
・AWS code deployを利用して特定のgitブランチにアップすると自動でサーバーにデプロイを行う
ということを組み合わせて行いPRベースでの開発を徹底しています。
そのあたりもまた機会があれば記事にしたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP基礎構文 ヒアドキュメントとNowdocの構文と違いについて

TL;DR

  • 基礎構文の1つでもあるヒアドキュメントNowdocの構文に関して
  • 基本的にヒアドキュメントで事足りる印象
  • 適切さでいうと、変数展開を利用しないのであれば、Nowdocを利用すべきなのかもしれない

ヒアドキュメントとは

文字列を変数に代入、または出力するために利用する機能
機能の特徴柄、主に長い文字列の代入・出力のために利用する事が一般的な利用方法

ヒアドキュメント構文

$text = 'hello';
echo <<< EOL
input text.
{$text}
EOL;

/*
 * 出力結果
 *
 * input text.
 * hello
 */

Nowdoc構文

$text = 'hello';
echo <<< 'EOL'
input text.
{$text}
EOL;

/*
 * 出力結果
 *
 * input text.
 * {$text}
 */

構文の違い

1. エンドトークンにクォートがついてるかどうか

  • ついてるのが、ヒアドキュメント
  • ついていないのが、Nowdoc

エンドトークンとは

  • EOLなどの特定の文字列を指す
  • 例文に利用してるEOL以外にも下記ものも利用できる
    • EOD
      • End Of Documentの略
    • EOF
      • End Of Fileの略
    • EOS
      • End Of Sentenceの略
    • EOT
      • End Of Textの略
    • EOL
      • End Of Lineの略

※利用するエンドトークンは同じである必要がある。
例えば、開始のエンドトークンにEOL、終わりのエンドトークンにEOD、などは利用できない

2. 変数展開ができるかどうか

  • できるのが、ヒアドキュメント
  • できないのが、Nowdoc
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む