20200721のPHPに関する記事は17件です。

Laravelで多角形の内外判定を行うアルゴリズムの実装案

ユーザの住所が配達を承る範囲内かどうかを調べる必要があったので

実装を行っていましたところ,
https://www.nttpc.co.jp/technology/number_algorithm.html
にとてもよいアルゴリズムがあったのでこれをLaravel(PHP)で使えるようにしようと言うのが今回です.

Crossing Number Algorithm

  • 調べたい範囲の多角形と,点を用意します.
  • 点から真横に線を伸ばす.
  • 多角形内に点があれば多角形の辺との交差数は奇数,外なら偶数になる

ざっとこんなイメージですがいいですかね.詳しくはサイト読んでください.
特殊ケースが4つぐらいあるので,そこは気をつけないといけません.

Laravelでの実装

多角形の準備

config\const.phpに判定したい多角形を準備します.
最初の座標と最後の座標は一緒にしてね.
下記の例だと,四角形だけど準備する点は5つです.

今回はGoogleMapAPI使うので,latとlngで区別します.latをy軸(縦方向),lngをx軸(横方向)として捉えてます.
lng,latの順のほうが良かったな.数学っぽいし.

config\const.php
    'DeliveryArea'=>[
        ["lat"=>1.0,"lng"=>0.0],
        ["lat"=>2.0,"lng"=>0.0],
        ["lat"=>2.0,"lng"=>1.0],
        ["lat"=>1.0,"lng"=>1.0],
        ["lat"=>1.0,"lng"=>0.0],
    ]

