20201025のPHPに関する記事は24件です。

laravelでモデルのリレーションで検索機能をつくってみた件

今回したかったこと

社員の出張管理アプリを作成しており、社員がどの場所に出張したか履歴を記録する処理を実装したところ、履歴の検索機能を作りたくなった。

Members(社員)--(1対多)--Histories(履歴)(中間テーブルに相当)--(多対1)--Locations(出張先)

テーブルには上記のようなリレーションが設定されており、この3つのモデルから、Historiesを軸に検索機能を実装したいことがあり挑戦したところ、大変勉強になったのでメモ。

処理の条件

・出張日、社員名、行先場所名で検索する
・3つのうち、リクエスト項目数は0~3でも、どんな組み合わせでも検索できる

テーブル詳細

Historiesというテーブルが中間テーブルに相当します。
ポイントとなる点は、リレーションできるようにHistoriesにはmember_id、location_idというカラムを持っている。

・Membersテーブル
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id    | int(10) unsigned | YES  |     | NULL    |                |
| name       | varchar(255)     | YES  |     | NULL    |                |
| address    | varchar(255)     | YES  |     | NULL    |                |
| latitude   | double(9,6)      | YES  |     | NULL    |                |
| longitude  | double(9,6)      | YES  |     | NULL    |                |
| created_at | timestamp        | YES  |     | NULL    |                |
| updated_at | timestamp        | YES  |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+

 ・Historiesテーブル
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| trip_date   | date             | NO   |     | NULL    |                |
| member_id   | int(10) unsigned | NO   | MUL | NULL    |                |
| start       | int(11)          | NO   |     | NULL    |                |
| location_id | int(10) unsigned | NO   | MUL | NULL    |                |
| distance    | int(11)          | YES  |     | NULL    |                |
| created_at  | timestamp        | YES  |     | NULL    |                |
| updated_at  | timestamp        | YES  |     | NULL    |                |
| deleted_at  | timestamp        | YES  |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------

 ・Locationsテーブル
+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255)     | NO   |     | NULL    |                |
| address    | varchar(255)     | YES  |     | NULL    |                |
| latitude   | double(9,6)      | YES  |     | NULL    |                |
| longitude  | double(9,6)      | YES  |     | NULL    |                |
| created_at | timestamp        | YES  |     | NULL    |                |
| updated_at | timestamp        | YES  |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+

モデルにリレーションを定義

モデルのクラス内に、それぞれ下記のリレーションを定義します。

Memberモデル

 public function history(){
      return $this->hasMany(History::class);
 }



Historyモデル

public function member(){
      return $this->belongsTo(Member::class);
}
public function location(){
      return $this->belongsTo(Location::class);
}



Locationモデル

public function history(){
      return $this->hasMany(History::class);
}

ビューへフォームを作成して、ルーティングを通す

Historyの一覧を表示するビューに検索フォームを作る。
リクエストすると、Historyコントローラーを通るようにルーティング処理を書いておく。

フォーム

 {{ Form::open(['method' => 'GET', 'route' => 'history.list']) }}
       {{ Form::input('date', 'trip_date', null, ['placeHolder' => '日付']) }}
       {{ Form::input('text', 'member_name', null, ['placeHolder' => '名前']) }}
       {{ Form::input('text', 'location_name', null, ['placeHolder' => '目的地']) }}
       {{ Form::submit('検索', array('class' => 'btn btn-primary')) }}
 {{ Form::close() }}

ルーティング

 Route::group(['prefix' => '/history', 'as' => 'history.'], function() {
        Route::get('/', [
            'as' => 'list',
            'uses' => 'HistoryController@index'
        ]);
 });

出来上がった処理

飛んできたリクエストを配列で拾い、リクエストあれば検索、なければ全件でif分岐する。
というような感じになっています。今回のポイントになった点は下記の通り。

whenメソッド・・・第1引数がtrueなら、第2引数のクロージャーを処理するようになっている。
本例の場合、出張日がリクエストされていれば、SQLでwhere文を付加するようになっている。

whereHasメソッド・・・第1引数にモデルに定義したリレーションのメソッド名を渡すと、第2引数のクロージャーを処理するようになっている。本例の場合、出張先名、社員名がリクエストされていれば、SQLでwhere~like文を付加するようになっている。

  public function index(Request $request)
    {
        $title = 'History List';
        $search = array(
            'trip_date' => $request->get('trip_date'),
            'member_name' => $request->get('member_name'),
            'location_name' => $request->get('location_name')
        );
        if(array_filter($search)){
            $trip_date = $search['trip_date'];
          //////////今回作った処理/////////
            $histories = History::when($trip_date, function($query, $trip_date){
                return $query->where('trip_date', $trip_date);
            })
            ->whereHas('location',function($q) use ($search) {$q->where('name', 'like', '%'.$search['location_name'].'%');})
            ->whereHas('member',function($q) use ($search) {$q->where('name', 'like', '%'.$search['member_name'].'%');})
            ->get();
         //////////////////////////////
        }else{
            $histories = History::all();
        }
        return view('history/index', ['title' => $title], ['histories' => $histories]);
    }

ちなみに発行されたクエリを確認すると以下のようになっていました。
exists句が使われており、続く()内のサブクエリでレコードが存在しているか見ています。
サブクエリはwhere~likeで実行されているので空白でリクエストしても抽出できるわけですね。

select * from `histories` where `trip_date` = '2020-09-30' 
and exists (select * from `locations` where `histories`.`location_id` = `locations`.`id` and `name` like '%%') 
and exists (select * from `members` where `histories`.`member_id` = `members`.`id` and `name` like '%佐々木%')

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

Laravel:DB(Sequel Pro)をつなげてmigrateする方法

【概要】

1.結論

2.どのように記載するか

3.開発環境

補足

1.結論

create databaseしたファイル名と.envとconfig/database.phpでのファイル名等を一致させ、php artisan make:migrationしてから必要な記述をしphp artisan migrateする!


2.どのように記載するか

MysqlやSequel Proの導入は省きます。

❶まずデータベースを作成します

$ mysql -u root -p #パスワードを何も設定してなければEnterで進めます。
mysql> create database ファイル名;

❷.env.exampleとconfig/database.phpでDB(Sequel Pro)をつなげます。

.env
DB_CONNECTION=mysql #mysqlを使用しているのでこのように記載します。
DB_HOST=127.0.0.1 #データーベースサーバーのホストの指定です。
DB_PORT=3306 #初期値がこの値なのでいじらないでOKです。そうでない場合はこの値にしてください。
DB_DATABASE= #create database ファイル名;のファイル名を記載してください。
DB_USERNAME=root #Sequel Proのユーザー名を記載してください。
DB_PASSWORD= #パスワードを設定していなければ空欄でOKです。
config/database.php
'mysql' => [
    'driver' => 'mysql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', ''),
    'username' => env('DB_USERNAME', 'root'),
# .env.example と一致させます。

❸php artisan make:migrationして、php artisan migrateします。

$ php artisan make:migration ファイル名

php artisan make:migration ファイル名を記載するとファイルが生成されるので必要なカラム名や型を設定します。

#省略

    public function up()
    {
        Schema::create('people', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('mail');
            $table->integer('age');
            $table->timestamps();
        });
    }

#省略

ここでphp artisan migrateするとcreate databaseしたDBのファイル名に反映されます。


参考にさせていただいたURL:

❶DBに反映:LaravelでSequel Proを使ったMySQLのデータベース接続(Mac/初心者向き)

3.開発環境

PHP 7.4.10
Laravel 8.9
Apache 2.4.41
Mysql 5.6.47
Sequl Pro 1.1.2

補足

しかし、2.❸でphp artisan migrateしたところ、エラーが生じ、SQLSTATE[HY000] [1049] Unknown database 'laravel'とでました。スクリーンショット 2020-10-25 21.24.25.png

結論から言うと、"DB_DATABASE=Laravel"のデフォルトの記述のままでした。その場所はどこかというと、下記で入力した部分でした。

$ vi .env

ここに記載してあるDB_DATABASE= が"Laravel"のままで自分が.env.exampleで設定した名前と違っていたので直したところDBに反映できました。自分が記載した.envファイルは.env.exampleファイルに記載しており"vi .env"に記載していなかったためです。

参考にさせていただいたURL:
❷エラーについて:Laravel MySQL SQLSTATE[HY000] [1049] Unknown database が出た時に確認すること

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

Moodle 3.9 マニュアル - 管理者クイックガイド

原文

管理者クイックガイド

このページはMoodle管理についての紹介です。新しい空のMoodleサイトがインストールされています。次に何をしますか?

目次

1 フロント(ホーム)ページの設定
2 サイトの外観を変更する
3 コースの追加
4 ユーザーの追加
4.1 ステップ 1: 認証
4.2 ステップ2:登録
5 ファイル管理
6 重要なデフォルトサイトの設定
7 管理者のヒントとコツ

1 フロント(ホームページ)ページの設定

Boostテーマ:(翻訳準備中)をお使いの方は、フロントページ右上の歯車メニューから「設定の編集」をクリックしてください。または、フロントページの管理ブロックから「設定の編集」をクリックしてください。
必要に応じて、フルネームとショートネームを変更します。(短縮名はナビゲーションバーに表示されます)。
フロントページに表示するものを決めてください。ニュースアイテム、コース、コースカテゴリ、またはこれらのどれも表示しない場合はどうでしょうか?ログインしているユーザーとログインしていないユーザーで同じものを表示するか、異なるものを表示するか? その他の設定については、いつでも後から変更することができます。
詳細については、フロントページの設定:(翻訳準備中)をご覧ください。

600px-32frontpageMoodleCloud.png
Boostテーマを使用してMoodleCloudサイトの設定を開始する
1. フロントページの中央部分にテキストや画像を追加するには、「編集をオンにする」をクリックします。
ギアメニュー(Boostテーマ)、または他のテーマの場合は管理ブロック:(翻訳準備中)から「編集をオンにする」をクリックします。
2.画面上部近くの設定アイコン(歯車)をクリックします。(表示されない場合は、サイト管理 > フロントページ > フロントページの設定で「トピックセクションを含める」にチェックが入っていることを確認してください)
3. 要約ボックスにテキストおよび/または画像を追加します。
注: フロントページをコースや組織に関する情報の表示に使用したくない場合は、サイト管理 > セキュリティ > サイトセキュリティ設定で「強制ログイン」にチェックを入れることで、ログインページのみを表示することができます。

2 サイトの外観を変更する

・新しいMoodleサイトには「Boost」と「Classic」の2つのデフォルトテーマが付属しています。両方ともモバイルデバイスおよびデスクトップで動作し、サイト管理のテーマ:(翻訳準備中)エリアからカスタマイズすることができます。
・ドロップダウンメニュー、フッターリンク、Google Analyticsなどをサイトの外観:(翻訳準備中)に追加する方法をご覧ください。
・ご使用のインストールと組織で許可されている場合は、カスタムテーマをインストールすることができます。詳細については、プラグインのインストールを参照してください。
600px-ChangingTheLook.png
カスタマイズされたテーマを使用したサイト

3 コースの追加

・コースは教師と学生が一緒に学習するMoodleの学習エリアです。
・フロントページの「新しいコースの追加」ボタンをクリックするか、サイト管理の「コース」エリアの「コースとカテゴリの管理」リンクから新しいコースを追加することができます。
・多くのコースを持つことを計画している場合は、CSVファイルを介して一括でコースをアップロードすることができます。
・新しいコースを作成する際に、コース内の特定の設定を常に事前に設定しておきたい場合は、サイト管理 > コース > コースのデフォルト設定を確認してください。
コースの様々な側面の概要についてはコースを参照してください。また、Mount Orange Schoolのデモサイトでは、ユーザーデータを使ってコースを実際に動かしています。
600px-ExampleBoostTheme.png
Boostのデフォルトテーマを使用したコースの例

4 ユーザーの追加

効率化は可能ですが、これは2段階のプロセスです。
video
ユーザー追加の概要

4.1 ステップ1:認証

あなたのサイトを使用するすべての人がアカウントを持っている必要があります。メールベースの自己登録:(翻訳準備中)を使用してアカウントを作成したり、新しいユーザーを個別に追加したり、CSV ファイルを使用してアカウントを一括作成したり、他のいくつかの認証方法から選択したりすることができます。

詳細については、アカウント管理認証:(翻訳準備中)を参照してください。

注: 教師、学生、またはその他の参加者の役割を誰にするかはまだ決めていません。その理由が気になる場合は、ロールの割り当て:翻訳準備中のドキュメントを参照してください。

4.2 ステップ 2: 登録

ユーザーがアカウントを持ったら、コースに登録する必要があります。(今は学生、教師、またはその他の役割を与える時です。) ユーザーは自分で登録:(翻訳準備中)することもできますし、手動で登録:(翻訳準備中)することもできますし、他のいくつかの登録方法から選択することもできます。

詳細については、登録:(翻訳準備中)を参照してください。

注:ユーザーをアップロードすることで、必要に応じてアカウントを作成し、同時にコースにユーザーを登録することもできますし、コホート、サイト、またはカテゴリ全体のグループを探索することもできます。
600px-AddingUsers.png
学習者をコースに登録する

5 ファイル管理

・コースでファイルを扱う:(翻訳準備中)プロセスを調べて、他の人にアドバイスできるようにしましょう。
・ユーザーが外部ソース(Google Drive、Dropbox、MS OneDriveなど)からのファイルをコースで使用できるようにする方法については、リポジトリの管理:(翻訳準備中)を参照してください。
・アップロードされたファイルのサイズが問題となる場合は、管理 > サイト管理 > セキュリティ > サイトセキュリティ設定から、ユーザークォータとサイト全体の両方に制限を追加することができます。詳細については、サイトセキュリティ設定:(翻訳準備中)を参照してください。
600px-FileManagement.png
ファイルにアクセスするためのMoodleの多くの方法のいくつか

