20200406のPHPに関する記事は15件です。

【WordPress】ACFで、『投稿以外』でチェックボックス・セレクトボックスの値とラベルを全て表示 | get_field_object(っぽいもの)を使う【Advanced Custom Fields】

WordPressを使用した企業サイトで、「管理画面のカスタム投稿一覧で、カスタムフィールドを使った絞り込み検索がしたい」というオーダーがあった時の話。
カスタムタクソノミーや作成者での絞り込み検索の実装は過去に経験があったので、その要領でいけるだろうと考えていたら意外なところで詰まったので備忘録として。

要件

  • カスタムフィールドの実装
  • 管理画面のカスタム投稿一覧で、カスタムフィールドで絞り込み検索できるようにする

実装するカスタムフィールドはこんな感じ。
test-1.jpg
よくある簡単なセレクトボックス。

 

実装

ひとまず、管理一覧画面に自前の検索用セレクトボックスを追加するにはrestrict_manage_postsを使ってよしなにコードを書けばよい。

で、ACFにはget_field_objectという、カスタムフィールドの名前からそのフィールドの情報を引っ張ってくる便利な関数があるので、それを使えばまあ何とかなるだろうと考えて以下のようなコードを書いてみた。
上のテスト用フィールドではセレクトボックスのフィールドタイプなので、get_field_object('カスタムフィールドの名前')['choices']で選択肢の値とラベルのセットを取得できる。

function.php
function add_custom_edit_post_search_form( $post_type ){
    if ( $post_type === 'カスタム投稿タイプのスラッグ' ){
        $selected = get_query_var('test') ? get_query_var('test') : '';
        $field_obj = get_field_object('test');
        printf('<select name="%s">', $field_obj);
        printf('<option value="">すべての%s</option>', $field_obj['label']);
        foreach($field_obj['choices'] as $value => $label){
            $selected_text = '';
            if($selected === $value) {
                $selected_text = ' selected';
            }
            printf('<option value="%s"%s>%s</option>', $value, $selected_text, $label);
        }
        printf('</select>');
    }
};
add_action('restrict_manage_posts', 'add_custom_edit_post_search_form', 10, 1);

// 実際は`query_vars`や`pre_get_posts`等と組み合わせて、実際に投稿を絞り込まないと意味はないが、
// 本筋の話ではないためここでは省く

これでいける…かと思いきや、選択したフィールド値を持つ投稿が1件もない場合(要するに「見つかりませんでした」の時)にエラーになる。

色々と調べてみると、get_field_objectは引数に投稿IDを取っており、投稿に対してしか使えない。
何とも困ったので調べてみると同じ状況に陥っている人がいたようで、フィールドグループのpost_IDからフィールド情報を引っ張ってくるやり方を紹介しているページを見つけた。

https://www.healing-solutions.jp/tech/4298/

上記は2017年の記事で、ACFの仕様が変わったのか残念ながら上記のやり方ではできなかったが、「フィールド情報をカスタム投稿として保存しているなら、直接get_posts()」すればいけるのでは?」と思い、
さらに調べてみたら、フィールドグループは"acf-field-group"、その中のフィールドは"acf-field"という投稿タイプのポストデータとしてwp_postsテーブルに保存されていた。

test-2.png

(上がフィールドグループの投稿データ、下がフィールド名「test」の投稿データ)
 
 
 
 

データの中身

上記画像を見て分かる通り、get_field_objectで取得する際の['label']['name']は、それぞれpost_titlepost_nameに保存されている。

get_field_objectで取得できるのは、以下のような感じ。
※一部はダミーテキストと差し替えています

var_dump(get_field_object('test'))
var_dump(get_field_object('test'));

// 出力
array(25) {
  ["ID"]=>
  int(661)
  ["key"]=>
  string(19) "field_*************"
  ["label"]=>
  string(9) "テスト"
  ["name"]=>
  string(4) "test"
  ["prefix"]=>
  string(3) "acf"
  ["type"]=>
  string(6) "select"
  ["value"]=>
  string(4) "hoge"
  ["menu_order"]=>
  int(0)
  ["instructions"]=>
  string(27) "これはテストです。"
  ["required"]=>
  int(1)
  ["id"]=>
  string(0) ""
  ["class"]=>
  string(0) ""
  ["conditional_logic"]=>
  int(0)
  ["parent"]=>
  int(660)
  ["wrapper"]=>
  array(3) {
    ["width"]=>
    string(0) ""
    ["class"]=>
    string(0) ""
    ["id"]=>
    string(0) ""
  }
  ["choices"]=>
  array(6) {
    ["hoge"]=>
    string(6) "ほげ"
    ["fuga"]=>
    string(6) "ふが"
    ["piyo"]=>
    string(6) "ぴよ"
    ["foo"]=>
    string(6) "ふー"
    ["bar"]=>
    string(6) "ばー"
    ["baz"]=>
    string(6) "ばず"
  }
  ["default_value"]=>
  array(1) {
    [0]=>
    string(4) "hoge"
  }
  ["allow_null"]=>
  int(0)
  ["multiple"]=>
  int(0)
  ["ui"]=>
  int(1)
  ["ajax"]=>
  int(1)
  ["return_format"]=>
  string(5) "value"
  ["placeholder"]=>
  string(0) ""
  ["_name"]=>
  string(4) "test"
  ["_valid"]=>
  int(1)
}

 

 

対して、以下はget_posts()で取得できる投稿データ。
※一部はダミーテキストと差し替えています

var_dump(get_posts())
$args = array(
    'post_excerpt'  => 'test',  // 下記の出力内容を見ると分かるが、フィールド名は抜粋として保存されている
    'post_type' => 'acf-field', 
);
$acf_fields = get_posts( $args )[0];

// post_contentの中身はシリアライズ化されているので、もとに戻す。
$acf_fields->post_content = unserialize($acf_fields->post_content);

var_dump($acf_fields);

// 出力
object(WP_Post)#9316 (24) {
  ["ID"]=>
  int(661)
  ["post_author"]=>
  string(1) "1"
  ["post_date"]=>
  string(19) "2020-**-** **:**:**"
  ["post_date_gmt"]=>
  string(19) "2020-**-** **:**:**"
  ["post_content"]=>
  array(13) {
    ["type"]=>
    string(6) "select"
    ["instructions"]=>
    string(27) "これはテストです。"
    ["required"]=>
    int(1)
    ["conditional_logic"]=>
    int(0)
    ["wrapper"]=>
    array(3) {
      ["width"]=>
      string(0) ""
      ["class"]=>
      string(0) ""
      ["id"]=>
      string(0) ""
    }
    ["choices"]=>
    array(6) {
      ["hoge"]=>
      string(6) "ほげ"
      ["fuga"]=>
      string(6) "ふが"
      ["piyo"]=>
      string(6) "ぴよ"
      ["foo"]=>
      string(6) "ふー"
      ["bar"]=>
      string(6) "ばー"
      ["baz"]=>
      string(6) "ばず"
    }
    ["default_value"]=>
    array(1) {
      [0]=>
      string(4) "hoge"
    }
    ["allow_null"]=>
    int(0)
    ["multiple"]=>
    int(0)
    ["ui"]=>
    int(1)
    ["ajax"]=>
    int(1)
    ["return_format"]=>
    string(5) "value"
    ["placeholder"]=>
    string(0) ""
  }
  ["post_title"]=>
  string(9) "テスト"
  ["post_excerpt"]=>
  string(4) "test"
  ["post_status"]=>
  string(7) "publish"
  ["comment_status"]=>
  string(6) "closed"
  ["ping_status"]=>
  string(6) "closed"
  ["post_password"]=>
  string(0) ""
  ["post_name"]=>
  string(19) "field_*************"
  ["to_ping"]=>
  string(0) ""
  ["pinged"]=>
  string(0) ""
  ["post_modified"]=>
  string(19) "2020-**-** **:**:**"
  ["post_modified_gmt"]=>
  string(19) "2020-**-** **:**:**"
  ["post_content_filtered"]=>
  string(0) ""
  ["post_parent"]=>
  int(660)
  ["guid"]=>
  string(54) "https://www.example.com/?post_type=acf-field&p=661"
  ["menu_order"]=>
  int(0)
  ["post_type"]=>
  string(9) "acf-field"
  ["post_mime_type"]=>
  string(0) ""
  ["comment_count"]=>
  string(1) "0"
  ["filter"]=>
  string(3) "raw"
}

対応表

あまり使わないであろうものは省略。

$field = get_field_object() $field = get_posts()
$field["ID"] $field->ID
$field["key"] $field->post_name
$field["label"] $field->post_title
$field["name"] $field->post_excerpt
$field["type"] $field->post_content["type"]
$field["instructions"] $field->post_content["instructions"]
$field["choices"] $field->post_content["choices"]
$field["default_value"] $field->post_content["default_value"]
$field["placeholder"] $field->post_content["placeholder"]
$field["wrapper"]['id'] $field->post_content["wrapper"]['id']

フィールド名はpost_excerpt、フィールドラベルはpost_title
その他のだいたいのデータはpost_content内にシリアライズ化されて入っている模様。
 

最終的な実装

上記の表を踏まえて、get_posts()で取得したオブジェクトから適宜データを引っ張ってこればよい。
先にも書いたとおりpost_contentの中身はシリアライズ化されているため、unserialize()するのを忘れないように注意。