配達可能エリアとかそうそう変わらんだろってことで,constに指定.
config('const.DeliveryArea);だけで配列として使えるから便利だよね~.

ユーザの住所

これはGoogleMapAPIのGeoCodingを使います.詳しくは調べてネ.

  • 言語を指定
  • APIキーを指定(極秘事項です.気をつけてね)
  • リクエスト出して,jsonをもらってくる.
  • よしなに返す.

ここでは,arrayにして同時に返してます.受け取るときは,list関数で受け取ればいいかなと.
ああああ とか変な住所が入ってきたら,ZERO_RESULTが返ってくるらしいので,それで判定しました.

Polygon.php
    public static function geo($addr)
    {
        mb_language("Japanese"); //文字コードの設定
        mb_internal_encoding("UTF-8");

        $address = $addr;

        if (config('app.debug')) {
            //テスト環境, ローカル環境用の記述
            $myKey = "デバッグ用のキー";
        } else {
            $myKey = "本番環境用のキー";
        }

        $address = urlencode($address);

        $url = "https://maps.googleapis.com/maps/api/geocode/json?address=" . $address . "+CA&key=" . $myKey;

        $contents = file_get_contents($url);
        $jsonData = json_decode($contents, true);

        if($jsonData["status"]=="ZERO_RESULTS"){
            $lat = null;
            $lng = null;
        }else{
            $lat = $jsonData["results"][0]["geometry"]["location"]["lat"];
            $lng = $jsonData["results"][0]["geometry"]["location"]["lng"];
        }
        return array($lat, $lng);
    }

内外判定

まんまあったのをPHPの形式に直しただけです.あと,geo関数でnull返したので,それを扱うときの条件式を追加.
あと,判定の結果がどうなのかtrueとfalseで返してます.
交点の数が知りたい場合は,$cnを返せばOKでした.

Polygon.php
public static function isPointinPolygon($point,$PolygonArray){
        $cn = 0;
        if($point["lat"]==null){
            $point["lat"]=0.0;
        }
        if($point["lng"]==null){
            $point["lng"]=0.0;
        }
        for($i = 0; $i < count($PolygonArray) - 1; $i++){
            // 上向きの辺。点Pがy軸方向について、始点と終点の間にある。ただし、終点は含まない。(ルール1)
            if( (($PolygonArray[$i]["lat"] <= $point["lat"]) && ($PolygonArray[$i+1]["lat"] > $point["lat"]))
                // 下向きの辺。点Pがy軸方向について、始点と終点の間にある。ただし、始点は含まない。(ルール2)
                || (($PolygonArray[$i]["lat"] > $point["lat"]) && ($PolygonArray[$i+1]["lat"] <= $point["lat"])) ){
                // ルール1,ルール2を確認することで、ルール3も確認できている。
                // 辺は点pよりも右側にある。ただし、重ならない。(ルール4)
                // 辺が点pと同じ高さになる位置を特定し、その時のxの値と点pのxの値を比較する。
                $vt = ($point["lat"] - $PolygonArray[$i]["lat"]) / ($PolygonArray[$i+1]["lat"]- $PolygonArray[$i]["lat"]);
                if($point["lng"] < ($PolygonArray[$i]["lng"] + ($vt * ($PolygonArray[$i+1]["lng"] - $PolygonArray[$i]["lng"])))){
                    ++$cn;
                }
            }
        }
        if($cn%2 == 0){
            return false;//偶数点だと外部
        }
        else{
            return true;//奇数点だと内部
        }
    }

実際の使い方

考えられる状況は,ECサイトでのカート機能とかかと.
ユーザの住所みて,配送料に追加するとか,割引するとかかなと.

この場合は,latとlngをあとづけしたので既存ユーザのlatとlngのデータはnullになってる,けどaddrのデータはあるって人がいるのでこんなことしてるけど,普通は会員登録時とかマイページで情報編集したときにlatとlngを取る.

で,住所の文字列をgeo使ってlatとlngに変換して,ユーザ情報を保存.そのまま内外判定の関数に渡してます.

引数の1つ目はlatとlng要素の配列.
引数の2つ目は判定したいエリアの配列.

trueかfalse返すので自分の好きなように処理して,bladeにかえしてます.

CartController.php
<?php
use App\Utils\Polygon as Polygon;
// useはよしなに

class CartController extends Controller
{
    public function indexcart()
    {
            $Areaadd = 0;
            $message = "";
            if( Auth::user()->lat == null || Auth::user()->lng == null){
                list($lat,$lng) = Polygon::geo(Auth::user()->addr);
                if($lat == null || $lng == null){
                    //取得してもなお不正な場合
                    $message = "ちゃんと住所入れてくれ";
                }else{
                    User::where('id',Auth::id())->update([
                       'lat'=>$lat,
                       'lng'=>$lng
                    ]);
                }
            }

            if(Polygon::isPointinPolygon(["lat"=>Auth::user()->lat,"lng"=>Auth::user()->lng],config('const.DeliveryArea')) == false)
            {//   lat=1.5 lng=0.5とかなら範囲内
                $Areaadd += 999999999;
                $message = "届けてやるぜ";
            }else{//   lat=0.0 lng=0.0は範囲外
                $message = "すまん遠すぎる";
            }

            return view('user.cart')->with('message',$message)->with('Areaadd',$Areaadd);
    }
}

Githubにも置いてます.

https://github.com/a-msy/laravel-utils/blob/master/Polygon.php

以上です.

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

PHP-MySQLi-Database-Classの解説 日本語訳

MysqliDb -- MySQLi のシンプルなラッパーの解説日本語訳

PHP-MySQLi-Database-Classは日本ではあまり知られていませんが、
githubでは☆2800スターを集めて世界中の人に使われているものとなります。

本家githubはこちらから
https://github.com/ThingEngineer/PHP-MySQLi-Database-Class

利用方法

このクラスを利用するには、まずMysqliDb.phpをプロジェクトにインポート。

require_once ('MysqliDb.php');

composerでの利用方法

composer経由でライブラリをインストールすることも可能です。

composer require thingengineer/mysqli-database-class:dev-master

初期化 (initialization)

デフォルトではutf8のcharsetが設定されているので、簡単な初期化が可能です。
$db = new MysqliDb ('host', 'username', 'password', 'databaseName');

高度な初期化

$db = new MysqliDb (Array (
                'host' => 'host',
                'username' => 'username', 
                'password' => 'password',
                'db'=> 'databaseName',
                'port' => 3306,
                'prefix' => 'my_',
                'charset' => 'utf8'));

テーブルの接頭辞、ポート、データベースのcharsetパラメータはオプションです。
charsetを設定すべきでない場合は、nullに設定します。
また、すでに接続されている mysqli オブジェクトを再利用することも可能です。

$mysqli = new mysqli ('host', 'username', 'password', 'databaseName');
$db = new MysqliDb ($mysqli);

オブジェクトの作成時にテーブルのプレフィックスが設定されていなかった場合は、
後から別の呼び出しでテーブルのプレフィックスを設定することができます。

$db->setPrefix ('my_');

mysqlへの接続が切断される場合、Mysqlidbは自動的に一度データベースに再接続しようとします。
この動作を無効にするには以下のようにしてください。

$db->autoReconnect = false;

すでに作成された mysqliDb オブジェクトを別のクラスや関数から取得する場合は以下のように設定します。

    function init () {
        // db staying private here
        $db = new MysqliDb ('host', 'username', 'password', 'databaseName');
    }
    function myfunc () {
        // obtain db object created in init  ()
        $db = MysqliDb::getInstance();
        ...
    }

複数のデータベース接続

複数のデータベースに接続する必要がある場合は、以下の方法で使用してください。

$db->addConnection('slave', Array (
                'host' => 'host',
                'username' => 'username',
                'password' => 'password',
                'db'=> 'databaseName',
                'port' => 3306,
                'prefix' => 'my_',
                'charset' => 'utf8')
);

データベースを選択するには connection() メソッドを使用します。

$users = $db->connection('slave')->get('users');

オブジェクトのマッピング

dbObject.php は、モデル表現機能を提供するために mysqliDb の上に構築されたオブジェクトマッピングライブラリです。

挿入クエリ(Insert Query)

簡単な例

$data = Array ("login" => "admin",
               "firstName" => "John",
               "lastName" => 'Doe'
);
$id = $db->insert ('users', $data);
if($id)
    echo 'user was created. Id=' . $id;

fuunction挿入

$data = Array (
    'login' => 'admin',
    'active' => true,
    'firstName' => 'John',
    'lastName' => 'Doe',
    'password' => $db->func('SHA1(?)',Array ("secretpassword+salt")),
    // password = SHA1('secretpassword+salt')
    'createdAt' => $db->now(),
    // createdAt = NOW()
    'expires' => $db->now('+1Y')
    // expires = NOW() + interval 1 year
    // Supported intervals [s]econd, [m]inute, [h]hour, [d]day, [M]onth, [Y]ear
);

$id = $db->insert ('users', $data);
if ($id)
    echo 'user was created. Id=' . $id;
else
    echo 'insert failed: ' . $db->getLastError();

INSERT ... ON DUPLICATE KEY UPDATE 構文

$data = Array ("login" => "admin",
               "firstName" => "John",
               "lastName" => 'Doe',
               "createdAt" => $db->now(),
               "updatedAt" => $db->now(),
);
$updateColumns = Array ("updatedAt");
$lastInsertId = "id";
$db->onDuplicate($updateColumns, $lastInsertId);
$id = $db->insert ('users', $data);

複数のデータセットを一度に挿入

$data = Array(
    Array ("login" => "admin",
        "firstName" => "John",
        "lastName" => 'Doe'
    ),
    Array ("login" => "other",
        "firstName" => "Another",
        "lastName" => 'User',
        "password" => "very_cool_hash"
    )
);
$ids = $db->insertMulti('users', $data);
if(!$ids) {
    echo 'insert failed: ' . $db->getLastError();
} else {
    echo 'new users inserted with following id\'s: ' . implode(', ', $ids);
}

すべてのデータセットが同じキーしか持たない場合は、単純化することができます。

$data = Array(
    Array ("admin", "John", "Doe"),
    Array ("other", "Another", "User")
);
$keys = Array("login", "firstName", "lastName");

$ids = $db->insertMulti('users', $data, $keys);
if(!$ids) {
    echo 'insert failed: ' . $db->getLastError();
} else {
    echo 'new users inserted with following id\'s: ' . implode(', ', $ids);
}

データ置換(REPLACE文)

Replace() メソッドは insert() と同じ API を実装しています。

データを更新する(UPDATE文)

$data = Array (
    'firstName' => 'Bobby',
    'lastName' => 'Tables',
    'editCount' => $db->inc(2),
    // editCount = editCount + 2;
    'active' => $db->not()
    // active = !active;
);
$db->where ('id', 1);
if ($db->update ('users', $data))
    echo $db->count . ' records were updated';
else
    echo 'update failed: ' . $db->getLastError();

update() はリミットパラメータもサポートしています。
php:
$db->update ('users', $data, 10);
// Gives: UPDATE users SET ... LIMIT 10

データを取得する(SELECT文)

セレクト/ゲット関数が呼び出された後、金額または返された行は$count変数に格納されます。

$users = $db->get('users'); //contains an Array of all users 
$users = $db->get('users', 10); //contains an Array 10 users

またはカスタムカラムを設定して選択します。関数を使用することもできます。

$cols = Array ("id", "name", "email");
$users = $db->get ("users", null, $cols);
if ($db->count > 0)
    foreach ($users as $user) { 
        print_r ($user);
    }

一行だけを選択する

$db->where ("id", 1);
$user = $db->getOne ("users");
echo $user['id'];

$stats = $db->getOne ("users", "sum(id), count(*) as cnt");
echo "total ".$stats['cnt']. "users found";

1つの列の値または関数の結果を選択

$count = $db->getValue ("users", "count(*)");
echo "{$count} users found";

複数の行から1つの列の値または関数の結果を選択

$logins = $db->getValue ("users", "login", null);
// select login from users
$logins = $db->getValue ("users", "login", 5);
// select login from users limit 5
foreach ($logins as $login)
    echo $login;

データを追加する(INSERT文)

また、特定のテーブルに .CSV または .XML データを読み込むこともできます。
.csv データを挿入するには、以下の構文を使用します。

$path_to_file = "/home/john/file.csv";
$db->loadData("users", $path_to_file);

これは、/home/john/ (johnのホームディレクトリ)フォルダにfile.csvという.csvファイルをロードします。
オプションでオプションの配列を添付することもできます。有効なオプションは以下の通りです。

Array(
    "fieldChar" => ';',     // Char which separates the data
    "lineChar" => '\r\n',   // Char which separates the lines
    "linesToIgnore" => 1    // Amount of lines to ignore at the beginning of the import
);

Attach

$options = Array("fieldChar" => ';', "lineChar" => '\r\n', "linesToIgnore" => 1);
$db->loadData("users", "/home/john/file.csv", $options);
// LOAD DATA ...

DATAの代わりにLOCAL DATAを使用するように指定

$options = Array("fieldChar" => ';', "lineChar" => '\r\n', "linesToIgnore" => 1, "loadDataLocal" => true);
$db->loadData("users", "/home/john/file.csv", $options);
// LOAD DATA LOCAL ...

XML挿入

XML データをテーブルにロードするには、loadXML メソッドを使用します。構文は loadData 構文と同じです。

$path_to_file = "/home/john/file.xml";
$db->loadXML("users", $path_to_file);

オプションのパラメータを追加

Array(
    "linesToIgnore" => 0,       // Amount of lines / rows to ignore at the beginning of the import
    "rowTag"    => "<user>" // The tag which marks the beginning of an entry
)

使用例

$options = Array("linesToIgnore" => 0, "rowTag" => "<user>"):
$path_to_file = "/home/john/file.xml";
$db->loadXML("users", $path_to_file, $options);

ページネーション(Pagination)

ページ分割された結果を取得するには get() の代わりに paginate() を使用します。

$page = 1;
// set page limit to 2 results per page. 20 by default
$db->pageLimit = 2;
$products = $db->arraybuilder()->paginate("products", $page);
echo "showing $page out of " . $db->totalPages;

MAP

純粋な配列で結果を取得するのではなく、必要なキーを含む連想配列で結果を取得することも可能です。get() で取得するフィールドが 2 つだけの場合は配列($k => $v)で、それ以外の場合は配列 ($k => 配列 ($v, $v)) で結果を返します。

$user = $db->map ('login')->ObjectBuilder()->getOne ('users', 'login, id');
Array
(
    [user1] => 1
)

$user = $db->map ('login')->ObjectBuilder()->getOne ('users', 'id,login,createdAt');
Array
(
    [user1] => stdClass Object
        (
            [id] => 1
            [login] => user1
            [createdAt] => 2015-10-22 22:27:53
        )

)

リターン型の定義

MysqliDbは、3つの異なるフォーマットで結果を返すことができます。配列の配列、オブジェクトの配列、Json文字列です。返り値の型を選択するには、ArrayBuilder()、ObjectBuilder()、JsonBuilder()メソッドを使用します。ArrayBuilder() はデフォルトの戻り値の型であることに注意してください。

// Array return type
$= $db->getOne("users");
echo $u['login'];
// Object return type
$u = $db->ObjectBuilder()->getOne("users");
echo $u->login;
// Json return type
$json = $db->JsonBuilder()->getOne("users");

生のSQLクエリの実行

$users = $db->rawQuery('SELECT * from users where id >= ?', Array (10));
foreach ($users as $user) {
    print_r ($user);
}

長いifチェックを避けるために、生のクエリの選択結果を扱うためのヘルパー関数があります。
結果の1行を取得します。

$user = $db->rawQueryOne ('select * from users where id=?', Array(10));
echo $user['login'];
// Object return type
$user = $db->ObjectBuilder()->rawQueryOne ('select * from users where id=?', Array(10));
echo $user->login;

1カラムの値を文字列として取得

$password = $db->rawQueryValue ('select password from users where id=? limit 1', Array(10).
echo "パスワードは {$password} です。
注意: rawQueryValue() が配列ではなく文字列を返すようにするには、 クエリの最後に 'limit 1' を追加しなければなりません。

複数の行から1列の値を取得します。

$logins = $db->rawQueryValue ('select login from users limit 10');
foreach ($logins as $login)
    echo $login;

応用例

$params = Array(1, 'admin');
$users = $db->rawQuery("SELECT id, firstName, lastName FROM users WHERE id = ? AND login = ?", $params);
print_r($users); // contains Array of returned rows

// will handle any SQL query
$params = Array(10, 1, 10, 11, 2, 10);
$q = "(
    SELECT a FROM t1
        WHERE a = ? AND B = ?
        ORDER BY a LIMIT ?
) UNION (
    SELECT a FROM t2 
        WHERE a = ? AND B = ?
        ORDER BY a LIMIT ?
)";
$results = $db->rawQuery ($q, $params);
print_r ($results); // contains Array of returned rows
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP-MySQLi-Database-Classの解説

MysqliDb -- MySQLi のシンプルなラッパーの解説 日本語訳

PHP-MySQLi-Database-Classは日本ではあまり知られていませんが、
githubでは☆2800スターを集めて世界中の人に使われているものとなります。

本家githubはこちらから
https://github.com/ThingEngineer/PHP-MySQLi-Database-Class

利用方法

このクラスを利用するには、まずMysqliDb.phpをプロジェクトにインポート。

require_once ('MysqliDb.php');

composerでの利用方法

composer経由でライブラリをインストールすることも可能です。

composer require thingengineer/mysqli-database-class:dev-master

初期化 (initialization)

デフォルトではutf8のcharsetが設定されているので、簡単な初期化が可能です。
$db = new MysqliDb ('host', 'username', 'password', 'databaseName');

高度な初期化

$db = new MysqliDb (Array (
                'host' => 'host',
                'username' => 'username', 
                'password' => 'password',
                'db'=> 'databaseName',
                'port' => 3306,
                'prefix' => 'my_',
                'charset' => 'utf8'));

テーブルの接頭辞、ポート、データベースのcharsetパラメータはオプションです。
charsetを設定すべきでない場合は、nullに設定します。
また、すでに接続されている mysqli オブジェクトを再利用することも可能です。

$mysqli = new mysqli ('host', 'username', 'password', 'databaseName');
$db = new MysqliDb ($mysqli);

オブジェクトの作成時にテーブルのプレフィックスが設定されていなかった場合は、
後から別の呼び出しでテーブルのプレフィックスを設定することができます。

$db->setPrefix ('my_');

mysqlへの接続が切断される場合、Mysqlidbは自動的に一度データベースに再接続しようとします。
この動作を無効にするには以下のようにしてください。

$db->autoReconnect = false;

すでに作成された mysqliDb オブジェクトを別のクラスや関数から取得する場合は以下のように設定します。

    function init () {
        // db staying private here
        $db = new MysqliDb ('host', 'username', 'password', 'databaseName');
    }
    function myfunc () {
        // obtain db object created in init  ()
        $db = MysqliDb::getInstance();
        ...
    }

複数のデータベース接続

複数のデータベースに接続する必要がある場合は、以下の方法で使用してください。

$db->addConnection('slave', Array (
                'host' => 'host',
                'username' => 'username',
                'password' => 'password',
                'db'=> 'databaseName',
                'port' => 3306,
                'prefix' => 'my_',
                'charset' => 'utf8')
);

データベースを選択するには connection() メソッドを使用します。

$users = $db->connection('slave')->get('users');

オブジェクトのマッピング

dbObject.php は、モデル表現機能を提供するために mysqliDb の上に構築されたオブジェクトマッピングライブラリです。

挿入クエリ(Insert Query)

簡単な例

$data = Array ("login" => "admin",
               "firstName" => "John",
               "lastName" => 'Doe'
);
$id = $db->insert ('users', $data);
if($id)
    echo 'user was created. Id=' . $id;

fuunction挿入

$data = Array (
    'login' => 'admin',
    'active' => true,
    'firstName' => 'John',
    'lastName' => 'Doe',
    'password' => $db->func('SHA1(?)',Array ("secretpassword+salt")),
    // password = SHA1('secretpassword+salt')
    'createdAt' => $db->now(),
    // createdAt = NOW()
    'expires' => $db->now('+1Y')
    // expires = NOW() + interval 1 year
    // Supported intervals [s]econd, [m]inute, [h]hour, [d]day, [M]onth, [Y]ear
);

$id = $db->insert ('users', $data);
if ($id)
    echo 'user was created. Id=' . $id;
else
    echo 'insert failed: ' . $db->getLastError();

INSERT ... ON DUPLICATE KEY UPDATE 構文

$data = Array ("login" => "admin",
               "firstName" => "John",
               "lastName" => 'Doe',
               "createdAt" => $db->now(),
               "updatedAt" => $db->now(),
);
$updateColumns = Array ("updatedAt");
$lastInsertId = "id";
$db->onDuplicate($updateColumns, $lastInsertId);
$id = $db->insert ('users', $data);

複数のデータセットを一度に挿入

$data = Array(
    Array ("login" => "admin",
        "firstName" => "John",
        "lastName" => 'Doe'
    ),
    Array ("login" => "other",
        "firstName" => "Another",
        "lastName" => 'User',
        "password" => "very_cool_hash"
    )
);
$ids = $db->insertMulti('users', $data);
if(!$ids) {
    echo 'insert failed: ' . $db->getLastError();
} else {
    echo 'new users inserted with following id\'s: ' . implode(', ', $ids);
}

すべてのデータセットが同じキーしか持たない場合は、単純化することができます。

$data = Array(
    Array ("admin", "John", "Doe"),
    Array ("other", "Another", "User")
);
$keys = Array("login", "firstName", "lastName");

$ids = $db->insertMulti('users', $data, $keys);
if(!$ids) {
    echo 'insert failed: ' . $db->getLastError();
} else {
    echo 'new users inserted with following id\'s: ' . implode(', ', $ids);
}

データ置換(REPLACE文)

Replace() メソッドは insert() と同じ API を実装しています。

データを更新する(UPDATE文)

$data = Array (
    'firstName' => 'Bobby',
    'lastName' => 'Tables',
    'editCount' => $db->inc(2),
    // editCount = editCount + 2;
    'active' => $db->not()
    // active = !active;
);
$db->where ('id', 1);
if ($db->update ('users', $data))
    echo $db->count . ' records were updated';
else
    echo 'update failed: ' . $db->getLastError();

update() はリミットパラメータもサポートしています。
php:
$db->update ('users', $data, 10);
// Gives: UPDATE users SET ... LIMIT 10

データを取得する(SELECT文)

セレクト/ゲット関数が呼び出された後、金額または返された行は$count変数に格納されます。

$users = $db->get('users'); //contains an Array of all users 
$users = $db->get('users', 10); //contains an Array 10 users

またはカスタムカラムを設定して選択します。関数を使用することもできます。

$cols = Array ("id", "name", "email");
$users = $db->get ("users", null, $cols);
if ($db->count > 0)
    foreach ($users as $user) { 
        print_r ($user);
    }

一行だけを選択する

$db->where ("id", 1);
$user = $db->getOne ("users");
echo $user['id'];

$stats = $db->getOne ("users", "sum(id), count(*) as cnt");
echo "total ".$stats['cnt']. "users found";

1つの列の値または関数の結果を選択

$count = $db->getValue ("users", "count(*)");
echo "{$count} users found";

複数の行から1つの列の値または関数の結果を選択

$logins = $db->getValue ("users", "login", null);
// select login from users
$logins = $db->getValue ("users", "login", 5);
// select login from users limit 5
foreach ($logins as $login)
    echo $login;

データを追加する(INSERT文)

また、特定のテーブルに .CSV または .XML データを読み込むこともできます。
.csv データを挿入するには、以下の構文を使用します。

$path_to_file = "/home/john/file.csv";
$db->loadData("users", $path_to_file);

これは、/home/john/ (johnのホームディレクトリ)フォルダにfile.csvという.csvファイルをロードします。
オプションでオプションの配列を添付することもできます。有効なオプションは以下の通りです。

Array(
    "fieldChar" => ';',     // Char which separates the data
    "lineChar" => '\r\n',   // Char which separates the lines
    "linesToIgnore" => 1    // Amount of lines to ignore at the beginning of the import
);

Attach

$options = Array("fieldChar" => ';', "lineChar" => '\r\n', "linesToIgnore" => 1);
$db->loadData("users", "/home/john/file.csv", $options);
// LOAD DATA ...

DATAの代わりにLOCAL DATAを使用するように指定

$options = Array("fieldChar" => ';', "lineChar" => '\r\n', "linesToIgnore" => 1, "loadDataLocal" => true);
$db->loadData("users", "/home/john/file.csv", $options);
// LOAD DATA LOCAL ...

XML挿入

XML データをテーブルにロードするには、loadXML メソッドを使用します。構文は loadData 構文と同じです。

$path_to_file = "/home/john/file.xml";
$db->loadXML("users", $path_to_file);

オプションのパラメータを追加

Array(
    "linesToIgnore" => 0,       // Amount of lines / rows to ignore at the beginning of the import
    "rowTag"    => "<user>" // The tag which marks the beginning of an entry
)

使用例

$options = Array("linesToIgnore" => 0, "rowTag" => "<user>"):
$path_to_file = "/home/john/file.xml";
$db->loadXML("users", $path_to_file, $options);

ページネーション(Pagination)

ページ分割された結果を取得するには get() の代わりに paginate() を使用します。

$page = 1;
// set page limit to 2 results per page. 20 by default
$db->pageLimit = 2;
$products = $db->arraybuilder()->paginate("products", $page);
echo "showing $page out of " . $db->totalPages;

MAP

純粋な配列で結果を取得するのではなく、必要なキーを含む連想配列で結果を取得することも可能です。get() で取得するフィールドが 2 つだけの場合は配列($k => $v)で、それ以外の場合は配列 ($k => 配列 ($v, $v)) で結果を返します。

$user = $db->map ('login')->ObjectBuilder()->getOne ('users', 'login, id');
Array
(
    [user1] => 1
)

$user = $db->map ('login')->ObjectBuilder()->getOne ('users', 'id,login,createdAt');
Array
(
    [user1] => stdClass Object
        (
            [id] => 1
            [login] => user1
            [createdAt] => 2015-10-22 22:27:53
        )

)

リターン型の定義

MysqliDbは、3つの異なるフォーマットで結果を返すことができます。配列の配列、オブジェクトの配列、Json文字列です。返り値の型を選択するには、ArrayBuilder()、ObjectBuilder()、JsonBuilder()メソッドを使用します。ArrayBuilder() はデフォルトの戻り値の型であることに注意してください。

// Array return type
$= $db->getOne("users");
echo $u['login'];
// Object return type
$u = $db->ObjectBuilder()->getOne("users");
echo $u->login;
// Json return type
$json = $db->JsonBuilder()->getOne("users");

生のSQLクエリの実行

$users = $db->rawQuery('SELECT * from users where id >= ?', Array (10));
foreach ($users as $user) {
    print_r ($user);
}

長いifチェックを避けるために、生のクエリの選択結果を扱うためのヘルパー関数があります。
結果の1行を取得します。

$user = $db->rawQueryOne ('select * from users where id=?', Array(10));
echo $user['login'];
// Object return type
$user = $db->ObjectBuilder()->rawQueryOne ('select * from users where id=?', Array(10));
echo $user->login;

1カラムの値を文字列として取得

$password = $db->rawQueryValue ('select password from users where id=? limit 1', Array(10).
echo "パスワードは {$password} です。
注意: rawQueryValue() が配列ではなく文字列を返すようにするには、 クエリの最後に 'limit 1' を追加しなければなりません。

複数の行から1列の値を取得します。

$logins = $db->rawQueryValue ('select login from users limit 10');
foreach ($logins as $login)
    echo $login;

応用例

$params = Array(1, 'admin');
$users = $db->rawQuery("SELECT id, firstName, lastName FROM users WHERE id = ? AND login = ?", $params);
print_r($users); // contains Array of returned rows

// will handle any SQL query
$params = Array(10, 1, 10, 11, 2, 10);
$q = "(
    SELECT a FROM t1
        WHERE a = ? AND B = ?
        ORDER BY a LIMIT ?
) UNION (
    SELECT a FROM t2 
        WHERE a = ? AND B = ?
        ORDER BY a LIMIT ?
)";
$results = $db->rawQuery ($q, $params);
print_r ($results); // contains Array of returned rows

データを取得する条件を設定する(WHERE句) & グループ化したデータを取得する条件を設定する(HAVING句)

where(), orWhere(), having(), orHaving() メソッドを使用すると、クエリの where と having の条件を指定することができます。where() でサポートされているすべての条件は、having() でも同様にサポートされています。

警告: カラム間の比較を行うためには、生のwhere条件のみをカラム名として使用するか、関数をバインド変数として渡すことはできません。

$db->where ('id', 1);
$db->where ('login', 'admin');
$results = $db->get ('users');
// Gives: SELECT * FROM users WHERE id=1 AND login='admin';
$db->where ('id', 1);
$db->having ('login', 'admin');
$results = $db->get ('users');
// Gives: SELECT * FROM users WHERE id=1 HAVING login='admin';

演算子を使用して、カラム間の比較

// 間違い
$db->where ('lastLogin', 'createdAt');
// 正しい
$db->where ('lastLogin = createdAt');
$results = $db->get ('users');
// Gives: SELECT * FROM users WHERE lastLogin = createdAt;
$db->where ('id', 50, ">=");
// or $db->where ('id', Array ('>=' => 50));
$results = $db->get ('users');
// Gives: SELECT * FROM users WHERE id >= 50;

BETWEEN / NOT BETWEEN:

$db->where('id', Array (4, 20), 'BETWEEN');
// or $db->where ('id', Array ('BETWEEN' => Array(4, 20)));

$results = $db->get('users');
// Gives: SELECT * FROM users WHERE id BETWEEN 4 AND 20

IN / NOT IN:

$db->where('id', Array(1, 5, 27, -1, 'd'), 'IN');
// or $db->where('id', Array( 'IN' => Array(1, 5, 27, -1, 'd') ) );

$results = $db->get('users');
// Gives: SELECT * FROM users WHERE id IN (1, 5, 27, -1, 'd');

OR CASE:

$db->where ('firstName', 'John');
$db->orWhere ('firstName', 'Peter');
$results = $db->get ('users');
// Gives: SELECT * FROM users WHERE firstName='John' OR firstName='peter'

NULL比較

$db->where ("lastName", NULL, 'IS NOT');
$results = $db->get("users");
// Gives: SELECT * FROM users where lastName IS NOT NULL

LIKE比較

$db->where ("fullName", 'John%', 'like');
$results = $db->get("users");
// Gives: SELECT * FROM users where fullName like 'John%'
$db->where ("id != companyId");
$db->where ("DATE(createdAt) = DATE(lastLogin)");
$results = $db->get("users");

Or raw condition with variables:

$db->where ("(id = ? or id = ?)", Array(6,2));
$db->where ("login","mike")
$res = $db->get ("users");
// Gives: SELECT * FROM users WHERE (id = 6 or id = 2) and login='mike';

ページネーション総数の例

$offset = 10;
$count = 15;
$users = $db->withTotalCount()->get('users', Array ($offset, $count));
echo "Showing {$count} from {$db->totalCount}";

ページネーション総数の例

クエリーキーワード

LOW PRIORITY | DELAYED | HIGH PRIORITY | IGNORE およびその他の mysql キーワードを INSERT ()、REPLACE ()、GET ()、UPDATE ()、DELETE() メソッド、または FOR UPDATE | LOCK IN SHARE MODE に SELECT () に追加するには、以下のようにします。

$db->setQueryOption ('LOW_PRIORITY')->insert ($table, $param);
// GIVES: INSERT LOW_PRIORITY INTO table ...
$db->setQueryOption ('FOR UPDATE')->get ('users');
// GIVES: SELECT * FROM USERS FOR UPDATE;

キーワードの配列を使用

$db->setQueryOption (Array('LOW_PRIORITY', 'IGNORE'))->insert ($table,$param);
// GIVES: INSERT LOW_PRIORITY IGNORE INTO table ...

同じようにキーワードはSELECTクエリでも使用できます。

$db->setQueryOption ('SQL_NO_CACHE');
$db->get("users");
// GIVES: SELECT SQL_NO_CACHE * FROM USERS;

オプションで、メソッドチェーニングを使用して、何度もオブジェクトを参照せずに何度もどこを呼び出すことができます。

$results = $db
    ->where('id', 1)
    ->where('login', 'admin')
    ->get('users');

データを削除する(DELETE文)

$db->where('id', 1);
if($db->delete('users')) echo 'successfully deleted';

取得するデータをソートする(ORDER BY句)

$db->orderBy("id","asc");
$db->orderBy("login","Desc");
$db->orderBy("RAND ()");
$results = $db->get('users');
// Gives: SELECT * FROM users ORDER BY id ASC,login DESC, RAND ();

値で順番に並べる

$db->orderBy('userGroup', 'ASC', array('superuser', 'admin', 'users'));
$db->get('users');
// Gives: SELECT * FROM users ORDER BY FIELD (userGroup, 'superuser', 'admin', 'users') ASC;

setPrefix()の機能を使用していて、orderBy()メソッドでテーブル名を使用する必要がある場合は、
テーブル名が ``でエスケープされていることを確認してください。

$db->setPrefix ("t_");
$db->orderBy ("users.id","asc");
$results = $db->get ('users');
// WRONG: That will give: SELECT * FROM t_users ORDER BY users.id ASC;

$db->setPrefix ("t_");
$db->orderBy ("`users`.id", "asc");
$results = $db->get ('users');
// CORRECT: That will give: SELECT * FROM t_users ORDER BY t_users.id ASC;

データをグループ化する(GROUP BY句)

$db->groupBy ("name");
$results = $db->get ('users');
// Gives: SELECT * FROM users GROUP BY name;

内部結合を行う(INNER JOIN句)

$db->join("users u", "p.tenantID=u.tenantID", "LEFT");
$db->where("u.id", 6);
$products = $db->get ("products p", null, "u.name, p.productName");
print_r ($products);

結合文にAND条件を追加

$db->join("users u", "p.tenantID=u.tenantID", "LEFT");
$db->joinWhere("users u", "u.tenantID", 5);
$products = $db->get ("products p", null, "u.name, p.productName");
print_r ($products);
// Gives: SELECT  u.login, p.productName FROM products p LEFT JOIN users u ON (p.tenantID=u.tenantID AND u.tenantID = 5)

結合文にOR条件を追加

$db->join("users u", "p.tenantID=u.tenantID", "LEFT");
$db->joinOrWhere("users u", "u.tenantID", 5);
$products = $db->get ("products p", null, "u.name, p.productName");
print_r ($products);
// Gives: SELECT  u.login, p.productName FROM products p LEFT JOIN users u ON (p.tenantID=u.tenantID OR u.tenantID = 5)

プロパティ共有

プロパティをコピーすることも可能です。

$db->where ("agentId", 10);
$db->where ("active", true);

$customers = $db->copy ();
$res = $customers->get ("customers", Array (10, 10));
// SELECT * FROM customers where agentId = 10 and active = 1 limit 10, 10

$cnt = $db->getValue ("customers", "count(id)");
echo "total records found: " . $cnt;
// SELECT count(id) FROM users where agentId = 10 and active = 1

サブクエリ

Subquery init
Subquery init without an alias to use in inserts/updates/where Eg. (select * from users)

$sq = $db->subQuery();
$sq->get ("users");
A subquery with an alias specified to use in JOINs . Eg. (select * from users) sq

$sq = $db->subQuery("sq");
$sq->get ("users");
Subquery in selects:

$ids = $db->subQuery ();
$ids->where ("qty", 2, ">");
$ids->get ("products", null, "userId");

$db->where ("id", $ids, 'in');
$res = $db->get ("users");
// Gives SELECT * FROM users WHERE id IN (SELECT userId FROM products WHERE qty > 2)

MySQLサブクエリを使ったINSERT文

$userIdQ = $db->subQuery ();
$userIdQ->where ("id", 6);
$userIdQ->getOne ("users", "name"),

$data = Array (
    "productName" => "test product",
    "userId" => $userIdQ,
    "lastUpdated" => $db->now()
);
$id = $db->insert ("products", $data);
// Gives INSERT INTO PRODUCTS (productName, userId, lastUpdated) values ("test product", (SELECT name FROM users WHERE id = 6), NOW());

MySQLサブクエリを使ったJOIN文

$usersQ = $db->subQuery ("u");
$usersQ->where ("active", 1);
$usersQ->get ("users");

$db->join($usersQ, "p.userId=u.id", "LEFT");
$products = $db->get ("products p", null, "u.login, p.productName");
print_r ($products);
// SELECT u.login, p.productName FROM products p LEFT JOIN (SELECT * FROM t_users WHERE active = 1) u on p.userId=u.id;

EXISTS と NOT EXISTS

$sub = $db->subQuery();
    $sub->where("company", 'testCompany');
    $sub->get ("users", null, 'userId');
$db->where (null, $sub, 'exists');
$products = $db->get ("products");
// Gives SELECT * FROM products WHERE EXISTS (select userId from users where company='testCompany')

Has

この関数の前に where メソッドを呼び出して指定した where 条件を満たす要素が少なくとも存在する場合に TRUE を返す便利な関数です。

$db->where("user", $user);
$db->where("password", md5($password));
if($db->has("users")) {
    return "You are logged";
} else {
    return "Wrong user/password";
}

Helper methods

データベースから切断

    $db->disconnect();

mysql の接続が切れた場合の再接続

if (!$db->ping())
    $db->connect()

最後に実行されたSQLクエリを取得します。この関数がSQLクエリを返すのはデバッグ目的のみであることに注意してください。

    $db->get('users');
    echo "Last executed query was ". $db->getLastQuery();

テーブルが存在するかどうかをチェック

    if ($db->tableExists ('users'))
        echo "hooray";

mysqli_real_escape_string() wrapper:

$escaped = $db->escape ("' and 1=1");

トランザクションヘルパー

トランザクションはinnoDBのテーブルで動作していることを覚えておいてください。挿入に失敗した場合は、トランザクションをロールバックします。

$db->startTransaction();
...
if (!$db->insert ('myTable', $insertData)) {
    //Error while saving, cancel new record
    $db->rollback();
} else {
    //OK
    $db->commit();
}

エラーヘルパー

クエリを実行した後、エラーが発生したかどうかをチェックするオプションがあります。
MySQLのエラー文字列や、最後に実行したクエリのエラーコードを取得することができます。

$db->where('login', 'admin')->update('users', ['firstName' => 'Jack']);

if ($db->getLastErrno() === 0)
    echo 'Update succesfull';
else
    echo 'Update failed. Error: '. $db->getLastError();

クエリ実行時間のベンチマーク

クエリの実行時間を追跡するためには、setTrace()関数を呼び出す必要があります。

$db->setTrace (true);
// As a second parameter it is possible to define prefix of the path which should be striped from filename
// $db->setTrace (true, $_SERVER['SERVER_ROOT']);
$db->get("users");
$db->get("test");
print_r ($db->trace);
    [0] => Array
        (
            [0] => SELECT  * FROM t_users ORDER BY `id` ASC
            [1] => 0.0010669231414795
            [2] => MysqliDb->get() >>  file "/avb/work/PHP-MySQLi-Database-Class/tests.php" line #151
        )

    [1] => Array
        (
            [0] => SELECT  * FROM t_test
            [1] => 0.00069189071655273
            [2] => MysqliDb->get() >>  file "/avb/work/PHP-MySQLi-Database-Class/tests.php" line #152
        )

テーブルロック

テーブルをロックするには、setLockMethodと一緒にロックメソッドを使用します。
次の例では、書き込みアクセスのためにテーブルのユーザをロックします。

$db->setLockMethod("WRITE")->lock("users");

別のロックを呼び出すと、最初のロックが解除されます。

$db->unlock();

以前にロックされていたテーブルのロックを解除します。複数のテーブルをロックするには、配列を使用します。

$db->setLockMethod("READ")->lock(array("users", "log"));

これは、テーブルのユーザーとログをロックします。その後に*unlock()を使用するか、
テーブルがロックされたままになることを確認してください!

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

ウェブカツ!!が業界初のプログラミング漫画「はたらくプログラミング」を作ってしまった話

まえおき

どうも、最近運営しているプログラミングスクール「ウェブカツ!!」を売却しようとして「40億で売れる?」ってIT業界で有名なM&Aエージェントに相談してみたら「25億が現実的な落とし所ですねー。。」と渋い顔で言われてしまった、かずきちです。

一時、ベンチャー経営者やアンチがうちの生徒雇わない発言して物議を醸しましたが、一向にうちの生徒の内定報告いただいている状態で何よりです。
今年の秋口からは、うちの本が続々と出ます。
SNSマーケの口コミのみで、広告やSEOすらやってなかったので、もう
伸び代しかないですね。(ケイスケホンダ選手風)

アクティブで一時期1500人超える勢いだったんですが、安っぽいアフィリエイターみたいな副業気分で学ぼうとしてる甘っちょろい人間も増えてたので、僕が相当生徒数削りました。事業やってて思うのは、やっぱ費用が安いと意識の安い人間が入りますね。
(安くしちゃえば1万人は超えるし、売り上げも上がるのは分かってますが、その分しょうもないエンジニア増やしてもなという。)

因みにうちで身に付くレベルがどれかというと卒業したばかりの生徒達が作ったものを一部お見せするとこんな感じです。

https://itengineer-scouter.com/
※このサービスは、今は余力がないので運営ほったらかしてますが、スクールや教育が増えれば今後確実に定量評価出来るものが必要になるんでコンテンツちゃんと考えれば伸ばせるやつ。
競合ではpaizaとかあって僕もやりましたが、あれってエンジニアとしてのお仕事で使う技術能力とはかなりかけ離れちゃってるんですよね。技術というより数学テストだし。

うちは他スクールと違って、生徒に「作品」を作らせることはしてません。
だって、仕事としてお金もらってやってるのって
「商品」を作ること
ですから。

趣味レベルのものを作ってもしょうがないんですよね。
「商品」として成り立つレベルが作れなきゃ。

うちの生徒には元医師やらBIG4税理士法人、大手金融機関、大手メーカー勤めの高学歴な人〜中卒、40代主婦まで、地域は北海道から沖縄果てはフィリピン、マレーシア、インド、デンマークといったバリエーション豊かな人達が学んでいます。
(中卒も40代主婦もきちんと内定もらって今はエンジニアとしてバリバリやっているので、これからフリーランスで稼いでくれるのが楽しみです)

余談ですが、最近テック〇〇の面談受けた人がまた入ってきたんですが、営業に
「都内に出てこれない人は本気度が低いから無理だよ」
と言われたそうです。(うちは地方でも内定決まっているというのに。。)
都内が案件多いとはいえ、斡旋して自分らの利益にしたいからとはいえ。ね。
まぁ、正社員の斡旋するだけで、年収の3割もらえるんで、一人100万以上入ってくるものね。
そりゃ、売り上げ主義でいけば都内就職者だけ扱いたいよね。

「はたらくプログラミング」を作りたい!

前置き長くなりましたが、そんな中で今回うちの生徒(今は卒業生)で「ちょっと昔趣味で漫画描いてた」という人に

「はたらく細胞」みたいにプログラミング版を作りたいんだけど出来ない?

とお願いし、それがようやく完成。反響も上々なようで今回その製作の裏側をご紹介したいと思います。

amazonとboothで現在発売中です。
https://www.amazon.co.jp/dp/B087Z6J1RZ/ref=cm_sw_r_tw_dp_x_-qQfFbBE70Y32

「はたらくプログラミング」を作ろうと思った経緯

僕はいわゆる「ガチエンジニア」じゃ決してありません。
技術自体が全く好きではありません。
異業種の営業マンからエンジニアに転向する際の独学で、このITエンジニア業界の
「教育能力なさすぎ」
「伝えるスキルなさすぎ」
「ビジネス、マーケティング能力なさすぎ」
の3拍子にイラッとし、その改革のためにスクールを立ち上げた(昔はただの動画サービスだったけど)わけですが、そんな中でずっと

漫画で学べりゃ楽なのになぁ

と思っていました。
僕は漫画大好きなんですもの。字とか嫌い。
スクールも全て動画で教えているくらいですしね。

が、漫画を書くスキルはもちろんないですし、そもそもどんな漫画にすりゃいいのか思いつかなかったんですね。
そんなある時、出会っちゃったわけですよ、あれに。

「はたらく細胞」

ですよ。
これを読んだ瞬間、降りてきちゃったわけです。

神が。

これだ!って感覚です。

「はたらく細胞」をインスパイアしよう!

そう思ったわけです。
神の命です。

「はたらく細胞」をパクインスパイアしちゃいなさい。

そういう神の命を頂いたわけです。

何をどういう順序でどういう世界観で伝えるか。それが重要だ。

とはいえ、「プログラミングを擬人化する」というところまではきたんですが(まぁ、調べたらそういう擬人化した漫画ちっくなものはありましたが)、それぞれをどう伝えるか悩みました。
実際、わかばちゃんシリーズであったりコード学園ってのがありましたが、どっちも僕が求めるものとは違ったんです。

スクリーンショット 2020-03-12 05.11.19のコピー.png

ちなみに僕はいわゆる「オタク」が好きそうな萌え系キャラが大嫌いです。
エンジニア界隈では好かれるでしょうが、僕のような一般人からしたら気持ち悪さしかないんですね。(申し訳ないけどそれが本音だよ)
だから、そんな擬人化キャラにはしたくなかったですし、わかばちゃんのような4コマ漫画では、はたらく細胞のように

難解な専門知識を漫画としてストーリー仕立てで各キャラの個性や世界観も感じながら楽しみながら学習したい

という僕の堕落し甘えきった目的には到達できなかったんです。
だって、あれは漫画部分が一部あるだけの「解説本」ですし。
初心者の頃の僕はコードが出てきた段階で拒絶反応ですしね。
一番の初心者ってそっからなわけで。

僕はエンジニアが大嫌いだ

そもそも、僕は異業種の人をエンジニアとして育ててこの業界に送り込みたいんです。
だって僕は

エンジニア大嫌いなんですもの。

でも、

エンジニアは大好きなんです。

言ってること分かります?

営業やってた僕からすれば、エンジニアという職業は指動かしてるだけで金もらえるわ、
リモートも出来るわ、
ヘッドフォンしながら仕事出来るわ、
スマホいじりながら仕事出来るわ、
短期間で一気に報酬上げられるわ、
自動化して自分で金稼げるわ
で「天国」なんですよ。
エンジニアで月100万稼ぐのと営業で月100万稼ぐのじゃ天と地ほどの差な労力です。
女性ならライフステージによっても他の仕事より融通効きやすいだろうしね。
(女っ気なくてむさ苦しい現場が多いのが残念だけども。うちのスクールの女性比率はかなり高いので女性生徒からすると現場出てみて女性が少なすぎる現状にびっくりするみたいです。)

でも、僕のQiitaやらTwitterやらにウジのように湧いてくるキモいエンジニアは嫌いなんですよ。
教え方下手くそなエンジニアも、ビジネスもわからない子供エンジニアも嫌いなんですよ。
商品より作品作りたがったり、顧客課題をすっぽかして技術情報だけで鼻息荒くしてるエンジニアも大嫌いなんですよ。

だから、もうこの業界変えるには既存エンジニアより異業種のコミュケーション能力高い人や素養ある人に営業もビジネスも税務も法務も教えてエンジニアも並より上のスキル教える方が大事だと思っているわけです。
(最近、SNS訴訟に強い弁護士が教えてくれる法務部がうちのスクールに出来ました。営業部も日本で唯一大学で営業を学問として教えている生保で元億プレーヤーの花田敬さんに見てもらってます。)

なので、そもそも

プログラミングって何それ?聞いたことあるくらいだけど

な人たちでも読みたくなるようなものを作りたかったんですね。
売れない営業マンが漫画喫茶で手にとって読めるレベルのやつが。
そんで、あわよくば

「あ、プログラミングって面白そうかも」

と思ってくれりゃあ御の字っていう。

まぁ、とは言え、結構細かな技術部分の違いも入れて作ってるので、プログラミング学習中の人でもためになる内容になってます。
なので、各スクールさんで補助教材の1つとして使えたり、プログラミングが小学校必修となって親御さんなり教師がプログラミングがどんなものか学ぶ第一歩としても役立つと思います。
(実際に今有名な出版社さんから、うちの持ち出しなしで出版依頼も来てます)
image.png

世界観どうしよう

ターゲットも定まったところで世界観どうしようか悩みました。
ちなみにこの漫画は「漫画を読みながらWEBプログラミングの世界を学べる」ようにしたかったので、擬人化は
・HTML言語
・CSS言語
・JavaScript言語
・PHP言語
・SQL言語
を出そうと思ってました。
それぞれの言語の役割がざっくりイメージ出来て、自分たちが普段見ているHPが表示される中ではこんなやり取りが行われているんだという通信の流れもイメージできればいいなと。
(あとあとでRubyも出すことにしました。どんな登場でどんなキャラかは漫画でのお楽しみで)
スクリーンショット 2020-03-12 05.12.03.png
まぁ、キャラの動かし方としては、二郎系インスパイアの感じにしかならんだろうなとは思っていたので、
あとは色々とやり取りを重ねていくうちに世界観が見えてくるようになりました。
スクリーンショット 2020-03-12 05.12.34.png
スクリーンショット 2020-03-12 05.12.45.png
スクリーンショット 2020-03-12 05.13.13.png

HTML言語のキャラをざっくりどうするか

キャラ設定的なのどうするか考えていったんですが、
htmlはwebの根幹みたいなもんでしょう。
建築で言えば「基礎工事」や「家の骨組み」を作る人なわけなので、

熱血漢なてやんでぇ大工職人

という感じになりました
まぁ、ベタですが変に凝ると初心者がそもそも入っていけない世界になっていっちゃうので、ベタな王道で行くことにしました。

css言語のキャラをざっくりどうするか

cssは骨組みだけの簡素な家に外観つけて華やかにしていく美術さん的な感じですね。
ここらへんもベタに行きたかったので、お色気担当を入れることになり、

お色気のある大人な美術担当の女性

にすることになりました。
ストーリー内でも、htmlが土台を作り、cssが装飾を行う。という実際のweb制作の仕組みと同じにしています。
(こんな女性が実際の現場にいて欲しいと切に願ってます)

js言語のキャラをざっくりどうするか

これは、はたプロ(はたらくプログラミング)作りたい!ってちょろってツイートした時に
「言語を女性に全て擬人化したらこんなんだよね」
ってのが生徒からもらってたんで、それが採用されました。

jsはまぁフロントやってる人なら分かりますが、javaなりphpなりといった言語と違って標準のオブジェクトをぶっ壊す事も出来てしまうし、型とか関係なく放り込めるし、

ある意味柔軟で自由奔放、ある意味で大雑把

みたいな言語なので、そのまま
自由奔放でガサツで未熟感のある女性
になりました。
たぶん、B型女性かO型でしょうね。
僕はA型なんで、たぶん合わないです。
(振り回されてみたい願望もあるけど)

また、このjsちゃんをメインキャラに据える事にしました。
まぁ、うちのスクールがそもそも
バックも分かるフロントエンドエンジニア
を創出してるので、自然とjsメインになった感じです。
今後のテクノロジー成長の事も考えるとnodeもあるし、業務システムなどにある様な無機質な「システム」ではなく「サービス」としての価値勝負になっていき、UIリッチ、UXリッチでSPA化が当たり前になり、バックはAPIのみで簡素化されていく事を考えるとフロントエンド案件が今後どんどん増えていくのは必死ですしね。

つづく

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

【超初心者用】Laravel用のサーバーを起動する

Apacheを使用しなくてもいい

Apacheは8000をリッスンしている。

Apacheを起動してブラウザにlocalhostを入力すると、ルートディレクトリを開くことになるので、設定しているファイルが開かれる。

自分の場合はtop.phpというファイルが開かれる。

Laravelを使うときにもサーバーをたちあげる必要あり

動画なので説明をみていると、MAMPを使っていたり、いきなりlocalhostにアクセスすることで

image.png

この画面が起動していましたが、自分はApacheを使用してサーバーを立ち上げていて、8000をlocalhostに設定していました。

なので当然上記の画面が表示されませんでした。

ポートを指定してサーバーを立ち上げる?!

ウェブ職人のためのPHPフレームワーク。というサイトにちゃんと便利なことも書いてありました。
http://laravel.jp/

image.png

https://readouble.com/laravel/4.2/ja/quick.html

$ php artisan serve --port=8080

後ろの--port=8080というのが8080をポート番号に指定するということらしい。
これにより、複数のサーバーを立てる場合はこのポート番号をかぶらないように指定すればいいみたいですね。

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

【超初心者用】Laravelのインストールがうまくいかなかった

comporserでlaravelをインストールしようとしたら

$ composer global require "laravel/installer"

Using version ^3.2 for laravel/installer
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for laravel/installer ^3.2 -> satisfiable by laravel/installer[v3.2.0].
    - laravel/installer v3.2.0 requires ext-zip * -> the requested PHP extension zip is missing from your system.


Installation failed, reverting ./composer.json to its original content.

問題が発生しているらしい

./composer.json has been updated

なんかよくわからないが、バージョンが違うのかな。
先の方を読むと、

Installation request for laravel/installer ^3.2 -> satisfiable by laravel/installer[v3.2.0].

リクエストをlaravel/installer[v3.2.0]に変えればいいよという感じですかね。

laravel/installer v3.2.0 requires ext-zip * -> the requested PHP extension zip is missing from your system
ここは、PHPのextensionのzipが見つからないということでしょうか。

とりあえず、最初の方を試してみる。

laravel/installer[v3.2.0]で実行してもダメ

  [InvalidArgumentException]                                                                                         
  Could not find a matching version of package laravel/installer[v3.2.0]. Check the package spelling, your version   
  constraint and that the package is available in a stability which matches your minimum-stability (stable).         


require [--dev] [--prefer-source] [--prefer-dist] [--fixed] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]...