6 重要なデフォルトサイトの設定

  1. サイト管理 > 言語 > 言語設定でデフォルト言語を設定してください。米国英語または他の言語が必要な場合、最初に言語 > 言語パックに追加してください。
  2. あなたのデフォルトのタイムゾーンと国をサイト管理 > ロケーション > ロケーション設定で設定してください。
  3. Site administration > Plugins > Enrolments > Manage enrol pluginsでパブリックゲストアクセスをオフにし、Site administration > Plugins > Authentication > Manage authentication > Guest login buttonでそのボタンを非表示にします(あなたがそれを使用する方法を知っていると確信している場合を除く)。
  4. 必要であれば、サイト管理>セキュリティ>サイトセキュリティ設定>パスワードポリシーで最小パスワードを調整してください。
  5. インストール時にcronを設定した場合は、cronが起動していることを確認してください。これがないとサイトが正常に動作しません。サイト管理>通知で "The cli/cron.php maintenance script has been not been run for least 24 hours. "というメッセージが表示された場合は正しく動作していない可能性があります。

7 管理者のヒントとコツ

・管理 > サイトの管理 > プラグイン > アクティビティモジュールで各アクティビティを確認し、あなたのMoodleに最も適したデフォルト設定を決定してください。管理 > サイト管理 > 成績 > 一般設定で、成績表についても同じことを行ってください。
・管理 > サイト管理 > 高度な機能にアクセスして、ブログ:(翻訳準備中)RSSフィード:(翻訳準備中)、完了トラッキング (アクティビティ完了:(翻訳準備中)コース完了:(翻訳準備中)の両方)、条件付きアクセス:(翻訳準備中)ポートフォリオ:(翻訳準備中)またはバッジ:(翻訳準備中)などの追加機能を利用するかどうかを検討してください。
・サイト管理 >モバイルアプリ >モバイル設定 "からモバイルアプリ:(翻訳準備中)へのアクセスを有効にすることを検討してください。
サイト管理 > 言語 > 言語パックで追加の言語パック:(翻訳準備中)をインストールして、異なる言語でのユーザーインターフェースを提供します。
・管理 > サイト管理 > 言語 > 言語のカスタマイズでMoodleのデフォルト用語を変更することで、あなたのユーザに適した言葉を使用することができます。
600px-TipsAndTricks.png
バッジ -検討すべき機能

カテゴリ | サイト管理 | クイックガイド

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

【PHP】配列のnullチェック

foreachで使う変数がnullの場合エラーが起こるため事前に配列の中身をチェックします。

変数のnullチェック

通常、変数に値があるかどうかを確認したいのであればempty()やisset()関数を用いることが多いと思います。

index.php
    if(!empty($array)){
        foreach($array as $value){
            処理
        }
    }
index.php
    if(isset($array)){
        foreach($array as $value){
            処理
        }
    }

上記の場合、変数の中身が配列で確定しているのであれば問題ないですが、単なる変数のnullチェックになるので中身が配列型で入って来ているかの確認はできません。

変数が配列型かどうかをチェック

index.php
    if(is_array($array)){
        foreach($array as $value){
            処理
        }
    }

is_array関数を使えば変数の中身が配列型かどうかの確認ができます。

もっと簡潔に書く場合、変数を配列にキャストさせるといった書き方もできるみたいです。

index.php
    foreach ((array)$array as $value) {
        処理
    }

参考文献:
https://quartet-communications.com/info/topics/7094

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

【WordPress】画像のアップロード時にファイル名を記事IDに変更する

説明

WordPressの投稿画面から画像をアップロードする際、画像のファイル名を記事IDに合わせて自動で変更します。

ご注意

・記事の投稿画面(ブロックエディタ、クラシックエディタ)から画像をアップロードして挿入する場合のみ動作確認済みです。
・メディアライブラリへ直接アップロードする場合、画像ファイル名は変更されますが、メディアタイトルとして元のファイル名が残ります。
・変更は自己責任でお願いいたします。

コード

以下をfunctions.phpに貼り付けてください。

funtions.php
function rename_file($filename) {

    global $filename_new;

    // 記事ID取得 (クラシックエディタ)
    $post_id = isset( $_POST['post_id'] ) ? (int)$_POST['post_id'] : ''; 
    // 記事ID取得 (ブロックエディタ)
    if( !$post_id ){
        $post_id = isset( $_POST['post'] ) ? (int)$_POST['post'] : ''; 
    }

    // ファイル拡張子を取得
    $ext = pathinfo( $filename, PATHINFO_EXTENSION );   

    // 記事IDをファイル名とする
    if( $post_id ){
        $filename_new = $post_id;

    // メディアライブラリへ直接アップロードした場合のファイル名(日付時刻)
    }else{
        $filename_new = date_i18n('ymdHi');
    }
    //ファイル名+拡張子を出力
    return $filename_new.'.'.$ext;
}

add_filter('sanitize_file_name', 'rename_file', 10);

参考:ねんでぶろぐ

日本語名の画像ファイルアップロード時、ファイル名を英数字に変更する(ブロックエディタ対応)
https://nendeb.com/815

クラシックエディタを使った場合、ファイル名の変更はされますが、メディアタイトルとして元のファイル名が残ってしまうようです。
対策として、以下のコードを追加してタイトルの設定をします。

funtions.php
function rename_media_title($response, $attachment, $meta){
    global $filename_new;
    //変更後のファイル名をメディアタイトルに使用
    $response['title'] = $filename_new;
    return $response;
}
add_filter('wp_prepare_attachment_for_js', 'rename_media_title', 10, 3);

参考:わんどのweb

WordPressで画像のtitle属性の初期値を変更する | わんどのweb
https://wandonoweb.com/wordpress%E3%81%A7%E7%94%BB%E5%83%8F%E3%81%AEtitle%E5%B1%9E%E6%80%A7%E3%81%AE%E5%88%9D%E6%9C%9F%E5%80%A4%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B/)様

これでブロックエディタ・クラシックエディタともに、画像ファイル名・メディアタイトルが自動で変更されます。

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

[CodeIgniter]バリデーションのエラーメッセージの前後に付与する文字列を変更する方法

はじめに

バリデーションのエラーメッセージにデフォルトで<p>タグが付与されます。

if ( $this->form_validation->run() )
{
    $data['error_message'] = $this>form_validation->error_string();
    $this->load->view('purchase_form', $data);
}

echo $data['error_message'];
/*
<p>名前 は必須項目です。</p>
<p>数量 は数値を入力してください。</p>
*/

今回、<p>タグを付与せず指定の文字列に変更する方法をまとめました。

エラーメッセージの変更

set_error_delimiters()メソッドを使用します。
第一引数、第二引数でエラーメッセージの前後に付与する文字列を指定します。

$this->form_validation->set_error_delimiters('<div class="err">', '</div>');
$error_message = $this>form_validation->error_string();
echo $error_message;
/*
<div class="err">名前 は必須項目です。</div>
<div class="err">数量 は数値を入力してください。</div>
*/

参考

CodeIgniter Guide

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

【PHP】リファレンスの使用(≒参照渡し)をせずに配列の中身をイジろう

はじめに

ちょっと前に書いた
「【PHP】全ボクが実務で泣いた「値渡し」と「参照渡し」を完!全!理!解」
で連想配列の中身をいじる時に参照渡しリファレンスの使用を使いました。

(前の記事でコメントをいただいたのですが、正確には参照渡しではなくリファレンスの使用と言うらしいです)

※その違いについては上記の記事のコメント欄をチェックしてください。

が、前述の方法では後続の処理が上手くいきませんでした。

そんな時に先輩にこの記事でご紹介する方法を教えてもらったのでまとめます。

しかも実用的:sunglasses:

この記事のオススメ読者

  • 実務に入ったばかりの方
  • これから実務に入る予定の方

3分もあれば全部読めるのでサクッと読んでみてください:wink:

リファレンスの使用(≒参照渡し)をせずに配列の中身をこねくり回す方法

上記の記事と同じコードを使います。

<?php

// 配列を2つ定義
$members = [
    'Aさんの好きな果物' => [
        'りんご' => 'apple',
        'ぶどう' => 'grape',
        'バナナ' => 'banana',
    ],
    'Bさんの好きな果物' => [
        'マンゴー' => 'mango',
        'パイナップル' => 'pineapple',
        'ドラゴンフルーツ' => 'dragon fruit',
    ],
];
$fruits = [
    'ざくろ' => 'pomegranate',
    'オレンジ' => 'orange',
    'もも' => 'peach',
];

// $membersに$fruitsの要素を追加
foreach ($members as &$member) {
    $member['ざくろ'] = $fruits['ざくろ'];
    $member['もも'] = $fruits['もも'];
}

// 配列を出力
var_dump($members);

foreachの中の

&$member

これが参照渡しリファレンス渡しを表す書き方。

で、これを使わずに欲しい内容を出力する方法はこちら!ドン!

<?php

// 配列を2つ定義
$members = [
    'Aさんの好きな果物' => [
        'りんご' => 'apple',
        'ぶどう' => 'grape',
        'バナナ' => 'banana',
    ],
    'Bさんの好きな果物' => [
        'マンゴー' => 'mango',
        'パイナップル' => 'pineapple',
        'ドラゴンフルーツ' => 'dragon fruit',
    ],
];
$fruits = [
    'ざくろ' => 'pomegranate',
    'オレンジ' => 'orange',
    'もも' => 'peach',
];

// $membersに$fruitsの要素を追加
foreach ($members as $index => $member) {
    $members[$index]['ざくろ'] = $fruits['ざくろ'];
    $members[$index]['もも'] = $fruits['もも'];
}

// 出力
var_dump($members);

出力結果

array(2) {
  ["Aさんの好きな果物"]=>
  array(5) {
    ["りんご"]=>
    string(5) "apple"
    ["ぶどう"]=>
    string(5) "grape"
    ["バナナ"]=>
    string(6) "banana"
    ["ざくろ"]=>
    string(11) "pomegranate"
    ["もも"]=>
    string(5) "peach"
  }
  ["Bさんの好きな果物"]=>
  array(5) {
    ["マンゴー"]=>
    string(5) "mango"
    ["パイナップル"]=>
    string(9) "pineapple"
    ["ドラゴンフルーツ"]=>
    string(12) "dragon fruit"
    ["ざくろ"]=>
    string(11) "pomegranate"
    ["もも"]=>
    string(5) "peach"
  }
}

ちゃんと各配列に要素が2つ追加されてます!!

変更したのは

foreach ($members as &$member) {
    $member['ざくろ'] = $fruits['ざくろ'];
    $member['もも'] = $fruits['もも'];
}

ここを

foreach ($members as $index => $member) {
    $members[$index]['ざくろ'] = $fruits['ざくろ'];
    $members[$index]['もも'] = $fruits['もも'];
}

こう変えました。

コードの説明(簡単に)

foreach ($members as $index => $member) {
    $members[$index]['ざくろ'] = $fruits['ざくろ'];
    $members[$index]['もも'] = $fruits['もも'];
}

まず、foreach ($members as $index => $member)で、1つ目の連想配列については

  • $index:'Aさんの好きな果物'
  • $members:
[
    'りんご' => 'apple',
    'ぶどう' => 'grape',
    'バナナ' => 'banana',
]

です。

なので $members[$index]['ざくろ'] = $fruits['ざくろ'];とすることにより、

$members['Aさんの好きな果物']['ざくろ'] = 'pomegranate';

となります。

つまり、元の連想配列membersに要素を追加することができているわけですね。
$members[$index]['もも'] = $fruits['もも'];も同様なので割愛。

このようにすると、リファレンスの使用を使わなくてもforeachを使っても配列の中身をイジることが可能です。

(面白いことに$memberは使わないんですよね)

おわり

この方法、結構使えます!実務でも結構使ってます!オススメです!

連想配列の扱い方が上手くなれば実務での開発スピードは結構上がるなと日々痛感してます。
(もちろん他のスキルもたくさんいるけどw)

もし内容を気に入っていただけたらLGTMをお願いします。

最後まで読んでいただきありがとうございました:bow:

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

【PHP】超実用的!リファレンスの使用(≒参照渡し)をせずに配列の中身をイジろう

はじめに

ちょっと前に書いた
「【PHP】全ボクが実務で泣いた「値渡し」と「参照渡し」を完!全!理!解」
で連想配列の中身をいじる時に参照渡しリファレンスの使用を使いました。

(前の記事でコメントをいただいたのですが、正確には参照渡しではなくリファレンスの使用と言うらしいです)

※その違いについては上記の記事のコメント欄をチェックしてください。

が、前述の方法では後続の処理が上手くいきませんでした。

そんな時に先輩にこの記事でご紹介する方法を教えてもらったのでまとめます。

しかも実用的:sunglasses:

この記事のオススメ読者

  • 実務に入ったばかりの方
  • これから実務に入る予定の方

3分もあれば全部読めるのでサクッと読んでみてください:wink:

リファレンスの使用(≒参照渡し)をせずに配列の中身をこねくり回す方法

上記の記事と同じコードを使います。

<?php

// 配列を2つ定義
$members = [
    'Aさんの好きな果物' => [
        'りんご' => 'apple',
        'ぶどう' => 'grape',
        'バナナ' => 'banana',
    ],
    'Bさんの好きな果物' => [
        'マンゴー' => 'mango',
        'パイナップル' => 'pineapple',
        'ドラゴンフルーツ' => 'dragon fruit',
    ],
];
$fruits = [
    'ざくろ' => 'pomegranate',
    'オレンジ' => 'orange',
    'もも' => 'peach',
];

// $membersに$fruitsの要素を追加
foreach ($members as &$member) {
    $member['ざくろ'] = $fruits['ざくろ'];
    $member['もも'] = $fruits['もも'];
}

// 配列を出力
var_dump($members);

foreachの中の

&$member

これが参照渡しリファレンス渡しを表す書き方。

で、これを使わずに欲しい内容を出力する方法はこちら!ドン!

<?php