function.php
function add_custom_edit_post_search_form( $post_type ){
    if ( $post_type === 'カスタム投稿タイプのスラッグ' ){
        $selected = get_query_var('test') ? get_query_var('test') : '';

        args = array(
            'post_excerpt'  => 'test',
            //'name'    => 'field_*************',   あまり無いとは思うが、同じ名前のフィールドを複数使っている場合はフィールドキーで取得するほうが良い
            //'parent'  => ***,             もしくは、フィールドグループが親投稿として指定されているのでそれと組み合わせる
            'post_type' => 'acf-field', 
        );
        $field_obj = get_posts( $args )[0];
        $field_obj->post_content = unserialize($field_obj->post_content);

        printf('<select name="%s">', $field_obj->post_excerpt);
        printf('<option value="">すべての%s</option>', $field_obj->post_title);
        foreach($field_obj->post_content['choices'] as $value => $label){
            $selected_text = '';
            if($selected === $value) {
                $selected_text = ' selected';
            }
            printf('<option value="%s"%s>%s</option>', $value, $selected_text, $label);
        }
        printf('</select>');
    }
};
add_action('restrict_manage_posts', 'add_custom_edit_post_search_form', 10, 1);

 

管理画面以外でも、たとえばサイドバー等に検索用のセレクトボックスを表示したいときなど、何かしらループ外で処理したいときに役に立つと思われる。

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

【WordPress】ACFで、『投稿以外』でチェックボックスやセレクトボックスの値とラベルを全て取得 | get_field_object(っぽいもの)を使う【Advanced Custom Fields】

WordPressを使用した企業サイトで、「管理画面のカスタム投稿一覧で、カスタムフィールドを使った絞り込み検索がしたい」というオーダーがあった時の話。
カスタムタクソノミーや作成者での絞り込み検索の実装は過去に経験があったので、その要領でいけるだろうと考えていたら意外なところで詰まったので備忘録として。

要件

  • カスタムフィールドの実装
  • 管理画面のカスタム投稿一覧で、カスタムフィールドで絞り込み検索できるようにする

実装するカスタムフィールドはこんな感じ。
test-1.jpg
よくある簡単なセレクトボックス。

 

実装

ひとまず、管理一覧画面に自前の検索用セレクトボックスを追加するにはrestrict_manage_postsを使ってよしなにコードを書けばよい。

で、ACFにはget_field_objectという、カスタムフィールドの名前からそのフィールドの情報を引っ張ってくる便利な関数があるので、それを使えばまあ何とかなるだろうと考えて以下のようなコードを書いてみた。
上のテスト用フィールドではセレクトボックスのフィールドタイプなので、get_field_object('カスタムフィールドの名前')['choices']で選択肢の値とラベルのセットを取得できる。

function.php
function add_custom_edit_post_search_form( $post_type ){
    if ( $post_type === 'カスタム投稿タイプのスラッグ' ){
        $selected = get_query_var('test') ? get_query_var('test') : '';
        $field_obj = get_field_object('test');
        printf('<select name="%s">', $field_obj);
        printf('<option value="">すべての%s</option>', $field_obj['label']);
        foreach($field_obj['choices'] as $value => $label){
            $selected_text = '';
            if($selected === $value) {
                $selected_text = ' selected';
            }
            printf('<option value="%s"%s>%s</option>', $value, $selected_text, $label);
        }
        printf('</select>');
    }
};
add_action('restrict_manage_posts', 'add_custom_edit_post_search_form', 10, 1);

// 実際は`query_vars`や`pre_get_posts`等と組み合わせて、実際に投稿を絞り込まないと意味はないが、
// 本筋の話ではないためここでは省く

これでいける…かと思いきや、選択したフィールド値を持つ投稿が1件もない場合(要するに「見つかりませんでした」の時)にエラーになる。

色々と調べてみると、get_field_objectは引数に投稿IDを取っており、投稿に対してしか使えない。
何とも困ったので調べてみると同じ状況に陥っている人がいたようで、フィールドグループのpost_IDからフィールド情報を引っ張ってくるやり方を紹介しているページを見つけた。

https://www.healing-solutions.jp/tech/4298/

上記は2017年の記事で、ACFの仕様が変わったのか残念ながら上記のやり方ではできなかったが、「フィールド情報をカスタム投稿として保存しているなら、直接get_posts()すればいけるのでは?」と思い、
さらに調べてみたら、フィールドグループは"acf-field-group"、その中のフィールドは"acf-field"という投稿タイプのポストデータとしてwp_postsテーブルに保存されていた。

test-2.jpg

(上がフィールドグループの投稿データ、下がフィールド名「test」の投稿データ)
 
 
 
 

データの中身

上記画像を見て分かる通り、get_field_objectで取得する際の['label']['name']は、それぞれpost_titlepost_excerptに保存されている。

get_field_objectで取得できるのは、以下のような感じ。
※一部はダミーテキストと差し替えています

var_dump(get_field_object('test'))
var_dump(get_field_object('test'));

// 出力
array(25) {
  ["ID"]=>
  int(661)
  ["key"]=>
  string(19) "field_*************"
  ["label"]=>
  string(9) "テスト"
  ["name"]=>
  string(4) "test"
  ["prefix"]=>
  string(3) "acf"
  ["type"]=>
  string(6) "select"
  ["value"]=>
  string(4) "hoge"
  ["menu_order"]=>
  int(0)
  ["instructions"]=>
  string(27) "これはテストです。"
  ["required"]=>
  int(1)
  ["id"]=>
  string(0) ""
  ["class"]=>
  string(0) ""
  ["conditional_logic"]=>
  int(0)
  ["parent"]=>
  int(660)
  ["wrapper"]=>
  array(3) {
    ["width"]=>
    string(0) ""
    ["class"]=>
    string(0) ""
    ["id"]=>
    string(0) ""
  }
  ["choices"]=>
  array(6) {
    ["hoge"]=>
    string(6) "ほげ"
    ["fuga"]=>
    string(6) "ふが"
    ["piyo"]=>
    string(6) "ぴよ"
    ["foo"]=>
    string(6) "ふー"
    ["bar"]=>
    string(6) "ばー"
    ["baz"]=>
    string(6) "ばず"
  }
  ["default_value"]=>
  array(1) {
    [0]=>
    string(4) "hoge"
  }
  ["allow_null"]=>
  int(0)
  ["multiple"]=>
  int(0)
  ["ui"]=>
  int(1)
  ["ajax"]=>
  int(1)
  ["return_format"]=>
  string(5) "value"
  ["placeholder"]=>
  string(0) ""
  ["_name"]=>
  string(4) "test"
  ["_valid"]=>
  int(1)
}

 

 

対して、以下はget_posts()で取得できる投稿データ。
※一部はダミーテキストと差し替えています

var_dump(get_posts())
$args = array(
    's'         => 'test',      // フィールド名は抜粋に入っているため、キーワード検索で良い
    'post_type' => 'acf-field', 
);
$acf_fields = get_posts( $args )[0];

// post_contentの中身はシリアライズ化されているので、もとに戻す。
$acf_fields->post_content = unserialize($acf_fields->post_content);

var_dump($acf_fields);

// 出力
object(WP_Post)#9316 (24) {
  ["ID"]=>
  int(661)
  ["post_author"]=>
  string(1) "1"
  ["post_date"]=>
  string(19) "2020-**-** **:**:**"
  ["post_date_gmt"]=>
  string(19) "2020-**-** **:**:**"
  ["post_content"]=>
  array(13) {
    ["type"]=>
    string(6) "select"
    ["instructions"]=>
    string(27) "これはテストです。"
    ["required"]=>
    int(1)
    ["conditional_logic"]=>
    int(0)
    ["wrapper"]=>
    array(3) {
      ["width"]=>
      string(0) ""
      ["class"]=>
      string(0) ""
      ["id"]=>
      string(0) ""
    }
    ["choices"]=>
    array(6) {
      ["hoge"]=>
      string(6) "ほげ"
      ["fuga"]=>
      string(6) "ふが"
      ["piyo"]=>
      string(6) "ぴよ"
      ["foo"]=>
      string(6) "ふー"
      ["bar"]=>
      string(6) "ばー"
      ["baz"]=>
      string(6) "ばず"
    }
    ["default_value"]=>
    array(1) {
      [0]=>
      string(4) "hoge"
    }
    ["allow_null"]=>
    int(0)
    ["multiple"]=>
    int(0)
    ["ui"]=>
    int(1)
    ["ajax"]=>
    int(1)
    ["return_format"]=>
    string(5) "value"
    ["placeholder"]=>
    string(0) ""
  }
  ["post_title"]=>
  string(9) "テスト"
  ["post_excerpt"]=>
  string(4) "test"
  ["post_status"]=>
  string(7) "publish"
  ["comment_status"]=>
  string(6) "closed"
  ["ping_status"]=>
  string(6) "closed"
  ["post_password"]=>
  string(0) ""
  ["post_name"]=>
  string(19) "field_*************"
  ["to_ping"]=>
  string(0) ""
  ["pinged"]=>
  string(0) ""
  ["post_modified"]=>
  string(19) "2020-**-** **:**:**"
  ["post_modified_gmt"]=>
  string(19) "2020-**-** **:**:**"
  ["post_content_filtered"]=>
  string(0) ""
  ["post_parent"]=>
  int(660)
  ["guid"]=>
  string(54) "https://www.example.com/?post_type=acf-field&p=661"
  ["menu_order"]=>
  int(0)
  ["post_type"]=>
  string(9) "acf-field"
  ["post_mime_type"]=>
  string(0) ""
  ["comment_count"]=>
  string(1) "0"
  ["filter"]=>
  string(3) "raw"
}

対応表

あまり使わないであろうものは省略。