ext-zipが足りない

laravel/installer v3.2.0 requires ext-zip * -> the requested PHP extension zip is missing from your system.

という記載があるので、laravel/installer v3.2.の要求に関してはext-zipが必要だということですかね。

とりあえずPHPをインストールし直した

$ brew install php

でインストールし直しました

できた!

$ composer global require "laravel/installer"
Changed current directory to /Users/onoharamakoto/.composer
Using version ^3.2 for laravel/installer
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 19 installs, 0 updates, 0 removals
  - Installing symfony/polyfill-php80 (v1.18.0): Downloading (100%)         
  - Installing symfony/process (v5.1.2): Downloading (100%)         
  - Installing symfony/polyfill-ctype (v1.18.0): Downloading (100%)         
  - Installing symfony/filesystem (v5.1.2): Downloading (100%)         
  - Installing symfony/polyfill-mbstring (v1.18.0): Downloading (100%)         
  - Installing symfony/polyfill-intl-normalizer (v1.18.0): Downloading (100%)         
  - Installing symfony/polyfill-intl-grapheme (v1.18.0): Downloading (100%)         
  - Installing symfony/string (v5.1.2): Downloading (100%)         
  - Installing psr/container (1.0.0): Downloading (100%)         
  - Installing symfony/service-contracts (v2.1.3): Downloading (100%)         
  - Installing symfony/polyfill-php73 (v1.18.0): Downloading (100%)         
  - Installing symfony/console (v5.1.2): Downloading (100%)         
  - Installing psr/http-message (1.0.1): Downloading (100%)         
  - Installing psr/http-client (1.0.1): Downloading (100%)         
  - Installing ralouphie/getallheaders (3.0.3): Downloading (100%)         
  - Installing guzzlehttp/psr7 (1.6.1): Downloading (100%)         
  - Installing guzzlehttp/promises (v1.3.1): Downloading (100%)         
  - Installing guzzlehttp/guzzle (7.0.1): Downloading (100%)         
  - Installing laravel/installer (v3.2.0): Downloading (100%)         