// 配列を2つ定義
$members = [
    'Aさんの好きな果物' => [
        'りんご' => 'apple',
        'ぶどう' => 'grape',
        'バナナ' => 'banana',
    ],
    'Bさんの好きな果物' => [
        'マンゴー' => 'mango',
        'パイナップル' => 'pineapple',
        'ドラゴンフルーツ' => 'dragon fruit',
    ],
];
$fruits = [
    'ざくろ' => 'pomegranate',
    'オレンジ' => 'orange',
    'もも' => 'peach',
];

// $membersに$fruitsの要素を追加
foreach ($members as $index => $member) {
    $members[$index]['ざくろ'] = $fruits['ざくろ'];
    $members[$index]['もも'] = $fruits['もも'];
}

// 出力
var_dump($members);

出力結果

array(2) {
  ["Aさんの好きな果物"]=>
  array(5) {
    ["りんご"]=>
    string(5) "apple"
    ["ぶどう"]=>
    string(5) "grape"
    ["バナナ"]=>
    string(6) "banana"
    ["ざくろ"]=>
    string(11) "pomegranate"
    ["もも"]=>
    string(5) "peach"
  }
  ["Bさんの好きな果物"]=>
  array(5) {
    ["マンゴー"]=>
    string(5) "mango"
    ["パイナップル"]=>
    string(9) "pineapple"
    ["ドラゴンフルーツ"]=>
    string(12) "dragon fruit"
    ["ざくろ"]=>
    string(11) "pomegranate"
    ["もも"]=>
    string(5) "peach"
  }
}

ちゃんと各配列に要素が2つ追加されてます!!

変更したのは

foreach ($members as &$member) {
    $member['ざくろ'] = $fruits['ざくろ'];
    $member['もも'] = $fruits['もも'];
}

ここを

foreach ($members as $index => $member) {
    $members[$index]['ざくろ'] = $fruits['ざくろ'];
    $members[$index]['もも'] = $fruits['もも'];
}

こう変えました。

コードの説明(簡単に)

foreach ($members as $index => $member) {
    $members[$index]['ざくろ'] = $fruits['ざくろ'];
    $members[$index]['もも'] = $fruits['もも'];
}

まず、foreach ($members as $index => $member)で、1つ目の連想配列については

  • $index:'Aさんの好きな果物'
  • $members:
[
    'りんご' => 'apple',
    'ぶどう' => 'grape',
    'バナナ' => 'banana',
]

です。

なので $members[$index]['ざくろ'] = $fruits['ざくろ'];とすることにより、

$members['Aさんの好きな果物']['ざくろ'] = 'pomegranate';

となります。

つまり、元の連想配列membersに要素を追加することができているわけですね。
$members[$index]['もも'] = $fruits['もも'];も同様なので割愛。

このようにすると、リファレンスの使用を使わなくてもforeachを使っても配列の中身をイジることが可能です。

(面白いことに$memberは使わないんですよね)

おわり

この方法、結構使えます!実務でも結構使ってます!オススメです!

連想配列の扱い方が上手くなれば実務での開発スピードは結構上がるなと日々痛感してます。
(もちろん他のスキルもたくさんいるけどw)

もし内容を気に入っていただけたらLGTMをお願いします。

最後まで読んでいただきありがとうございました:bow:

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

Moodle 3.9 マニュアル - カテゴリ:クイックガイド

原文

カテゴリ:クイックガイド

クイックガイドのインデックス。

カテゴリー「クイックガイド」のページ
全15ページのうち、以下の15ページがこのカテゴリに属しています。

A
Admin quick guide
Attendance quick guide
B
BigBlueButtonBN quick guide
C
Checklist quick guide
E
E-examination quick guide
G
Grading quick guide
Group choice quick guide
I
Installation quick guide
L
Learning analytics quick guide
Level up! quick guide
Q
Quizventure quick guide
T
Teacher quick guide
U
User quick guide
W
Word count quick guide
Workshop quick guide

メインページ

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

PHPで配列をCSV出力する

きっかけ

業務でDBのデータをcsv出力したいという要件から実装
これからも必要になってくると思ったのでまとめました。

今回の内容

csvとして出力したデータをボタンを押すとダウンロードする

こんな感じ
csv-test.gif

phpでcsv出力する際、fputcsv関数SplFileObjectクラスを使用する方法があるみたいですが、今回はfputcsv関数を用いました。
この関数やクラスを使わなくても実現は可能

csvとは?

「CSV」とは "Comma Separated Value" の略で、データをカンマ(" , ")区切った値の事です。アプリケーション間でデータをやり取りする際に使われます。CSV形式で保存されたファイルを「CSVファイル」と呼びます。

Excelとの違いはExcelは文字に色とか罫線とかデータに装飾可能ですが、csvはただテキストで構成されているデータみたいです。
余分な情報がないのでアプリケーション間でテキストデータのやり取りが可能になります。

いざ実装

  • 出力したいデータ
$user = array(
    array(
        'id' => 1,
        'name' => 'Aさん',
        'email' => 'aaa@a.com',
        'password' => 'aaaaa'
    ),
    array(
        'id' => 2,
        'name' => 'Bさん',
        'email' => 'bbb@b.com',
        'password' => 'bbbbb'
    ),
    array(
        'id' => 3,
        'name' => 'Cさん',
        'email' => 'ccc@c.com',
        'password' => 'ccccc'
    ),
);

※こういうデータをDBから取ってきて配列に入れる。


  • ダウンロードボタン表示
<h1>Hello World!<h1>
<a href="./csv.php">
    <button>csvダウンロード</button>
</a>

スクリーンショット 2020-10-25 11.31.17.png


  • csv出力andダウンロード処理
$user =[配列]

function putCsv($data) {

    try {

        //CSV形式で情報をファイルに出力のための準備
        $csvFileName = '/tmp/' . time() . rand() . '.csv';
        $fileName = time() . rand() . '.csv';
        $res = fopen($csvFileName, 'w');
        if ($res === FALSE) {
            throw new Exception('ファイルの書き込みに失敗しました。');
        }

        // 項目名先に出力
        $header = ["id", "name", "email", "password"];
        fputcsv($res, $header);

        // ループしながら出力
        foreach($data as $dataInfo) {
            // 文字コード変換。エクセルで開けるようにする
            mb_convert_variables('SJIS', 'UTF-8', $dataInfo);

            // ファイルに書き出しをする
            fputcsv($res, $dataInfo);
        }

        // ファイルを閉じる
        fclose($res);

        // ダウンロード開始

        // ファイルタイプ(csv)
        header('Content-Type: application/octet-stream');

        // ファイル名
        header('Content-Disposition: attachment; filename=' . $fileName); 
        // ファイルのサイズ ダウンロードの進捗状況が表示
        header('Content-Length: ' . filesize($csvFileName)); 
        header('Content-Transfer-Encoding: binary');
        // ファイルを出力する
        readfile($csvFileName);

    } catch(Exception $e) {

        // 例外処理をここに書きます
        echo $e->getMessage();

    }
}

putCsv($user);

※追記(@rana_kualu さんのコメントを受けて)

上記の方法では出力するために作った$csvFileNameファイルが/tmp/に残ったままになっています。

  • ファイルを出力した後にunlinkでファイルを削除する。
  • tmpfileで自動的に一時ファイルを削除する。
  • fopenfilenamephp://outputを指定して一時ファイルを作らずcsv出力する。

一時ファイル


■fopen関数で出力するファイルを指定して開く
第2引数にwを指定して書き込みモードにする。

■fputcsv関数で上記で開いたファイルにcsvを出力していく
foreachで回す前にヘッダーとなる部分を出力する。
その後にforeachで配列を回して出力する。

■ダウンロードのためにHTTPヘッダーを設定する
Content-Type以下でphpの出力形式を指定する。
今回はcsvファイルとして出力する指定をしているので、ブラウザで閲覧できるページとしては出力されない。

出力結果

スクリーンショット 2020-10-25 11.41.24.png
mb_convert_variables('SJIS', 'UTF-8', $dataInfo);で文字コードを変換しているので日本語部分の「さん」がちゃんと表示されている。
これがないとExcelで開いたときに文字化けしてしまう。

以上です

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

kubernetes上にLNMP環境を構築した際のメモ

前回の記事にてminikube環境を構築した。

その後、Kubernetesのチュートリアルを一通りこなした後、勉学のためLNMPスタックの構築を実施した際のメモ。

実行環境

前回記事と同様のため省略

構築する環境外観

こちらの記事(Docker × PHP7.3 × Laravel環境作ってみた)の環境構築手順を参考とさせていただき、最終的にこんな感じの構成を目指す。

なお、本記事ではLaravelの構築については触れない。

構築するKubernetesオブジェクトについてのおさらい

ここで、構築にて使用するkubernetesオブジェクトについて簡単に説明する。

Pod

Kubernetesの中で最小のオブジェクト。

アプリケーションを実行する際の項目 (コンテナ、ストレージ、ネットワークIP、実行オプション) をカプセル化したもの。

今回は一つのPodにつき、一つのコンテナイメージが実行されるようにする。

Node

Podが動作する環境。複数のPodを動作させることが可能。

今回は特に操作しない。

Namespace

クラスタの仮想的な分割機能。

今回は特に操作せず、defaultを使用。

Deployment

デプロイするアプリケーション (Pod) について、レプリケーション数、ローリングアップデートなどの制御を実施する。

Service

実行されているPodへのアクセス経路を提供する。

PodのIPアドレスは一意でないため、アクセスにはService名の指定が必要となる。

また、Podへのアクセス方式にはいくつか種類があり、今回は以下を使用する。

  • ClusterIP: 外部疎通性のないアクセス方式。クラスタの内部IPを作成する。
  • NodePort: 外部疎通性のあるアクセス方式。Nodeの指定ポートへのアクセスをコンテナに転送する。

ConfigMap

アプリケーションの設定データを管理する。

あとで更新が必要などの理由から、コンテナイメージに内包したくない設定については、こちらで持つようにする。

PersistentVolume (PV)

アプリケーションが使用できる、独立した永続化ストレージ。

PersistentVolumeClaim (PVC)

アプリケーションがPVを利用する際に経由される。

環境の構築作業

以下の手順で構築した。

nginx環境の構築

まず、PV, PVC, ConfigMapの構築から行う。こちらを行わずDeploymentの構築を実施すると失敗となった。

ConfigMapの作成

今回、nginx-phpポッド間の通信をservice経由とするので、nginxのdefault.confをコンテナに内包させずConfigMapとする。

default.confを以下のように記載。この際、phpポッドへのアクセスにservice名とPort:9000を指定する。

server {
  listen 80;

  root  /var/www/public/;
  index index.php;

  location / {
    try_files $uri $uri/ /index.php$is_args$args;
  }

  location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    # php serviceに送信する設定にする。
    fastcgi_pass   lnmp-php-service:9000;
    fastcgi_index  index.php;

    include        fastcgi_params;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param  PATH_INFO $fastcgi_path_info;
  }
}
$ kubectl create configmap nginx-config --from-file={ホスト上のnginx.confの配置されているパス}

PV, PVCの作成

続いてPV, PVCの構築を行う。

今回、nginxポッドがphpアプリケーションを公開するため、ストレージはphpポッドと共有する。

ということでマニュフェストファイルを、lnmp-php-server-persistentvolumeclaim.yamlとして作成。

# PV
kind: PersistentVolume
apiVersion: v1
metadata:
  name: php-server-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    # とりあえず100Miくらいの容量で試す
    storage: 100Mi
  # ホストないに作成したストレージへ、Kubernetes nodeがアクセスする際の制約事項
  # 今回は、単一のnodeのみに書き込み/読み込みを可能とする設定とした
  accessModes:
    - ReadWriteOnce
  hostPath:
    # ホストマシンのどこにストレージを割り当てるか
    path: '/root/src/lnmp/server'
---
# PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: php-server-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Mi

コマンドを実行してpv, pvcを作成。

$ kubectl apply -f lnmp-php-server-persistentvolumeclaim.yaml

以下のkubectlコマンドから、作成されていることが確認できた。

$ kubectl get pv
NAME                   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS   REASON   AGE
php-server-pv-volume   100Mi      RWO            Retain           Bound    default/php-server-pv-claim   manual                  5d20h
$ kubectl get pvc
NAME                  STATUS   VOLUME                 CAPACITY   ACCESS MODES   STORAGECLASS   AGE
php-server-pv-claim   Bound    php-server-pv-volume   100Mi      RWO            manual         5d20h

deploymentを作成

次に、deploymentを作成する。

マニュフェストファイルを、lnmp-nginx-deployment.yamlとして作成。

# Deployment設定
apiVersion: apps/v1
kind: Deployment
metadata:
  name: lnmp-nginx-deployment
  labels:
    app: lnmp
spec:
  # レプリケーション設定、今回は1台のみの構成とする
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      # 配置するnginxコンテナ
      containers:
      - name: lnmp-nginx-deployment
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-persistent-storage
          mountPath: /var/www
        # default.conf共有パス
        - name: nginx-config
          # default.confの配置先
          mountPath: /etc/nginx/conf.d
      volumes:
        # nginxの永続化ボリューム
        - name: nginx-persistent-storage
          persistentVolumeClaim:
            claimName: php-server-pv-claim
        # nginx設定のinject
        - name: nginx-config
          configMap:
            name: nginx-config
            items:
            - key: default.conf
              # マウントしたボリュームのmountPath配下に、どういう階層で登録されるかを記載
              path: default.conf
              mode: 0777

serviceを作成

つづいて、serviceを作成する。

マニュフェストファイルを、lnmp-nginx-service.yamlとして作成。

apiVersion: v1
kind: Service
metadata:
  name: lnmp-nginx-service
spec:
  selector:
    app: nginx
  type: NodePort
  ports:
  - port: 80
    targetPort: 80

以下コマンドを実行し、デプロイを実施。

$ kubectl apply -f lnmp-nginx-deployment.yaml
$ kubectl apply -f lnmp-service-deployment.yaml

これでnginxの構築は完了。この時点でnginxサーバーのポートは確認できるけど、index.phpを配置していないので502エラーになる。