$field = get_field_object() $field = get_posts()
$field["ID"] $field->ID
$field["key"] $field->post_name
$field["label"] $field->post_title
$field["name"] $field->post_excerpt
$field["type"] $field->post_content["type"]
$field["instructions"] $field->post_content["instructions"]
$field["choices"] $field->post_content["choices"]
$field["default_value"] $field->post_content["default_value"]
$field["placeholder"] $field->post_content["placeholder"]
$field["wrapper"]['id'] $field->post_content["wrapper"]['id']

フィールド名はpost_excerpt、フィールドラベルはpost_title
その他のだいたいのデータはpost_content内にシリアライズ化されて入っている模様。
 

最終的な実装

上記の表を踏まえて、get_posts()で取得したオブジェクトから適宜データを引っ張ってこればよい。
ただし注意点として、先にも書いたとおりpost_contentの中身はシリアライズ化されているため、unserialize()するのを忘れないように注意。

function.php
function add_custom_edit_post_search_form( $post_type ){
    if ( $post_type === 'カスタム投稿タイプのスラッグ' ){
        $selected = get_query_var('test') ? get_query_var('test') : '';

        $args = array(
            's' => 'test',
            //'name'    => 'field_*************',   あまり無いとは思うが、同じ名前のフィールドを複数使っている場合はフィールドキーで取得するほうが良い
            //'parent'  => ***,                     もしくは、フィールドグループが親投稿として指定されているのでそれと組み合わせる
            'post_type' => 'acf-field', 
        );
        $field_obj = get_posts( $args )[0];
        $field_obj->post_content = unserialize($field_obj->post_content);

        printf('<select name="%s">', $field_obj->post_excerpt);
        printf('<option value="">すべての%s</option>', $field_obj->post_title);
        foreach($field_obj->post_content['choices'] as $value => $label){
            $selected_text = '';
            if($selected === $value) {
                $selected_text = ' selected';
            }
            printf('<option value="%s"%s>%s</option>', $value, $selected_text, $label);
        }
        printf('</select>');
    }
};
add_action('restrict_manage_posts', 'add_custom_edit_post_search_form', 10, 1);

 

管理画面以外でも、たとえばサイドバー等に検索用のセレクトボックスを表示したいときなど、何かしらループ外で処理したいときに役に立つと思われる。

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

【PHPで解いてみた】2章 C++入門 AtCoder Programming Guide for beginners (APG4b)

2.01.ループの書き方と範囲for文

 キーポイント

  • パターンがあるプログラムを見つけたらループ文を使う。
  • 配列の要素にはループを用いてアクセスする。
  • ループ分の使い分け
    • for文:カウンタによって繰り返す回数を管理したいとき
    • while文:上記以外

気付き・学び

  • 改行コードが含まれていることを念頭においてプラグラムを組む必要がある。

EX16 - 隣り合う同じ値を探す

<?php
    $Ai = trim(fgets(STDIN));

    $Ai_list = explode(" ",$Ai);

    $flag = false;

    for($i=0;$i<count($Ai_list)-1;$i++){
        if($Ai_list[$i] == $Ai_list[$i+1]){
            echo "YES\n";
            $flag = true;
            break;
        }
    }

    if(!$flag){
        echo "NO\n";
    }

2.02.多重ループ

キーポイント

  • 内側のループでbreakを書くと、内側のループは抜けれるが外側のループは抜けれない。
  • 多重ループを1度に抜ける時はフラグ変数を用いる。

EX17 - 果物屋さんでお買い物

<?php
    fscanf(STDIN,"%d %d", $n,$s);

    $Ai_list = explode(" ",trim(fgets(STDIN)));
    $Pi_list = explode(" ",trim(fgets(STDIN)));

    $count = 0;

    for($i=0;$i<count($Ai_list);$i++){
        for($j=0;$j<count($Pi_list);$j++){
            if($Ai_list[$i]+$Pi_list[$j] == $s){
                $count ++;
            }
        }
    }

    echo $count."\n";

2.03.多次元配列

キーポイント

  • 2次元配列は、2次元の表を扱うときに用いる。
  • 2次元配列は、「1次元配列の配列」で表す。
  • 2次元配列を構成する1次元配列の要素数が要素ごとに異なる配列をジャグ配列という。
  • 多次元配列は、「N-1次元配列の配列」とかんがえる。

気付き・学び

  • forを用いる時はインデックス変数の値とループ条件でミスが起こりやすいので注意する。
  • 2次元配列を作成したいとき、2重配列内でfor文を用いるときはbreakを用いる。そうしなければ、2次元配列にはならない。

EX18.ゲーム大会

<?php
fscanf(STDIN,"%d %d", $n,$m);



$games = array();

if($m == 0){
    echo "-";
}else {

    for ($t = 0; $t < $m; $t++) {
        fscanf(STDIN, "%d %d", $a, $b);
        $games[] = [$a, $b];
    }

    for ($i = 1; $i <= $n; $i++) {
        for ($j = 1; $j <= $n; $j++) {
            for ($k = 0; $k < count($games); $k++) {
                if ($i == $games[$k][0] && $j == $games[$k][1]) {
                    if ($j == $n) {
                        echo "o";
                    } else {
                        echo "o ";
                    }
                    break;
                } elseif ($i == $games[$k][1] && $j == $games[$k][0]) {
                    if ($j == $n) {
                        echo "x";
                    } else {
                        echo "x ";
                    }
                    break;
                } elseif ($k == count($games) - 1) {
                    if ($j == $n) {
                        echo "-";
                    } else {
                        echo "- ";
                    }
                }
            }
        }
        echo "\n";
    }

}

2.04.参照

キーポイント

  • 参照渡しとは変数の参照先(メモリアドレス)をコピーすること。
  • 参照は、関数で変数を複数返したいときに使える

気付き・学び

  • 今回のEXでは、3つの変数情報を返したい関数だったが、参照を使うことでシンプルな関数で3つの変数を返せることを実感できた。

EX19 - 九九の採点

<?php
$A_list = array();
for($i=0;$i<9;$i++){
    $A_list[$i] = explode(" ",trim(fgets(STDIN)));
}


$correct_count = 0;
$wrong_count = 0;
function saiten(&$A,&$correct_count,&$wrong_count){
    for($m=1;$m<=9;$m++){
        for($n=1;$n<=9;$n++){
            if($m*$n == $A[$m-1][$n-1]){
                $correct_count++;
            }else{
                $A[$m-1][$n-1]=$m*$n;
                $wrong_count++;
            }
        }
    }
}

saiten($A_list,$correct_count,$wrong_count);

for($t=0;$t<9;$t++){
    for($s=0;$s<9;$s++){
        if($s==8){
            echo $A_list[$t][$s];
        }else{
            echo $A_list[$t][$s]." ";
        }
    }
    echo "\n";
}


echo $correct_count."\n";
echo $wrong_count."\n";

2.05.再帰関数

キーポイント

  • 「ある関数の中で同じ関数を呼び出す」ことを再帰呼び出しという。
  • 再帰呼び出しを行わずに完了できる処理をベースケースといい、ベースケースが再帰の終了ポイント。
  • 再帰呼出しを行い、その結果を用いて行う処理のことを再帰ステップという、このステップが再帰を起こす。
  • 再帰関数のイメージは、再帰的に再起関数を呼び出して深くもぐり、下まで到達したのちに、返り値を複数回返すことで上っていくイメージ。
  • 再起関数を作るとき、同じ意味を持つ関数を再起関数内で呼び出す。この時、呼び出した関数のアルゴリズムまで考えず、この関数はこういう処理をしてこの値を返してくれるんだと定義することでわかりやすく関数を作れる。

EX20 - 報告書の枚数

<?php
  function calc_report($par, $children, &$reports) {
    if(count($children[$par])==0){
        return $reports[$par]=1;
    }
    $send_reports = 1;
    foreach($children[$par] as $child){
        $send_reports += calc_report($child,$children,$reports);
    }
    return $reports[$par] = $send_reports;
  }


  function main() {
    fscanf(STDIN, "%d", $n);
    $p = explode(" ",trim(fgets(STDIN)));

    # 組織図を隣接リストで持つ
    $children;
    for ($i=0; $i<$n-1; $i++) {
      $parent = $p[$i];
      $children[$parent][] = $i+1;
    }

    $reports = array(); # それぞれの組織が提出するレポートの数

    for ($i=0; $i<$n; $i++) $reports[] = -1;
    calc_report(0, $children, $reports); # 0番目の組織から計算していく

    for ($i=0; $i<$n; $i++) {
      echo $reports[$i] . "\n";
    }

  }

  main()
?>

2.0.6 計算量

EX21は与えられたコードから1つの関数呼び出しをコメントアウトするだけなので、PHPへの書き換えは割愛します。

キーポイント

  • コンピュータの記憶領域(メモリ)は有限であり、プログラムで変数を使用した分だけメモリを消費する
  • プログラムの実行時間: 時間計算量 、メモリ使用量:空間計算量。これらは時間計算量はオーダーで表される。
  • オーダー記法では O(N)と表されます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでMySQLと接続するために行ったこと

こんにちは、現在研修でPHP+DBでCRUD機能をつけた掲示板を作成しているのですがPHPとDBでうまく接続ができなかった為、共有したいと思います。なんかの参考になれば幸いです。

QそもそもなぜなかなかDB接続できなかったのか。

try{
    $db = new PDO('mysql:dbname=php_db;host=localhost;charset=utf8', 'root', 'root');
  } catch (PDOException $e) {
    echo 'DB接続エラー'. $e -> getMessage();
  }