symfony/service-contracts suggests installing symfony/service-implementation
symfony/console suggests installing symfony/event-dispatcher
symfony/console suggests installing symfony/lock
symfony/console suggests installing psr/log (For using the console logger)
guzzlehttp/psr7 suggests installing zendframework/zend-httphandlerrunner (Emit PSR-7 responses)
guzzlehttp/guzzle suggests installing psr/log (Required for using the Log middleware)
Writing lock file
Generating autoload files
11 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

なんかエラーメッセージが消えました。
どうやらインストールできたようです。

パスを通す

$ vim ~/.bashrc

新しくファイルを作成して開きます。
下の記述を入れる。

export PATH="$HOME/.composer/vendor/bin:$PATH"

:wqで保存して終了する。

次のコマンドを実行する

$ source ~/.bashrc

ここまで来たらLaravelのコマンドが使える

$ laravel
Laravel Installer 3.2.0

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  help  Displays help for a command
  list  Lists commands
  new   Create a new Laravel application

エラーメッセージが出てません。動いているらしい。

ターミナルをいったん閉じると

zsh: command not found: laravel

Laravelのコマンドは使えなくなるみたいです。
もう一度source ~/.bashrcを実行してからやればまた使えるようになりました。

結論

Problem 1
    - Installation request for laravel/installer ^3.2 -> satisfiable by laravel/installer[v3.2.0].
    - laravel/installer v3.2.0 requires ext-zip * -> the requested PHP extension zip is missing from your system.