mysql環境の構築

こちらも引き続き構築していく。

ConfigMapのデプロイ

mysql設定ファイル、my.cnfをConfigMapとする。なお、これは元記事のものをそのまま使用する。

$ kubectl create configmap mysql-config --from-file={ホスト上のmy.cnfの配置されているパス}

PV, PVCの作成

元記事同様、mysqlのデータディレクトリをマウントするPV, PVCを作成。

kind: PersistentVolume
apiVersion: v1
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    # とりあえず5Gでマウントする
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    # プルパスで記載しないとエラーになった
    path: '/root/src/lnmp/mysql/db/data'
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

deploymentを作成

nginxの場合と同様、以下のマニュフェストファイルを作成し、デプロイ。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: lnmp-mysql-deployment
  labels:
    app: lnmp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: lnmp
  template:
    metadata:
      labels:
        app: lnmp
    spec:
      containers:
      - name: lnmp-mysql-deployment
        image: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: root
        - name: MYSQL_DATABASE
          value: test_db
        - name: TZ
          value: 'Asia/Tokyo'
        ports:
        - containerPort: 3306
        args:
        - mysqld
        - --character-set-server=utf8mb4
        - --collation-server=utf8mb4_unicode_ci
        volumeMounts:
        # mysqlストレージ用パス
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
        # my.cnf共有パス
        - name: mysql-config
          # my.cnfの配置先
          mountPath: /etc/mysql/conf.d
      volumes:
      # mysqlの永続化ボリューム
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
      # mysql設定のinject
      - name: mysql-config
        configMap:
          name: mysql-config
          items:
          - key: my.cnf
            # マウントしたボリュームのmountPath配下に、どういう階層で登録されるかを記載
            path: my.cnf
            mode: 0644

serviceを作成

こちらもnginxの場合と同様のマニュフェストファイルを作成しデプロイする。

ただし、mysqlへの通信はクラスタ内ポッド間でのみ行われるので、Service.spec.typeNodePortではなくClusterIPとしている。

apiVersion: v1
kind: Service
metadata:
  name: lnmp-mysql-service
spec:
  selector:
    app: lnmp
  type: ClusterIP
  ports:
  - port: 3306
    targetPort: 3306

php環境の構築

deploymentを作成

phpコンテナをデプロイする。

デプロイするphpイメージは元記事のDockerfileから作成する。

コンテナイメージを毎回dockerhubから取得しようとしてエラーとなっていたため、コンテナ取得タイミングをIfNotPresentとした。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: lnmp-php-deployment
  labels:
    app: lnmp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: lnmp
  template:
    metadata:
      labels:
        app: lnmp
    spec:
      containers:
      - name: lnmp-php-deployment
        image: laravel
        # なんかdockerhubから取ろうとしてた
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9000
        volumeMounts:
          - name: php-persistent-storage
            mountPath: /var/www
      volumes:
      # サーバーログ出力先(nginxと共有)
      - name: php-persistent-storage
        persistentVolumeClaim:
          claimName: php-server-pv-claim

また、PV, PVCはnginxポッドと共有する。

serviceを作成

nginxポッドがアクセスするサービスを作成する。

こちらもクラスタ内ポッド間でのみの通信となるため、Service.spec.typeClusterIPとしている。

apiVersion: v1
kind: Service
metadata:
  name: lnmp-php-service
spec:
  selector:
    app: lnmp
  type: ClusterIP
  ports:
  - port: 9000
    protocol: TCP

php環境の動作確認

この段階でLNMP環境にアクセスできることを確認する。

nginxのdefault.confに従い、phpポッドの/var/www/public/に、index.phpを配置してみる。

nginx, phpポッドに割り当てた/root/src/lnmp/serverに以下の内容のindex.phpを配置する。

<h1>kubenetes-php</h1>
<p>Served by Nginx</p>
<?php phpinfo();?>

この状態でnginxサーバーにアクセスしてみる。