上記のコードがMySQLと接続する際のコードになるのですが、PDOというPHPに接続するための関数の第一引数にデーターベース名とhost名、文字コードなどを指定でき、第二引数にユーザーの権限、第三引数にパスワードを入力しないといけないとのことですが、XAMPPを使用している方なら第三引数を特に指定せず ' ' だけでいいらしいのですがMAMP環境下だと第三引数にパスワードを指定しないといけなく、私は省略していいものだと思ってずっと接続できずにいました。アホでした。笑

Qどうやってテーブルにユーザー情報を格納したのか

これ、他のエンジニア様のコードや参考書に記載されているコード色々拝見したのですが、どれもわかりずらくそのままコピペしてもうまくいかなったんですよね。もしかしたら私のコードを他のエンジニア様がご覧になった際には怒られてしまうかもしれませんが一応うまくいったので共有します。

<?php 
  session_start();
  require('../db/connect.php');
//HTMLのコードが並ぶ
 $statement = $db->prepare('INSERT INTO users SET name=?, email=?, password=?');
  $statement -> execute(array(
    $_POST['name'],
    $_POST['email'],
    password_hash($_POST['password'], PASSWORD_DEFAULT)
  ));
?>

session_startと記載することでpostした情報を取得することができます。それを利用してテーブルに情報を格納していく形になります。

requireは先ほどのDB接続しているファイルを読み込んでいます。何回も同じ文を書きたくないですからね。

次にprepareと記載することでより安全にDBに情報を格納できるとのことだったので記載しております。引数にSQL文を記載していく形になります。INSERT INTOでユーザー情報を格納しています。

name = ?というのが下の文に書いてある$_POST['name']に入っている値が?の情報になります。
?に置き換わっているカラムの数だけ記載してあります。
最後にパスワードをハッシュ化してDBに格納します。

最後に

自分はWeb系の企業に就職して初めてPHPを書いています。
PHPだけで掲示板サイト作るのとか勉強していないし無理ーーーー?とか思ってました。意外となんとかなっているというのが今の現状です。
今までは独学でRuby on Railsの勉強をしていました。
フレームワークってあんなにも便利だったんだなと改めて実感しています。
早く先輩方に追いつけるように日々頑張っていかないといけませんね。

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

【PHP8.0】throw文がthrow式になる

throw expressionというRFCが投票中です。

最初のアイデアは2019/12/06のSebastiaan Andewegによるツイート

それに対して2020/03/19にCarusoが反応し、そしてその日のうちにiluuu1994が最初のプルリクを出しました
はえーよ。

throw expression

Introduction

PHPのthrowは文であるため、アロー関数や三項演算子、NULL合体演算子などの式しか許されない場所から例外を投げることができません。
このRFCでは、それらを可能にするためthrow文を式に変更することを提案します。

Proposal

式を記述可能なあらゆるコンテキストでthrowが利用可能になります。
以下は思いついた便利そうな例です。

// アロー関数
$callable = fn() => throw new Exception();

// nullチェック
$value = $nullableValue ?? throw new InvalidArgumentException();

// trueっぽいチェック
$value = $falsableValue ?: throw new InvalidArgumentException();

// 空ではない配列チェック
$value = !empty($array)
    ? reset($array)
    : throw new InvalidArgumentException();

他にも、議論の余地のある使用方法があります。
このRFCでは、以下のような記述も許可されています。

// ifを使った方が意図が明確になる
$condition && throw new Exception();
$condition || throw new Exception();
$condition and throw new Exception();
$condition or throw new Exception();

Operator precedence

throwが式になると、優先順位を決める必要があります。
以下は現時点で有効な書式の例です。

throw $this->createNotFoundException();
// こうなる
throw ($this->createNotFoundException());
// こうではない
(throw $this)->createNotFoundException();

throw static::createNotFoundException();
// こうなる
throw (static::createNotFoundException());
// こうではない
(throw static)::createNotFoundException();

throw $userIsAuthorized ? new ForbiddenException() : new UnauthorizedException();
// こうなる
throw ($userIsAuthorized ? new ForbiddenException() : new UnauthorizedException());
// こうではない
(throw $userIsAuthorized) ? new ForbiddenException() : new UnauthorizedException();

throw $maybeNullException ?? new Exception();
// こうなる
throw ($maybeNullException ?? new Exception());
// こうではない
(throw $maybeNullException) ?? new Exception();

throw $exception = new Exception();
// こうなる
throw ($exception = new Exception());
// こうではない
(throw $exception) = new Exception();

throw $exception ??= new Exception();
// こうなる
throw ($exception ??= new Exception());
// こうではない
(throw $exception) ??= new Exception();

throw $condition1 && $condition2 ? new Exception1() : new Exception2();
// こうなる
throw ($condition1 && $condition2 ? new Exception1() : new Exception2());
// こうではない
(throw $condition1) && $condition2 ? new Exception1() : new Exception2();

共通して言えるのは、全てがthrowキーワードより高い優先順位を持つということです。
このため、このRFCではthrowキーワードの優先順位を可能な限り低くすることを提案します。
現在有効なコードは、たとえ直感に反する動作だったとしても、今後も同じ動作をし続けます。
なぜなら、一般的にthrowは最後に使用するべき演算子であり、それ以降に記述した式は評価されないからです。

低い優先順位の唯一の欠点は、短絡評価のために括弧が必須になることです。

$condition || throw new Exception('$condition must be truthy') && $condition2 || throw new Exception('$condition2 must be truthy');
// こうなる
$condition || (throw new Exception('$condition must be truthy') && $condition2 || (throw new Exception('$condition2 must be truthy')));
// こうではない
$condition || (throw new Exception('$condition must be truthy')) && $condition2 || (throw new Exception('$condition2 must be truthy'));

もっとも、こんなコードはほぼ使われていないでしょう。

Backward Incompatible Changes

後方互換性のない変更はありません。

Other languages

C#では同じ文法が2017年に実装されました。

このような言語は他にはあまりありません。
ECMAScriptにプロポーザルがありますが、これは同じ問題を抱えているからです。

Proposed PHP Version(s)

PHP8。

投票

投票は2020/04/19まで、2/3の賛成で受理されます。
2020/04/06時点では賛成14、反対1で、受理される見込みです。

過去のML

9年前とか15年前にも同じ発想があったようですが、そのときは立ち消えになりました。
当時とはPHPのおかれた環境やRFCの出し方などがだいぶ異なることと、そしてなにより実物のプルリクがあるというのは大きいでしょう。

感想

ややこしいよね文と式。
全てが式になればいいのに。

というわけで、今後はもっと気軽にthrowすることができるようになります。
それどころかアロー関数で引数によって値を返したり例外Aを出したり例外Bを出したりすることもできちゃいますよ。
まあ正直、throwを出すようなややこしい式をアロー関数に書くんじゃないよと思ったりはするわけですが。

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

Laravel6系 Migration テーブルコメントをセットするには(MySQL, PostgreSQL)

ありそで無かったので記事にしますが、PostgreSQL未検証です。みたいに適当なので、参考程度にしてください。

もう書いちゃう方が早いので、以下コードを紹介します。

ProvidersへMigrateMacroProviderを作る

php artisan make:provider MigrateMacroProvider

App\Providers\MigrateMacroProvider::bootを編集

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Grammar;
use Illuminate\Database\Schema\Grammars\MySqlGrammar;
use Illuminate\Database\Schema\Grammars\PostgresGrammar;

class MigrateMacroProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Blueprint::macro('comment', function($comment) {
            $this->addCommand('comment', [
                'table'     => $this->getTable(),
                'comment'   => $comment,
            ]);

            Grammar::macro('compileComment', function(
                Blueprint $blueprint,
                $params
            ) {
                $compileComment = null;
                if (get_class($this) === MySqlGrammar::class) {
                    $compileComment = "ALTER TABLE `{$params->table}` COMMENT '{$params->comment}'";
                } elseif (get_class($this) === PostgresGrammar::class) {
                    $compileComment = "COMMENT ON TABLE {$params->table} IS '{$params->comment}'";
                }

                return $compileComment;
            });
        });
    }
}

config/app.phpへProvider追加

'providers' => [
   // ... 略
   App\Providers\MigrateMacroProvider::class,
   // ... 略
],

最後にShema::createの中でコメントセット

Schema::create('users', function (Blueprint $table) {
    $table->bigIncrements('id')
        ->comment('ID');
    // ... 中略
    // テーブル コメつけ
    $table->comment('ユーザテーブルでんねん');
}

$table->comment 使いたくない人は、適宜変えちゃってください。

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

Laravel6系 Migrationでテーブルコメつけたい(MySQL, PostgreSQL)

ありそで無かったので記事にしますが、PostgreSQL未検証です。みたいに適当なので、参考程度にしてください。

もう書いちゃう方が早いので、以下コードを紹介します。
いろいろ書き方あると思うので、こうやった方がもっと綺麗だよねーってあれば教えてください :bow:

ProvidersへMigrateMacroProviderを作る

php artisan make:provider MigrateMacroProvider

App\Providers\MigrateMacroProvider::bootを編集

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Grammar;
use Illuminate\Database\Schema\Grammars\MySqlGrammar;
use Illuminate\Database\Schema\Grammars\PostgresGrammar;

class MigrateMacroProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Blueprint::macro('comment', function($comment) {
            $this->addCommand('comment', [
                'table'     => $this->getTable(),
                'comment'   => $comment,
            ]);

            Grammar::macro('compileComment', function(
                Blueprint $blueprint,
                $params
            ) {
                $compileComment = null;
                if (get_class($this) === MySqlGrammar::class) {
                    $compileComment = "ALTER TABLE `{$params->table}` COMMENT '{$params->comment}'";
                } elseif (get_class($this) === PostgresGrammar::class) {
                    $compileComment = "COMMENT ON TABLE {$params->table} IS '{$params->comment}'";
                }

                return $compileComment;
            });
        });
    }
}

