20200715のPHPに関する記事は13件です。

【Laravel5.5】 tinkerコマンドで管理者ユーザー登録

やりたいこと

今回はタイトルにもある通り、tinkerコマンドを使い管理者(Admin側)でのユーザー登録の流れを解説しようと思います。

なぜコマンドで登録するのか

Laravelでは標準で用意されている会員登録機能が認証(Auth側)のみ用意されており、管理者ユーザーのテーブルで直接パスワードを入力してインサートしてもハッシュ化されていないためログインが出来ません。
そのためtinkerコマンドでハッシュ化したパスワードをインサートする必要が出てくる場合があります。

コマンド操作流れ

tinker
php artisan tinker
Psy Shell v0.9.12 (PHP 7.1.29  cli) by Justin Hileman
>>> use App\Admin;
>>> $admin = new Admin();
=> App\Admin {#2914}
>>> $admin->name = 'ユーザー名';
=> "ユーザー名"
>>> $admin->email = 'hoge@hoge.co.jp';
=> "hoge@hoge.co.jp"
//bcrypt関数でハッシュ化
>>> $admin->password = bcrypt('パスワード');
=>"ハッシュ化されたパスワード"
>>> $admin->save();
=> true
//quitまたはexitでtinkerの終了
>>> quit

こちらがtinkerコマンドでの管理者ユーザー登録の一連になります。
tinkerはメソッド等実行出来るのでデバッグや実際の動きを確認したり、
テーブルに登録されているユーザーの一覧を取得出来たり便利なので積極的に使いたいですね!

以上になります!LGTMを押していただけると励みになります。
お疲れ様でした!

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

PHPでフレームワークを使用せずに投稿サイトを作る

やりたいこと

PHPで管理画面も含めた用語集サイトを作ります。
わかりやすさを優先して、最低限のソースコードにしています。
管理者パスワード設定もないので、第三者が勝手にコンテンツを変更できる仕様です。
セキュリティが緩いので個人情報を取り扱わないでください。

仕様

  • トップページ(index.php)は用語名、用語のフリガナの一覧を表示する。
  • トップページの用語のハイパーリンクをクリックすると、用語詳細ページ(item.php)画面に遷移する。
  • 用語詳細ページは、用語名、分類、用語のフリガナ、用語の内容を表示する。
  • 管理画面で用語の新規登録、編集、削除ができる。
  • 管理画面で用語の新規登録、編集、削除ボタンを押すと、確認ページ(xxxx_check.php)に遷移し実行ボタンを押すと、実行結果確認ページ(xxxx_done.php)に遷移する。

その他

サーバーはロリポップを契約、ドメインはムームードメインで取得しています。

ページ構成と画面遷移

ページ一覧

index.php
item.php
list.php
branch.php
add.php
add_check.php
add_done.php
edit.php
edit_check.php
edit_done.php
delete.php
delete_done.php
ng.php

画面遷移図

PHPサイト画面遷移図 (2).jpg

各ページのソースコード

index.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
 <div class="wrapper">
  <div id="site_title"><a href="https://XXXX.com/">Webサイトのタイトル</a></div>
   <div class="top_page_title_list">
    <ul>
     <li>
      <?php
       try {
        //DB名、ユーザー名、パスワード
        $dsn = 'mysql:dbname=LAAXXXX-XXXX;host=mysqlXXX.phy.lolipop.lan'; //データベース名。''内は一切スペースを入れてはいけない
        $user = 'LAAXXXX'; //''内にはユーザ名を入力
        $password = 'XXXX';  //''内にはパスワードを入力

        $PDO = new PDO($dsn, $user, $password); //MySQLのデータベースに接続
        $PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //PDOのエラーレポートを表示
        $sql = 'SELECT * FROM contents ORDER BY name asc'; //SELECT文を変数に格納。 contentsテーブルてからname列の値を取り出し、name列順に並び替え
        $stmt = $PDO->query($sql); // SQLステートメントを実行し、結果を変数に格納

        // foreach文で配列の中身を一行ずつ出力
        foreach ($stmt as $row) {
        // 取り出した一行を名称とフリガナで表示し、ハイパーリンク設定
        echo "<div class='title_name'><a href='http://XXXX.com/item.php?name=".$row['name']."'>".$row['name'].
        "<span class='reading'>(".$row['reading'].")</span></a></div>\n";
        }
       } catch (PDOException $e) {
       exit('データベースに接続できませんでした。' . $e->getMessage());
      }
     ?>
    </li>
   </ul>
  </div>
 </div>
</body>
</html>

item.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
 <div class="wrapper">
  <div id="site_title"><a href="https://XXXX.com/">Webサイトのタイトル</a></div>
   <div class="detail_page_contents">
    <?php
     try {
      //URLのidの値を取得
      $name = $_GET['name'];

      //DB名、ユーザー名、パスワード
      $dsn = 'mysql:dbname=LAAXXXX-XXXX;host=mysqlXXX.phy.lolipop.lan'; //データベース名。''内は一切スペースを入れてはいけない
      $user = 'LAAXXXX'; //''内にはユーザ名を入力
      $password = 'XXXX';  //''内にはパスワードを入力

      $PDO = new PDO($dsn, $user, $password); //MySQLのデータベースに接続
      $PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      $result = $PDO->query("SELECT * FROM contents WHERE name = '$name'");
      while($row = $result->fetch(PDO::FETCH_ASSOC)){
       echo "<p>分類 | ".$row['category']."</p>";
       echo "<h1>".$row['name']." (".$row['reading'].")</span></h1>";
       echo "<p>".nl2br($row['description'])."</p>"; //nl2br関数で改行を反映する
      }

     } catch (PDOException $e) {
      exit('データベースに接続できませんでした。' . $e->getMessage());
     }
    ?>
   </div>
  </div>
 </div>
</body>
</html>

list.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
  <link rel="stylesheet" href="/main.css">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
<?php
//tryはデータベースの接続エラー対策
try
{
  //データベースに接続
  $dsn = 'mysql:dbname=LAAXXXX-XXXX;host=mysqlXXX.phy.lolipop.lan'; //データベース名。''内は一切スペースを入れてはいけない
  $user = 'LAAXXXX'; //''内にはユーザ名を入力
  $password = 'XXXX';  //''内にはパスワードを入力
  $dbh = new PDO($dsn, $user, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //この行は定型文としてこのまま書く

  // SQL分を使ってレコードを読み込み
  $sql ='SELECT name FROM contents WHERE 1'; //'SELECT name FROM contents WHERE 1'を変数$sqlに格納する。contentsテーブルのnameカラムのすべての情報(Where 1で全部という意味)を取得する
  $stmt = $dbh->prepare($sql); // レコードを呼び出す準備。この行は定型文としてこのまま書く
  $stmt->execute(); // SQL文で指令を出すための命令文。この行は定型文としてこのまま書く

  // データベースから切断するプログラム
  $dbh = null;

  // 一覧を表示する
  print '用語一覧<br /><br />';

  print '<form method="post" action="branch.php">';

  While(true){
    $rec = $stmt->fetch(PDO::FETCH_ASSOC); //$stmtから1レコードを取り出す
    if($rec==false){ //もし、$recがなければ(もうデータがなければ)、Whileから抜け出す
      break;
    }
    print '<input type="radio" name="name" value="'.$rec['name'].'">';
    print $rec['name']; //もし、$recがあれば、$recのnameを表示
    print "<br />\n"; //\nはソースコードの改行コード。\nはシングルクオテーションで囲うと文字として出力されてしまうので注意
    // 上記3行は結合すると右記のように表示される。 <input type="radio" name="glossary" value="AA">AA<br />
  }
  print '<input type="submit" name="add" value="追加">';
  print '<input type="submit" name="edit" value="修正">';
  print '<input type="submit" name="delete" value="削除">';
  print '</form>';
}
catch (Exception $e)
{
  print 'ただいま障害により表示できません。';
  exit(); //強制終了の命令
}
?>

</body>
</html>

branch.php

branch.phpを使って、list.phpで押したボタンに応じて遷移先を制御する。

<?php
if(isset($_POST['add'])==true){ //もし「追加」ボタンが押されていて
  header('Location:add.php');
  exit();
}

if(isset($_POST['edit'])==true){ //もし「修正」ボタンが押されていて

  if(isset($_POST['name'])==false){ //もしラジオボタンが何も選択されていなければ
    header('Location:ng.php');
    exit();
  }

  $name=$_POST['name'];
  header('Location:edit.php?name='.$name);
  exit();
}

if(isset($_POST['delete'])==true){ //もし「修正」ボタンが押されていて

  if(isset($_POST['name'])==false){ //もしラジオボタンが何も選択されていなければ
    header('Location: ng.php');
    exit();
  }

  $name=$_POST['name'];
  header('Location:delete.php?name='.$name);
  exit();
}
?>

add.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
  用語追加<br />
  <br />
  <form method="post" action="glossary_add_check.php"> <!--postメソッドでglossary_add_check.phpに情報を引き渡す-->
    用語名を入力してください<br />
    <input type="text" name="name" style="width:100px"><br /> <!--入力された値はtext型値はnameと名づける-->

    読み方を入力してください英語はカナ日本語は平仮名<br />
    <input type="text" name="reading" style="width:100px"><br /> <!--入力された値はtext型値はreadingと名づける-->

    カテゴリを入力してください<br />
    <input type="text" name="category" style="width:100px"><br /> <!--入力された値はtext型値はcategoryと名づける-->

    用語の説明文を入力してください<br />
    <textarea name="description" style="width:500px; height:200px;" wrap="soft"></textarea>
    <br />
    <input type="button" onclick="history.back()" value="戻る"> <!--戻るボタンをクリックすると前の画面に遷移する-->
    <input type="submit" value="OK"> <!--OKボタンをクリックするとglossary_add_check.phpに遷移する-->
  </form>
</body>
</html>

add_check.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
<?php
$name=$_POST['name']; //前の画面から入力値を受け取り、$nameに格納
$reading=$_POST['reading']; //前の画面から入力値を受け取り、$readingに格納
$category=$_POST['category']; //前の画面から入力値を受け取り、$categoryに格納
$description=$_POST['description']; //前の画面から入力値を受け取り、$descriptionに格納

$name=htmlspecialchars($name,ENT_QUOTES,'UTF-8'); //文字列に変換(セキュリティ対策)
$reading=htmlspecialchars($reading,ENT_QUOTES,'UTF-8'); //文字列に変換(セキュリティ対策)
$category=htmlspecialchars($category,ENT_QUOTES,'UTF-8'); //文字列に変換(セキュリティ対策)
$description=htmlspecialchars($description,ENT_QUOTES,'UTF-8'); //文字列に変換(セキュリティ対策)

//$nameがカラならエラーメッセージを表示する
//$nameが入力されていれば、$nameを表示する
if($name==''){
  print '用語名が入力されていません。<br />';
}
else
{
  print '用語名:';
  print $name;
  print '<br />';
}

//$readingがカラならエラーメッセージを表示する
//$readingが入力されていれば、$readingを表示する
if($reading==''){
  print '読み方が入力されていません。<br />';
}
else
{
  print '読み方:';
  print $reading;
  print '<br />';
}

//$categoryがカラならエラーメッセージを表示する
//$categoryが入力されていれば、$categoryを表示する
if($category==''){
  print 'カテゴリが入力されていません。<br />';
}
else
{
  print 'カテゴリ:';
  print $category;
  print '<br />';
}

//$descriptionがカラならエラーメッセージを表示する
//$descriptionが入力されていれば、$categoryを表示する
if($description==''){
  print '用語説明が入力されていません。<br />';
}
else
{
  print '用語説明:';
  print $description;
  print '<br />';
}

//$name、$reading、$category、$descriptionのいずれかがカラなら、戻るボタンのみを表示する
//入力項目が適切なら、戻るボタンとOKボタンを表示する。
if($name==''|| $reading==''||$category=='' || $description==''){
  print '<form>';
  print '<input type="button" onclick="history.back()" value="戻る">';
  print '<form>';
}
else
{
  print '<form method="post" action="add_done.php">';
  print '<input type="hidden" name="name" value="'.$name.'">'; //'<input type="hidden" name="name" value="'と$nameをドットで連結
  print '<input type="hidden" name="reading" value="'.$reading.'">'; //'<input type="hidden" name="reading" value="'と$nameをドットで連結
  print '<input type="hidden" name="category" value="'.$category.'">'; //'<input type="hidden" name="category" value="'と$categoryをドットで連結
  print '<input type="hidden" name="description" value="'.$description.'">'; //'<input type="hidden" name="description" value="'と$descriptionをドットで連結
  print '<input type="button" onclick="history.back()" value="戻る">';
  print '<input type="submit" value="OK">';
  print '</form>';
}

?>
</body>
</html>

add_done.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
<?php
//tryはデータベースの接続エラー対策
try
{
  // POSTメソッドで前の画面の入力値を取得する
  $name = $_POST['name'];
  $reading = $_POST['reading'];
  $category = $_POST['category'];
  $description = $_POST['description'];

  // セキュリティ対策入力値を文字列に変換
  $name = htmlspecialchars($name,ENT_QUOTES,'UTF-8');
  $reading = htmlspecialchars($reading,ENT_QUOTES,'UTF-8');
  $category = htmlspecialchars($category,ENT_QUOTES,'UTF-8');
  $description = htmlspecialchars($description,ENT_QUOTES,'UTF-8');

  //データベースに接続
  $dsn = 'mysql:dbname=LAAXXXX-XXXX;host=mysqlXXXX.lan'; //データベース名。''内は一切スペースを入れてはいけない
  $user = 'LAAXXXX'; //rootにはユーザ名を入力
  $password = 'XXXX';  //''内にはパスワードを入力
  $dbh = new PDO($dsn, $user, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //この行は定型文としてこのまま書く

  // SQL分を使ってレコードを追加
  $sql ='INSERT INTO contents(name,reading,category,description) VALUES (?,?,?,?)'; //'INSERT INTO mst_staff(name,reading,category,description) VALUES (?,?,?,?)'を変数$sqlに格納する
  $stmt = $dbh->prepare($sql); // レコードを追加する準備。この行は定型文としてこのまま書く
  $data[] = $name; // 一つ目の?にセットしたいデータが入っている変数を書く
  $data[] = $reading; // 二つ目の?にセットしたいデータが入っている変数を書く
  $data[] = $category; // 三つ目の?にセットしたいデータが入っている変数を書く
  $data[] = $description; // 四つ目の?にセットしたいデータが入っている変数を書く
  $stmt->execute($data); // SQL文で指令を出すための命令文。この行は定型文としてこのまま書く

  // データベースから切断するプログラム
  $dbh = null;

  //結果を表示
  print $name;
  print 'を追加しました。<br />';

}
catch (Exception $e)
{
  print 'ただいま障害により接続できません。';
  exit(); //強制終了の命令
}
?>

<a href="list.php">戻る</a>
</body>
</html>

edit.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
<?php
//tryはデータベースの接続エラー対策
try
{
  $name=$_GET['name']; //前画面のlist.phpからGETでglossaryを受け取る

  //データベースに接続
  $dsn = 'mysql:dbname=LAAXXXX-XXXX;host=mysqlXXXX.lan'; //データベース名。''内は一切スペースを入れてはいけない
  $user = 'LAAXXXX'; //''内にはにはユーザ名を入力
  $password = 'XXXX';  //''内にはパスワードを入力
  $dbh = new PDO($dsn, $user, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //この行は定型文としてこのまま書く

  // SQL分を使ってレコードを読み込み
  $sql ='SELECT reading, category, description FROM contents WHERE name=?'; //'SELECT name FROM contents WHERE glossary=?'を変数$sqlに格納する。contentsテーブルのnameカラムのあとで指定する(?のこと)値の情報を取得する
  $stmt = $dbh->prepare($sql); // レコードを検索する準備。この行は定型文としてこのまま書く
  $data[] = $name; // 上の上の行の?の値を設定する
  $stmt->execute($data); // SQL文で指令を出すための命令文。この行は定型文としてこのまま書く

  $rec=$stmt->fetch(PDO::FETCH_ASSOC);
  $reading=$rec['reading'];
  $category=$rec['category'];
  $description=$rec['description'];

  // データベースから切断するプログラム
  $dbh = null;
}
catch (Exception $e)
{
  print 'ただいま障害により表示できません。';
  exit(); //強制終了の命令
}
?>

Name<br />
<form method="post" action="edit_check.php">
<input type="text" name="name" value="<?php print $name; ?>">
<br /><br />
Reading<br />
<form method="post" action="edit_check.php">
<input type="text" name="reading" value="<?php print $reading; ?>">
<br /><br />
Category<br />
<form method="post" action="edit_check.php">
<input type="text" name="category" value="<?php print $category; ?>">
<br /><br />
Discription<br />
<form method="post" action="edit_check.php">
<textarea name="description" style="width:500px; height:200px;" wrap="soft">
<?php print $description; ?>
</textarea>
<br />
<input type="button" onclick="history.back()" value="戻る">
<input type="submit" value="OK">
</form>
</body>
</html>

edit_check.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
<?php
$name=$_POST['name']; //前の画面から入力値を受け取り、$nameに格納
$reading=$_POST['reading']; //前の画面から入力値を受け取り、$readingに格納
$category=$_POST['category']; //前の画面から入力値を受け取り、$categoryに格納
$description=$_POST['description']; //前の画面から入力値を受け取り、$escriptionに格納

//$nameがカラならエラーメッセージを表示する
//$nameが入力されていれば、$nameを表示する
if($name==''){
  print '用語名が入力されていません。<br />';
}
else
{
  print '用語名:';
  print $name;
  print '<br /><br />';
}

//$readingがカラならエラーメッセージを表示する
//$readingが入力されていれば、$readingを表示する
if($reading==''){
  print '読み方が入力されていません。<br />';
}
else
{
  print '読み:';
  print $reading;
  print '<br /><br />';
}

//$categoryがカラならエラーメッセージを表示する
//$categoryが入力されていれば、$categoryを表示する
if($category==''){
  print 'カテゴリが入力されていません。<br />';
}
else
{
  print 'カテゴリ:';
  print $category;
  print '<br /><br />';
}

//$descriptionがカラならエラーメッセージを表示する
//$descriptionが入力されていれば、$categoryを表示する
if($description==''){
  print '用語説明が入力されていません。<br /><br />';
}
else
{
  print '用語説明:';
  print $description;
  print '<br /><br />';
}

//$name、$category、$descriptionのいずれかがカラなら、戻るボタンのみを表示する
//入力項目が適切なら、戻るボタンとOKボタンを表示する。
if($name==''|| $reading==''|| $category=='' || $description==''){
  print '<form>';
  print '<input type="button" onclick="history.back()" value="戻る">';
  print '<form>';
}
else
{
  print '<form method="post" action="edit_done.php">';
  print '<input type="hidden" name="name" value="'.$name.'">'; //'<input type="hidden" name="name" value="'と$nameをドットで連結
  print '<input type="hidden" name="reading" value="'.$reading.'">'; //'<input type="hidden" name="namereading" value="'と$readingをドットで連結
  print '<input type="hidden" name="category" value="'.$category.'">'; //'<input type="hidden" name="category" value="'と$categoryをドットで連結
  print '<input type="hidden" name="description" value="'.$description.'">'; //'<input type="hidden" name="description" value="'と$descriptionをドットで連結
  print '<input type="button" onclick="history.back()" value="戻る">';
  print '<input type="submit" value="OK">';
  print '</form>';
}
?>
</body>
</html>

edit_done.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
<?php
//tryはデータベースの接続エラー対策
try
{
  // POSTメソッドで前の画面の入力値を取得する
  $name = $_POST['name'];
  $reading = $_POST['reading'];
  $category = $_POST['category'];
  $description = $_POST['description'];

  //データベースに接続
  $dsn = 'mysql:dbname=LAAXXXX;host=mysqlXXXX.lan'; //データベース名。''内は一切スペースを入れてはいけない
  $user = 'XXXX'; //rootにはユーザ名を入力
  $password = 'XXXX';  //''内にはパスワードを入力
  $dbh = new PDO($dsn, $user, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //この行は定型文としてこのまま書く

  // SQL分を使ってレコードを追加
  $sql ='UPDATE contents SET name=?,reading=?,category=?,description=? WHERE name=?'; //$sql ='UPDATE contents SET name=?,reading=?,category=?,description=? WHERE name=?'を変数$sqlに格納する
  $stmt = $dbh->prepare($sql); // レコードを追加する準備。この行は定型文としてこのまま書く
  $data[] = $name; // 一つ目の?にセットしたいデータが入っている変数を書く
  $data[] = $reading; // 二つ目の?にセットしたいデータが入っている変数を書く
  $data[] = $category; // 三つ目の?にセットしたいデータが入っている変数を書く
  $data[] = $description; // 四つ目の?にセットしたいデータが入っている変数を書く
  $data[] = $name; // 五つ目の?にセットしたいデータが入っている変数を書く
  $stmt->execute($data); // SQL文で指令を出すための命令文。この行は定型文としてこのまま書く

  // データベースから切断するプログラム
  $dbh = null;

}
catch (Exception $e)
{
  print 'ただいま障害により接続できません。';
  exit(); //強制終了の命令
}
?>

修正しました。 <br /><br />
<a href="list.php">戻る</a>

</body>
</html>

delete.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
<?php
//tryはデータベースの接続エラー対策
try
{
  $name=$_GET['name']; //前画面のlist.phpからGETでnameを受け取る

  //データベースに接続
  $dsn = 'mysql:dbname=LAAXXXX;host=mysqlXXXX.lan'; //データベース名。''内は一切スペースを入れてはいけない
  $user = 'XXXX'; //''内にはにはユーザ名を入力
  $password = 'XXXX';  //''内にはパスワードを入力
  $dbh = new PDO($dsn, $user, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //この行は定型文としてこのまま書く

  // SQL分を使ってレコードを読み込み
  $sql ='SELECT reading, category, description FROM contents WHERE name=?'; //'SELECT name FROM contents WHERE glossary=?'を変数$sqlに格納する。contentsテーブルのnameカラムのあとで指定する(?のこと)値の情報を取得する
  $stmt = $dbh->prepare($sql); // レコードを検索する準備。この行は定型文としてこのまま書く
  $data[] = $name; // 上の上の行の?の値を設定する
  $stmt->execute($data); // SQL文で指令を出すための命令文。この行は定型文としてこのまま書く

  $rec=$stmt->fetch(PDO::FETCH_ASSOC);
  $category=$rec['reading'];
  $category=$rec['category'];
  $description=$rec['description'];

  // データベースから切断するプログラム
  $dbh = null;
}
catch (Exception $e)
{
  print 'ただいま障害により表示できません。';
  exit(); //強制終了の命令
}
?>

用語削除<br /><br />
用語<br />
<?php print $name; ?>
<br />
この用語を削除してよろしいですか?<br /><br />
<form method="post" action="delete_done.php">
<input type="hidden" name="name" style="width:200px" value="<?php print $name?>"><br />
<input type="button" onclick="history.back()" value="戻る">
<input type="submit" value="OK">
</form>

</body>
</html>

delete_done.php

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
<?php
//tryはデータベースの接続エラー対策
try
{
  // POSTメソッドで前の画面の入力値を取得する
  $name = $_POST['name'];

  //データベースに接続
  $dsn = 'mysql:dbname=LAAXXXX;host=mysqlXXXX.lan'; //データベース名。''内は一切スペースを入れてはいけない
  $user = 'XXXX'; //rootにはユーザ名を入力
  $password = 'XXXX';  //''内にはパスワードを入力
  $dbh = new PDO($dsn, $user, $password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //この行は定型文としてこのまま書く

  // SQL分を使ってレコードを追加
  $sql ='DELETE FROM contents WHERE name=?'; //DELETE FROM contents WHERE name=?を変数$sqlに格納する
  $stmt = $dbh->prepare($sql); // レコードを追加する準備。この行は定型文としてこのまま書く
  $data[] = $name; // 一つ目の?にセットしたいデータが入っている変数を書く
  $stmt->execute($data); // SQL文で指令を出すための命令文。この行は定型文としてこのまま書く

  // データベースから切断するプログラム
  $dbh = null;
}
catch (Exception $e)
{
  print 'ただいま障害により接続できません。';
  exit(); //強制終了の命令
}
?>

削除しました。 <br /><br />
<a href="list.php">戻る</a>

</body>
</html>

ng.php

<!DOCTYPE html>
<html lang="ja">
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="description" content="Webサイトのコンテンツ内容">
 <title>Webサイトのタイトル</title>
 <link rel="stylesheet" href="/main.css">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>

<body>
用語が選択されていません
<a href="list.php">戻る</a>
</body>

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

?【CakePHP2】Controllerで動的に一時テーブルを作成し使用する方法

環境

PHP 7.2.21
CakePHP 2.10.18
MySQL 5.7.27

やりたいこと

テンポラリテーブルを使いたい

やったこと

ModelをuseTable = falseで用意してControllerでテーブル作成しセット

Model

TemporaryTable.php
<?php

App::uses('AppModel', 'Model');

class TemporaryTable extends AppModel
{
    public $useTable    = false;
    public $primaryKey  = '';
    public $useDbConfig = '';

    function setSource($tableName) {
        $this->setDataSource($this->useDbConfig);
        $db = ConnectionManager::getDataSource($this->useDbConfig);
        $db->cacheSources = ($this->cacheSources && $db->cacheSources);

        $this->table = $this->useTable = $tableName;
        $this->tableToModel[$this->table] = $this->alias;
        $this->schema();
    }
}

Controller

HogeController.php
<?php

class HogeController extends AppController {
    public function index() {
        // TEMPORARY TABLE名
        $tempTableName = 'temporary_table';
        // database.phpのDBconfigをModelに設定
        $this->TemporaryTable->useDbConfig = 'admin';
        // TEMPORARY TABLEを作成
        $this->TemporaryTable->query('CREATE TEMPORARY TABLE ' . $tempTableName . ' (id INT(11), name VARCHAR(256));');
        // 作成したTEMPORARY TABLE名をModelに設定
        $this->TemporaryTable->useTable = $tempTableName;

        // 先ずfindしてみる -> result:array(0)
        $data = $this->TemporaryTable->find('all');

        // テストデータをsaveする
        $testData = [
            'id'   => '1',
            'name' => '_test',
        ];
        $this->TemporaryTable->create();
        $this->TemporaryTable->save($testData);
        // テストデータ2をsaveする
        $testData2 = [
            'id'   => '2',
            'name' => 'test_test',
        ];
        $this->TemporaryTable->create();
        $this->TemporaryTable->save($testData2);
        // 再度find -> result:array(2)
        $data = $this->TemporaryTable->find('all');
        var_dump($data);
        /* find結果
            Array
            (
                [0] => Array
                    (
                        [TemporaryTable] => Array
                            (
                                [id] => 1
                                [name] => _test
                            )
                    )
                [1] => Array
                    (
                        [TemporaryTable] => Array
                            (
                                [id] => 2
                                [name] => test_test
                            )
                    )
            )
        */
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel バージョン確認コマンド

はじめに

これは初心者エンジニアがフルスタックエンジニアを目指すための備忘録である

Laravelバージョン確認コマンド

php artisan --version

または

php artisan -V

のどちらかです。

php artisan -v

// これが出たらOK!

Laravel Framework 5.8.38 

//←投稿者:現在のバージョン

以上です。

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

PHP『include()』と『require()』違い

『include()』と『require()』の違い

ファイルの読み込みに失敗した場合の処理に違いがあって、
『include()』の場合はファイルの読み込みに失敗しても『include()』の
処理部分だけ中止してその先に続く記述は処理が実行される。

逆に『require()』ではファイルの読み込みに失敗した場合はその場ですぐエラーとなり、
その先の記述を含めて処理が停止される。

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

Laravel npm installを実行しようとしたらエラーが出た話 超簡易版

目的

  • Laravelアプリに必要なJSパッケージをインストールするためにnpm installを実行したがエラーが出た話をまとめる

問題までの経緯

  1. Laravelアプリ名ディレクトリの一つ上の階層で下記コマンドを実行した。

    $ npm install
    

問題

  1. 下記エラーが発生しコマンドが正常実行されない。

    npm WARN saveError ENOENT: no such file or directory, open '/usr/share/nginx/html/package.json'
    npm WARN enoent ENOENT: no such file or directory, open '/usr/share/nginx/html/package.json'
    npm WARN html No description
    npm WARN html No repository field.
    npm WARN html No README data
    npm WARN html No license field.
    
    audited 15 packages in 0.511s
    
    3 packages are looking for funding
      run `npm fund` for details
    
    found 0 vulnerabilities
    

問題解決までの経緯

  1. $ npm installを実行するディレクトリを間違えていた。
  2. Laravelアプリ名ディレクトリに移動して$ npm installを実行したところ正常に実行された。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP タイムスタンプと日時を相互変換

PHPの日付関連パーサーによらず自前で変換するにはどうすればいいのか気になったので試しに作ってみたクラスです。

date_to_timestamp_class.php
<?php
class DateToTimestamp {
    private $offsetDays;

    public function __construct() {
        // 0001-01-01から1970-01-01までのオフセット日数
        $this->offsetDays = 719163;
    }
    /**
     *  日時 -> UNIXタイムスタンプ変換
     */
    public function dateToTimestamp(...$args) {
        // 引数変数名リスト
        $vars = ['year', 'month', 'day', 'hour', 'minute', 'second'];

        // 第一引数が配列なら各引数に振り分け
        if(isset($args[0]) && is_array($args[0]))
            foreach($vars as $i => $vn) $args[$i] = func_get_arg(0)[$vn] ?? null;

        // 各引数を該当変数へ
        foreach($vars as $i => $vn)
            ${$vn} = $args[$i] ?? (int) date(['Y','m','d','H','i','s'][$i]);

        try {
            if(array_reduce([$year, $month, $day, $hour, $minute, $second],
                function($a, $b) { return $a += !is_numeric($b);}
            )) {
                throw new Exception(__METHOD__. ': Non-numeric value specified for argument.');
            }
        } catch (Exception $e) {
            return $e->getmessage();
        }

        // 月オーバーフロー補正
        $year += floor(($month - 1) / 12);
        $month = ($month + 11) % 12 + 1;

        // 前年
        $prevYear = $year - 1;

        // 0001-01-01からの日数
        $absoluteDays =
            (365 * $prevYear) + floor($prevYear / 4) - floor($prevYear / 100) + floor($prevYear / 400) +
            array_sum(array_slice($this->monthLastDays($year), 0, $month - 1) ) + $day;

        return ($absoluteDays - $this->offsetDays) * 86400 // 日数を秒に変換
            + $hour * 3600 + $minute * 60 + $second // 時分秒
            + mktime(0, 0, 0, 1, 1, 1970); // 時差補正
    }

    /**
     *  UNIXタイムスタンプ -> 日時変換
     */
    public function timestampToDate(
        $format    = null,
        $timestamp = null
    ) {
        $timestamp = $timestamp ?? time();
        try {
            if($format === null) {
                throw new Exception(__METHOD__. ': Specify parameters for format string.');
            } elseif(!is_numeric($timestamp)) {
                throw new Exception(__METHOD__. ': Non-numeric value specified for argument.');
            }
        } catch (Exception $e) {
            return $e->getmessage();
        }

        // 時差補正
        $timestampTz = $timestamp - mktime(0, 0, 0, 1, 1, 1970);
        $tzDiff = date('Z');
        $absTzDiff = abs($tzDiff);

        // 1日内の秒数
        $seconds = $timestampTz % 86400;
        $seconds = $seconds < 0 ? 86400 + $seconds : $seconds;

        // 0001-01-01 00:00:00 を0とした通算日数
        $absoluteDays = (int) (floor($timestampTz / 86400) + $this->offsetDays) - 1;

        // 曜日
        $w = ($absoluteDays + 1) % 7;

        // 年算出
        $r = [
            400 => 146097, // 400年 = 36524 * 4 + 1日
            100 =>  36524, // 100年 = 1461 * 25 - 1日
              4 =>   1461, // 4年 = 365 * 4 + 1日
              1 =>    365,
        ];
        $y = [];

        // 通算日数マイナス補正
        $absDaysMinusMagnification = 0;
        if($absoluteDays < 0) {
            $absDaysMinusMagnification = floor(($absoluteDays -1) / $r[400]);
            $absoluteDays = (($absoluteDays % $r[400]) + $r[400]) % $r[400];
            $w = ($absoluteDays + 1) % 7;
        }
        foreach($r as $k => $v) {
            $y[$k] = floor($absoluteDays / $v) * $k;
            $absoluteDays %= $v;
        }

        // 閏年末日補正
        if(($y[1] == 4 || ($y[400] % 400 == 0 && $y[100] == 400)) && $absoluteDays == 0) {
            $y[1]--;
            $absoluteDays = 365;
        }

        // 年月日取得
        $year = array_sum($y) + 1;
        $absoluteDays += 1;
        $monthLastDays = $this->monthLastDays($year);

        for($month = 1;
            array_sum(array_slice($monthLastDays, 0, $month)) < $absoluteDays && $month < 13;
            $month++
        );

        // 年内日数
        $day = $absoluteDays - array_sum(array_slice($monthLastDays, 0, $month - 1));

        // 通算日数マイナス補正戻し
        if($absDaysMinusMagnification) {
            $year += 400 * $absDaysMinusMagnification;
        }

        // 時分秒取得
        $hour = (($seconds % 86400) / 3600) % 24;
        $minute = ($seconds % 3600) / 60;
        $second = $seconds % 60;

        // フォーマット文字置換
        $result = preg_replace('/(\w)/', ":$1:", $format);
        $result = preg_replace('/\\\\:(\w):/', "$1", $result);
        $result = str_replace(':Y:', sprintf($year < 0 ? '%05d' : '%04d', $year), $result);
        $result = str_replace(':m:', sprintf('%02d', $month), $result);
        $result = str_replace(':d:', sprintf('%02d', $day), $result);
        $result = str_replace(':H:', sprintf('%02d', $hour), $result);
        $result = str_replace(':i:', sprintf('%02d', $minute), $result);
        $result = str_replace(':s:', sprintf('%02d', $second), $result);

        $result = str_replace(':y:', sprintf('%02d', $year % 100), $result);
        $result = str_replace(':n:', $month, $result);
        $result = str_replace(':j:', $day, $result);
        $result = str_replace(':G:', $hour, $result);
        $result = str_replace(':h:', sprintf('%02d', ($hour + 11) % 12 + 1), $result);
        $result = str_replace(':g:', ($hour + 11) % 12 + 1, $result);
        $result = str_replace(':L:', $monthLastDays[1] === 29 ? 1 : 0, $result);

        $result = str_replace(':w:', $w, $result);
        $result = str_replace(':N:', ($w + 6) % 7 + 1, $result);
        $result = str_replace(':z:', $absoluteDays - 1, $result);
        $result = str_replace(':t:', $monthLastDays[$month - 1], $result);

        $result = str_replace(':U:', $timestamp, $result);

        $result = str_replace(':a:', $hour < 12 ? 'am' : 'pm', $result);
        $result = str_replace(':A:', $hour < 12 ? 'AM' : 'PM', $result);
        $result = str_replace(':l:',
            ['Sunday',   'Monday', 'Tuesday', 'Wednesday',
             'Thursday', 'Friday', 'Saturday'][$w], $result);
        $result = str_replace(':D:',
            ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][$w], $result);
        $result = str_replace(':F:',
            ['January',   'February', 'March',    'April',
             'May',       'June',     'July',     'August',
             'September', 'October',  'November', 'December'][$month - 1], $result);
        $result = str_replace(':M:',
            ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
             'Sep', 'Oct', 'Nov', 'Dec'][$month - 1], $result);

        // ISO8601
        $result = str_replace(':c:',
            sprintf($year < 0 ? '%05d' : '%04d', $year).
            sprintf('-%02d-%02d', $month, $day). 'T'.
            sprintf('%02d:%02d:%02d', $hour, $minute, $second).
            ($tzDiff < 0 ? '-' : '+').
            sprintf('%02d:%02d', floor($absTzDiff / 3600) % 24, floor($absTzDiff / 60) % 60)
            , $result);

        // タイムゾーン関連等、日時計算を伴わないものはとりあえずdate()の結果を素通し
        $result = preg_replace_callback('/:([eIOPTZ]):/', function($m) { return date($m[1]);}, $result);

        return preg_replace('/:(\w):/', "$1", $result);
    }

    /**
     *  指定した年の各月末日を返す
     */
    private function monthLastDays($year) {
        $year = (($year % 400) + 400) % 400; // マイナス補正
        return [31, (!($year % 4) && ($year % 100) || !($year % 400)) ? 29 : 28,
                31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    }
}

素直にDateTimeImmutable等を使えばいいので利用価値はほとんどありませんが、参考に置いておきます。
もっとスマートに計算できる方法があるかもしれませんが、現状これで精一杯。
計算間違いもあるかもしれません。

動作チェックサンプル

test.php
<?php
$timezone = 'UTC';
//$timezone = 'Asia/Tokyo';
//$timezone = 'Etc/Gmt-9';

require_once('./date_to_timestamp_class.php');
$d = new DateToTimestamp;
date_default_timezone_set($timezone);

$dti = new DateTimeImmutable;
$dti->setTimezone(new DateTimeZone($timezone));

$time = time();
$format = 'Y-m-d H:i:s';
$endtime = $d->dateToTimestamp(-2000,1,1,0,0,0);
do {
    printf("%d %s %s %s\n",
        $time,
        $res1 = $dti->setTimestamp($time)->format($format),
        $res2 = $d->timestampToDate($format, $time),
        $res1 == $res2 ? '-' : '*' // 差異チェック
    );
    $time -= 86400 * 7;
} while($time > $endtime);

for($year = -1000; $year < 2100; $year++){
    for($month = 1; $month <= 12; $month++){
        foreach([-1, 0, 29, 40] as $day){
        printf("%04d-%02d-%02d %s %s %s\n",
            $year,$month,$day,
            $res1 = $dti->setTime(0,0,0)->setDate($year,$month,$day)->getTimestamp(),
            $res2 = $d->dateToTimestamp($year,$month,$day, 0,0,0),
            $res1 == $res2 ? '-' : '*'
        );
        }
    }
}

こちらのコメントで教えて頂いたタイムゾーンがAsia/Tokyoの場合の特例には対応していないので、該当範囲内では異なる結果になります。

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

Laravel MySQL 既存テーブルの任意の場所にカラムを追記する 簡易版

目的

  • 途中でカラムを追加する際に既存カラムの間に新規カラムを追加する方法をまとめる

実施環境

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

記載例

  • 下記の様にカラムを追加するマイグレーションファイルに記載することにより任意のカラムの後に新規カラムを追加することができる。

    $table->データ型('新規カラム名', オプション)->after('新規カラムを追加したい位置の一つ前ののカラム名');
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

test

test

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

【Laravel】プロフィール画像アップロード

実装したいこと

  • usersテーブルにプロフィール画像を追加
  • デフォルト画像を用意
  • プロフィール画像変更
  • 変更時、選択した画像を表示
  • アップロード時、画像をリサイズ

前提

  • Laravel7
  • マイグレーション済み
  • routes/web.phpにRoute::resource('user', 'UserController');を記述済み

実装

データベースに画像のファイル名を保存しておいて、そのファイル名をもとに画像の読み出しを行う、という戦略をとることとします。
画像の保存場所はpublic/storage/profiles/とします。このディレクトリにあらかじめデフォルトの画像を配置しておいてください。

profile_imageカラムの追加

マイグレーションファイルを作成します。

% php artisan make:migration add_column_to_users_table --table=users

カラムを追加する処理を書きます。

database/migrations/20XX_00_00_000000_add_column_to_users_table.php
class AddColumnToUsersTable extends Migration
{
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('profile_image')->default('default.png');
        });
    }

    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('profile_image');
        });
    }
}

マイグレーション

% php artisan migrate

User.phpの$fillableにカラムを追加

app/User.php
    protected $fillable = [
        'name', 'email', 'password', 'profile_image'
    ];

画像の表示

画像の表示の一例を以下に示します

コントローラー

app/Http/Controllers/ProfileController.php
    public function profile() {
        $user = Auth::user();
        return view('profile', ['user' => $user]);
    }

ビュー

resources/views/profile.blade.php
<img src="{{ asset('storage/profiles/'.$user->profile_image) }}" alt="プロフィール画像">

プロフィール画像編集(アップロード)

ここで、Intervention Imageを用います。
Intervention Imageを使用するには、以下のコマンドを実行するだけでいいです。

% composer require intervention/image

Intervention Imageの使い方は以下のブログを参考にしました。
完全網羅!Intervention Image(PHP)で画像を編集する全実例
公式はこちら

コントローラー
Intervention Imageでの処理は、saveProfileImage()内で行っています。
バリデーションはフォームリクエストUserRequestで行います。(省略)

app/Http/Controllers/UserController.php
    public function edit($id) {
        $user = Auth::user();
        return view('user.edit', ['user' => $user]);
    }

    public function update($id, UserRequest $request) {
        $user = Auth::user();
        $form = $request->all();

        $profileImage = $request->file('profile_image');
        if ($profileImage != null) {
            $form['profile_image'] = $this->saveProfileImage($profileImage, $id); // return file name
        }

        unset($form['_token']);
        unset($form['_method']);
        $user->fill($form)->save();
        return redirect('/home');
    }

    private function saveProfileImage($image, $id) {
        // get instance
        $img = \Image::make($image);
        // resize
        $img->fit(100, 100, function($constraint){
            $constraint->upsize(); 
        });
        // save
        $file_name = 'profile_'.$id.'.'.$image->getClientOriginalExtension();
        $save_path = 'storage/profiles/'.$file_name;
        $img->save($save_path);
        // return file name
        return $file_name;
    }

ビュー

  • 現在のプロフィール画像を最初に表示
  • プロフィール画像をクリックしたら、画像ファイルを選択できる
  • 選択した画像ファイルが表示される

以上を満たすように実装します。
「選択画像が変化したらアップロードしようとしている画像を表示する」といった処理をJavaScriptで実装しています。

ファイルをアップロードするためには、enctype="multipart/form-data"をformタグに追加する必要があります。

resources/views/user/edit.blade.php
<form method="post" action="{{ route('user.update', ['user' => $user->id]) }}" enctype="multipart/form-data">
  @csrf
  @method('PATCH')

  <label for="profile_image">プロフィール画像</label>

  <label for="profile_image" class="btn">
    <img src="{{ asset('storage/profiles/'.$user->profile_image) }}" id="img">
    <input id="profile_image" type="file"  name="profile_image" onchange="previewImage(this);">
  </label>

  <button type="submit" class="btn btn-primary">
    変更
  </button>
</form>

<script>
  function previewImage(obj)
  {
    var fileReader = new FileReader();
    fileReader.onload = (function() {
      document.getElementById('img').src = fileReader.result;
    });
    fileReader.readAsDataURL(obj.files[0]);
  }
</script>

SASS(CSS)

input#profile_image {
    display: none;
}

おわり

画像のリサイズやJavaScriptの部分は一例に過ぎないので、他のサイトで別の方法をとてもいいかも
Intervention Imageは便利なので上で紹介したサイトで勉強してみては?

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

Laravelで同じURLにルーティングをする方法

概要

Laravelでルーティングをしようと思った際に困ったことがあり、ネットにあまり情報がなかったので備忘録として投稿しました。
初めてQiitaに投稿するので至らないところがあると思いますがご了承下さい。
また、PHPに触れるのも2,3週間ほどしか経っていないです。

困ったこと

/routes/web.phpにルーティングについての記述をしていました。

/routes/web.php
Route::get('/sql/hogehoge', function () {
    return view('sql/hogehoge');
});

Route::get('/sql/hogehoge/', 'SQLController@search');
Route::post('/sql/hogehoge/', 'SQLController@searchResults');

Route::get('/sql/hogehoge/', 'SQLController@index1_search');
Route::post('/sql/hogehoge/', 'SQLController@index1_searchResults');

SQLController.php
public function search(){
       $sql = "SELECT name FROM tables";
       $stmt = $pdo->prepare($sql);
       $stmt->execute(); 

       return view('sql/hogehoge', [
            'names' => $stmt
        ]);
}

public function searchResults(){

    $name_id = request('name');
    return $name_id;
}

public function index1_search(){
       $sql = "SELECT date FROM tables";
       $stmt = $pdo->prepare($sql);
       $stmt->execute();

       return view('sql/hogehoge', [
            'dates' => $stmt
        ]);
}
public function index1_searchResults(){
       $date_id = request('date');
       return $date_id;
}

Route::get()の第1引数にはURLを書きます。第2引数にはどこのメソッドで処理をするのか書きます。
上記の例ですとhttp://localhost/sql/hogehoge と検索したときにSQLController.phpにあるsearchメソッドが呼び出されて処理が行われるという意味になります。

また、SQLController.phpで処理したデータを表示させるために以下のresult.php(仮称)を作成しました。

result.php
 <form action ="./SQLController" method="POST">

                <label for="Hogehoge">Hogehoge</label>
                <select class="form-control" id="name" name="name">
                    <option value="" selected></option>  
                    <?php 
                        if(isset($names))
                        foreach ($names as $v) { ?>
                        <option value=<?php echo $v["first"]; ?>><?php echo $v["last"]; ?></option>
                    <?php } ?>
                </select>
                </form>

 <form action ="./SQLController" method="POST">

                <label for="Tournament">Tournament</label>
                <select class="form-control" id="date" name="date">
                    <option value="" selected></option>  
                    <?php 
                        if(isset($dates))
                        foreach ($dates as $v) { ?>
                        <option value=<?php echo $v["month"]; ?>><?php echo $v["year"]; ?></option>
                    <?php } ?>
                </select>
                </form>

だいぶ省略していますがこれを実行しようとするとプルダウンメニューがname用とdate用で2つでてくるのですが、なんとdate用のプルダウンメニューしかsqlの処理がなされませんでした。name用のプルダウンメニューには何も表示されませんでした。

原因

原因はweb.phpにありました。

/routes/web.php
Route::get('/sql/hogehoge/', 'SQLController@search');
Route::post('/sql/hogehoge/', 'SQLController@searchResults');

Route::get('/sql/hogehoge/', 'SQLController@index1_search');
Route::post('/sql/hogehoge/', 'SQLController@index1_searchResults');

上記ではRoute::getの第1引数が同じものを使っていることがわかると思います。
同じものを記述するとあとに記述したもの(ここではSQLController@index1_searchResults)が優先されて実行されるようです。
実際そのような結果が筆者は確認できました。

解決方法

同じ第1引数を記述したことに原因があるのでそれについて変更を加えました。
web.phpは処理をするメソッドを一つにまとめました。

/routes/web.php
Route::get('/sql/hogehoge/', 'SQLController@search');
Route::post('/sql/hogehoge/', 'SQLController@searchResults');

それにともないSQLController.phpのほうのメソッドも一つにまとめました。

SQLController.php
public function search(){
       $name_sql = "SELECT name FROM tables";
       $name_stmt = $pdo->prepare($name_sql);
       $name_stmt->execute(); 

       $date_sql = "SELECT date FROM tables";
       $date_stmt = $pdo->prepare($date_sql);
       $date_stmt->execute();


       return view('sql/hogehoge', [
            'names' => $name_stmt, 
            'dates' => $date_stmt
        ]);
}

public function searchResults(){

    $name_id = request('name');
    return [$name_id, $date_id];
}

メソッドで複数の値を返したいときはreturn [];の形で書きます。

以上のようにしてみたら上手く複数のプルダウンメニューが表示できるようになりました。

おわりに

コードは適当に変数や値をつけてしまった部分があるので間違いがあると思います。
ほかにもいい方法があるのかもしれませんが、3,4時間ほど悩んでようやく解決できた苦痛を他の方々にも味わってほしくないと思い、ここに書き記しました。

なにか間違い等ありましたら教えて下さい。また、ほかにもなにかいい方法がありましたら教えて下さい。

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

paizaラーニングを使ってみて良いなと思ったところ

paizaラーニングの良いなと思ったところについて書いていきます。

その1:開発環境を用意しなくていい
その2:取り扱っている講座が豊富で選びやすい
その3:とりあえず無料で始められる

ここからそれぞれの良いところを自分なりに少し掘り下げていきます。

その1:開発環境を用意しなくていい
 paizaラーニングはpaiza.IOという、ブラウザで動くオンライン実行環境を使って
学習ができます。
paizaラーニングを始めるにあたって、特別な知識や経験は必要ないのです。

その2:取り扱っている講座が豊富で選びやすい
 paizaラーニングの講座は、「目的別」、「言語別」、「技術別」の大きく3種類に分かれており、自分が学びたいと思っている分野が選択しやすいです。

その3:とりあえず無料で始められる
 paizaラーニングは無料で利用ができます。(paizaのユーザー登録が必須です)
もちろん有料でないと完走できない講座もありますが、完全無料で完走ができる講座もあります。
 paizaラーニングを通して、プログラミングの楽しさを感じられる人は、有料会員になって片っ端から講座を網羅するのも良いかもしれません。

あとがき
 paizaラーニングを使ってみて良いなと思ったところについて、書いてみました。

 今回の3点の中では、やはり無料で始められるのが大きい点ではないでしょうか。
パソコンとネット環境があれば、とりあえずプログラミングの勉強が始められるなんて、良い時代になったなと思います。

 最後に余談ですが、私は[PHP入門編]を完走しました。
https://twitter.com/2go_r2071/status/1283063220106752002

 なぜPHPを選んだかというと、業務で使っているソフトのコードの文法がPHPによく似ていたためです。

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

PHP タイムゾーンがAsia/Tokyoの場合、1888-01-01より前のdateの結果がおかしい

確認スクリプト

test.php
<?php
$format = 'Y-m-d H:i:s';
foreach(['Asia/Tokyo', 'Etc/GMT-9'] as $timezone) {
    date_default_timezone_set($timezone);
    echo "$timezone\n";
    $datetime = new DateTimeImmutable();
    echo "     / timestamp   / date                / DateTimeImmutable\n";
    for($i = 0; $i < 10; $i++) {
        $timestamp = -2587712405 + $i;
        printf("%4d / %d / %s / %s\n",
            $i,
            $timestamp,
            date($format, $timestamp),
            $datetime->setTimestamp($timestamp)->format($format)
        );
    }
    echo "\n";
}

foreach(['Asia/Tokyo', 'Etc/GMT-9'] as $timezone) {
    date_default_timezone_set($timezone);
    echo "$timezone\n";
    echo strtotime('1887-12-31 23:59:59')."\n";
    echo strtotime('1888-01-01 00:00:00')."\n\n";
}
$ php -v
PHP 7.4.4 (cli) (built: Mar 20 2020 13:47:45) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.4, Copyright (c), by Zend Technologies

$ php test.php
Asia/Tokyo
     / timestamp   / date                / DateTimeImmutable
   0 / -2587712405 / 1888-01-01 00:18:54 / 1888-01-01 00:18:54
   1 / -2587712404 / 1888-01-01 00:18:55 / 1888-01-01 00:18:55
   2 / -2587712403 / 1888-01-01 00:18:56 / 1888-01-01 00:18:56
   3 / -2587712402 / 1888-01-01 00:18:57 / 1888-01-01 00:18:57
   4 / -2587712401 / 1888-01-01 00:18:58 / 1888-01-01 00:18:58
   5 / -2587712400 / 1888-01-01 00:00:00 / 1888-01-01 00:00:00
   6 / -2587712399 / 1888-01-01 00:00:01 / 1888-01-01 00:00:01
   7 / -2587712398 / 1888-01-01 00:00:02 / 1888-01-01 00:00:02
   8 / -2587712397 / 1888-01-01 00:00:03 / 1888-01-01 00:00:03
   9 / -2587712396 / 1888-01-01 00:00:04 / 1888-01-01 00:00:04

Etc/GMT-9
     / timestamp   / date                / DateTimeImmutable
   0 / -2587712405 / 1887-12-31 23:59:55 / 1887-12-31 23:59:55
   1 / -2587712404 / 1887-12-31 23:59:56 / 1887-12-31 23:59:56
   2 / -2587712403 / 1887-12-31 23:59:57 / 1887-12-31 23:59:57
   3 / -2587712402 / 1887-12-31 23:59:58 / 1887-12-31 23:59:58
   4 / -2587712401 / 1887-12-31 23:59:59 / 1887-12-31 23:59:59
   5 / -2587712400 / 1888-01-01 00:00:00 / 1888-01-01 00:00:00
   6 / -2587712399 / 1888-01-01 00:00:01 / 1888-01-01 00:00:01
   7 / -2587712398 / 1888-01-01 00:00:02 / 1888-01-01 00:00:02
   8 / -2587712397 / 1888-01-01 00:00:03 / 1888-01-01 00:00:03
   9 / -2587712396 / 1888-01-01 00:00:04 / 1888-01-01 00:00:04

Asia/Tokyo
-2587713540
-2587712400

Etc/GMT-9
-2587712401
-2587712400

タイムゾーンをEtc/GMT-9にすると大丈夫っぽい。
他のタイムゾーンは未確認。

JavaScriptはどうなのか確認してみたら、同様のずれ。
(Chrome 83で確認)

{
    for(let i = 0; i < 10; i++) {
        const timestamp = -2587712405 + i;
        console.log(timestamp * 1000, new Date(timestamp * 1000));
    }
}
-2587712405000 Sun Jan 01 1888 00:18:54 GMT+0918 (日本標準時)
-2587712404000 Sun Jan 01 1888 00:18:55 GMT+0918 (日本標準時)
-2587712403000 Sun Jan 01 1888 00:18:56 GMT+0918 (日本標準時)
-2587712402000 Sun Jan 01 1888 00:18:57 GMT+0918 (日本標準時)
-2587712401000 Sun Jan 01 1888 00:18:58 GMT+0918 (日本標準時)
-2587712400000 Sun Jan 01 1888 00:00:00 GMT+0900 (日本標準時)
-2587712399000 Sun Jan 01 1888 00:00:01 GMT+0900 (日本標準時)
-2587712398000 Sun Jan 01 1888 00:00:02 GMT+0900 (日本標準時)
-2587712397000 Sun Jan 01 1888 00:00:03 GMT+0900 (日本標準時)
-2587712396000 Sun Jan 01 1888 00:00:04 GMT+0900 (日本標準時)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む