$ kubectl get services/lnmp-nginx-service
NAME                 TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
lnmp-nginx-service   NodePort   10.98.216.9   <none>        80:32626/TCP   11d```

Podのアクセス先が32626ポートだと確認できた。

http://localhost:32626へブラウザからアクセスする。

phpの情報が出た!!

元記事ではLaravelアプリケーションの構築までを実施していたが、長くなってしまうので一旦ここまで。

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

PHP初心者もできる else句を使わない読みやすいコード ※追記

業務でネストの深いIF文を見ると、追うのが大変でうんざりすることがあります。
大体の場合、ちょっとした修正のために付け加えていって肥大化したパターンです。
これを予め防ぐ、簡単な工夫を一例として示します。

ちなみに、未経験でエンジニア求人に応募するときも、
実際にこのような記法を知っていれば、保守性について話せてポイントが高いと思います。

早期リターン&ガード節

以下は与えられた値の型を判断するだけのコードです。

$value = [1,2,3];

function judgeType($value) :string //タイプヒンティング;この関数がstringを返すことを明示
    {
        if (is_string($value)) {
            $result = $value . ' is string.';
        } else if (is_int($value)) {
            $result = $value . ' is integer.';
        } else {
            $result = $value . ' is neither string nor integer.';
        }
        return $result;
    }

echo judgeType($value); // Given value is neither string nor integer.

この程度の量なら読めなくはないですが、無駄があります。
これを下記のように書き換えることが出来ます、

$value = [1,2,3];

function judgeType($value) :string //タイプヒンティング;この関数がstringを返すことを明示
    {
        if (is_string($value)) {
            return  $value . ' is string.';
        } 

        if (is_int($value)) {
            return $value . ' is integer.';
        } 

        return $value . ' is neither string nor integer.';
    }

echo judgeType($value); // Given value is neither string nor integer.

スッキリしましたね。
ポイントを整理すると

  • $resultというローカル変数が不要。
  • 各IF句にreturnがあるので、条件に当てはまった場合関数は戻り値を返して終了する。
  • elseがなくても2つのIF句がスキップされ最終行のreturnが実行される。

このようにローカル変数を置かず即時にreturnを置くことを早期リターンと呼びます。
また、else句を使わず早期リターンする方法をガード節といいます。

やっていることは単純なので、ぜひガード節を用いて保守性の高いコードを書くことを意識してみてください。

追記:2020年10月26日

コメントより、括弧を取る書き方もあるので追記します。

function judgeType($value) :string //タイプヒンティング;この関数がstringを返すことを明示
  {
    if (is_string($value)) return $value . 'is string.';
    if (is_int($value)) return $value . 'is integer.';
    return $value . 'is neither string nor integer.';
  }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP else句を使わないで読みやすく

業務でネストの深いIF文を見ると、追うのが大変でうんざりすることがあります。
大体の場合、ちょっとした修正のために付け加えていって肥大化したパターンです。
これを予め防ぐ、簡単な工夫を一例として示します。

ちなみに、未経験でエンジニア求人に応募するときも、
実際にこのような記法を知っていれば、保守性について話せてポイントが高いと思います。

早期リターン&ガード節

以下は与えられた値の型を判断するだけのコードです。

$value = [1,2,3];

function judgeType($value) :string //タイプヒンティング;この関数がstringを返すことを明示
    {
        if (is_string($value)) {
            $result = $value . ' is string.';
        } else if (is_int($value)) {
            $result = $value . ' is integer.';
        } else {
            $result = $value . ' is neither string nor integer.';
        }
        return $result;
    }

    echo judgeInput($value); // Given value is neither string nor integer.

この程度の量なら読めなくはないですが、無駄があります。
これを下記のように書き換えることが出来ます、

$value = [1,2,3];

function judgeType($value) :string //タイプヒンティング;この関数がstringを返すことを明示
    {
        if (is_string($value)) {
            return  $value . ' is string.';
        } 

        if (is_int($value)) {
            return $value . ' is integer.';
        } 

            return $value . ' is neither string nor integer.';
    }


    echo judgeInput($value); // Given value is neither string nor integer.

スッキリしましたね。
ポイントを整理すると

  • $resultというローカル変数が不要。
  • 各IF句にreturnがあるので、条件に当てはまった場合関数は戻り値を返して終了する。
  • elseがなくても2つのIF句がスキップされ最終行のreturnが実行される。

このようにローカル変数を置かず即時にreturnを置くことを早期リターンと呼びます。
また、else句を使わず早期リターンする方法をガード節といいます。

やっていることは単純なので、ぜひガード節を用いて保守性の高いコードを書くことを意識してみてください。

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

PHP初心者もできる else句を使わない読みやすいコード

業務でネストの深いIF文を見ると、追うのが大変でうんざりすることがあります。
大体の場合、ちょっとした修正のために付け加えていって肥大化したパターンです。
これを予め防ぐ、簡単な工夫を一例として示します。

ちなみに、未経験でエンジニア求人に応募するときも、
実際にこのような記法を知っていれば、保守性について話せてポイントが高いと思います。

早期リターン&ガード節

以下は与えられた値の型を判断するだけのコードです。

$value = [1,2,3];

function judgeType($value) :string //タイプヒンティング;この関数がstringを返すことを明示
    {
        if (is_string($value)) {
            $result = $value . ' is string.';
        } else if (is_int($value)) {
            $result = $value . ' is integer.';
        } else {
            $result = $value . ' is neither string nor integer.';
        }
        return $result;
    }

echo judgeType($value); // Given value is neither string nor integer.

この程度の量なら読めなくはないですが、無駄があります。
これを下記のように書き換えることが出来ます、

$value = [1,2,3];

function judgeType($value) :string //タイプヒンティング;この関数がstringを返すことを明示
    {
        if (is_string($value)) {
            return  $value . ' is string.';
        } 

        if (is_int($value)) {
            return $value . ' is integer.';
        } 

        return $value . ' is neither string nor integer.';
    }

echo judgeType($value); // Given value is neither string nor integer.

スッキリしましたね。
ポイントを整理すると

  • $resultというローカル変数が不要。
  • 各IF句にreturnがあるので、条件に当てはまった場合関数は戻り値を返して終了する。
  • elseがなくても2つのIF句がスキップされ最終行のreturnが実行される。

このようにローカル変数を置かず即時にreturnを置くことを早期リターンと呼びます。
また、else句を使わず早期リターンする方法をガード節といいます。

やっていることは単純なので、ぜひガード節を用いて保守性の高いコードを書くことを意識してみてください。

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

Laravel8 Auth0のRoute/web.php

Route/web.php

Laravelのバージョンが8になってRouteの書き方変わったので、Auth0のRouteの書き方も当然変わった。
公式には過去のバージョンの書き方しかないっぽいので下記の通り書けば動く。

use App\Http\Controllers\Auth\Auth0IndexController;
use Auth0\Login\Auth0Controller;

Route::get( '/auth0/callback', [Auth0Controller::class, 'callback'])->name( 'auth0-callback' );
Route::get('/login', [Auth0IndexController::class, 'login'] )->name( 'login' );
Route::get('/logout', [Auth0IndexController::class, 'logout'] )->name( 'logout' );

にしてもLaravel8になってmoodelの場所も変わったし、Routeの書き方も変わったし回収大変かもね・・・。

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

【PHP】クラスの private property・method に読み書き・実行する方法

概要

諸事情によりクラスのprivateなproperty、Methodを触る必要がありました。
その時のメモです。

ReflectionClass

ReflectionClass
ReflectionClass を使用することで指定されたクラスのことを調べることが可能です。

サンプルコード

実際に ReflectionClass を使用してprivateなproperty、Methodに読み書き、使用してみる。

<?php
class SampleClass
{
    /**
     * @var string プライベートプロパティ
     */
    private $text = 'text';

    /**
     * プライベート変数を出力
     */
    public function textOutput()
    {
        echo $this->text.PHP_EOL;
    }

    /**
     * !で囲う
     * @param @string $str 文字列
     * @return string
     */
    private function output(string $str):string
    {
        return "!{$str}!";
    }

    /**
     * num1とnum2を足す
     * @param int num1 足す数値
     * @param int num2 足す数値
     * @return int
     */
    private function sum(int $num1, int $num2):int
    {
        return $num1+$num2;
    }
}

$sample = new SampleClass();
$refrection = new ReflectionClass(get_class($sample));

// プライベートプロパティ text の内容を出力
echo 'Private Property Before--------------------'.PHP_EOL;
$sample->textOutput(); 

// ------プロパティの読み書き------
// 該当のプロパティを取得
$privateText = $refrection->getProperty('text');

// アクセス権限の取得
$privateText->setAccessible(true);

// 書き込み
$privateText->setValue($sample, "Change Text");

echo PHP_EOL.'Private Property After----------------------'.PHP_EOL;
// 読み込み
echo $privateText->getValue($sample).PHP_EOL;

// プライベートプロパティ text の内容を出力
$sample->textOutput();

echo PHP_EOL.'Private Method------------------------------'.PHP_EOL;

// ------メソッドの実行------
// 該当の関数を取得
$output = $refrection->getMethod('output');

// アクセス権限の取得
$output->setAccessible(true);
// 確認実行
echo $output->invoke($sample, 'Hello,World').PHP_EOL;

// 該当の関数を取得
$sum = $refrection->getMethod('sum');

// アクセス権限の取得
$sum->setAccessible(true);
// 確認実行 変数が複数の時
echo $sum->invoke($sample, 1, 2).PHP_EOL;

↓実行結果

Private Property Before--------------------
text

Private Property After----------------------
Change Text
Change Text

Private Method------------------------------
!Hello,World!
3

通常アクセスした場合

private property

echo $sample->text;

↓実行結果

PHP Fatal error:  Uncaught Error: Cannot access private property SampleClass::$text

private method

echo $sample->output('Hello,World');

↓実行結果

PHP Fatal error:  Uncaught Error: Call to private method SampleClass::output() from context

注意

本来 private になどにされて外部からアクセス出来ないようにされているのは何らかの理由があります。
これはそれを捻じ曲げて行う行為ですので注意してください。
本当にそこをいじるべきなのか、いじったらどんな影響があるかを考えてみてから使用したほうが良いです。

僕は本当にどうしようもなかったので使わざるを得なかったです。

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

PHP: isset、is_null、if($var)、empty 比較一覧表

比較一覧表

値 or 要素 isset is_null empty if($var) !empty
1 $var 未定義
(null)
false error
(true)
true error
(false)
false
2 $var = NULL null false true true false false
3 $var = ""; string true false true false false
4 $var = 0; int true false true false false
5 $var = "0"; string true false true false false
6 $var = 1 int true false false true true
7 $var = "1"; string true false false true true
8 $var = array() array true false true false false
9 $var = array(1) array true false false true true

解説

isset(公式)
empty(公式)
empty($var)!isset($var) || $var == false と同義(公式より)
!empty($var)isset($var) && $var == true と同義。

!isset()をis_nullの代わりに使うほうがセーフティ。
!emptynullチェックif($var)ダブルチェック

まとめ

isset未定義を含む、nullチェック
empty未定義を含む、値がないときのチェック。
if($var)未定義チェックなしの値があるときのチェック。
!empty未定義チェックありの値があるときのチェック。
青字の判定部分かなり微妙で、数値的な0文字列の"0"ないと判定されます。

LGTMお願いします!
ストックのついでにお願いします!
モチベーションがあがります!

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

Mac PhpRedis PHP拡張をインストールする

目的

  • PhpRedisのインストール方法とアンインストール方法をまとめる

実施環境

  • ハードウェア環境
項目 情報
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

概要

  1. インストール
  2. アンインストール

詳細

  1. インストール

    1. 下記コマンドを実行してインストールを実施する。

      $ pecl install redis
      
    2. インストール中に下記を問われるが基本何も入力せずにEnterを押下する。

      enable igbinary serializer support? [no] : 
      enable lzf compression support? [no] : 
      enable zstd compression support? [no] : 
      
    3. 下記の4行が表示されればインストールは完了しPhpRedis PHP拡張が有効になっている。

      Build process completed successfully
      Installing '/usr/local/Cellar/php/7.4.11/pecl/20190902/redis.so'
      install ok: channel://pecl.php.net/redis-5.3.1
      Extension redis enabled in php.ini
      
  2. アンインストール

    1. アンインストールしたいときは下記を実行する。

      $ pecl uninstall redis
      
    2. 下記の二行が出力されればアンインストールは完了したことになる。

      xtension redis disabled in php.ini
      uninstall ok: channel://pecl.php.net/redis-5.3.1
      
    3. 下記コマンドを実行してphp.iniファイルの場所を出力する。

      $ php -r "echo phpinfo();" | grep "php.ini"
      
    4. 下記コマンドを実行してphp.iniファイルのバックアップを作成する。(下記コマンドは筆者の環境でのphp.iniファイルの場所を指しており、皆さんは先のコマンドで出力されたphp.iniのパスを指定してバックアップを作成していただきたい。)

      cp /usr/local/etc/php/7.4/php.ini /usr/local/etc/php/7.4/php.ini_org
      
    5. 下記コマンドを実行してphp.iniファイルを開く。(下記コマンドは筆者の環境でのphp.iniファイルの場所を指しており、皆さんは先のコマンドで出力されたphp.iniのパスを指定してファイルを開いていただきたい。)

      $ vi /usr/local/etc/php/7.4/php.ini
      
    6. ファイルに下記の記載があるはずなのでコメントアウト後に保存する。

      • 修正前

        /usr/local/etc/php/7.4/php.ini
        extension="redis.so"
        
      • 修正後

        /usr/local/etc/php/7.4/php.ini
        ;extension="redis.so"
        
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Moodle 3.9 マニュアル - プロフィールを見る

原文

プロフィールを見る

ユーザーは、右上のユーザーメニューから自分の完全なプロフィールを見ることができます。画面右上の「このページをカスタマイズする」ボタンをクリックして、表示をカスタマイズすることができます。
生徒またはコース教師はナビゲーション>マイコース(コース名)>参加者でユーザーの名前をクリックすることで、ユーザーのコースプロファイルを見ることができます。 教師はコースプロファイルの下部にあるリンク「フルプロファイル」をクリックすることでフルプロファイルにアクセスすることができます(許可されている場合。 下記の「フルプロファイルの表示」を参照)。
管理者は、管理>サイト管理>ユーザー>アカウント>ユーザーの一覧を参照から、さらにフルプロフィールにアクセスして閲覧することができます。

目次

1 全(サイト)プロフィール
2 コースプロフィール
3 完全なプロフィールを見る
4 サイト管理の設定
5 関連項目

1 全(サイト)プロフィール

ユーザーがプロフィールに追加した内容に応じて、最初と最後のアクセス、一般的な興味、個々のコースプロフィールへのリンクなどの情報が表示されます。ユーザは自分のプロフィールの上部にあるボタンからメッセージを送ることができます。

管理者またはCapabilities/moodle/user:viewlastipケイパビリティ:(翻訳準備中)を持つ人は、ユーザが最後にMoodleにアクセスしたIPアドレスへのリンクも表示されます。

2 コースプロファイル

ユーザーのプロフィールはコース内で利用可能で、コース内での役割やグループ、メッセージへのリンクが含まれています。関連する権限を持っている方は、ここから全プロフィールにアクセスすることができます。

3 完全なプロフィールを見る

コースの説明にコース連絡先として記載されているユーザーのフルプロフィールは誰でも見ることができます。

それ以外の場合、デフォルトでは管理者と管理者のみがフルプロフィールを閲覧することができます。

全員、つまりすべての認証済みユーザーがフルプロフィールを閲覧できるようにするには、以下の手順に従います。

  1. 管理 > サイト管理 > ユーザー > アクセス権 > ロールの定義
  2. 認証済みユーザーの編集アイコンをクリックします。
  3. フィルタボックスにmoodle/user:viewdetailsと入力し、この機能を許可するためにチェックボックスにチェックを入れてください。
  4. 変更を保存」ボタンをクリックします。

教師が完全なプロファイルを表示できるようにするには

  1. 管理] > [サイト管理] > [ユーザー] > [権限] > [役割の定義]に進み、[新しい役割の追加]ボタンをクリックします。
  2. ロールに「Full profile viewer」などの名前を与え、ロールが割り当てられるコンテキストタイプとして「System」にチェックを入れます。
  3. moodle/user:viewdetailsをコピーしてフィルタボックスに貼り付けてください。
  4. このロールの作成」ボタンをクリックします。
  5. 管理] > [サイト管理] > [ユーザー] > [アクセス権] > [システムロールの割り当て] に移動し、[フルプロファイルビューア] をクリックします。
  6. 右側の潜在的なユーザーのリストからそれらを選択し、左側のリストに追加することによって、教師に「完全なプロフィールビューア」の役割を割り当てます。

4 サイト管理の設定

管理者は、サイト管理 > ユーザー > アカウント > ユーザーのデフォルト設定から、特定のユーザー プロファイル フィールドのデフォルトを設定することができます。

これらは以下の通りです。

電子メールの表示
電子メール形式
メールダイジェストタイプ
フォーラムの自動配信
フォーラムのトラッキング

5 関連項目

ユーザープロファイル:(翻訳準備中)
プロフィールの編集
ケイパビリティmoodle/user:viewdetails:(翻訳準備中)

カテゴリ | アカウント | サイト管理

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

Moodle 3.9 マニュアル - ユーザープロフィールのフィールド

原文

ユーザープロフィールのフィールド

目次
1 概要
2 共通の設定
3 特定の設定
4 使用例
5 カスタムプロファイルフィールドの翻訳
6 GDPR
7 関連項目

1 概要

管理者は、管理 > サイト管理 > ユーザー > アカウント > ユーザープロファイルフィールドで、新しいユーザープロファイルのカテゴリとフィールドを作成することができます。

プロファイルフィールドは、選択肢のメニュー、テキストエリア、テキスト入力、またはチェックボックスのいずれかであり、必須である場合とそうでない場合があります。

新しいプロファイルフィールドは、"このフィールドは誰に表示されますか?"が "表示しない "に設定されていない限り、各ユーザーのプロファイルページに表示されます。この場合、管理者のみがフィールドを見ることができます。"サインアップページに表示するか?"が "はい "に設定されている場合、フィールドはサインアップページにも表示されます("表示しない "に設定されている場合は表示されません)。

ユーザー プロファイル フィールド ページの上下矢印を使用して、関連するプロファイル カテゴリの下にカスタム プロファイル フィールドが表示される順序を設定できます。

新しいプロファイル フィールドを作成するには、ドロップダウン リストから必要なプロファイル フィールドの形式を選択します。

2 共通の設定

すべての新しいフィールドには、一意の短い名前と名前を付ける必要があります(これはプロフィールページに表示されます)。また、参照用にフィールドの説明を入力することもできます。

また、すべてのカスタム・プロファイル・フィールド・タイプに共通して、多くの設定オプションがあります。

このフィールドは必須ですか?
このフィールドは必須ですか? このオプションは、ユーザー アカウントの必須フィールドかオプションかを指定します。
このフィールドはロックされていますか?
このオプションは、一度このフィールドに情報を入力すると、ユーザーが編集できないようにするかどうかを決定します。
データは一意にする必要がありますか?
フィールドに入力された情報をシステム全体で一意にする必要がある場合(ID番号など)、このオプションに「はい」を選択すると、プロファイル ページの更新で入力されたデータの検証チェックが実行されます。
サインアップ ページに表示しますか?
あなたのMoodleサイトで使用されている認証方法によっては、一部のユーザが自分のアカウントを作成している場合があります。このカスタムフィールドを登録またはサインアップページに表示させたい場合は、「はい」を選択してください。
フィールドは誰に表示されますか?
各カスタムフィールドには3つの可視化設定のうちの1つを設定することができます。
・全員に表示
・見えない
・ユーザーから見える
非表示設定は通常、ユーザーのプライベートデータを保持したい管理者によって設定されます。ユーザーに見える設定は通常、機密情報を保持するフィールドに選択されますが、全員に見える設定はどのようなタイプの情報にも使用できます。

3 特定の設定

また、デフォルト値やサイズに関する設定が必要なフィールドタイプの特定の設定もいくつかあります。

重要な
サイト管理者が .csv ファイルを使用してユーザー データを一括アップロードする場合、新しいプロファイル フィールドを表現するために正しい規約を使用することが不可欠です。規約は profile_field_shortname です。

shortname」を新しいプロファイル・フィールドに使用される実際の短い名前に置き換えます。したがって、フィールドは profile_field_dob のような読み方になるはずです。

また、それを使用してファイルをアップロードしようとする前に、管理 > サイト管理 > ユーザー > アカウント > ユーザープロファイルフィールド > 新しいプロファイルフィールドの作成にアクセスする必要があります。サイト管理で最初にユーザー・プロファイル・フィールドを作成せずに、カスタム・プロファイル・フィールドを使用して最初にアップロード・ファイルを作成すると、profile_field_shortnameは有効なフィールド名ではありませんというエラーが発生します。

4 使用例

・ ハウスシステム
多くの学校では「ハウス」というシステムを採用しており、生徒やスタッフは指定されたハウスに割り当てられています。例えば、ある学校には創立者の名を冠した8つのハウスがあります。Adderton、Coolock、Gorry、Loretto、McAuley、Mercedes、Tighe、Whittyです。サイト管理者は、ユーザーに「選択肢のメニュー」を提供するユーザープロファイルフィールドを追加することができます。デフォルト値を選択することもできます。管理者が.csvファイルでユーザーのための家データを一括アップロードすることを決定すれば、値の上記の配列をAdderton、Coolock、Gorry、Loretto、McAuley、Mercedes、TigheおよびWhittとしてファイルで表現することは重要である。すなわち、ユーザがAdderton Houseに所属している場合、与えられたレコードの「House」ユーザプロファイルフィールドには、値「Adderton」が入力されるべきであることを示す。

・米国の州
これを利用して、(例えば)州を含めることで、曖昧な郵便物の住所を改善することができます。

誕生日ブロック (貢献されたコード)

5 カスタムプロファイルフィールドの翻訳

多言語コンテンツフィルタは、ユーザーに多言語フィールドラベルを提供するために使用することができます。多言語コンテンツフィルタ:(翻訳準備中)がアクティブで、コンテンツと見出しに適用されていることを確認してください。

6 GDPR

GDPRはGeneral Data Protection Regulation(一般データ保護規則)の略で、欧州連合内のすべての個人のデータ保護に関する欧州連合の規則を指します。管理者のためのGDPR:(翻訳準備中)を参照してください。

7 関連項目

dev:ユーザープロファイルフィールド:(翻訳準備中) 開発者向けドキュメント

カテゴリ | アカウント | サイト管理

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

Moodle 3.9 マニュアル - ユーザーの写真

原文

ユーザーの写真

目次

1 すべてのユーザーの写真を見る
2 ユーザー画像の更新
3 ユーザーの写真を一括でアップロードする
4 グラビア
5 アバターを作るためのツール
6 関連項目

1 すべてのユーザーの写真を見る

管理者は、管理者としてログインしているときにブラウザhttp://www.YOURMOODLESITE.com/userpix と入力することで、ユーザーの写真を簡単にチェックすることができます。

2 ユーザー画像の更新

ユーザーは、ユーザーメニューからプロフィールを編集することで、プロフィール画像を更新することができます。(サイト管理者は、サイト管理 > セキュリティ > サイトセキュリティ設定 > ユーザーのプロフィール画像を無効にするで、ユーザーのプロフィール画像を変更する機能を無効にすることができます)

ユーザーの写真は通常、名前の横に表示されます。例えば、フォーラムや参加者リストに投稿した内容の横に表示されます。

Moodleサイトで使用されるデフォルト画像があります。サイト管理者は、テーマフォルダ/theme/themename/pix_core/u/またはコアフォルダ/pix/u/にあるF1およびF2画像を置き換えることで、サイトのデフォルト画像を変更することができます。画像の変更を確認するには、ブラウザの更新[Ctrl + F5]を忘れないでください!

・削除ボックスがあります。チェックを入れると、プロフィールが保存されたときに写真が削除されます。
・ユーザーが画像ファイルを見つけてアップロードするためのブラウズボタンがあります。
・アップロードされた画像は縮小され、100px×100pxと35px×35pxの2つのファイルに保存されます。
・画像の上をマウスが転がると、画像の説明が表示されます。

ヒント: デフォルトの画像を空白にしたい場合は、同じファイル名の1px x 1pxの透明なスペーサー画像に置き換えてください。画像の拡張子は'.png'または'.gif'のいずれかです。

3 ユーザーの写真を一括でアップロードする

管理者は、サイト管理 > ユーザー > アカウント > ユーザー画像のアップロードで、ユーザー画像を含むzipファイルをアップロードすることができます。
Uploaduserpictures.png
Uploaduserpictures.png

・画像ファイルには、ユーザ名、ID番号、または学生のIDにちなんだ名前をつけてください (正しい画像ファイルの拡張子と一緒に)。 例えば、ユーザ名がjbrown98の学生は、jbrown98.jpgという名前の画像を持っています
・サポートされている画像の種類には、gif、jpg、pngがあります。
・画像ファイル名は大文字小文字を区別しません。

4 グラビア

管理者は、サイト管理 > ユーザ > 権限 > ユーザポリシーでグラビアの使用を有効にすることができます。ユーザがユーザ画像をアップロードしていない場合、Moodleはユーザのメールアドレスが関連するグラビアを持っているかどうかをチェックし、持っている場合、そのグラビアをユーザ画像として使用します。

ロール設定:翻訳準備中のGravatarデフォルト画像URLも参照してください。

5 アバターを作るためのツール

このフォーラムのスレッドには、アバターを作成するためのいくつかの無料ツールがリストアップされています。

6 関連項目

プロフィール写真をアップロードまたは変更することができません フォーラムの議論

カテゴリ | アカウント | サイト管理

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

【Laravel】Redis::pipelineが爆速だったメモ

最近業務の一貫で他エンジニアのコードレビューをしている時にコメントした件を備忘録として残しておく。

自身が所属するプロジェクトではPHP×Laravelを使用していて、今回以下のような実装要件であった。
複数のリスト型キーをRedisにセットする

そして以下のような実装がされていた。

foreach($key_map as $key => $value) {
   Redis::lpush($key, $value);
   Redis::expire($key, self::CACHE_EXPIRE_TIME);
}

普通に動くと思うし、パフォーマンスを気にする必要ないのなら、スルーでもいいですが、せっかくなので、
Laravelの公式ドキュメント( https://readouble.com/laravel/5.4/ja/redis.html )をみると、こんなコマンドがあるよと。

パイプラインコマンド
一回の操作でサーバに対し多くのコマンドを送る必要がある場合はパイプラインを使うべきでしょう。pipelineメソッドは引数をひとつだけ取り、Redisインスタンスを取る「クロージャ」です。このRedisインスタンスで全コマンドを発行し、一回の操作で全部実行できます。

Redis::pipeline(function ($pipe) {
    for ($i = 0; $i < 1000; $i++) {
        $pipe->set("key:$i", $i);
    }
});

ふむふむ、インスタンス内で全コマンドを発行して一回の操作で全部実行とな。
それは確かにパフォーマンス良さそう。

ということで適当にartisanコマンド作って試してみる。
こんな感じ

        // 直接
        $startTime = microtime(true);
        for ($i = 0; $i < $count; $i++) {
            Redis::lpush('test_key' . $i, 'hoge');
        }
        $runningTime =  microtime(true) - $startTime;
        var_dump('straight running time: ' . $runningTime . ' [s]');

        // pipeline
        $startTime = microtime(true);
        $redis = Redis::connection('redis_0');
        $redis->pipeline(function ($pipe) use($count) {
            for ($i = 0; $i < $count; $i++) {
                $pipe->lpush('test_key_pipeline' . $i, 'hoge');
            }
        });
        $runningTime =  microtime(true) - $startTime;
        var_dump('pipeline running time: ' . $runningTime . ' [s]');

↓↓結果↓↓

// 10件一括
# php artisan redis_test 10
string(42) "straight running time: 0.0122230052948 [s]"
string(46) "pipeline running time: 0.00022292137145996 [s]"

// 1000件一括
# php artisan redis_test 1000
string(43) "straight running time: 0.15764403343201 [s]"
string(45) "pipeline running time: 0.0015499591827393 [s]"

パフォーマンス的には100倍くらいでてる。使わない理由はなさそう。
setとかはアトミックにそもそもできるから、使う必要はないだろうけど、
リスト型やハッシュ型とかで複数一括実行が必要になった際は使えそうです。

注意なのが、Redis複数台ある時は、全部に同じコマンドが発行されて無駄が出たりするので、
手前でconnectionごとにグルーピングして、それぞれのRedisサーバーに対して適切なキーリストを渡してあげることかなと思います。

これからも勉学に励みます。

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

Dockerで環境構築 初心者編

dockerを使った環境構築を行ったのでその手順をご紹介します。
初めてdockerで環境構築を行ったので、少しでも参考になればと思います。

環境

環境     バージョン
PHP 7.0.27
FW CakePHP3.2.13
Apache 2.2.15
OS CentOS6.9
DB MySQL5.6.39

手順

  1. DockerフォルダDL
  2. Git Clone
  3. Docker コンテナに入る
  4. Apache起動
  5. ドキュメントルート設定
  6. Composer install
  7. mysql起動
  8. データベース作製
  9. app.phpの設定
  10. データベース接続

環境構築してみよう

DockerフォルダDL

今回は元々用意していたDockerFile,ymlファイルなどをまとめてダウンロードしてます。
下記、フォルダ構造になります。

-/
 |- docker-compose.yml
 |- docker
    |- db
    |   |- Dockerfile
    |   |- files
    |      |- entry.sh
    |      |- my.cnf
    |     
    |
    |- web
        |- Dockerfile
        |- files
           |- composer.phar
           |- dev.conf
           |- php.ini
           |- ssl.conf
 

Git Clone

githubにてプロジェクトの取得。
フォルダ構造は下記のようになりました。

-/
 |- project
 |- docker-compose.yml
 |- docker

Dockerコンテナに入る

コンテナに入る前にイメージの作製、コンテナを起動します。
コマンドラインで下記を実行する。

  • ビルド

    docker build
    // ビルドすることでdocker fileからイメージが得られます。
    
  • コンテナの起動

    $ docker-compose up -d
    // コンテナを起動します。
    

     下記のコマンドで、ビルドとコンテナの起動を一度に行えます。
  • ビルドとコンテナの起動

    $ docker-compose up -d --build
    // イメージのビルドから始めてコンテナを起動します。
    
$ docker ps
//起動中のコンテナの確認

 

オプション-dについて


$ docker-compose up
// ターミナルでコンテナを起動しているため、同じターミナルでは作業できない。

$ docker-compose up -d
// コンテナをバックグラウンドで起動するため、同じターミナルで作業ができる。

下記のコマンドでコンテナに入ります。

$ docker exec -it CONTAINER ID /bin/bash
//CONTAINER IDは docker psをした際に確認できます。

Apache起動


webコンテナ内で、下記コマンドでApache起動する。
service httpd start
Apache起動

service httpd stop
Apache停止

service httpd restart
Apache再起動

/etc/init.d/httpd status
Apache状態確認

ドキュメントルート設定

web内のhttpd.confのドキュメントルートの設定を確認します。
今回のディレクトリ構造だとvar/www/htmlで問題なさそうです。

composer install

composer installができませんでした。
アクセス権限の問題だと思い、アクセス権限777に変更しました。
アクセス権限に関しては下記の記事を参考にしました。
https://qiita.com/t-a-run/items/239ed690ece7a011804a

下記、実行できました。
因みに、composer installはprojectフォルダで行いました。

compser install

この状態でアクセスするとデータベースエラーとブラウザに表示されます。
エラーの内容は、そのような(接続した)ファイルは見つからない。

database error(SQLSTATE[HY000] [2002] No such file or directory

mysql起動

mysqlを動作させます。
webコンテナに入った時、同様にdbコンテナに入ります。

service mysqld status
//mysqlの起動状態の確認
service mysqld start
//mysqlの起動

ログイン前に初期のユーザー名とパスワードを確認します。
DBコンテナ内でmysqld.logを確認します。

cat mysqld.log

A temporary password is〜に書いてあるユーザー名パスワードを取得。
ユーザー名:root

データベースにログインします。

docker exec -it コンテナID mysql -u root -p

パスワード再設定->データベース作製->テーブル作成をおこないます。

app.phpの設定

git clone時には、app.phpは含まれていません。作成しましょう。

-/
 |- project
    |- config
        |-app.php

app.phpの書き方はネットで検索すると出てきます。今回は他で使っていたファイルをそのままコピーして中身を編集しました。主にデータベースのしか触っていないです。
 'Datasources' => [
        'default' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
            'persistent' => false,
            'host' => 'localhost',
            /**
             * CakePHP will use the default DB port based on the driver selected
             * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
             * the following line and set the port accordingly
             */
            //'port' => 'non_standard_port_number',
            'username' => 'root',
            'password' => 'データベース作成時に設定したパスワード',
            'database' => 'データベース作成時に設定したパスワード',
            'port' => ポート番号,
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'flags' => [],
            'cacheMetadata' => true,
            'log' => false,

cat mysqld.log
A temporary password isに書いてあるパスワードを取得
->dbコンテナでmysqlをrestartし、ログイン->パスワード再設定->データベース作製->テーブル作成->

データベース接続

sequel proを使い、クイック接続にて接続テストを実行しました。
無事に接続完了しました。

ブラウザでlocalhostと検索すると無事開けました。

参考資料

docker-compose.yml の内容を理解しよう
https://futureys.tokyo/lets-understand-contents-of-docker-compose-yml/

Docker compose ことはじめハンズオン
https://qiita.com/TsutomuNakamura/items/7e90e5efb36601c5bc8a

Dockerfileの書き方と使い方
https://blog.codecamp.jp/docker-file-how-to

起動中の docker コンテナのシェルに入る
https://qiita.com/sekizo/items/27cc9b406332afc674f6

データベース作製
https://noumenon-th.net/programming/2019/04/01/docker-entrypoint-initdb01/

chmodで設定する権限の書き方
https://qiita.com/irasally/items/6ebc3c68e22905fb7330

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

【2020年新規WEB開発者にむけて】仕組みを理解しながら1円でSSL化してWordpressをホストするまでのヤバイチュートリアル【前編】

後編は現在執筆中です。前編は非SSLですが無料でWordpressをホストするまでのチュートリアルです

短く終わらせるつもりだったけど長くなった経歴(茶番)

WEB開発をはじめたばかり。
初めてのWEBページが完成し、ブラウザにindex.htmlをドラック&ドロップして、いざ友達にURLを共有して見てもらおうと...
IMG_0086.png

あっれ〜?σ(・ω・,,`)?