上記のようなエラーが出た場合は
1. PHPをインストールし直すbrew install php
2. composer global require "laravel/installer"を実行してlaravelをインストールする

これで大丈夫みたいです。

真似しないようにお願いします

当方、かなりの素人です。何をやっても一つずつエラーが発生して前に進まないのです。

とりあえず作業を記録しています。自分向けに書いてます。できるだけ他の肩の記事を参考に処理を進めてください。大変なことになるかもしれませんので。

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

phpDocumentorでNo summary was found for this file.

TL;DR

PHP7.1以上のプロジェクトのドキュメントをphpDocumentor2で生成しようとしていませんか?GitHubのREADMEをきちんと読みましょう!

以下そこにたどり付くまでの記録です。

事象1 public constがダメ?

以下のファイルをphpDocumentorに突っ込むとNo summary was found for this fileというエラーが出る

Awesome.php
<?php
    /**
     * AwesomeなClassを定義するファイル
     */

    namespace MyAwesome;

    /**
     * AwesomeなClass
     */
    class Awesome {
        /**
         * PO は HOGEHOGE とする
         *
         * @var string
         */
        public const PO = "HOGEHOGE";
    }

対処

検索

StackOverflow「phpDocブロックを2つ置けば解決する」
ググり方が下手なので、これ以外の情報は検索しても出てこなかった