config/app.phpへProvider追加

'providers' => [
   // ... 略
   App\Providers\MigrateMacroProvider::class,
   // ... 略
],

最後にShema::createの中でコメつけ

Schema::create('users', function (Blueprint $table) {
    $table->bigIncrements('id')
        ->comment('ID');
    // ... 中略
    // テーブル コメつけ
    $table->comment('ユーザテーブルでんねん');
}

$table->comment 使いたくない人は、適宜変えちゃってください。

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

Laravel6.xでよく使うコマンド一覧 〜マイグレーションからモデルやコントローラ作成まで〜

個人的によく使うコマンドの使用例

コマンド一覧を見てもオプション関係がゴッチャになるので、定型文的によく使うLaravelのコマンドをメモ。
バージョンはLaravel 6.x です。最近リリースされた7では確認していませんが、確認が取れたらまた更新します。

マイグレーション関連

Composerのオートローダを再生成

composer dump-autoload

シーダクラスを作成した後などは、Composerのオートローダを再生成するためにこちらのコマンドを実行する必要があります。
シーディングやマイグレーション時にデータベースのエラーが出るときは大体このコマンドを忘れているパターンが多いです。
上記コマンドを打たなくてもエラーにならない場合もあり、例えば元々あったシーダーを編集してシーディングし直すときは必要なさそうでした。

マイグレーションファイルの作成

php artisan make:migration create_users_table --table=users

オプションを付加しながらマイグレーションファイルの作成。--table=の後にテーブル名を記載します。
コマンドを実行した日時によって、例えば下記のように、 年_月_日_時間+指定した名前 といったファイル名でマイグレーションファイルは作成されます。
2020_02_19_155441_create_users_table.php

テーブル作成だけでなく、「カラムの修正」や「外部キーの設定」とか、操作したい内容によって modify_colmn_type_user_id_on_users.phpadd_foreign_keys_to_users_tableなど、わかりやすい名前にすると良いかと思います。

シーダーファイルの作成

php artisan make:seeder UsersTableSeeder

データベースにテストデータを流し込むための入れ物を作ります。
今回の場合はUsersテーブル用のシーダーということになります。
テーブル名は各自で適切なものを設定してください。

マイグレーションのやり直しと同時にシーディング

php artisan migrate:fresh --seed

今あるテーブルを全ドロップしてテーブルを作り直し、DatabaseSeeder.phpに登録されたシーダーを同時に実行します。

php artisan migrate:refresh などでエラーになったときは、この php artisan migrate:fresh で解消することもあります。

シーディングのみ実行

php artisan db:seed

マイグレーションとは別にDatabaseSeeder.phpに登録されたシーディングだけ実行したいときはこちらのコマンドを打ちます。

特定のシーダー、クラスのシーディングのみ実行

php artisan db:seed --class=UsersTableSeeder

DatabaseSeeder.phpに登録していないものや、登録はしたが個別にシーダーを実行したいという際に使用します。
--class=の後にシーダーのクラス名を指定すればOKです。

モデル・コントローラ関連

モデル作成

php artisan make:model User

モデルを作ります。通常Laravelでは、テーブル名は複数形、モデル名はテーブル名の単数形とします。

モデルと同時にコントローラ作成

php artisan make:controller UserController

コントローラを作ります。こちらもテーブル名の単数形が良いです。
指定した名前でコントローラが作成され、今回の場合はUserController.phpというファイル名になります。

php artisan make:controller UserController --model=Models/User

モデルも一緒に作成したい場合は後ろにオプションを付けて作成可能です。
--model=Userとすればappディレクトリ配下にUser.phpのモデル用ファイルができますが、モデル用のディレクトリを切って管理したい場合などは --model=Models/User とすればapp/Modelsのディレクトリ配下にモデルが作成されます。

php artisan make:controller UserController --resource

自分でイチからfunctionを書かなくても、最初からCRUD処理用のメソッドをpublic functionとして最初に用意しておいてくれるのが上記のように --resource のオプションを付けた場合です。
これらは下記のように繋げて書くことで一度に実行できます。

php artisan make:controller UserController --resource --model=Models/User

--resource のオプションを付けた場合は、公式のドキュメントにあるように、下記のルートに紐づけられるメソッドが入れ物として用意されていますので、メソッドの内容は開発時に適宜上書きしていきます。

 動詞       | URI                  | アクション | ルート名
------------------------------------------------------------------
 GET       | /photos              | index   | photos.index
 GET       | /photos/create       | create  | photos.create
 POST      | /photos              | store   | photos.store
 GET       | /photos/{photo}      | show    | photos.show
 GET       | /photos/{photo}/edit | edit    | photos.edit
 PUT/PATCH | /photos/{photo}      | update  | photos.update
 DELETE    | /photos/{photo}      | destroy | photos.destroy

このリソースフルな状態でコントローラを作成すれば、下記のようにルートを定義すれば1行ですべてをまかなえます。

web.php
Route::resource('photos', 'PhotoController');

ただ自分はすべてのリソースを使う必要がなく、URL(URI)も/photos/{photo}/edit等から変更したいということも多く、割と従来通りに自分でルートの定義はしていたりします。

今日はいったんそんな感じです。

参考URL

■マイグレーション -> Laravel 6.x データベース:マイグレーション
■シーディング -> Laravel 6.x データベース:シーディング
■モデル -> Laravel 6.x Eloquent:利用の開始
■コントローラ -> Laravel 6.x コントローラ

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

Laravelのroutingでprefixにパラメーターを入れる事ができる

LaravelでRestAPIライクなURLを設定する時には、prefix内にパラメーターを設定すると子のroute側でパラメーターが受け取れます。

/posts/{id}/comments
/posts/{id}/recommends

というようなAPIを作成するとします。

以下のようにしてRouteのところにidを入れてURLを指定することも出来ますが、

api.php
Route::prefix('posts')->group(function () {
    Route::get('/{id}/comments', 'PostController@comments');
    Route::get('/{id}/recommends', 'PostController@recommends');
});

以下のようにprefixにパラメーターを入れる形でもControllerのメソッド側でidをパラメーターとして受け取ることが出来ます。

api.php
Route::prefix('posts/{id}')->group(function () {
    Route::get('/comments', 'PostController@comments');
    Route::get('/recommends', 'PostController@recommends');
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel環境構築(Mac)

はじめに

Laravelを初めて4月から初めて触る初学者です。
元はRailsを少し触っていましたが、色々あってLaravelを学習することにしたので、環境構築について自分なりにまとめておきます。

PC情報

  • macOS Catalina 10.15.3
  • Macbook Pro (16-inch 2019)

構築手順

(1) Composer

composerのダウンロード

PHPのパッケージ管理システムであるComposerをインストールします。
上記リンクから

Download >> Manual Download(下の方)

にアクセスし最新バージョンをダウンロードします。
(2020年4月時点では、1.10.1が最新のためこれをダウンロード)

ダウンロードフォルダにcomposer.pharがダウンロードされたかと思います。

composerをいつても呼び出せるようにする

ターミナルを起動し、以下のコマンド入力

$ cd ~/Downloads
$ sudo mv composer.phar /usr/local/bin/composer

パスワードを聞かれるので、自分のOSログイン時のパスワードを入力します。
これでcomposerがbinファイルに移行され、いつでもcomposerが呼び出せるようになりました。

実行権限の設定

このままだと、composerへのアクセス権が制限されている場合があるので、アクセス権限を変更します。

$ chmod a+x /usr/local/bin/composer

Composerが実行可能か確認する

$ composer -v

インストールしたバージョン(1.10.1)が表示されていれば完了です。

(2) Laravel Installerのインストール

Composerを用いたLaravel Installerのインストール

ターミナル上で以下のコマンドを実行します。

$ composer global require laravel/installer

少し待つとインストールが完了します。

環境変数の追加

このままだと、LaravelのInstallerを実行するたびにファイルパスを指定する必要があります。
そのため、PATHを通しておきます。

$ echo "export PATH=~/.composer/vendor/bin:$PATH" >> ~/.bash_profile
$ source ~/.bash_profile

(3) Laravelがインストールできてるか確認

testappの作成

1つプロジェクトを作ってみます。
ディレクトリをデスクトップに移動した上で、以下を実行します。

$ laravel new testapp

これでデスクトップ上にtestappというLaravelアプリケーションが作成されたと思います。

アプリケーションを実行してみる。

testappに移動して

$ cd testapp

サーバーを起動

$ php artisan serve

これで、localhost:8000にアクセスすると、LaravelアプリケーションのTOPページが表示されたと思います。
あとはアプリケーションを作っていくのみです!

参考にしたサイトなど

Laravel日本語サイト
PHPフレームワーク Laravel入門(書籍)

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

Smartyを使ってみる

1 インストール

1 download
 以下のサイトよりダウンロードします。
 http://www.smarty.net/

2 インストール
 ダウンロードしたzipを解凍します。使うのはlibsフォルダのみです。
 DocumentRootにsampleフォルダを作成しサンプルアプリを置くものとして

 htdocs + sample
      └ libs      <- ダウンロードしたsmartyのlibs

 ※libsを複数プロジェクト共通で利用する場合は、
  c:\smarty3\libs 等適当な場所へ配置しましょう。

3 設定
 smartyのlibsを共通で使う場合は、php.iniのinclude_pathの設定を行います。
";"を削除し、";c:\smarty3\libs"等と追記します。

php.ini
; UNIX: "/path1:/path2"
;include_path = ".:/php/includes"
 ↓
include_path = ".:/php/includes:/usr/local/lib/smarty3/libs"
;
; Windows: "\path1;\path2"
;include_path = ".;c:\php\includes"
 ↓
include_path = ".;c:\php\includes;c:\smarty3\libs"

 その他フォルダを作成します。
 htdocs + sample
      ├ libs ← ダウンロードしたsmartyのlibs
      ├ templates ← 空のフォルダを作成
      ├ templates_c ← 空のフォルダを作成
      ├ cache ← 空のフォルダを作成
      └ configs ← 空のフォルダを作成

2 sampleアプリケーション作成

 sampleフォルダにテンプレートとプログラムを作成します。

sample.tpl
<html>
<head>
<title>Smarty Test</title>
</head>
<body>

<h1>{$msg}</h2>

</body>
</html>
sample.php
<?php
require_once('Smarty.class.php');

$smarty = new Smarty();

$smarty->template_dir = dirname(__FILE__).'/templates';
$smarty->compile_dir  = dirname(__FILE__).'/templates_c';
$smarty->config_dir   = dirname(__FILE__).'/configs';
$smarty->cache_dir    = dirname(__FILE__).'/cache';

$smarty->assign('msg','Hello World!');
$smarty->display('sample.tpl');

?>

3 設定ファイルを使う(Smarty3)

 プログラムから設定ファイルを読み込んでみます。
 まずは、configs/sample.confを作り定義します。

sample.conf
# global
url = "www.hoge.com"
title = "Smarty"

[mysql]
DSN = "mysql:dbname=sample_db; host=localhost; charset=utf8"
USER = "user1"
PASS = "user1pass"

 そして、呼出側しは

sample.php
 :
//設定ファイル読込み
$smarty->configLoad('sample.conf');
//取得
$url = $smarty->getConfigVars('url');
//テスト表示
echo "url = ".$url."<br />";

//設定ファイル読込み(セクション)
$smarty->configLoad('sample.conf', 'mysql');
//取得
$dsn = $smarty->getConfigVars('DSN');
//テスト表示
echo "dsn = ".$dsn."<br />";

$smarty->display('sample.tpl');

4 DBからの一覧表示(PDO)

 DBのm_syainテーブルを全件取得し一覧表示してましょう。
 まずは、DBまわり用のクラス

CDbAccess.php
<?php

class DbAccess {
    private $pdo;

    /**
    * コンストラクタ

    * @param $smarty smartyオブジェクト
    */
    public function __construct($smarty){
        try {
            //configs/sample.confより設定取得
            $smarty->configLoad('sample.conf', 'mysql');
            $dsn = $smarty->getConfigVars('DSN');
            $user = $smarty->getConfigVars('USER');
            $pass = $smarty->getConfigVars('PASS');

            //PDOインスタンス生成
            $this->pdo = new PDO($dsn, $user, $pass);
            //接続語にオプション指定
            //(エラー発生時に例外をスローする指定)
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }

        catch(PDOException $Exception) {
            self::abort('エラー:' . $Exception->getMessage());
        }
    }

    /**
    * クローズ処理
    */
    public function close(){
        try {
            $this->pdo = null;
        }

        catch(PDOException $Exception) {
            //エラーは無視
        }
    }

    /**
    * テーブルデータ取得
    * 
    * @return テーブルデータ一覧
    */
    public function get_list(){
        try {
            //m_syainテーブルより全件取得
            $sql = "select * from m_syain where del_flg <> :delflg";
            $stt = $this->pdo->prepare($sql);
            $stt->execute(array(':delflg'=>'1'));
            //$sql = "select * from m_syain where del_flg <> ?";
            //$stt = $this->pdo->prepare($sql);
            //$stt->execute(array('1'));

            if ($list = $stt->fetchAll(PDO::FETCH_ASSOC) ){
                return $list;
            }
        }

        catch(PDOException $Exception) {
            self::abort('エラー:' . $Exception->getMessage());
        }
    }

    /**
    * フィールド名一覧取得
    * 
    * @return フィールド名一覧
    */
    public function get_keys(){
        try {
            $sql = 'show columns from m_syain';
            $stt = $this->pdo->prepare($sql);
            $stt->execute();

            while ($column = $stt->fetch(PDO::FETCH_ASSOC) ){
                $keys[] = $column['Field'];
            }
        }

        catch(PDOException $Exception) {
            self::abort('エラー:' . $Exception->getMessage());
        }
        finally {
            return $keys;
        }
    }

    /**
    * die()文字化け対策
    */
    private function abort($status=0) {
        if (is_string($status) && !headers_sent()) {
            //ヘッダにcharset追加
            header('Content-Type: text/plain; charset=UTF-8');
        }
        die($status);
    }
}

 呼出し側は

CDbAccess.php
<?php
 :
require_once ("CDbAccess.php");
 :
$smarty = new Smarty();
 :
$testdb = new DbAccess($smarty);
$keys = $testdb->get_keys();
$list = $testdb->get_list();
$values = [
       'title' => 'test',
       'head' => 'テスト',
       'keys' => $keys,
       'list' => $list
];

$smarty->assign ('values', $values);

//データベース接続終了
$testdb->close();
 :
$smarty->display('sample.tpl');
?>

 あとテンプレートは

template/sample.tpl
 :
    <h4>{$values.head}</h4>
    <table border=1 >
        {foreach $values.keys as $key}<th>{$key}</th>{/foreach}
        {foreach $values.list as $row}
            <tr>{foreach $row as $val}<td>{$val}</td>{/foreach}</tr>
        {/foreach}
    </table>
 :

5 エラー情報出力

 とりあえずphpファイルの先頭に以下の2行を入れる。

sample.php
<?php
ini_set('display_errors',1);        //1:エラー表示、0:非表示
error_reporting(E_ALL);         //エラー表示レベル(E_ALL & ~E_NOTICE等々指定)
 :

6 デバッグ

<?php
:
//デバッグコンソールを出す
$smarty->debugging = true;
:
//var_dumpで出力
var_dump($変数);
:
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP+αでTodoアプリを作る話②

前回

前回、PHPでTodoアプリを作るにあたって必要な環境設定を行ってまいりました。そちらをまだご覧になっていない方は是非そちらもご覧ください。

PHP+αでTodoアプリを作る話①
https://qiita.com/FangJoker7714/items/ed28eb66aa0947977f21

はじめに

今回は基本的な機能の実装を行っていきたいと思います。具体的には、DBの設定、表示、検索、削除という流れになっております。

DBの設定

まずはデータベースの作成を行っていきたいと思います。前回の記事でphpMyadminにログインしたと思いますがそちらのほうに移動してください。
コメント 2020-03-30 110311.png

次に、データベースというボタンをクリックして下記の様な画面に移動してください。
コメント 2020-03-30 110441.png

筆者は既に入力していますが、データベースを作成するというフォームに好きなデータベース名を入力してください。照合順序とは何かを調べたところ、Microsoftの公式ドキュメントにこのように記されていました。

SQL Server の照合順序により、並べ替え規則、大文字と小文字の区別、およびアクセントの区別のプロパティをデータで利用できるようになります。
https://docs.microsoft.com/ja-jp/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-ver15

Qiita内を調べたところ照合順序に関してわかりやすい記事を書いていた方がいらっしゃったのでそちらの記事も参考にされるとなお理解が深まるかと思います。

【MySQL】照合順序とは?
https://qiita.com/kazu56/items/6af85ffcf8d3954455ad

長くなってしまいましたが、画面右側にデータベースが追加されたら成功です。
コメント 2020-03-30 110518.png

次にテーブルを作成していきたいと思います。先ほどの画面に下記の様に入力してください。
コメント 2020-03-30 120045.png

テーブル名は大文字と小文字を区別するものと区別しないものがあります。調べたところphpMyadmin(というかMySQL?)はこれらを区別しないという記事を発見しました。

[MySQL] テーブル名が小文字になってしまうので大文字にしたい
https://b.0218.jp/20121014022533.html

カラム数とはのことです。似たような言葉にという言葉もありますが、両者の違いは下記のサイトがわかりやすく解説してくださっています。

データベースの用語を理解しよう 「テーブル」「レコード」「カラム」「フィールド」とは?
https://academy.gmocloud.com/know/20160425/2259

先ほどの画面で実行をクリックすると下記の様な画面が表示されると思います。

※仮にカラム数を5にすると下記の様になります。
コメント 2020-03-30 113815.png

どのような要素を使うかによってここはカスタマイズすればいいと思います。

次に具体的な値を入れていきます。詳しい説明はこの後致します。下記の様に編集してください。
コメント 2020-03-30 115856.png

何やらややこしそうですが関係するのは名前データ型長さ/値インデックスA_Iだけです。ひとつひとつ解説していきたいと思います。

まずは名前です。さっそく説明が難しいですが要はの名前を付けるための項目です。今回は4種類あります。

・idとは作られたtodoの番号を割り振るための項目です。1番目に作られたならば1番、2番目なら2番といった要領です。
・todoとはどのような予定なのかを保存するための項目です。勉強をするとかギターを弾くといった具体的な予定を収めるための項目といった理解でいいと思います。
・createは作成された日時を割り振るための項目です。
※このような名前は一例で全てのDBでこのような名称及び機能で使用されるわけではありません。

データ型とは、使う文字の範囲を決定するための項目です。整数だけなのか文字も使うのかといった感じです。(プログラミングをしていると時々見かけますね。)

・idは数値を扱うので数値型というものを使用します。今回はINTですが、もっと細かい値や大きな値を使用したいときはFLOAT等を使用するといいかと思います。
・todoは文字を扱うので文字型というものを使用します。今回はVARCHARを使用します。似たようなものにCHARというものも存在します。両者の違いを分かりやすく解説している方がいらっしゃったのでそちらの記事を参考にされるといいかと思います。またTEXTというものもありますが、より大きなデータを扱う際にはこちらの方がいいとのことでした。
・createは日時を扱うのでDATEを使用します。時間を扱いたい場合はTIME等を使用するといいかと思います。

※下記サイトを参考にしました。
SQLのデータ型
http://park18.wakwak.com/~little-box/Dreamweaver/ultra405.htm

「固定長カラム」と「可変長カラム」の違い
https://wa3.i-3-i.info/diff358db.html

【SQL入門】CHARとVARCHARの違い
https://qiita.com/oseibo/items/c589430bdb00c6ab4922

MySQL の VARCHAR と TEXT
http://3.1415.jp/ju5gxdka/

長さ/値とは、文字数などを設定する際に使用します。
インデックスとは、todoの検索の際にこれを設定しておくと便利というものです。
A_Iとは、todoを作成したときに自動で順番を割り当てるというものです。

長くなってしまいましたが設定はまだ終わっていません。前回作成したフォルダの中にtodo.phpというファイルを作成し下記の様に編集してください。

todo.php
<?php
  $dsn = 'mysql:dbname = todo_app;host=localhost;charset=utf8mb4'; //DBの名前を定義
  $user = 'root'; //ユーザー名
  $password = 'xxxxx'; //パスワード
  try { //接続を試みるための処理
    $dbh = new PDO($dsn, $user, $password); //データベースへ接続
  } catch (PDOExeption $e) { //接続できない場合の処理
    die('Connect Error: ' . $e->getCode()); //接続できなかった場合にはエラーを表示
  }
  $dbh = null; //DBから切断
  echo "hello world";
?>

小難しいコードが並んでいますね。コメントでも書いてありますが、補足を少々書き足しておきたいと思います。まず一番上はDBの名前を定義しています。よく見るとデータベース名と照合順序に設定したものに似た文字が並んでいるのがわかりますね。

次にユーザー名とパスワードを設定します。普段ログインに使用しているものを入力してください。

tryというところから何やらゴチャゴチャしていますね。PDO(PHP Data Objects)とはPHPからSQLを操作するための標準のライブラリです。(参考文献に記載)

一番最後のechoは必ず必要というわけではないですが、成功しても何も表示されないよりかはいいかと思い書き足させていただきました。

少々長くなってしまいましたが、これでDBとPHPの紐づけは完了です。

おわりに

今回はPHPとDBの細かい設定を行ってまいりました。当初は一気に完成まで開設するつもりでしたが、思ったよりも内容が濃くなってしまったので本格実装は次回に回したいと思います。

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

phpで標準入力する方法

今回は備忘録をかねて、様々な例を使ってphpで行う標準入力について、まとめてみました。

基本編

app.php
/* 1行のデータを取得する場合
* 例:「hogehoge」
*/
$data = rtrim(fgets(STDIN));

/* ' '(空白行)で分けられた1行のデータを配列で取得する場合
* 例:「hogehoge gehogeho」
*/
$data = explode(' ', rtrim(fgets(STDIN)));

応用編

app.php
/* 先頭の行に数字Nが書かれてて、後続のN行分のデータを配列で取得する場合
* 例:「2
*    hogehoge
*    gehogeho」
*/
$data = rtrim(fgets(STDIN));
  $data_arr=array();
for($i=0;$i<$data;$i++){
  $data_arr[]=rtrim(fgets(STDIN));
}
/* 出力値
*    Array(
*    [0] => gehogeho
*    [1] => hogehoge
*    )
*/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Slackでモーダル(ダイアログ)を使ってデータを送信する

最近、弱々エンジニア会に参加しました。
記事貢献を兼ねて、久しぶりのQiita投稿です!
今回はSlackでSlackAppを使って、モーダル(ダイアログ)使ったデータの入出力を行う方法を解説していきたいと思います。

今回作るもの

/modal というSlashコマンドを実行すると Block Kit Builder で作られたモーダルを表示して、入力された値をサーバーに送るSlackアプリを作ります。
ezgif.com-video-to-gif (3).gif

参考資料

https://api.slack.com/surfaces/modals/using

開発環境

  • PHP 7.2 (Laravel 6.16)
  • GAE Standard (外部からアクセスできるサーバーであればなんでも可)
  • Slack (フリープランでも可)

実装方法

Slackアプリの用意

モーダルを出す方法がメインなので細かくは書きません。
細かい部分は 運用中のブログ にまとめてるので気になる方はご覧ください〜

アプリの作成

https://api.slack.com/apps から Create New App を選択して作っていきます。
slack-app-create.jpg

Slashコマンドの登録

適当にSlashコマンドを登録します。
slash-install.jpg

権限の設定

今回のサンプルであれば commands のみで作成できます。
※ Slashコマンドを登録すると勝手に入ると思います。
いけなかったら教えてください\(^o^)/

interactivity の設定

ダイアログに値を入力された後、その値が送られる向き先の設定をする必要があります。
interactivity.jpg

ワークスペースにインストール

Install App からインストールします。
表示に従っていればインストールできると思いますが、一点初回インストールの場合は下記のApp Display Name を設定しないと「インストールできるボットユーザーがありません」と怒られてしまいます。
apphome.jpg

以上でSlackの設定は終了です!(結構細かったかもしれん

コーディング

ようやく本編です。Laravelで書いていきます。
トークンとかは分けるべきなんですが、都合上まとめてますので良い感じで整理してください。

/modal が実行されたときの処理

Route::post('/modal', 'SlackController@modal')->name('modal');

な感じでSlackControllerにリクエストが飛んだ後の処理を書いていきます

SlackController.php(折り畳み)
<?php

namespace App\Http\Controllers;

use GuzzleHttp\Client;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

class SlackController extends Controller
{
    const BOT_TOKEN = 'xoxb-xxxxxxxx'

    /**
     * /modal のSlashコマンドを実行すると呼ばれる場所
     *
     * @param Request $request
     * @return Response
     */
    function modal (Request $request) {
        $url = 'https://slack.com/api/views.open';
        $token = self::BOT_TOKEN;
        $view = $this->getModalContent();
        $trigger_id = $request->input('trigger_id');

        $params = [
            'view' => \GuzzleHttp\json_encode($view),
            'trigger_id' => $trigger_id
        ];

        $headers = [
            'Content-type' => 'application/json',
            'Authorization'  =>  'Bearer ' . $token
        ];

        $client = new Client();
        $response = $client->request(
            'POST',
            $url, // URLを設定
            [
                'headers' => $headers,
                'json' => $params
            ] // パラメーターがあれば設定
        );

        $log = \GuzzleHttp\json_decode($response->getBody()->getContents(), true);
        Log::info(print_r($log, true));

        return response('',200);
    }

    /**
     * ダイアログのテンプレートを作る
     *
     * @return array
     */
    function getModalContent () {
        return [
            "type" => "modal",
            "title" => [
                "type" => "plain_text",
                "text" => "メンバー登録",
                "emoji" => true
            ],
            "submit" => [
                "type" => "plain_text",
                "text" => "登録",
                "emoji" => true
            ],
            "close" => [
                "type" => "plain_text",
                "text" => "キャンセル",
                "emoji" => true
            ],
            "blocks" => [
                [
                    "type" => "input",
                    "block_id" => "name",
                    "element" => [
                        "type" => "plain_text_input",
                        "action_id" => "氏名",
                        "placeholder" => [
                            "type" => "plain_text",
                            "text" => "田中 太郎"
                        ],
                    ],
                    "label" => [
                        "type" => "plain_text",
                        "text" => "氏名"
                    ]
                ],
                [
                    "type" => "input",
                    "block_id" => "mail",
                    "element" => [
                        "type" => "plain_text_input",
                        "action_id" => "メールアドレス",
                        "placeholder" => [
                            "type" => "plain_text",
                            "text" => "xxx@gmail.com"
                        ],
                    ],
                    "label" => [
                        "type" => "plain_text",
                        "text" => "メールアドレス"
                    ]
                ],
                [
                    "type" => "input",
                    "block_id" => "language",
                    "optional" => true,
                    "element" => [
                        "type" => "checkboxes",
                        "action_id" => "得意言語",
                        "options" => [
                            [
                                "text" => [
                                    "type" => "plain_text",
                                    "text" => "PHP",
                                    "emoji" => true
                                ],
                                "value" => "value-0"
                            ],
                            [
                                "text" => [
                                    "type" => "plain_text",
                                    "text" => "Ruby",
                                    "emoji" => true
                                ],
                                "value" => "value-1"
                            ],
                            [
                                "text" => [
                                    "type" => "plain_text",
                                    "text" => "Javascript/Node.js",
                                    "emoji" => true
                                ],
                                "value" => "value-2"
                            ],
                            [
                                "text" => [
                                    "type" => "plain_text",
                                    "text" => "Python",
                                    "emoji" => true
                                ],
                                "value" => "value-3"
                            ]
                        ]
                    ],
                    "label" => [
                        "type" => "plain_text",
                        "text" => "得意言語",
                        "emoji" => true
                    ]
                ]
            ]
        ];
    }

ちなみに以下のライブラリを使用しています。
GuzzleHttp
Google StackDriver Logging

メイン処理ですが Slack API の view.open というAPIを利用しています。

$url = 'https://slack.com/api/views.open';

ダイアログの中身は Block Kit Builder で作ったものを使っていますが、細かい部分を修正しているので紹介します。

block_id と action_id は設定しておいたほうがいい

デフォルトでは付いていないのですが、 block_id と action_id はちゃんと設定したほうがいいです。
理由は次に紹介するパラメータの受け取り時にどの値なのかわからなくなりパラメータの取得が面倒になります。
それぞれ日本語も入れられたので結果を返す時にちゃっかり使っています。

optional の場所を間違えないで

地味にやらかしたのが optional の場所。
optionalを付けることで任意の値として設定できるようになりますが、 element の中 に入れてしまっていたので何回か詰まっていました。正しくは element の外 です。

正しく view.open が実行されていれば /modal を実行した時にダイアログは表示されるようになっているはずです。

モーダル経由で入力値を送られてきたときの処理

Route::post('/interactiveMessage', 'SlackController@interactiveMessage')->name('interactiveMessage');

な感じでSlackControllerにリクエストが飛んだ後の処理を書いていきます

SlackController.php(折り畳み)
<?php

namespace App\Http\Controllers;

use GuzzleHttp\Client;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

class SlackController extends Controller
{
    const BOT_TOKEN = 'xoxb-xxxxxxxx';

    // 先程のControllerに追記する形で

    /**
     * モーダルから送られてくるリクエストを受け取る
     *
     * @param Request $request
     * @return Response
     */
    function interactiveMessage (Request $request) {
        // 早期レスポンス
        response('',200)->send();

        $payload = $request->input('payload');
        $postData = \GuzzleHttp\json_decode($payload, true);

        Log::info(print_r($postData, true));

        switch ($postData['type']) {
            case 'view_submission':
                // submit された時

                /*
                 * なんらかの処理
                 */

                $text = '登録しました!';
                $user =  $postData['user']['id'];

                $attachments = $this->makeAttachments($postData['view']['state']['values']);

                Log::info(print_r($attachments, true));

                // メッセージを送る
                // modalを使う場合、response_urlを取得できなかったので強制的にメッセージ投稿
                $response = $this->postEphemeral($user, $text, $attachments);

                $log = \GuzzleHttp\json_decode($response->getBody()->getContents(), true);
                Log::info(print_r($log, true));
                break;
            case 'block_actions':
                // チェックボックスなどが更新された時
                break;
        }
    }

    /**
     * 「あなただけに表示されています」のメッセージを送る
     *
     * @param $user
     * @param $text
     * @param $attachments
     * @return mixed
     */
    function postEphemeral ($user, $text, $attachments = []) {
        $url = 'https://slack.com/api/chat.postEphemeral';
        $token = self::BOT_TOKEN;

        $params = [
            'token' => $token,
            'attachments' => \GuzzleHttp\json_encode($attachments),
            'channel' => $user,
            'text' => $text,
            'user' => $user,
        ];

        Log::info(print_r($params, true));

        $client = new Client();
        $response = $client->request(
            'POST',
            $url,
            ['query' => $params]
        );

        return $response;
    }

    /**
     * 回答を受け取った結果をまとめる
     *
     * @param $values
     * @return array
     */
    function makeAttachments ($values) {
        $text =  "";
        foreach ($values as $key => $value) {
            foreach ($value as $name => $detail) {
                switch ($detail['type']) {
                    case 'plain_text_input':
                        $text .= $name . ' : ' . $detail['value'] . "\n";
                        break;
                    case 'checkboxes':
                        $checkedValue = '';
                        if (isset($detail['selected_options'])) {
                            foreach ($detail['selected_options'] as $selected_option) {
                                $checkedValue .= $selected_option['text']['text'] . " ";
                            }
                        } else {
                            $checkedValue .=  '指定なし';
                        }

                        $text .= $name . ' : ' . $checkedValue . "\n";
                        break;
                }
            }
        }
        $attachments[] = [
            'text' => $text,
        ];
        return $attachments;

}

こちらも同様に注意すべきポイントを紹介します。

早期レスポンスをしておいたほうが良い
// 早期レスポンス
response('',200)->send();

モーダルからの値を送った後、3秒以内にレスポンスを返さないとエラーが返る仕様になっています。
DB更新処理など少し時間がかかる処理を挟む場合、早期レスポンスでまずダイアログを閉じてしまってから処理を行いましょう。

モーダルは色々なtypeで送られてくるのでtypeによる振り分けが必要

モーダルで「登録」を押した時、 type = 'view_submission' でリクエストが飛びます。
しかし、それ以外にもセレクトボックスを更新した場合も type = 'block_actions' でリクエストが飛びます。
https://api.slack.com/surfaces/modals/using#response_actions

そのため、typeによってうまくハンドリングしないとエラーになってしまうのでそこんとこうまく書きましょう。

モーダルの処理が完了した後は完了メッセージはない?

分からなかった点ですが、普通であれば response_url が返ってくるのですが、モーダルの場合は response_url が返ってこなかったので完了メッセージを送ることが出来ませんでした(詳しい方教えて下さい)
完了メッセージがないのは気持ち悪いので、今回は chat.postEphemeral を使ってレスポンスを返しました。

$url = 'https://slack.com/api/chat.postEphemeral';

積極的に綺麗なモーダルを作って行こうな

以前は dialog.open というAPIを使ってダイアログを表示するのが主流でしたが、新しい機能として modal が作られ、まだまだ参考資料が少ない状態です。
複数モーダルに渡るパラメータの入力なども可能なので、今回の記事は本当に基礎の基礎のような記事です。
この記事を参考にもっと色々な使い方が増えていってほしいと思います。

Twitter もやっているので、なにかありましたらフォロー&アドバイス頂けると嬉しいです

ここまで読んで頂きありがとうございました〜〜

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

laravel入門2版(本)が全然始められない人のためのまとめ

吐きそうになりながらlaravelインストールした話

ララベルの代表書であるララベル入門第二版を購入してインストールしようとしたがつまずくポイントが多すぎて吐きそうになったので過去の自分を助けてあげるつもりで解決策を書いていこうと思います。

環境/対象者

mac Os catalina
これからlaravel6とphp7.2を使おうとしている人
terminalのコマンドがある程度(directoryの移動程度)ができる人

詰みポイントのまとめ

  • まずこれが設定できていないと詰む zshとbash
  • composerでlaravelをインストールしようとしてもphpのバージョンがふるかったり相性が合わないと詰む
  • pathの通しかたが分からなくて詰む、というかパスが何か分からなくて詰む

ますこれができていないと詰むzashとbash

まずlaravelを初めてインストールしようとした時にterminalのshellがzshだとパスがうまく通せなくて詰みます。
bashに変更しましょう。

echo $SHELL

でshellがbashになっているか確認しましょう。

bin/bash

の表示が出ればbashになっているのでOKです。
もしbashになっていなかったらshellスクリプトの変えかた でググればすぐでてくるので検索してみてください。

composerでlaravelをインストールしようとしてもphpのバージョンがふるかったり相性が合わないと詰む

さてまずはcomposerのインストールの方法ですが
composer公式サイト(getcomposer.org)でcomposer.pharをダウンロードしてきてください。
ダウンロードした後は、composer.pharを

sudo mv composer.phar /usr/local/bin/composer

でbinディレクトリーに移動させた後に

chamod a+x /usr/local/bin/composer

でパーミッションを変更してあげてください。
ここまできたら

composer global require laravel/installer

でララベルのインストールをすることになるのですが、おそらくできないと思います。

- 原因

Installation request for laravel/installer ^2.1 -> satisfiable by laravel/installer[v2.1.0].
laravel/installer v2.1.0 requires ext-zip * -> the requested PHP extension zip is missing from your system.

多分こんなかんじのエラーが出てくると思いますが、ざっくり説明すると、ext-zipっていうphpのファイルがないよっていってます。

  • 解決策

調べていたら、yumでphpのたりないファイルをインストールする方法が書いていたのですが、macでyumを使おうとするとめんどくさそうだったのでhomebrewを使って、ext-zipをインストールします。

brew update
brew install php@7.4
brew link php@7.4

上のコードを打つとphpのファイルがインストールされるのでひとまずララベル自体をインストールすることはできるます。

pathの通しかたが分からなくて詰む、というかパスが何か分からなくて詰む

最後に pathの通しかたが分からなくて詰む、というかパスが何か分からなくて詰む、ですが、
ますはpathとは何かというのをざっくり説明します。

pathを通すというのは、terminal上で「laravel」という文言を使えるようにすることです。
pathが通っていないと

laravel:command not found

とでてきてコマンド使えなくて作業できません。

pathは
.bash_profileというファイルに直接書き込まなければいけないので、ますはホームディレクトリに移動してください。その後に

$ ls -a
$ cd .bash_profile

で隠しファイルの.bash_profileが存在することを確認してから.bash_profileに移動してください。
次に

$ vim ~/.bash_profile

でvimを起動して.bash_profileを編集していきます。
vimの操作は他にいい記事がたくさんあるので検索してください。
vimで開いた後は.bash_profileの最後尾に

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

を追加して保存した上で終了してください。
最後に

$ source ~/.bash_profile

を入力することで変更を保存します。

$ laravel -V

でlaravelコマンドが使えることを確認すればひとまず完了です。

まとめ

本を買ったはいいけどlaravelがインストールできなくてくるしんでる人の助けになれば嬉しいです。
他に不明点や間違いがあればコメントにてご指摘お願いします。

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