〜 3日後 〜
「なるほど、一般公開するにはサーバーとドメインを契約しなければいけないのか」

〜 1週間後 〜
「自分が作ったはじめてのWEBサイトをホストできた!」

〜 1ヶ月後 〜
「たくさんサイトを作って、メインページをそれらのまとめサイト的な形にしたのはいいけれど、更新のたびにHTMLを編集するのが大変だな」
「Wordpressを使うといいみたい。。」

〜 2ヶ月後 〜
「Wordpressすげー!」
「ブログ始めようかな。これでアフィリエイターの仲間入り!」

〜 3ヶ月後 〜
「ブログって書くの想像以上に大変だな」
(三日坊主で終わった)
「やっぱ自分にはコンテンツを書くより、コーディングのほうが合っているのかもしれない」
今までの作品を管理できるようなWPテーマを自作。そこにしょうもない作品ばかり作っては投稿する日々。

〜 4ヶ月後 〜
「なんだかんだサーバー・ドメイン代合計10,000円ぐらい払ってきたな。収益ゼロだけど...」
「このままじゃいけない」
自分の興味本位や快楽の赴くままコーディングをするのではなく、ユーザー視点でどうしたら価値あるものが作れるのか考えはじめるように..

〜 5ヶ月後 〜
と、ちょっとした精神変化があったものの、とりあえず今のサイトを無料で運営できるようにしたいという欲が勝った。
「はて。AWS, GCPとな。いや、PaaSって言われても。。」

〜 6ヶ月後 〜
「よし、Apacheの設定とかSSLとかキツかったけど、なんとか完全無料路線に移行できた!」

〜 7ヶ月後 〜
模試を数日後に控え、絶賛Qiita執筆中。


と、大雑把にこんな流れで今に至ります。(だいぶいい加減)
ちなみに、この間にrailsやlaravel、はたまたpythonで機械学習やスクレイピングをやってみたりなど結構寄り道しています。

さて、やっとのことですが本題に入ります。

上の茶葉にもあるように、過去の私ははじめて書いたWEBサイトを一般公開したいという気持ちが強く、右往左往しながらもWEBサーバーを借りてサイトを公開するに至りました。
けれども、主に金銭、もしくは時間などの理由で断念する初心者の方も多いと思います。

スペックはそこまで気にしないから、自分と他数人の間で共有できるWEB上の空間が欲しいというニーズは私たちWEB開発に携わる人間であれば結構多かったりするのではないでしょうか。

このようなニーズには、レンタルサーバーの最安プランでもオーバースペックだろうし、収益がないのにサーバー代が食われるばかりでは辛い気持ちになってきます。

私自身、インフラに関してはまだまだ初心者ですが、試行錯誤の上に「ほぼ無料」な方法にたどり着いたので、この場を借りて共有したいと思います。

IMG_0087.png
彼はとてもいいやつであった。

全体像

まずは全体の流れから説明します。
まずはじめにGoogle Cloud Platformを利用して、最低限Wordpressの初期画面が見れるまでを前編に。
その後にオプショナルで独自ドメインを取得し、セキュリティ強化や便利なツールの導入などを後編で紹介していきます。