切り分け

:x:定数のdocコメントを消してもダメ

Awesome.php
<?php
    /**
     * AwesomeなClassを定義するファイル
     */

    namespace MyAwesome;

    /**
     * AwesomeなClass
     */
    class Awesome {
        public const PO = "HOGEHOGE";
    }

:o: 定数ごと消すとOK

Awesome.php
<?php
    /**
     * AwesomeなClassを定義するファイル
     */

    namespace MyAwesome;

    /**
     * AwesomeなClass
     */
    class Awesome {
    }

:o: 関数は定義してもOK

Awesome.php
<?php
    /**
     * AwesomeなClassを定義するファイル
     */

    namespace MyAwesome;

    /**
     * AwesomeなClass
     */
    class Awesome {
        /**
         * po は HOGEHOGE
         *
         * @return string
         */
        public function po(): string {
            return "HOGEHOGE";
        }
    }

:o: public constconstにするとOK

Awesome.php
<?php
    /**
     * AwesomeなClassを定義するファイル
     */

    namespace MyAwesome;

    /**
     * AwesomeなClass
     */
    class Awesome {
        const PO = "HOGEHOGE";
    }

:o: publicを消すとdocコメントを付けてもOK

Awesome.php
<?php
    /**
     * AwesomeなClassを定義するファイル
     */

    namespace MyAwesome;

    /**
     * AwesomeなClass
     */
    class Awesome {
        /**
         * PO は HOGEHOGE とする
         *
         * @var string
         */
        const PO = "HOGEHOGE";
    }

原因

オブジェクト定数のアクセス範囲を指定するとダメっぽい…?PHP 7.1から言語機能に追加されたはずでは……

事象2 nullableな引数がダメ?

以下のファイルをphpDocumentorに突っ込むとNo summary was found for this fileというエラーが出る

Awesome2.php
<?php
    /**
     * Awesome2 を定義するファイル
     */

    namespace MyAwesome;

    use OtherPackage\OtherClass;

    /**
     * Awesomeなclassその2
     */
    class Awesome2 {
        /**
         * 何もしないコンストラクタ
         *
         * @param OtherClass|null $other
         */
        public function __construct(?OtherClass $other) {
        }
    }

対処

:o: nullableじゃなくしてみる

Awesome2.php
<?php
    /**
     * Awesome2 を定義するファイル
     */

    namespace MyAwesome;

    use OtherPackage\OtherClass;

    /**
     * Awesomeなclassその2
     */
    class Awesome2 {
        /**
         * 何もしないコンストラクタ
         *
         * @param OtherClass $other
         */
        public function __construct(OtherClass $other) {
        }
    }

:o: デフォルト引数を指定してみる

Awesome2.php
<?php
    /**
     * Awesome2 を定義するファイル
     */

    namespace MyAwesome;

    use OtherPackage\OtherClass;

    /**
     * Awesomeなclassその2
     */
    class Awesome2 {
        /**
         * 何もしないコンストラクタ
         *
         * @param OtherClass|null $other
         */
        public function __construct(OtherClass $other = null) {
        }
    }

原因

nullableな型指定がダメっぽい…?PHP 7.1から言語機能に追加されたはずでは……

事象3 nullableな返り値がダメ?

以下のファイルをphpDocumentorに突っ込むとNo summary was found for this fileというエラーが出る

Awesome3.php
<?php
    /**
     * Awesome3 を定義するファイル
     */

    namespace MyAwesome;

    /**
     * Awesomeなclassその3
     */
    class Awesome3 {
        /**
         * 名前を検証する
         *
         * @param string $name
         * @return string|null
         */
        public function validate(string $name): ?string {
            if($name === "po"){
                return null;
            }else{
                return $name;
            }
        }
    }

対処

:o: mixedにしてみる

Awesome3.php
<?php
    /**
     * Awesome3 を定義するファイル
     */

    namespace MyAwesome;

    /**
     * Awesomeなclassその3
     */
    class Awesome3 {
        /**
         * 名前を検証する
         *
         * @param string $name
         * @return string|null
         */
        public function validate(string $name) {
            if($name === "po"){
                return null;
            }else{
                return $name;
            }
        }
    }

原因

nullableな型指定がダメっぽい…?PHP 7.1から言語機能に追加されたはずでは……

結論

「No summary was found for this file」というエラーメッセージに騙されましたが、単純にGitHubのREADMEに書いてある通りphpDocumentor2はPHP7.0までしか対応してないという話でした。~完~

おとなしくphpDocumentor3を待ちましょう。今RC版までリリースされています。

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

PHPとJavaScriptを使ったプルダウンを覚えたので記録しておく

PHPを使ったプルダウンを覚えたので記録しておく。

一部分を抜き出しているので非常にわかりにくいかもしれませんが...
PHP、JavaScript、Ajax、jsonを使用。

PHPファイルその1

<select name="category" size="1" id="category">
  <option value="大型犬" data-filter="大型犬">大型犬</option>
  <option value="中型犬" data-filter="中型犬">中型犬</option>
  <option value="小型犬" data-filter="小型犬">小型犬</option>
</select>
<select name="subcategory" size="1" id="subcategory">
</select>

サブカテゴリにデータを入れていきます。

PHPファイルその2

<?php

if(isset($_GET['category']) == true){
  $category = $_GET['category'];
}

//json形式で項目を入力しておく
if($category  == '大型犬'){
  $array = "[{\"name\": \"ゴールデンレトリバー\", \"value\": \"ゴールデンレトリバー\"},{\"name\": \"ラブラドールレトリバー\", \"value\": \"ラブラドールレトリバー\"},{\"name\": \"サモエド\", \"value\": \"サモエド\"}]";
}else if($category == '中型権'){
  $array = "[{\"name\": \"コーギー\", \"value\": \"コーギー\"},{\"name\": \"柴犬\", \"value\": \"柴犬\"}]";
}else if($category == '小型犬'){
  $array = "[{\"name\": \"チワワ\", \"value\": \"チワワ\"},{\"name\": \"マルチーズ\", \"value\": \"マルチーズ\"}]";
}

header('Content-Type: application/json; charset=utf-8');
print($array);

  //カテゴリ設定
  $('#category').on('change', function (event) {
    category();
  });
});

//カテゴリ設定の関数
function category() {
  $.ajax({
    url: '***ファイル名を入力***',
    type: 'GET',
    data: {
      category: $('#category').val(),
      select: 'subcategory',
    },
    dataType: 'json',
    cashe: false
  }).done(function (data) {
    const target_number = event.target.value;
    const subcategory = $('#subcategory');
    const data_sub = $('#category').children().eq(target_number).data(subcategory);
    subcategory.empty();
    for (let i = 0; i < data.length; i++) {
      let option = $('<option></option>');
      option.val(data[i]['value']);
      option.text(data[i]['name']);
      subcategory.append(option);
    }
  }).fail(function (data, textStatus, error) {
    alert('error');
  });
}

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

PHPを使ったページネーション(ページング)を習得したので記録しておく

PHPを使った恐らく1番簡単なページネーション(ページング)を習得したので記録しておく

今回は複数の画像を表示することを想定。
一部分を抜き出してきたためやや分かりにくいがメモ程度に。
$_GET取得です。
デザインしやすいようリストを使用。

  //ページネーション設定
  $perPage = 6;//最大表示画像数
  $imgs = count($img);//画像数をカウント
  $maxPage = ceil($imgs / $perPage);

  if (!isset($_GET['page'])){
    $page = 1;
  } else {
    $page = (int)$_GET['page'];
  }
 
  $startNo = ($page - 1) * $perPage;
  $img = array_slice($img, $startNo, $perPage, true);


  //ページネーション表示
  if ($page > 1) {//最初以外のページにいる場合は最初のページをリンク表示
    print('<li><a href="./index.php?page=1"><< first</a></li>');
  } else if($page == 1){//最初のページにいる場合は表示しない
    print('');
  }
 //全ページをリンク表示
  for ($i = 1; $i <= $maxPage; $i++) {
    if ($i == $page) {//閲覧中のページはリンクなし
      print('<li>'.$i.'</li>');
    } else {//それ以外のページはそのページへのリンクを貼る
      print('<li><a href="./index.php?page='.$i.'">'.$i.'</a></li>');
    }
  }

  if ($page < $maxPage) {//最後以外のページにいる場合は最後のページをリンク表示
    print('<li><a href="./index.php?page='.$maxPage.'">last >></a></li>');
  } else if($page == $maxPage){//最後のページにいる場合は表示しない
    print('');
  }

CSS部分はこんな感じで中央寄せのページネーション。

a{
  color: yellow;
  text-decoration: none;
}

li{
  color: white;
  list-style: none;
  text-align: center;
  padding: 16px;
  display: inline-block;
}

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

Laravel ルーティング情報を表示してくれるコマンド

目的

  • すでに定義されているルーティング情報の一覧を出力するコマンドを忘れない様に書き留める。

紹介

  • 下記コマンドをLaravelアプリ名ディレクトリに移動してから実行する。

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

[PHP on Windows] composer をインストーラから導入、プロキシ関連エラーが出たら手入力指定で回避できるかも

はじめに

企業内プロキシ配下の Windows10 に XAMPP + Composer を導入しました。
Composer-setup.exe から既定の設定でインストールしたとき、次のエラーで失敗しました。

The "https://getcomposer.org/versions" file could not be downloaded: SSL operation failed with code 1. OpenSSL Error messages: error:1408F10B:SSL routines:ssl3_get_record:wrong version number Failed to enable crypto failed to open stream: operation failed Retrying...

エラーの原因はさまざまあるかもしれませんが、この現象を回避して無事インストールできたので一例として共有します。

環境

  • Windows 10 Pro(プロキシ配下)
  • XAMPP 7.4.8
  • Composer 1.10.9

回避策