これはWordpressサイトをホストすることを中心に書かれたチュートリアルですが、さらに一般的に、ただのPHP実行環境付きWEBサーバーとしても利用できます。

実際、開発者の場合ですとWorpdressだけでは物足りなくなるだろうと思うので、サブドメインを取得してメインのWordpressサイトと他のプロジェクトを1つのサーバーでホストするところまでをカバーします。

個人開発用にWEBサーバーを持っておきたいのなら前編だけで大丈夫ですし、今後発展していくサイトになる場合は後編までやって、あとから適宜インフラのスケールアップができるようになるといった構成になります。

後編のドメイン取得で唯一料金が発生しますが、年間1円から1,500円程度なので、WEBサーバーを借りて普通にやるより断然安く済みます。

最低限の前提知識

この記事にたどり着いたぐらいの方であればおそらく皆さんは問題ないと思いますが、以下に関する説明はしませんので、分からないことがあったら各自調べてください。。

  • Wordpressとは
  • ドメイン/WEBサーバーとは
  • 基本的なLinuxコマンドとターミナルエディタ(本記事ではvimを使用します)

なお、AWS, GCPなどのPaaSに触れたことがなくても、とりあえず記事に従って環境を構築すれば、なんとなくそれがどのようなものかが掴めるようになります。
説明不足や難しい用語は排除しているつもりですが、分からないことがあってもとりあえずコマンドコピペしていけば大丈夫です。
こういうのはとりあえずやってみて、後から理解するという形の方がストレスも少なくて良いと思います。

中・上級者の方にとっては説明が回りくどく感じる部分もあるかもしれませんが、ご了承ください。

本環境のメリット/デメリット (開発者視点)

普通のレンタルサーバーを利用する場合と比較して感じる本環境のメリット・デメリットです。

メリット デメリット
最低スペックだと非常に安い レスポンスが遅い(急なアクセスに弱い)
自由に設定ができる 初心者には難しい要素が多い
「モダンな技術を使ってる私・俺カッコいい」感を味わえる 多機能すぎて混乱に陥りやすい
勉強になる 初めてだと詰まって時間がかかるかも

また、無料でできる範囲ではどうしても低スペックな環境となりますが、課金すればレンタルサーバーよりも柔軟にスケールアップができるので、将来的に大きなプロジェクトになるものであってもその初期段階として使う分には全く問題ありません。

本当はもっと簡単にできる

前半でやりますが、GCPのCompute Engineでのインスタンス作成の際、便利なことに既にサーバーやWordpress、SSLまでの設定を済ましてあるイメージを利用する方法もあります。

一度私もこういった物を利用したことがあるのですが、最適化されていると言えど、複雑なディレクトリ配置や、仕組みをよくわからないまま使っている恐ろしさがあったのでそれ以来使っていません。(ドキュメント読むのがめんどくさい(殴))

それに、1から自分でこれらの環境を構築した方が勉強になると思います。

前編の流れ

  • GCPを使って新しいVMインスタンスを作る(CentOS7)
  • IPアドレスを固定する
  • ローカルマシンからSSH接続する
  • ファイヤウォールの設定をする
  • Apacheをインストール
  • PHPをインストール&設定
  • MariaDBをインストール&設定
  • Wordpressを設置&設定

ここまでだと

  • http://example.comではなく34.83.xxx.xxxのようなIPアドレス直打ちでのWEBページ閲覧
  • サイトのセキュリティがイマイチ
  • 急激なアクセス増加になった時に対応できない

といった問題が残ります。

後編の流れ

  • 独自ドメイン取得(1~1,500円)
  • CloudflareでSSL化(full strictモード)
  • Wordpressのおすすめプラグイン
  • phpMyAdminをインストール
  • サブドメインを取得し、別のプロジェクトを設置する
  • ターミナルの見た目を良い感じにする
  • WP記事の画像などのアセットをGoogle Clould Storageに移行 (執筆候補)

ここまでで、個人(もしくは数人)で閲覧/管理する程度であれば十分なWordpress環境の出来上がりです。
同時アクセスは10人以下、月間1万VP程度未満なら余裕で耐えられますが、レスポンスは一般的なレンタルサーバーよりも原則遅くなります。
しかし、アクセスが増えるようなら Compute Engine をグレードアップしたり。
(画像や動画などのアセットが増えてきたら Cloud Storage をアップグレードしたり。)
という感じにスケーラブルな環境となります。

この作業は大体何時間でできますと言いたいところなのですが、茶番の前半にある私のような初心者の方と、ある程度linuxなどに触れたことがある方では全然違うと思うので、明言は避けたいと思います。
完全に初心者な方でも、はじめは見よう見まねでやれば全然OKですし、ここからインフラ周りの勉強をはじめるきっかけとなったら嬉しいです。

では、Wordpressの「Hello World!」まで駆け抜けていきましょう!

STEP 0 Googleアカウント作成(任意)

Googleアカウント作成
もちろん既存の物を使ってもOKです。
step0-1.png

ちなみに私はこの記事のために新しく作成しました。
(皆さんは二段階認証を設定しましょうね!!!)

STEP 1 GCPに登録しよう

Google Cloud Platform (GCP)にアクセスして、無料トライアル枠を有効にします
step1-1.png

登録には住所、クレジットカードなどの情報が必要ですが、知らぬ間に課金される心配はありませんし、$300分の猶予もあるので安心です。

GCPは、AWSやAzure同様、PaaSといって、まあ、クラウド上でいろんなタイプのマシンを借りることができ、本当にいろんなことができるサービスのことです()
その中で、Google Compute Engine (GCE) という、汎用的なコンピューティング機能を持った仮想マシンを利用して、それをWEBサーバーにしていきます。

詳しいことは各々調べてくださいと言いたいところですが、もしあなたが全く無知の初心者で、PaaSについて調べて「なるほど理解」ってなったらあなたは天才です。
とりあえず意味もわからずやってみて、そうやってるうちに分かるようになってくるものなので、先に進んで大丈夫です。
(教訓臭くてごめんなさい。)

次に、プロジェクトの名前を変更します。

デフォルトでは「My First Project」となっているので、 左上のナビゲーションメニュー > IAMと管理 > 設定 から簡潔でわかりやすい名前に変更してください。
step1-2.png

STEP 2 GCEインスタンスを作成しよう

ナビゲーションメニュー > Compute Engine でインスタンス作成画面に行きます。
step2-1.png
「作成」からインスタンス初期設定をはじめていきます

step2-2.png
以下のようにポチポチしていってください

項目 備考
名前 後によく使用するので覚えられるようなものにしましょう。
リージョン [us-west/central/east]のいずれか ただしus-east4を除く ※us-west1が地理的に一番日本から近いです
ゾーン 適当に

マシンの構成

項目
シリーズ N1
マシンタイプ f1-micro

下に行って、ブートディスクの「変更」から
step2-3.png

項目
OS CentOS
バージョン CentOS7
ブートディスクの種類 標準の永続ディスク(HDD)
サイズ 30 (GB)

以上の設定がAlways Free枠でできる最大スペックのインスタンスです。
※Always Freeとは、無料トライアル期間が終わっても、ずっと無料で使える枠のことです。
(この枠は稀に仕様変更があるので注意)

右側の見積もり金額に「$5.08/月」とありますが、気にしないでください。

こんな感じにインスタンス(仮想マシン)が起動しているのが分かったら成功です。
step2-4.png

しかし、この外部IPアドレス(インスタンスに外部からアクセスするための住所)は、このままだと再起動するたびに変わってしまうという状態なので、次はそれを固定させる作業をします。

STEP3 動的(エフェメラル)IPから静的IPに変更しよう

ナビゲーションメニュー > VPCネットワーク > 外部IPアドレス
から、今作成したインスタンスの外部IPを「エフェメラル」から「静的」に変更します

step3-1.png

step3-2.png
名前は適当に。

これでインスタンスを再起動してもIPアドレスが変わることはなくなりました。

STEP 4 ローカルマシンからSSH接続しよう

ここでは作成したクラウド上にあるVMマシンに、皆さんが今操作しているパソコンのターミナルで接続します。

本当は、コンソール画面をブラウザで開いてそこで操作することもできるのですが、これから長いこと使うことになるかもしれないので、ローカルのターミナルから接続できるようにしておくと便利です。

ローカルマシンはMacを想定しています。
今からやるSSH接続はWindowsでも同じコマンドでできると思いますが、できなかった場合は「GCE SSH接続 windows」で調べてみてください。

では、ローカルのターミナルを開いて、以下のコマンドでSSH接続に必要な鍵を生成します。

ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_gce -C '[インスタンス名]'

入力すると、パスフレーズの設定を促されますが、そこは任意で大丈夫です。(空白でenterを2回でパスフレーズは設定されません)

次に生成された公開鍵をGCEに登録していきます。

cat ~/.ssh/id_rsa_gce.pub

で表示される長い文字列("ssh-rsa xxxx〜== [インスタンス名]" まで)をコピーします。

そうしたら、ブラウザでGCEのインスタンス名をクリックして詳細画面を開き、「編集」をクリックします。

step4-1.png

一番下の方のSSH 認証鍵が0 個ありますとある部分に先ほど保存した公開鍵をペーストし、そのまま保存します。
step4-2.png

これで準備が整ったので、ローカルで以下のコマンドを打ちます

ssh -i ~/.ssh/id_rsa_gce [インスタンス名]@[先ほど固定した外部IP]
Last login: Fri Oct 23 14:10:59 2020 from xxxxx
[main@main ~]$

のような表示になったら接続成功です。

STEP 5 SSHのポートを変更する

SSH接続には現在デフォルトで22番ポートを利用していますが、デフォルトなだけあって攻撃を受けやすいです。
/var/log/secureあたりで、すでに怪しげなログが見られるかもしれません。

そのため第一にこの22番ポートを閉鎖し、他のポートでSSH接続をするようにしていきます。

まず、ファイアウォールはGCPのパネルで操作していくので、こちらでは無効化しておきます。

sudo systemctl disable firewalld

次にSElinuxを無効化します。
これはコマンドを入力するのではなく、設定ファイルを直接書き換えるといった方法をとります。

最低限のvim

ここで初めてvimを使用する方のために、一応さくっと説明しておきます。
vimはターミナル上でのテキストエディタツールで、非常に高機能なのですが初心者には手が出しづらい印象があります。
といっても、以下の流れをおさえればとりあえずは大丈夫です。

  1. vim [ファイル名] でエディタ起動
  2. i でデフォルトの閲覧モードから編集モードへ
  3. 普通に文字編集
  4. escで閲覧モードに戻る
  5. 閲覧モードの状態から:wqで保存して終了

参照: Vim初心者に捧ぐ実践的入門

では早速設定ファイルを開きます。

sudo vim /etc/selinux/config

step5-1.png
ここで、SELINUX=enforcingとなっていると思うので、それをdisabledに書き換えます。

設定を反映するために、GCEのコントロール画面でインスタンスを再起動してください。
step5-2.png

ローカルのターミナルの方は、勝手に接続が切れてるので、再起動後にまたSSH接続してください。

ssh -i ~/.ssh/id_rsa_gce [インスタンス名]@[外部IP]

そして

getenforce

の結果がDisabledとなっていればOKです。

では、ポートの設定をしていきます。
ポート番号は1024から49151の範囲で任意に決めてください
私は45678にしますので、今後この数字があったら各自のものに置き換えてください。

sudo vim /etc/ssh/sshd_config

で、# Port 22となっているところのコメントアウト("#")を外して、自分が決めたポート番号に置き換えます。
step5-3.png

保存したら

sudo systemctl restart sshd && sudo systemctl restart sshd

でsshdを再起動&ステータス確認します。

● sshd.service - OpenSSH server daemon
   Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
   Active: active (running) since 金 2020-10-23 15:12:15 UTC; 11s ago
     Docs: man:sshd(8)
           man:sshd_config(5)
 Main PID: 1204 (sshd)
   CGroup: /system.slice/sshd.service
           ├─1184 sshd: [accepted]
           └─1204 /usr/sbin/sshd -D

10月 23 15:12:15 main systemd[1]: Stopped OpenSSH server daemon.
10月 23 15:12:15 main systemd[1]: Starting OpenSSH server daemon...
10月 23 15:12:15 main sshd[1204]: Server listening on 0.0.0.0 port 45678. <= 自分が設定した番号になっている
10月 23 15:12:15 main sshd[1204]: Server listening on :: port 45678.
10月 23 15:12:15 main systemd[1]: Started OpenSSH server daemon.

※しばらくすると勝手にSSH接続が切れます。

次にGCPの画面に移動して、 ナビゲーションメニュー > VPCネットワーク > ファイアウォール から、 default-allow-sshをクリックしてルールを変更していきます。
step5-4.png

右上の「編集」から下に行って、tcpポートを自分の設定した番号に変更し、保存してください。
step5-5.png

これで新しいポートからの接続ができるようになりましたが、22番ポートからのアクセスを拒否するルールがまだなので、そちらを作成します。

ファイアフォールの設定画面トップの「ファイアウォール ルールを作成」から、以下のように設定します。(赤枠が変更を加えた部分です)
step5-6.png

step5-7.png

注意ですが、この2つのルールは全てデフォルトで適応されるものなので、今後同じプロジェクトでGCEのインスタンスを増やしたりした場合、それに22番ポートでssh接続することはできないので心のどこかに留めておいてください。

さて、ターミナルに移りまして、まだインスタンスと接続されている場合はexitで一度接続解除してください。

ここで先ほどと同じように

ssh -i ~/.ssh/id_rsa_gce [インスタンス名]@[外部IP]

としても接続ができないことが確認できると思います。
ではこのステップの最後に、

ssh -i -p [ポート番号] ~/.ssh/id_rsa_gce [インスタンス名]@[外部IP]

として接続できたら成功です。
少し長かったですが、これでよりセキュアな環境となりました。

STEP 6 Apacheをインストールしよう