Composer-setup.exe インストーラーのウィザード画面で初期設定される、レジストリから読み出したプロキシ設定を、手入力したものへ変更します。
具体的には、Proxy Settings の画面で...

  1. Ignore settings from registry にチェックを入れます。Enter proxy url の初期設定が消え、Use a proxy...のチェックも消えます。
  2. Use a proxy server to connect to internet にチェックを入れます。Enter proxy url へ入力可能になります。
  3. Enter proxy url へプロキシサーバー情報を手入力します。(例 http://proxy.example.com:3128/)
  4. "Next >" で次の画面に進みます。

image.png

以上です

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

【Laravel】ロリポップ でComposer requireをするとPHP Fatal error: Out of memoryが出る場合にやったこと

原因

文字通りメモリ不足です。

つまりメモリがあれば良いわけなのですが、ロリポップではメモリを上限に設定してもPHP Fatal error: Out of memoryが出ます。

また、そもそもロリポップではphp.iniが変更できなかったりと制限があります。

$ composer require intervention/image

とかが普通にできないです。
そういえばcomposerでlaravelをインストールしようとした時もエラーが出たような・・・

対処(正解かどうかはわからない)

個人的にやったことをまとめます。

僕はこのやり方で一応なんとかなったのですが、責任は追いません。

というか、もっと良い方法ありませんか...?

scpコマンドを使用して1回ローカルに落とす

$ scp -r -P[ポート番号] ドメイン名@ssh.lolipop.jp:web [ダウンロード先]

scpコマンドはこちらの記事でわかりやすく解説してくださってます。

例えばこんな感じです。(macのデスクトップのhogeフォルダにコピーする場合)
$ scp -r -P1234 ×××@ssh.lolipop.jp:web ~/Desktop/hoge

ポート番号・パスワードが必要なので、そこはロリポップのメニューの「SSH」から見てみてください。
あと結構時間がかかります。

ローカルでcomposerを使う

$ cd [ダウンロードしたフォルダのパス]
$ composer require [パッケージ名]

composerがcommand not foundの場合はインストールしましょう。
僕はこちらの記事を参考にしました。

再びサーバーにコピー

$ scp -r -P[ポート番号] [ダウンロードしたいローカルのディレクトリパス] ドメイン名@ssh.lolipop.jp:up

これでwebディレクトリと同じ階層に、cpディレクトリが配置されました。

あとはwebディレクトリを削除し、cpディレクトリをwebにリネームすれば完了です。

$ mv web web2 (一応web2というディレクトリを作って今までのプロジェクトを保存しておく)
$ rm web
$ mv up web

これで一応完了なのですが、このあと画像をアップロードしたら画像がnot found(404)になるという事例が発生しました。

めちゃくちゃ焦りませした。。。

調べてみると、どうやらシンボリックを再作成する必要がありそうです。
ということで、

  • 一旦public/storageを削除
  • 再びシンボリックを作成

という処理をします。

# sshでログイン後・・・
$ cd web
$ cd public
$ rm storage
$ cd ../
$ php artisan storage:link

これで再びシンボリックが作成され、全ての工程が完了しました。。。

最後に・・・

きっともっと良い方法があるとは思うのですが(例えば手動でcomposer.jsonに追記して・・・とか)、僕の知識ではこれが一番安全でした。

そもそもレンタルサーバーでcomposerを使うのがいけないのかもしれませんね。

以上、Web初心者の苦労でした。

もっと良い方法あったらコメントお願いします!!!

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

【Laravel】ロリポップでComposer requireをするとPHP Fatal error: Out of memoryが出る場合にやったこと

原因

文字通りメモリ不足です。

つまりメモリがあれば良いわけなのですが、ロリポップではメモリを上限に設定してもPHP Fatal error: Out of memoryが出ます。

また、そもそもロリポップではphp.iniが変更できなかったりと制限があります。

$ composer require intervention/image

とかが普通にできないです。
そういえばcomposerでlaravelをインストールしようとした時もエラーが出たような・・・

対処(正解かどうかはわからない)

個人的にやったことをまとめます。

僕はこのやり方で一応なんとかなったのですが、責任は追いません。

というか、もっと良い方法ありませんか...?

scpコマンドを使用して1回ローカルに落とす

$ scp -r -P[ポート番号] ドメイン名@ssh.lolipop.jp:web [ダウンロード先]

scpコマンドはこちらの記事でわかりやすく解説してくださってます。

例えばこんな感じです。(macのデスクトップのhogeフォルダにコピーする場合)
$ scp -r -P1234 ×××@ssh.lolipop.jp:web ~/Desktop/hoge

ポート番号・パスワードが必要なので、そこはロリポップのメニューの「SSH」から見てみてください。
あと結構時間がかかります。

ローカルでcomposerを使う

$ cd [ダウンロードしたフォルダのパス]
$ composer require [パッケージ名]

composerがcommand not foundの場合はインストールしましょう。
僕はこちらの記事を参考にしました。

再びサーバーにコピー

$ scp -r -P[ポート番号] [ダウンロードしたいローカルのディレクトリパス] ドメイン名@ssh.lolipop.jp:up

これでwebディレクトリと同じ階層に、cpディレクトリが配置されました。

あとはwebディレクトリを削除し、cpディレクトリをwebにリネームすれば完了です。

$ mv web web2 (一応web2というディレクトリを作って今までのプロジェクトを保存しておく)
$ rm web
$ mv up web

これで一応完了なのですが、このあと画像をアップロードしたら画像がnot found(404)になるという事例が発生しました。

めちゃくちゃ焦りませした。。。

調べてみると、どうやらシンボリックを再作成する必要がありそうです。
ということで、

  • 一旦public/storageを削除
  • 再びシンボリックを作成

という処理をします。

# sshでログイン後・・・
$ cd web
$ cd public
$ rm storage
$ cd ../
$ php artisan storage:link

これで再びシンボリックが作成され、全ての工程が完了しました。。。

最後に・・・

きっともっと良い方法があるとは思うのですが(例えば手動でcomposer.jsonに追記して・・・とか)、僕の知識ではこれが一番安全でした。

そもそもレンタルサーバーでcomposerを使うのがいけないのかもしれませんね。

以上、Web初心者の苦労でした。

もっと良い方法あったらコメントお願いします!!!

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

Redis 安易になんでもSETしてたらGETできなくてハマった

サンプルコード

<?php

namespace App\Controller\Component;

use Cake\Controller\Component;
use Cake\Core\Configure;
use Predis\Client;

class HogeCacheManagerComponent extends Component
{
    /** @var object */
    private $client;

    /**
     * initialize
     *
     * @param  array $registory
     * @return void
     */
    public function initialize(array $registory)
    {
        parent::initialize($registory);

        $this->client = new Client(Configure::read('Redis'));
    }

    /**
     * getCache method
     *
     * @param  string      $key
     * @return null|string
     */
    public function getCache($key)
    {
        return $this->client->get($key);
    }

    /**
     * setCache method
     *
     * @param  string $key
     * @param  mixed  $value
     * @return void
     */
    public function setCache($key, $value, $expire = 600)
    {
        $this->client->set($key, json_encode($value));
        $this->client->expire($key, $expire);
    }
}

テストコード

<?php
namespace App\Test\TestCase\Controller\Component;

use App\Controller\Component\HogeCacheManagerComponent;
use Cake\Controller\ComponentRegistry;
use Cake\TestSuite\TestCase;

/**
 * App\Controller\Component\HogeCacheManagerComponent Test Case
 */
class HogeCacheManagerComponentTest extends TestCase
{

    /**
     * Test subject
     *
     * @var \App\Controller\Component\HogeCacheManagerComponent
     */
    private $HogeCacheManager;

    /**
     * setUp method
     *
     * @return void
     */
    public function setUp()
    {
        parent::setUp();
        $registry = new ComponentRegistry();
        $this->HogeCacheManager = new HogeCacheManagerComponent($registry);
    }

    /**
     * tearDown method
     *
     * @return void
     */
    public function tearDown()
    {
        unset($this->HogeCacheManager);

        parent::tearDown();
    }

    /**
     * Test getCache method
     *
     * @return void
     */
    public function testGetCache()
    {
        $this->assertEmpty($this->HogeCacheManager->getCache('fruits'));

        $this->HogeCacheManager->setCache('fruits', 'apple');
        $this->assertSame('apple', str_replace('"', '', $this->HogeCacheManager->getCache('fruits')));
    }

    /**
     * Test setCache method
     *
     * @return void
     */
    public function testSetCache()
    {
        $this->assertEmpty($this->HogeCacheManager->getCache('animals'));

        $this->HogeCacheManager->setCache('animals', 'dog');
        $this->assertSame('dog', str_replace('"', '', $this->HogeCacheManager->getCache('animals')));
    }
}

SETの仕様

SET(key, value)
計算時間: O(1)
文字列値 value をキー key にセットします。文字列は1073741824バイト(1GB)以下でなければいけません。

返り値
Status code replyを返す。

引用元:http://redis.shibu.jp/commandreference/strings.html#command-SET

GETの仕様

GET(key)
計算時間: O(1)
指定したキー key に対応する値を取得します。もしキーが存在しなかったら特別な値 “nil” を返します。もしキーに対応する値が文字列型ではなかったらエラーが返ります。なぜなら GET は文字列型にしか対応していないからです。

返り値
Bulk replyを返す。

引用元:http://redis.shibu.jp/commandreference/strings.html#command-GET

ハマったこと

  • SET時に keyを文字列以外の値にする

なぜハマったのか

  • 上記に記されてるように、SETの仕様はバイト制限のみであり、型制限は設けられていない
  • そのため、型を意識せずとも セットしたいkeyvalueでセットできてしまう

ポイント

  • SET時のkey文字列型 にする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel CRUD処理を使った投稿アプリを作成する その6 投稿編集機能

目的

  • アプリを作成する上で基本となるCRUD処理を有したLaravelアプリをチュートリアル的に作成する方法をまとめる

実施環境

  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

前提情報

  • DockerやAWSなどは使用せずにMacのローカルに実施環境と同じLaravel開発環境を構築して実施する。
  • チュートリアルで実際に筆者が作成したソースコードをGitHubにて公開予定である。
  • CRUD処理の作成完了を最短目標にしてバリデーションなどは後々設定することとする。
  • 実施環境と同じ環境がDockerやAWSで用意できるなら都度読み替えていただければ実施が可能だと思う。
  • 公式ドキュメントと一冊の技術書を元に本記事を記載する。

この記事の読後感

  • 投稿内容を投稿後に編集することができる。

全ての記事(miriwo_laravelチュートリアル)を通した読後感

  • Laravelアプリでログインなどのユーザ認証付き投稿アプリの作成ができる。

概要

  1. ルーティングの記載
  2. コントローラの記載
  3. ビューファイルの修正
  4. 確認

概要

  1. ルーティングの記載

    1. laravel_crudディレクトリで下記コマンドを実行してルーティングファイルを開く。

      $ vi routes/web.php 
      
    2. 開いたファイルに下記の行を追記する。

      laravel_crud/routes/web.php
      Route::get('/edit', 'ContentController@edit')->name('edit');
      Route::post('/update', 'ContentController@update')->name('update');
      
  2. コントローラの記載

    1. laravel_crudディレクトリで下記コマンドを実行して作成したコントローラファイルを開く。

      $ vi app/Http/Controllers/ContentController.php
      
    2. 下記の内容をクラス内に追記する。

      laravel_crud/app/Http/Controllers/ContentController.php
      public function edit($content_id)
      {
          $contents_edit_query = Content::select('*');
          $contents_edit_query->where('id', $content_id);
          $edit_contents = $contents_edit_query->get();
          $edit_content = $edit_contents[0];
      
          return view('contents.edit', [
              'edit_content' => $edit_content,
          ]);
      }
      
      public function update(Request $request)
      {
          $contents_update_query = Content::select('*');
          $contents_update_query->where('id', $request['content_id']);
          $update_contents = $contents_update_query->get();
      
          $update_content = $update_contents[0];
          $update_content->content = $request['content'];
          $update_content->save();
      
          return redirect('/output');
      }
      
    3. 記載後のコントローラファイルの内容を下記に記載する。

      laravel_crud/app/Http/Controllers/ContentController.php
      <?php
      
      namespace App\Http\Controllers;
      
      use Illuminate\Http\Request;
      use App\Models\Content;
      
      class ContentController extends Controller
      {
          public function input()
          {
              return view('contents.input');
          }
      
          public function save(Request $request)
          {
              $input_content = new Content();
              $input_content->content = $request['content'];
              $input_content->save();
      
              return redirect('/output');
          }
      
          public function output()
          {
              $contents_get_query = Content::select('*');
              $all_contents = $contents_get_query->get();
      
              return view('contents.output', [
                  'all_contents' => $all_contents,
              ]);
          }
      
          public function delete(Request $request)
          {
              $contents_delete_query = Content::select('*');
              $contents_delete_query->where('id', $request['content_id']);
              $contents_delete_query->delete();
      
              return redirect('/output');
          }
      
          public function edit($content_id)
          {
              $contents_edit_query = Content::select('*');
              $contents_edit_query->where('id', $content_id);
              $edit_contents = $contents_edit_query->get();
              $edit_content = $edit_contents[0];
      
              return view('contents.edit', [
                  'edit_content' => $edit_content,
              ]);
          }
      
          public function update(Request $request)
          {
              $contents_update_query = Content::select('*');
              $contents_update_query->where('id', $request['content_id']);
              $update_contents = $contents_update_query->get();
      
              $update_content = $update_contents[0];
              $update_content->content = $request['content'];
              $update_content->save();
      
              return redirect('/output');
          }
      }
      
  3. ビューファイルの作成と修正

    1. laravel_crudディレクトリで下記コマンドを実行してビューファイルを作成する。

      vi resources/views/contents/edit.blade.php
      
    2. 作成して開いたビューファイルに下記の内容を追記する。

      laravel_crud/resources/views/contents/edit.blade.php
      <h1>edit</h1>
      
      <form action="{{route('update')}}" method="post">
          @csrf
          <textarea name="content" cols="30" rows="10">{{$edit_content['content']}}</textarea>
          <input type="hidden" name="content_id" value="{{$edit_content['id']}}">
          <input type="submit" value="送信">
      </form>
      
    3. laravel_crudディレクトリで下記コマンドを実行してビューファイルを開く。

      vi resources/views/contents/output.blade.php
      
    4. 開いたビューファイルに下記の内容を追記する。

      laravel_crud/resources/views/contents/output.blade.php
      <h1>output</h1>
      
      @foreach ($all_contents as $item)
          <hr>
          <p>{{$item['content']}}</p>
          <form action="{{route('delete')}}" method="post">
              @csrf
              <input type="hidden" name="content_id" value="{{$item['id']}}">
              <input type="submit" value="削除">
          </form>
          <a href="{{route('edit', ['content_id' => $item['id']])}}">
              <button>編集</button>
          </a>
      @endforeach
      
  4. 確認

    1. laravel_crudディレクトリで下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    2. ブラウザで下記にアクセスする。

    3. 「編集」ボタンをクリックする。

      127_0_0_1_8000_output.png

    4. 下記の様に先に投稿した内容がすでに記載されているテキストボックスが出力される。

      127_0_0_1_8000_edit_1.png

    5. 内容を任意の物に変更し、「送信」ボタンをクリックする。

      127_0_0_1_8000_edit_1.png

    6. 下記ページにリダイレクトして、投稿内容が変更されていれば本記事の内容は完了である。

      127_0_0_1_8000_output.png

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

マイグレーション作成

今回やること

マイグレーション機能(データベースのバージョン管理)を使用して、phpMyAdminに作ったデータベース(mommy_book)にテーブルを作成する!

ポイント

  • migrationファイルの内容の中には、テーブルを作成する処理が含まれている 

    • up(): テーブル作成(バージョンアップ処理)
    • down(): テーブル削除(ロールバック処理)
  • Laravelにはプロジェクト作成段階でユーザ認証が組み込まれてるようになっている

    • ユーザ用のテーブルパスワード再確認用テーブルを作成するmigrationファイルがデフォルトでdatabase/migrationの直下に保存されている  

今回のMommy_bookアプリでは、デフォルトのユーザ用migrationファイル(Usersテーブル用)に加えて、Babiesテーブル、Vaccinesテーブル、Baby_checkupsテーブル、Baby_teethテーブル、Matanity_checkupsテーブル、Albumsテーブルを作るため6つのmigrationファイルを作成する。

流れ

  1. migrationファイルを作成
  2. テーブル定義書をもとにmigrationファイルを編集
  3. migrationを実行
  4. phpMyAdminのデータベースにテーブルが作成されているかを確認

ここでは、①デフォルトでテーブルを作る場合(Usersテーブル)②自分でmigrationファイルを作る場合(ex. Babiesテーブル)の作成を説明します

①デフォルトでテーブルを作る場合(Usersテーブル)

1.migrationファイルは既にあるので、database/migrationの直下のcreate_users_table.phpを開く
[デフォルトではup()は以下の通り、down()はデフォルトのままなので省略]

 public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

2.テーブル定義書をもとにmigrationファイルを編集
image.png

3.migrationを実行 (コマンドで)

php artisan migrate

4.phpMyAdminで確認
image.png

☆データベースにテーブルが作成できているので成功☆

②自分でmigrationファイルを作る場合(ex. Babiesテーブル)

  1. migrationファイルを作成

コマンドで、以下の通り(-createオプション:テーブル名は複数形で)

php artisan make:migration create_babies_table --create=babies

database/migrationで作成したcreate_babies_table.phpを開く
[デフォルトではup()は以下の通り、down()はデフォルトのままなので省略]

  public function up()
    {
        Schema::create('tweets', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }
  1. テーブル定義書をもとにmigrationファイルを編集
    image.png

  2. migrationを実行

php artisan migrate
  1. phpMyAdminのデータベースにテーブルが作成されているかを確認 image.png

☆データベースにテーブルが作成できているので成功☆

カラム設定で出てきたもの

ベースとなる書き方
型がintegerのときは、引数を取ることできないので注意!!!!

 Schema::create('テーブル名', function (Blueprint $table) {
$table->カラムの型('カラム名', 引数)
}

nullのとき⇩

$table->カラムの型('カラム名', 引数)->nullable();

メールアドレスなど値の重複を防ぎたいとき⇩

$table->カラムの型('カラム名', 引数)->unique();

外部キー⇩

$table->foreign('外部キー名')->references('外部キーに対応する主キー名')->on('選択した主キーのテーブル名');
}   

timestamps()
有効(全体)桁数指定で、NULL値可能なcreated_atとupdated_atカラムを追加する
NULLが指定された場合は、デフォルト値が入る

まとめ

以下の手順を繰り返す!!
1. migrationファイルを作成
2. テーブル定義書をもとにmigrationファイルを編集
3. migrationを実行
4. phpMyAdminのデータベースにテーブルが作成されているかを確認

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

PHP-数値を3桁ごとにカンマを付与する関数

number_format関数

数字を千位毎にグループ化してフォーマットする

<?php
  echo number_fornmat(1234567); //1,234,567

laravelで使うときは下記のような感じで使う

$price = [1000, 2000, 3000,];
foreach ($price as $value){
<td>¥{{ number_format($value->price) }}</td>
}

結果
¥1,000
¥2,000
¥3,000

余談ですが、勝手に小数点を含む数値」を四捨五入するので、第二引数に桁数を指定すれば小数点以下も表示できます。

<?php
  echo number_format(1234.567,2); // 1,234.57
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む