いよいよこの仮想マシンがWEBサーバーになります。
WEBサーバーソフトウェアにはApacheとNginxという二大巨塔があるのですが、今回はApacheでいきたいと思います。

なお、ここからは実行時にスーパーユーザー権限を必要とするコマンドが多いので、毎回sudo ~~とする代わりに、はじめにルートユーザーになっておきます。

sudo su -

アクセスログの日時は日本時間であった方がわかりやすいので、マシンのタイムゾーンを変更します。

timedatectl set-timezone Asia/Tokyo

dateコマンドの結果が現在の日本時間になっていたらOKです。

Apacheインストール

yum install -y httpd

起動&永続化

systemctl start httpd && systemctl enable httpd

ここでまたファイアウォールの設定をします。
先ほどはSSHのルールを変えましたが、今度はそれとは別の通信プロトコルであるhttphttpsについての設定です。皆さんお馴染みのやつですね。

この2つのプロトコル経由でのアクセスはデフォルトで許可されていないので、このままだとブラウザからアクセスしてもブロックされて表示されません。

ですので、これらを許可するようにします。
といっても簡単です。

Compute Engine のインスタンス管理画面からインスタンスを選択して、右上の「編集」に移動します。
step6-1.png

中盤にあるこの2つの項目にチェックをするだけです。

実はこれ、インスタンスを作成するときに同時に設定できた項目なのですが、意味もわからずはじめにチェックするよりかは、このタイミングでやるほうが腑に落ちるのではないでしょうか。
というのは建前で普通に忘れていましたゴメンナサイ

さて、ここでブラウザの検索バーに
http://[インスタンスのIPアドレス]
でEnter!
この画面がでたら成功です。
step6-2.png

ここまでの段階でこのマシンは、WEBサーバーとして公開ディレクトリ(/var/www/html)においたファイルをストリームすることができるようになりました。

今回の目的である Wordpress というソフトウェアは主に、「①管理者が記事を書き」、そのデータを使って「②WEBサイトとして表示するためのHTMLを生成する」という機能を持ちます。

(このように、テンプレートに記事などのコンテンツを埋め込んで配信するシステムをCMSといいます。)

①では、データの格納先としてデータベースが必要です。
データベースから必要なデータを抜き出して、②のようなことをするのに、PHPというプログラミング言語を使用します。

というわけで、次はPHPの実行環境とMariaDB(データベース)をインストールしていきます。

STEP 7 PHPをインストールしよう

Apacheをインストールするとき yum コマンドを使用しました。
これはCentOSでソフトウェアのパッケージを管理するツールです。
yum install httpd と打つと、 リポジトリといって、パッケージが沢山置いてあるクラウド上の倉庫から httpd を探し、それを実行するのに必要な他のパッケージとともにインストールしてくれます。

今からPHP7.3をインストールしますが、デフォルトのリポジトリ群にはどこにもないのでこのままではインストールすることができません。

というわけで、まずはPHP7.3が置いてあるリポジトリを追加します。

yum install http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

これでPHP7.3がインストールできるようになったので、必要なライブラリも含めて一気にインストールします。

yum install -y php php-mysql php-mbstring php-gd --enablerepo=remi-php73

以下のようにしてPHP7.3が入っているか確認できます。

[root@main ~]# php --version
PHP 7.3.23 (cli) (built: Sep 29 2020 08:33:03) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.23, Copyright (c) 1998-2018 Zend Technologies

ここで

vim /var/www/html/info.php

でエディタを起動し、

<?php
  phpinfo();
?>

と書いて保存してください。

そうしたら http://[IPアドレス]/info.php
にアクセスしてみてください。

今入力した内容がそのままテキストで表示されています。
これは、PHPをインストールしたものの、Apacheがそれをまだ読み込んでいないため、ただのテキストファイルとしてそのままinfo.phpを配信したからです。

systemctl restart httpd

でApacheをリスタートすることで、PHPと連携するようになります。

ブラウザの画面をリロードしてください。
step7-1.png
このような画面が表示されたら成功です。
Apacheはhttpリクエストを受け取った後、info.phpを実行するように命令し、その結果をクライアントに送り返しました。

ターミナル上で

php /var/www/html/info.php

とやってみてください。
ブラウザと同じ内容が出力されたと思います。

PHPの設定をしよう

Wordpressを利用するにあたり、やっておいた方がいい設定を紹介します。

まず、PHPの設定ファイルは/etc/php.iniなのですが、これに編集を加える前に一応バックアップを取っておきます。

cp /etc/php.ini /etc/php.ini.bk

もし、PHPがエラーで動かなくなったら、php.iniを削除しphp.ini.bkphp.iniにリネームすれば全て元どおりになります。

vim /etc/php.ini

で、/timezoneと入力してEnterを押下するとタイムゾーン設定の部分に移動してくれるので、iで編集モードにして、そこのコメントアウト(";")を外して Asia/Tokyo とします。
step7-2.png

escで閲覧モードに戻り、/post_max_sizeでEnterして、ここの値を128Mぐらいに変えておきます。
step7-3.png

同様にしてupload_max_filesize128Mに変更しましょう。
step7-4.png

最後の2の設定は、Wordpressで重い処理をするとき(データの大きい記事をアップロード、プラグインのインストールなど)にエラーに引っかからないようにするためです。

ログ出力などのその他の設定は任意でこちらを参照してみてください。
【PHP】PHPをインストールしたらやっておきたい設定

では、:wqで設定ファイルを保存して閉じて

systemctl restart httpd

でApacheを再起動して読み込ませます。
ブラウザでinfo.phpを更新すると、変更を加えた部分が反映されて表示されているのがわかります。
step7-5.png

STEP 8 MariaDBをインストールしよう

PHPの環境ができたので、次はMariaDBというデータベース管理システムをインストールします。

以下のコマンドで一気にインストールから永続化&起動までします。

yum install -y mariadb-server \
&& systemctl enable mariadb \
&& systemctl start mariadb

データベースというと、MySQLをご存知な方もいると思いますが、利用者目線からするとMariaDBはそれの若干上位互換なのですが、その違いを実感する場面は初心者にはあまりないでしょう。

参考
MySQLとMariaDBを比較!各DBの特徴や違いについて確認しよう!

mysql_secure_installation

で、対話形式での初期設定を開始します。
基本Enterでいいのですが、途中データベースのルートユーザーのパスワードを設定するように促されるので、安全なものを入力してください。(キーボードを打ってもパスワードは表示されませんので、タイプミスに気をつけてください)

All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!

と、設定が完了したら

mysql -uroot -p

で先ほど設定したパスワードを使って、データベースのルートユーザーでログインします。
step8-1.png
このまま、以下のようにしてWordpress用のデータベースを作成し、show databases;で追加されているか確認してください。

MariaDB [(none)]> CREATE DATABASE wp;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| wp                 |
+--------------------+
4 rows in set (0.00 sec)

備考
"CREATE" などのSQL命令文は基本大文字にしていますが、小文字でも大丈夫です。
複数のWordpressサイトをこのサーバーにおく予定がある場合は、区別がつくようなデータベース名にしておくと、あとで楽です。

今使っているユーザーはルートユーザーといって、なんでも出来てしまうユーザーです。
Wordpressには、関係のない他のデータベースの操作を禁止するために、次ではそのための新しいユーザーを作成し、それに今作成したwpデータベースのみに対する操作権限を与えます。

MariaDB [(none)]> CREATE USER '[新規ユーザー名]'@'localhost' IDENTIFIED BY '[新規ユーザーのパスワード]';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> GRANT ALL PRIVILEGES ON wp.* TO '[新規ユーザー名]'@'localhost';
Query OK, 0 rows affected (0.00 sec)

これで専用のユーザーが作成できたので、一度exit;でログアウトして、そちらのユーザーでログインします。

MariaDB [(none)]> exit;
Bye
[root@main ~]# mysql -u [ユーザー名] -p
Enter password: !!!入力しても見ることはできません!!!
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 11
Server version: 5.5.65-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

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

MariaDB [(none)]>

ここでshow databases;の結果が

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| wp                 |
+--------------------+
2 rows in set (0.00 sec)

このように、information_schemaと先ほど作成したデータベースのみになっていればOKです。

STEP 9 Wordpressを設置しよう

いよいよWordpressを設置します。
Wordpressの公式サイトからダウンロードしようとすると、当たり前ですがローカルマシンにデータがダウンロードされてしまいます。

これをGCEインスタンスに設置する方法はいろいろありますが、ここではwgetを使いましょう。

wgetは、インターネットからコンテンツをダウンロードするためのコマンドで、今のCentOSには入っていないので、まずはインストールします。

yum install -y wget

公開ディレクトリのルートに移動して

cd /var/www/html

最新の日本語wordpressをダウンロードします。

wget https://ja.wordpress.org/latest-ja.tar.gz

lsしてみると、latest-ja.tar.gzがあるのが確認できると思います。

次に、このファイルをtarコマンドを使って展開します。

tar xvfz latest-ja.tar.gz

この時点でlsしてみると、このような結果となります。

[root@main html]# ls
info.php  latest-ja.tar.gz  wordpress

ここで、wordpress以外はいらないので、削除してしまいましょう。

rm -rf info.php latest-ja.tar.gz

さて、これでWordpressのデータをCentOSにインストールすることができました。

将来を見据えたディレクトリ構造にしよう

続いてこの記事の後編の都合により、こWordpressのデータの場所を変更していくのですが、少々内容が難しいかもしれないので理由は置いておいてこちらにスキップしても構いません。

Apacheはデフォルトでは、/var/www/htmlを公開ディレクトリのルートとしています。(今後は「ドキュメントルート」といいます)

今はhttp://[IPアドレス]/hoge.html
のようにアクセスしていますが、後半ではドメインを取得し、
http://example.com/hoge.html
という感じでアクセスするようになります。

個人開発が進むと、Wordpressのサイトだけでなく、別のプロジェクトも設置したくなるかもしれません。

そうなったとき、Wordpressのサイトは
http://example.com/wordpress/
をトップにして、別のプロジェクトのトップを
http://example.com/anotherproject/
とする

みたいな方法でも構わないのですが、Apacheサーバー的にはこれらを同じサイトとみなしてしまうので、同じ場所に2つのプロジェクトのログが書き込まれるなどして混乱の元となります。

このような場合、サブドメインを作成し、ApacheのVituralHost機能を使って結びつけるのが一般的だと思います。
今は詳しく理解する必要はありませんが、DNSの設定でメインのWordpressサイトはhttp://example.com、別プロジェクトは
http://another.example.comに割り当てるとします。

そうするとApache側の設定によって、example.com, another.example.comをそれぞれ別のドキュメントルートに割り当てることができます。

イメージとしてはこんな感じです。

プロジェクト名 URL ドキュメントルート
ブログ https://example.com /var/www/wordpress
楽しいサイト https://tanoshii.example.com /var/www/tanoshii

こうするとこで、片方のサイトにだけアクセス制限をするなどといった設定がしやすくなります。

アクセスの多い大きなサイトを運営するとなると、1つ(もしくは複数)のサーバーに1つのサイトをホストするのですが、個人ではそのような必要性はないので、どんどん同じサーバーにサブドメインを結びつけていくことができます。

といっても、VirtualHostを使って設定するのは後編でドメインを取得した後なのですが、以上の理由にのっとり、次のようにWordpressを移動させておきます。

前置き終わり
systemctl stop httpd # 一応Apacheを止めておきます
cd /var/www
mv html/wordpress .
chown -R apache:apache wordpress/ # 所有者をrootからapacheユーザーに変更
rm -rf html

次に、Apacheの設定で、デフォルトのドキュメントルートを/var/www/htmlから/var/www/wordpressに変更します。

cd /etc/httpd/conf
cp httpd.conf httpd.conf.bk # php.ini同様にバックアップ作成
vim httpd.conf

/DocumentRootで検索して、以下のように変更していってください。
step9-1.png

設定を保存した後、

httpd -t

で、構文エラーがないかチェックできます。
Apacheの設定には試行錯誤がつきものなので、これは覚えておくと便利です。

Syntax OKと、大丈夫そうなら

systemctl start httpd

で起動してみましょう。

ブラウザでhttp://[IPアドレス]にアクセス/リロードしてみて、このような画面がでたら成功です。
step9-2.png

STEP 8 で設定したworpdress専用のデータベースとユーザーの情報を入力します。
step9-3.png

正常に登録できたらインストール実行ボタンが出るので、それを押した後、Wordpress管理者としての情報を登録します。
step9-4.png

設定後、ログイン画面で今設定したユーザー名とパスワードでログインして
step9-5.png
管理画面にアクセスすることができました!

http://[IPアドレス]/にアクセスすると、メインページが表示されます。
step9-6.png

Hello World!
お疲れ様でした。

前編のまとめ

  • GCPを使って新しいVMインスタンスを作った(CentOS7)
  • IPアドレスを固定した
  • ローカルマシンからSSH接続した
  • SSH接続に使用するポート番号を変更した
  • Apacheをインストールした
  • PHPをインストール&設定した
  • MariaDBをインストール&設定した
  • Wordpressをwgetで取り込んだ
  • Apacheでドキュメントルートを変更した
  • Wordpressをインストールし、ログインした

ここまでかかったコストは0円です。
ここからWordpressのテーマ作って遊んだりいろいろできます。

しかし、個人開発でこのサーバーを利用するにしても、まだ問題は残ります。

  • 流石にIPアドレス直打ちでのアクセスはダサい
  • このWordpress以外のサイトを設置するのが難しい
  • なんかバズって急にアクセス来ても対応できない
  • セキュリティが不十分
  • ターミナルがダサい(モノクロで疲れるし、毎回systemctlって打ちなくないよ...)

というわけで、後編はこれの問題に対応していきます。
ただし、セキュリティについては無料で対策できる範囲、そして私の能力的にも限界があるので、最悪の事態を回避するためにこのサーバーに本当に大事な情報は置かないようにしましょう。

後編は現在準備中です Coming soon...

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