Quantcast
Channel: 初心者タグが付けられた新着記事 - Qiita
Viewing all 21579 articles
Browse latest View live

Markdown テーブル セル内で改行を行う方法

$
0
0

目的

  • Markdown内のテーブル内のセルで改行を行う方法をまとめる

結論

  • 改行したい箇所に<br>を入れることで改行を行うことが可能である。
  • <br><br>とする事によりセルないに空の行を挿入することも可能である。

書き方の例

  • 下記にMarkdownで記載したテーブルのセル内で改行を行う方法をまとめる

    |  | 見出しA | 見出しB |
    |---|---|---|
    | 情報1 | A<br>B<br>C<br>D | A<br>B<br>C<br>D<br>E |
    
  • 下記に先に記載したテーブルのサンプルコードのプレビューを記載する。

    見出しA見出しB
    情報1A
    B
    C
    D
    A
    B
    C
    D
    E

Bootstrapについてゼロから学習

$
0
0

初学者がBootstrapについてゼロから学んだことを記述しています。

#Bootstrapとは
 様々なブラウザや画面サイズに対応した本格的なWEBサイトを簡単に作ることができるWEBフレームワークである。
 ※WEBフレームワークは、WEBサイトの開発を容易にする「雛形」のこと

 ●Bootstrapのメリット
  ・個人開発〜大規模開発まで幅広い用途に対応
  ・レスポンシブデザイン
  ・テンプレートやプラグインによるカスタマイズ
   等を簡単に実装することができ、短期間で本格的なWEBサイトを作成できる。
 ●デメリット
  ・独自のルールを学習する必要がある
  ・似たようなデザインになりやすい
  ・細かくカスタマイズすると逆に工数が増える

  

Bootstrapの機能

グリッドシステム

  グリッドシステム=画面を縦に分割して、ブロックごとに図や文章を配置する手法で、画面サイズに合わせてコンテンツの幅と数を制御できる。
  ※縦に分割した列1つ1つをグリッドという
  グリッドシステムでは、必ず全体を「container」または「container-fluid」というクラスで囲む
  ●containerを使用した場合
   画面幅に応じて、コンテンツや余白の幅が自動で変わる。

ウィンドウ幅内部コンテンツ幅
576px未満ウィンドウ一杯
576〜768px未満常に540px
768〜992px未満常に720px
992〜1200px未満常に960px
1200px以上常に1140px

  ●container-fluidを使用した場合
   画面幅に関係なく、常に画面いっぱいに広がる
  
  ●rowとcolを使う
   内部コンテンツは、row(行)の中に、col(列)で作成する

例)
<body><divclass="container"><!--全体をcontainerで囲む--><divclass="row"><!--rowでcolを囲む--><divclass="col"style="background":red;height:200px;">A</div><divclass="col"style="background":yellow;height:200px;">B</div><divclass="col"style="background":blue;height:200px;">C</div></div></div></body>
例)
<body><divclass="container"><!--全体をcontainerで囲む--><divclass="row"><!--rowでcolを囲む--><!--colのあとに「-数字」で縦に12分割のうち、どれくらいの幅にするか指定する--><divclass="col−3"style="background":red;height:200px;">A</div><divclass="col-4"style="background":yellow;height:200px;">B</div><divclass="col-5"style="background":blue;height:200px;">C</div></div></div></body>
レスポンシブデザイン

 横幅が長いPC画面と、横幅が短いスマホなどの画面では表示が変わる
 ブレイクポイント
  PC表示とスマホ表示が変わる境界線をブレイクポイントと言う。
  一般的には、768px(col-md)でPC表示とスマホ表示を切り替える

0〜575px576〜767px768〜991px992〜1999px1200px以上
クラスcolcol-smcol-mdcol-lgcol-xl
例)
<body><divclass="container"><!--全体をcontainerで囲む--><divclass="row"><!--rowでcolを囲む--><!--col-md(768px)がブレイクポイントということ--><divclass="col-md−3"style="background":red;height:200px;">A</div><divclass="col-md-4"style="background":yellow;height:200px;">B</div><divclass="col-md-5"style="background":blue;height:200px;">C</div></div></div></body>
例)
<body><divclass="container"><!--全体をcontainerで囲む--><divclass="row"><!--rowでcolを囲む--><!--col-md(768px)未満の時はそれぞれcol-4で、それ以上の時は3,4,5ということ)--><divclass="col-4 col-md−3"style="background":red;height:200px;">A</div><divclass="col-4 col-md-4"style="background":yellow;height:200px;">B</div><divclass="col-4 col-md-5"style="background":blue;height:200px;">C</div></div></div></body>

 画面幅に応じた要素の非表示
 class="d-●●-none"とすると、
 ●●pxの画面以上は、特定の要素を非表示にできる。

0〜575px576〜767px768〜991px992〜1999px1200px以上
特定の要素を
非表示にするクラス
d-noned-sm-noned-md-noned-lg-noned-xl-none
例)
<body><divclass="container"><!--全体をcontainerで囲む--><divclass="row"><!--rowでcolを囲む--><!--md(768px)以上の時はそれぞれcol-4で、それ以上の時はA要素を非表示ということ)--><divclass="col-12 d-md-none"style="background":red;height:200px;">A</div><divclass="col-6 col-md-8"style="background":yellow;height:200px;">B</div><divclass="col-6 col-md-4"style="background":blue;height:200px;">C</div></div></div></body>

 画面幅に応じた要素の表示
 class="d-none d-●●-block"とすると、
 ●●pxの画面以上は、特定の要素を表示することができる。

0〜575px576〜767px768〜991px992〜1999px1200px以上
特定の要素を
表示するクラス
d-blockd-sm-blockd-md-blockd-lg-blockd-xl-block
例)
<body><divclass="container"><!--全体をcontainerで囲む--><divclass="row"><!--rowでcolを囲む--><!--md(768px)未満の時はA要素を非表示で、768px以上の時はA要素を表示する)--><divclass="col-12 d-none d-md-block"style="background":red;height:200px;">A</div><divclass="col-8"style="background":yellow;height:200px;">B</div><divclass="col-4"style="background":blue;height:200px;">C</div></div></div></body>

 ②シンプルで見やすいテーブル
  
 ③デザインされたボタン
  

Bootstrapの仕組み

 CSSとJavaScriptにより構成されている。
 Bootstrapを使用するには
 ①ネット上のBootstrapファイルを読み込んで使う...CDN(Contents Delivery Network)
 ②ソースファイルをダウンロードして使う
 ③コンパイル済みのファイルをダウンロードして使う
 ※本記事では③を仕様

Bootstrapの読み込み

  ●HTMLの

タグ内にcssファイルのパスを記述
   
  ●HTMLの直後にjsファイルのパスを記述
   

画像やブロック要素の調整

img-fluid

  画像の幅をブロック幅に合わせて表示する
  <img src="ファイル名" Class="img-fluid">

paddingとmargin

  padding... class="p●-X"
  margin... class="m●-X"

  方向を指定する(●の部分)
|記述方法|t|r|b|l|x|y|無し|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|間隔を開ける方向|上|右|下|左|上下|左右|上下左右|

記述方法012345
間隔00.25rem0.5rem1rem1.5rem3rem
Bootstrap特有のclass
①左寄せ、中央寄せ、右寄せ

  左寄せ... class="text-left"
  中央寄せ... class="text-center"
  右寄せ... class="text-right"
  

②見出し

  h1... <h1></h1> または class="h1"
  h2... <h2></h2> または class="h2"

③リスト

  <li></li>のデフォルト設定の点を削除... class="list-unstyled"
  リストでテキストを横並びにする... <ul class="list-inline">
                    <li class="list-inline-item"></li>
                  </ul>

④幅や高さの指定

  幅や高さを指定するclass
   幅を指定するclass

記述方法w-25w-50w-75w-100mw-100w-auto
意味width:25%width:50%width:75%width:100%width:100%max-width:100%

   高さを指定するclass

記述方法h-25h-50h-75h-100mh-100h-auto
意味height:25%height:50%height:75%height:100%height:100%max-height:100%

※横幅、高さを%で指定した場合、親要素を100%とした時の割合で表示される

⑤float

  グリッドシステムを無視して要素を横並びにする場合、
  通常のHTML/CSSのようにfloatを使用

  左寄せ... class="float-left"
  ※floatさせる要素自体に指定
  右寄せ... class="float-right"
  ※floatさせる要素自体に指定
  float解除... class="clear-fix"
  ※floatさせる要素全体を囲む親要素に指定

⑥ナビゲーションバーの型

  「navbar」「navbar-expand-ブレイクポイント」の2つのclassを記述
  <nav class="navbar navbar-expand-ブレイクポイント>
    リストでナビゲーションバーを作成する場合は、ulに「navbar-nav」というclassを記述
    <ul class="navber-nav">
     liに「nav-item」、aに「nav-link」というclassを記述
     <li class="nav-item"><a href="●●" class="nav-link>XXXX</a></li>
     
    </ul>
  </nav>ナビゲーションバーは<nav>で囲む
  

PHP入門

$
0
0

JavaやJavaScriptなどと並行して学習を進めており、基礎的な文法は省略しているところも多いが、初心者がゼロからphpに関して学習した内容を以下に記述しています。

PHPプログラミングの手順

  ①エディタを使用してプログラミングを記述
  ②任意の名前.phpで保存
  ③保存したphpファイルをサーバーへアップロード

環境構築

  今回はMAMPをインストールして利用した。

PHPの書き方

  <? php
   ここにプログラミングを記述する
  ?>

出力の方法

   echo "Hello World";
  または、
   print "Hello World";

  改行したい場合
   echo "1行目<br>2行目";

変数

  変数名の前にはをつける
  大文字と小文字は区別される
  変数名の先頭に数字、_以外の数字は変数名に使えない
 例)
  $ABC $abc $Abc

代入

  変数に値を代入するには「=」を使う
  $a = 1;
  $a = "文字列";   //文字列は「" "」で囲む

if文

  if (条件) {処理内容}
 例)もし$aが10より大きい場合は、Aと表示する
  $a = 15;
  if ($a > 10) {
  echo "A";
  }

elseif文

  if (条件) {処理内容}
 例)もし$aが10より大きい場合は、Aと表示する
   そうでない場合で、$aが10未満の場合は、Bと表示する
  $a = 15;
  if ($a > 10) {
  echo "A";
  } elseif ($a < 10){
  echo "B";
  }

else文

  if (条件) {処理内容}
 例)もし$aが10より大きい場合は、Aと表示する
   そうでない場合は、Bと表示する
  $a = 15;
  if ($a > 10) {
  echo "A";
  } else {
  echo "B";
  }
  

swich文

  switch ($a) {
    case〇〇 : 処理内容;
    break;
  }

配列

配列の書き方

  $name = array("田中"、"高橋"、"斎藤");

 または
  $name[0] = "田中";
  $name[1] = "高橋";
  $name[2] = "斎藤";

要素の追加

  $name = array("田中"、"高橋"、"斎藤");
  $name[] = "新しい要素";
  ※$name[3]に新しい要素が追加される

値の更新

  $name = array("田中"、"高橋"、"斎藤");
  $name[0] = "加藤";
  ※田中→加藤

要素の削除

  $name = array("田中"、"高橋"、"斎藤");
  unset($name[0]);
  ※田中を削除

連想配列

 例)普通の配列
  $name = array("田中"、"高橋"、"斎藤");
 例)連想配列
  $name = array("名前1" => "田中"、"名前2" => "高橋"、"名前3" => "斎藤");

 例)田中を呼び出す場合
  echo $name["名前1"];

多次元配列

  配列の各箱の中に、さらに小さな箱を入れて管理する方法
 例)
  $name = array(
        array("田中","高橋"),
        array("Bob","Emily"),
        array("キム","パク"),
  );
  
 例)田中を呼び出す場合
  echo $name[0][0];

多次元配列の値に、配列変数を指定することもできる
 例)アジア配列、アメリカ配列を、カントリー配列に代入
  $asia = array("日本","タイ");
  $america = array("アメリカ","ブラジル");
  $country = array($asia,$america);

 例)日本を呼び出す場合
  echo $country[0][0];

多次元連想配列

  配列の各箱の中に小さな箱が入っており、各箱の添え字に別の文字列等を使って管理する方法
 例)
  $country = array(
   "アジア" => array("東アジア" => "日本", "東南アジア" => "ブラジル"),
   "アメリカ" => array("北米" => "アメリカ", "南米" => "ブラジル"),
   "欧州" => array("西欧" => "フランス", "東欧" => "ロシア")
  );

 例)タイを呼び出す場合
  echo $country["アジア"]["東南アジア"]
  

シングルクォーテーションとダブルクォーテーションの違い

      
文字列計算式変数処理速度
シングルクォーテーションそのまま文字列として表示そのまま文字列として表示そのまま文字列として表示速い
ダブルクォーテーションそのまま文字列として表示そのまま文字列として表示変数を展開して中身を表示遅い

[.]ドット

  ドットは文字列の連結に使われる。
 例)
  $name = "田中";
  $age = 25;

echo $name."さん(".$age.”歳)”
  ↓
  田中さん(25歳)

関数

関数の書き方

  function 関数名(引数1、引数2、引数3・・・){
   処理1;
   処理2;
  
  return 戻り値;
  }

foreach文

  配列に格納した情報を順番に表示する
  $配列変数名 = array(値0、値1、値2);
   foreach($配列変数名 as $✕){
  処理内容;}

 例)
  <?php
   $a = array("山田","佐藤","鈴木");
    foreach($a as $value){    //$aを$valueとして処理する的な意味
    echo $value;
    }
  ?>
  ↓
  山田佐藤鈴木

foreachでキーを表示する方法

  $配列変数名 = array(値0、値1、値2);
   foreach($配列変数名 as $△ => $✕){
  echo $△.$✕;}

 例)
  <?php
   $a = array("山田","佐藤","鈴木");
    foreach($a as $b => $c){    //$aを$b(キー)と$c(値)として処理する的な意味
    echo $b.$c;           //キーと値をそれぞれ表示
    }
  ?>
  ↓
  0山田1佐藤2鈴木

連想配列で各キーに名前をつける方法

 例)
  <?php
   $a = array("【名前1】" => "山田","【名前2】" => "佐藤","【名前3】" => "鈴木");
    foreach($a as $b => $c){    //$aを$b(キー)と$c(値)として処理する的な意味
    echo $b.$c;           //キーと値をそれぞれ表示
    }
  ?>
  ↓
  【名前1】山田【名前2】佐藤【名前3】鈴木

foreach文の多次元連想配列

$food
   ●$vegetable($groupkey)
    ↓($namekey/$eigoname) => ($nihonname)
   ・"carrot" => "にんじん"
   ・"tomato" => "トマト"
   ・"onion" => "たまねぎ"
    
   ●$fruit($groupkey)
    ↓($namekey/$eigoname) => ($nihonname)
   ・"apple" => "りんご"
   ・"orange" => "オレンジ"
   ・"grape" => "ぶどう"

<?phpfood=array("vegetable"=>array("carrot"=>"にんじん","tomato"=>"トマト","onion"=>"たまねぎ"),"fruit"=>array("apple"=>"りんご","orange"=>"オレンジ","grape"=>"ぶどう");foreach(foodasgroupkey=>namekey){echo"■".groupkey."<br>";foreach(namekeyaseigoname=>nihonname){echoeigoname.":"nihonname."<br>";}echo"<br>";}?>

 ↓
■vegetable
  carrot:にんじん
  tomato:トマト
  onion:たまねぎ

 ■fruit
  apple:りんご
  orange:オレンジ
  grape:ぶどう

foreach文と配列とソート

 例)
  <?php
   $a = array("d","c","b","a");
    sort($a);
    foreach($a as
$key => $value){    //$aを$valueとして処理する的な意味
    echo $value."<br>";
    }
  ?>
  ↓
  a
  b
  c
  d と表示される

rsort(変数)

 例)配列$aの各値を降順に並べ替える
  <?php
   $a = array("d","c","b","a");
    rsort($a);
    foreach($a as
$key => $value){    //$aを$valueとして処理する的な意味
    echo $value."<br>";
    }
  ?>
  ↓
  d
  c
  b
  a と表示される(アルファベットの降順z→a)

ksort(変数)

 例)配列$aの各キーを昇順に並べ替える
  <?php
   $a = array(
          "1" => "d",
          "2" => "c",
          "3" => "b",
          "4" => "a");
    ksort($a);
    foreach($a as $key => $value){    //$aを$valueとして処理する的な意味
    echo $key.":".$value."
";
    }
  ?>
  ↓
  1:d
  2:c
  3:b
  4:a と表示される

関数の書き方並べ替えの基準意味
sort($配列変数)値(バリュー)配列変数の各値を昇順に並べ替え
rsort($配列変数)値(バリュー)配列変数の各値を降順に並べ替え
asort($配列変数)値(バリュー)配列変数の各値を昇順に並べ替え 連想配列において、各キーと値の関係を維持しつつソートする。
arsort($配列変数)値(バリュー)配列変数の各値を降順に並べ替え 連想配列において、各キーと値の関係を維持しつつソートする。
ksort($配列変数)添字(キー)配列変数の各値を昇順に並べ替え 連想配列において、各キーと値の関係を維持しつつソートする。
krsort($配列変数)添字(キー)配列変数の各値を降順順に並べ替え 連想配列において、各キーと値の関係を維持しつつソートする。

問い合わせフォームの作成

web上で問い合わせフォームが動く仕組み

  ①index.html(問い合わせ画面)ーーーユーザーが入力する
  ②mail_confirm.php(入力内容確認画面)
   →不備があれば①へ戻り、入力やり直し
  ③insert.php(SQL分を記述)
  ④phpファイルから、データベースへ通信
   HTMLから引き渡された情報がデータベースへ格納される。
   (SQLのinset文)

問い合わせフォーム作成の手順

  ①設定ファイルの編集(やらないと、フォームから送信された文字列が文字化けしてデータベースに登録されてしまう)
  ②データベース上にtableを作成する
  ③HTMLとCSSを作成する
  ④phpファイルを作成する

PHP側でのPOSTの使い方

  echo $_POST['箱の名前'];  //index.htmlで作成したform内のname名前を、formの入力欄ごとに指定する必要がある。

  処理の流れ(イメージ)

index.html(問い合わせフォーム画面)
<formmethod="post"action="abc.php">名前<inputtype="text"name="箱の名前"> //①
     <inputtype="submit"value="送信">      //②
   </form>

   ↓ ↓ ↓   ①テキストボックスに名前が入力される
   ↓ ↓ ↓   ②送信ボタンがクリックされる index.htmlからabc.phpへ"箱の名前”の値が送られる
   ↓ ↓ ↓   ③引き渡されたPOSTをechoで出力する

abc.php(フォーム入力内容確認画面)
echo$_POST['箱の名前'];//③入力された内容を表示して確認 <formaction="insert.php"method="post">  //フォームクリックされた場合、insert.phpへ送信<inputclass="submit"type="submit"value="送信する"> //送信ボタン<inputtype="hidden"value="<?php echo $_POST['箱の名前']; ?>"name="箱の名前"></form>

   ↓ ↓ ↓   
   ↓ ↓ ↓   上記で送信ボタンがクリックされると、入力された情報がinsert.phpに送信される。
   ↓ ↓ ↓   

insert.php
<?php   mb_internal_encoding("utf-8");//①DBへ情報を格納する際の文字化けを防ぐ文$pdo=newPDO("mysql:dbname=lesson01;host=localhost;","サーバID","サーバpass");//③DBへ接続するための文$pdo->exec("insert into テーブル名(カラム名,カラム名)values
     ('".$_POST['箱の名前']."','".$_POST['箱の名前']."');");?>

   ↓ ↓ ↓   今回はPDOというライブラリを使ってphp上でSQL文を書いている。
   ↓ ↓ ↓   ①DB接続をして
   ↓ ↓ ↓   ②insert文でデータベースに情報を格納する

データベース

PDOの使い方

 mb_internal_encoding("utf8);

 ◆データベースと接続するための文
 $pdo = new PDO("mysql:dbname=lesson01;host=localhost;" ,"root" ,"");
  ※mysql:dbname=lesson01 MySQLに接続し、データベース「lesson01」を利用するという意味
  ※host=localhost 通常はDB用のサーバー名を記述する。ローカル環境を使用している場合はこの記述。
  ※"root"," " サーバー接続する際のIDとパスワードを記述。

 ◆データベースにデータを格納するための文
 $pdo -> exec("insert into テーブル名(カラム名,カラム名)values
 ('".$_POST['箱の名前']."','".$_POST['箱の名前']."');");   //DBへ送るための情報

 ◆データベースからデータを取り出すための文
 $stmt = $pdo -> query("select * from テーブル名");

 ※PDO実行時に気をつけること
  ①select文を使うときは
   $stmt = $pdo -> query("sql文");

  ②insert文、update文、delete文を使うときは
   $pdo -> exec("sql文");

データベースから取得した情報を表示させる文の例

データベースから取得した情報を表示させるループ処理の例
while($row=$stmt->fetch()){echo$row[`取り出すデータのカラム名`];echo$row[`取り出すデータのカラム名`];echo$row[`取り出すデータのカラム名`];}//またはforeach($stmtas$row){echo$row[`取り出すデータのカラム名`];echo$row[`取り出すデータのカラム名`];echo$row[`取り出すデータのカラム名`];}//※rowは「行」のこと//※stmtは「statement/声明」の略//※fetchは「go and get/取ってくる」こと

リダイレクト

 あるwebページから自動的に他のwebページに移動すること。
 PHPでリダイレクトを使用する場合は、下記のheader関数を使用する
  header("Location: URL または 相対パス");

session

 sessionとは、ページを飛び越えて情報を引き継ぐ技術
  
  これまでのPOST通信は、form内にPOSTの記述をすることで、
  2つのページ間で情報を引き継ぐことが可能

  
  sessionでは、複数のページ(ファイル)間で情報を引き継ぐことが可能

sessionの書き方

 session_start;
  sessionを使う場合、ファイル内にsession_start;と記述する必要がある。
  session_start;以降はsessionが有効になる。

 $_SESSION['任意の変数名'] = "文字列等";
  sessionでは、このように任意の変数に文字列等を代入できる。

 echo $_SESSION['任意の変数名'];
  一度、sessionに代入した文字列等は、どのファイル(ページ)でもsession_start;を記述した後であればechoなどで出力することができる。

sessionー同じファイル内で出力する場合

index.php
<?phpsession_start();//①session_start以降はsessionが有効になる。$_SESSION['name']="山田太郎";//②$_SESSION['〇〇'] = "△△";//△△を〇〇に代入するという意味。(=サーバーに保存する)echo$_SESSION['name'];?>

sessionー複数のファイルで出力する場合

index.php
<?phpsession_start();//①session_start以降はsessionが有効になる。$_SESSION['name']="山田太郎";//②$_SESSION['〇〇'] = "△△";//△△を〇〇に代入するという意味。(=サーバーに保存する)
xyz.php
<?phpsession_start();//他のページでもsession_startと記述すれば、そのセッションを使用できる。echo$_SESSION['name'];//②で保存したSessionをechoで表示する。   ?>

特定のセッション変数の削除

  unset($_SESSION['削除するセッション変数名']);
 

すべてのセッション変数の削除

  session_destroy();

cookie

  webサイトを訪問したユーザー情報を一時的に保存する仕組み。
  cookieはブラウザに保存されている。

cookiesession
保存場所ブラウザサーバー
有効時間コード内で自由に設定「php.ini」で設定(デフォルトは、24分)
削除方法有効期限を現在の時間よりも前の時間に設定することで、cookieを削除することができる。session_destroy(); または、ブラウザを閉じたときに自動的に削除される。
使用例ログインフォームにおいて、「ログイン情報を保持する」 にチェックを入れると、ログアウトしても、ログインに必要なメールやパスワードが フォームに入力されたままになる。ログイン状態において、ページ間(=ファイル間)で情報を引き継ぐ。

cookieの使い方

  setcookie(クッキーにつける名前,保存したいもの,有効期限);
   ※有効期限の書き方:1週間の場合→time()+60*60*24*7
    クッキーを削除したい場合:time()-1と記述する

cookieー同じファイル内で出力する場合

index.php
<?phpsetcookie(name,"保存したいもの",time()+60*60*24*7);//nameという配列に、"保存したいもの"を格納する。保存期間は1週間echo$_COOKIE['name'];//配列に格納した"保存したいもの"を出力。?>

cookieー複数のファイルで出力する場合

index.php
<?phpsetcookie(name,"保存したいもの",time()+60*60*24*7);//nameという配列に、"保存したいもの"を格納する。保存期間は1週間?>
xyz.php
<?phpecho$_COOKIE['name'];//配列に格納した"保存したいもの"を出力。?>

empty

  ある配列や変数が空であるかを判定する
  if(empty(変数)){処理内容;}
  ※空や0、NULLである場合はTrueを返し、それ以外はFalseを返す

index.php
<?phpsession_start();$_SESSION['mail']="〇〇@gmail.com";?>
xyz.php
<?phpsession_start();if(empty($_SESSION['name'])){//nameには何もセットされていないので、条件式はtrueとなり、echoが実行される。echo"nameは空です";}session_destroy();?>

isset

  ある変数や配列に値がセットされていて、かつNULLでないかを判定する
  if(isset(変数)){処理内容;}
  ※値がセットされている場合や、空や0の場合はtrueを返し、NULLがセットされている場合はFalseを返す。

index.php
<?phpsession_start();$_SESSION['name']="山田太郎";$_SESSION['mail']="〇〇@gmail.com";?>
xyz.php
<?phpsession_start();if(isset($_SESSION['name'])){//nameに山田太郎がセットされているので、条件式はtrueとなり、echoが実行される。echo"セッションあり";}session_destroy();?>

バリデーション

  問い合わせフォームや会員登録などの際に、各入力項目に適した内容が入力されているかどうかチェックすること

正規表現

  特定の文字列のパターンを文字や記号で表現する手法

正規表現の使い方

  ■\dは、1つの半角数字(0123456789)を意味する「メタ文字」

  例)電話番号090-0000-0000の正規表現
   \d\d\d-\d\d\d\d-\d\d\d\d

  ■文字の個数を限定するときは、{n,m}{n}というメタ文字(量指定子)を使用する

  例)電話番号090-0000-0000
        03-0000-0000
        043-000-0000
        01234-0-0000 の正規表現
   \d{2,5}-\d{1,4}-\d{4}

  ■「AまたはBのいずれか1文字」を表す場合は[AB]と書く
   ※文字数制限はなく、[ABC]であれば、「AまたはBまたはCのいずれか1文字」となる。
   例)英数字またはアンダースコア
    [a-z0-9_]

  ■「〜が1文字、または無し」を表す場合は?を使う
 
  ■「任意の1文字」を表す場合は.を使う

  ■「任意の文字が1文字以上」を表す場合+を使う

  ■[任意の文字が0文字以上」を表す場合を使う

  ■「/」をエスケープする場合**を使う

  ■正規表現に( )を使うと、囲まれた部分がキャプチャされ、連番がつけられる。

  ■英単語を構成する文字(大文字も小文字も両方)を表す場合は\wを使う

  ■半角スペースやタブ文字、改行文字など、目に見えない「空白文字全般」を表すメタ文字\s

  ■「A以外の任意の文字」を表す場合は[^A]と書く

  ■タブ文字を表すメタ文字\t

  ■「文字列ABCまたは文字列CDF」のor条件を表す場合はABC|DEFと書く

  ■行頭を表すアンカー^

  ■行末を表すアンカー

  ■「英単語ABCだけ」を表す場合は\bABC\bと書く

  ■(肯定の)後読み
   abcの「直後の位置」を表す場合は(?<=abc)と書く

  ■(肯定の)先読み
   abcの「直前の位置」を表す場合は(?=abc)と書く

  ■(否定の)後読み
   「abc以外の文字列の直後」を表す場合は(?<!abc)と書く

  ■(否定の)先読み
   「abc以外の文字列の直後」を表す場合は(?!abc)と書く

  ■「( )でキャプチャされた1番目の文字列」を表す場合は、\1書く

正規表現を使ったバリデーション

  ■PHPの場合
  if(preg_match("/チェック方法/",チェック対象の変数)){処理内容;}

  ■HTML5
  <input type="XXX" name="XXX" pattern="チェック方法">

正規表現の使用例(PHP)

index.php 数値かどうかチェックする場合
<?phpabc=012;if(preg_match("/^[0-9]+$/",abc)){echo'数値です';}else{echo'数値ではありません';}?>
index.php メールアドレスをチェックする場合
<?phpmail="yamada@gmail.com";if(preg_match("/^[a-z0-9._%+-]+@[a-z0-9.-]¥.[a-z]{2,3}$/",mail)){echo'正しいメールアドレスです';}else{echo'正しいメールアドレスの形ではありません';}?>
index.php 電話番号をチェックする場合
<?phptel="090-1234-5678";if(preg_match("/^[0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}$/",tel)){echo'正しい電話番号です';}else{echo'正しい電話番号の形ではありません';}?>
index.php 郵便番号(ハイフンあり)をチェックする場合
<?phpzip="100-0013";if(preg_match("/^[0-9]{3}-[0-9]{4}$/",zip)){echo'正しい郵便番号です';}else{echo'正しい郵便番号の形ではありません';}?>
index.php 郵便番号(ハイフンなし)をチェックする場合
<?phpzip="1000013";if(preg_match("/^[0-9]{7}+$/",$zip)){echo'正しい郵便番号です';}else{echo'正しい郵便番号の形ではありません';}?>
index.php パスワード(半角英数で6〜8桁の場合)をチェックする場合
<?phppass="pass1234";if(preg_match("/^[a-zA-Z0-9]{6,8}+$/",$pass)){echo'正しいパスワードです';}else{echo'正しいパスワードの形ではありません';}?>
index.php 全角文字入力をチェックする場合
<?phpa="あいうえお";if(preg_match("/^[あ-んア-ケ一-龠]+$/u",a)){echo'全角文字です';}else{echo'全角文字ではありません';}//全角文字をUTF-8の文字コードで指定したい時は、終わりの部分を「+$/」ではなく「+$/u」にする。?>
index.html メールアドレスをチェックする場合
<body><formaction="XXX"method="post"><inputtype="text"name="XXX"pattern="^[a-z0-9._ %+-]+@[a-z0-9.-]+¥.[a-z]{2,3}$"></form></body>

PHPで正規表現を行うタイミング

 ①HTMLのフォームで入力された情報が、PHPファイルへPOST通信で引き渡される。
 ②POST通信で引き渡された情報をpreg_matchでチェックする。
 ③チェック結果が正しい場合→insert文でデータベースに格納
 ④チェック結果が正しくない場合→エラーメッセージ画面へリンク

HTMLで正規表現を行うタイミング

 ①index.htmlのform内のpatternでチェックする。
 ②チェック結果が正しい場合→HTMLのフォームで入力された情報が、PHPファイルへPOST通信で引き渡される。
 ③チェック結果が正しくない場合→フォーム画面でエラーメッセージを表示
 ④insert文でデータベースに格納

画像のアップロード方法

 ①formに<input type="file">を記述
 ②form内でアップロードした画像ファイルはデータベースに格納せずに、
  「move_uploaded_file(XXX,XXX);」という関数を使用し、指定したファイルへ移動
 ③データベースにvarcharで画像用のカラムを作成
 ④データベースの画像カラムには「移動先のファイルのパス」と「画像のファイル名」を格納
 ⑤画像を表示させる際は、<img src="XXXX">のXXXXに、
  「移動先のファイルのパス」と「画像のファイル名」を記述

form.html
<body><formmethod="post"action="upload.php"enctype="multipart/form-data"><!--ファイルをアップロードする際は「enctype="multipart/form-data"」を記述する--><inputtype="hidden"name="max_file_size"value="5000000"/><!--アップロードするファイルサイズの上限(5,000,000バイト(5メガバイト))--><inputtype="file"name="upfile"size="50"/><!--ファイルを選択ボタンができる--><!--通常はサーバにアップロードするために、ここにFTP接続をするための関数を記述する--><inputtype="submit"value="アップロード"/></form></body>
upload.php
<?php$temp_pic_name=$_FILES['upfile']['tmp_name'];//POSTでアップロードされた「upfile」はサーバー上に一時保存されている。 //一時保存されているファイル名を取得し、$temp_pic_nameに代入$original_pic_name=$_FILES['upfile']['name'];//サーバーに一時保存された「upfile」の本当のファイル名を取得し、$original_pic_nameに代入                                    move_uploaded_file($temp_pic_name,'./image/'.$original_pic_name);//アップロードされたファイルをサーバー上の一時保存領域からimageフォルダへ移動?>

move_uploaded_file

 アップロードしたファイルが有効なアップロードであるかどうか確認する関数。
 有効な場合は指定子たファイルの移動先に移動される。
 返り値として、有効だった場合はTRUE、有効ではない場合は移動されずFALSEを返す。
 move_uploaded_file(アップロードしたファイル名,ファイルの移動先);

prepared statement

 予め(=prepared)SQL文の型を記述しておく手法。
 PDOでデータベース接続しselect文を実行する際にセキュリティを高めるために必要な方法。

place holder

 prepared statementを使用する際に使われる手法で、prepared statementのSQL文の型の中に記述する。疑問符型と名前型がある。

rand();

 ランダムな数字を発生させる。

rand(最小値、最大値);

 最小値から最大値の間でランダムな数字を発生させる。
 例)サイコロのプログラムを作る場合、最小値:1、最大値:6

ー>(アロー演算子

 左辺から右辺を取り出す

オブジェクト指向

クラスの書き方

クラスの書き方
classクラス名{public$変数名;//プロパティpublicfunction__construct(引数){//コンストラクタ(クラスがインスタンス化されてからはじめに呼び出される関数)実行する処理;}publicfunctionメソッド名(){実行する処理;}}$インスタンス名=newクラス名("引数");

継承

  継承とは、あるクラスのプロパティやメソッドを引き継ぎ、クラスを拡張すること
php:継承の書き方
class 子クラス名 extends 親クラス名{
子クラス独自のプロパティやメソッドの追加のほか、
この中で親クラスで定義したプロパティやメソッドの上書き(オーバーライド)
}

ファイルの読み込み

 PHPでは、外部の別ファイルの中身を呼び出すことができる。

構文処理内容
requireファイルが読み込めない場合「」が出てその場で処理が終了する。
require_onceファイルが読み込めない場合「」が出てその場で処理が終了する。
さらに、一度読み込んだファイルは読み込みができない。
includeファイルが読み込めない場合「」が出るが処理は継続する。
include_onceファイルが読み込めない場合「」が出るが処理は継続する。
さらに、一度読み込んだファイルは読み込みができない。

 ◆requieでファイルを読み込むプログラムの例

name.php(nameクラスのファイル)
<?phpclassName{//Nameクラスprivate$name;//フィールドの宣言publicfunction__construct($name){//コンストラクタ$this->name=$name;//nameフィールドに引数nameを代入}publicfunctionAisatsu(){//Aisatsuメソッドreturn"私の名前は".$this->name."です。";//戻り値}}?>

↓↓↓requireでname.phpを読み込む↓↓↓

index.php
<?phprequire"name.php";//requireでname/phpを読み込み//読み込めない場合は「fatal error」が出てその場で処理が終了する。$instance=newName("");//読み込んだName型のインスタンスの生成echo$instance->Aisatsu();//メソッド呼び出し?>

カプセル化

 クラス内部のプロパティにprivateを使い、クラス外部からアクセスできなくすること

PHPのプロパティやメソッドには3タイプの権限がつけられる
|権限|説明|
|:-:|:-:|
|public|どこからでも利用(参照)できる
※メソッドには基本的に「public」を使う|
|private|同一クラス内しか利用(参照)できない
※プロパティには基本的に「private」を使う|
|protected|親クラス、継承クラスからしか利用(参照)できない
※基本的に親クラスのプロパティで使う|

getterとsetter

 カプセル化されたプロパティには、getterやsetterというメソッドを利用することで
 間接的にアクセスできるようになる。

getterとsetterの書き方
//getter--------------------functiongetプロパティ名(){return$this->プロパティ名;}//setter--------------------functionsetプロパティ名(引数){$this->プロパティ名=引数;}

初心者がソフトウェアテストについて学んだこと(ホワイトボックス、ブラックボックス等)

$
0
0

プログラミングを学習し始めて間もない初学者がソフトウェアテストについて学んだことをいかに記述しています。

以下の書籍で学習した内容のまとめです。(1〜3章まで)
高橋寿一『知識ゼロから学ぶソフトウェアテスト』翔泳社
https://www.amazon.co.jp/dp/B00HQ7S5CA/ref=cm_sw_em_r_mt_dp_U_LgFfEbBB2SVR7

テストケースは膨大なので、完全なテストは存在しない
テストは限られた時間内でもすべての機能をテストしなくては行けないので、どの手法を用いるべきか常に考えるべき

ホワイトボックステスト

全てのソフトウェアテストの基礎となる手法。
プログラムの論理構造が正しいかテストする。ソフトウェアの使用が間違っていて発生するバグは発見できない。

制御パステスト法

 制御パスは命令と条件分岐の組み合わせのこと。
 制御パスの総数はとても多いので、以下の手法を用いて、網羅する基準にしたがって実施する。

■ステートメントカバレッジ手法

  カバレッジ(カバー(網羅)してる範囲、テストが終わった範囲)
  ステートメント(命令文)
  命令文をテストする手法で、条件分岐でのバグは発見できない、非常に弱いテスト手法

■ブランチカバレッジ手法

  分岐コードに対して、判定がTRUE、FALSEの結果を少なくとも1回ずつ持つようにテストケースを書く手法
  網羅するという点では優れているが、テストケースの数が増えるのが難点

■カバレッジ基準

  一般の商用ソフトウェアなら60%〜90%で十分だが、人命に関わるソフトウェアなら100%、つまりソフトウェアによる。

■テスト駆動開発(TDD/Test-Driven Development)

  ①赤:小さい動作しないテストを書く
  ②緑:そのテストを通すコードを書く
  ③リファクタリング:コードをきれいにする

  TDDで開発するとテストを書く時間がないとか言うことにならない。

ブラックボックステスト

 プログラムを1種のブラックボックスに見立てて、ソースコードを見ずに様々な入力を行ってテストする手法
 ・ソフトウェアの4つの振る舞い
  ●入力を処理する
  ●出力を処理する
  ●計算を行う
  ●データを保存する
  この4つの動きしかしないので、この4つの振る舞いをテストすれば良い。

★同値分割法・協会分析法(ブラックボックステストの基本)

 4つの振る舞いのうち、入力処理と出力処理をソフトウェアが正しく処理しているかテストする。
 ユーザーからの入力だけでなく、ネットワーク経由のデータの受信など様々な入力も含む

■同値分割法

 入力領域を同値クラスと言う部分集合に分割して、その部分集合に含まれる値を等価とみなす作業
 例:
   入力A:1〜5まで入力可
   入力B:1〜5まで入力可
   出力C:A ✕ B

  6 __ __ __ __ __      
  5|           |
  4|           |
  3|    有効同値    |  無効同値
  2|           |    
  1|__ __ __ __ __|       
  0  1  2  3  4  5  6      

【有効同値のテストケース】
A=3、B=4
【無効同値のテストケース】
A= 6、B= 6
A= 6、B= 6
A=-1、B=-1
A= 0、B= 0

⭐0は特別な数値でバグになりやすいので必ずテストする

 

■境界値分析法

  「境界」(無効同値と有効同値の間、有効同値と有効同値の間)
  プログラム上、境界には必ず条件分が必要で、その条件分の間違いを境界分析法でチェックする。

  ●包括関係バグ
   > と >= の間違い
 
  ●On-Offポイント法
   境界値のバグを見つけるために、境界のどの値をチェックするか考える手法

有効同値無効同値境界値
入力A1から50以下6以上0,1,5,6
入力B1から50以下6以上0,1,5,6

   【テストケース】
    ●0 0は特別な数値でバグになりやすいので必ずテストする
    ●1 有効な値の下限
    ●3 有効な値の真ん中
    ●5 有効な値の上限
    ●6 無効な値の下限

■ディシジョンテーブルテスト

  すべての入力の組み合わせを表にして、その入力に対する動作、出力を明記する。
  多数の入力処理に対して、多数の出力処理をテストするというような手法。

■状態遷移テスト

  状態(state)、遷移(transition)
  状態遷移マトリックスという表をさくせいして、ソフトウェアがその項目道理動作しているかチェックする手法
  期待していない状態に遷移するバグ、ある状態からある状態に遷移できないバグが発見できる。
  ※状態の数が多いと、モデルが複雑すぎて、モデリングに時間がかかるしテスト項目も多すぎるという問題点がある
   

■ランダムテスト

  何ら考えもなしに入力や操作を行う手法。
  (アドホックテスト、アドリブテスト、モンキーテストなどとも呼ばれる)
  

★ブラックボックステストまとめ

  ●入力ダイアログボクスがあれば「境界値テスト」を行う※まず行う
  ●復数の入力ダイアログボクスがあれば「デシジョンテーブルテスト」を行う
  ●ダイアログボックスの遷移があれば「状態遷移テスト」を行う

Vue.jsドキュメントのスロットで躓いた箇所についての覚書①

$
0
0

はじめに

最近Vue.jsを学習中です。
Vuetifyに手を出した際にスロットに関する理解が不十分であることに気づいたので、Vue.js公式ドキュメントを一通り読みながら動かしてみようと思ったのですが、いくつか躓いた点があるので残しておきます。(※2020/1/8時点の内容です。)
Vue.js最近触り始めたよーって方の参考になれば幸いです。

※できるだけ分かりやすく書きますが完全に初心者向けの内容というわけではないので、完全初心者の方は公式ドキュメントの「基本的な使い方」あたりを見てからの方が理解できると思います。
ちなみにVue.jsの初学者向けの書籍としては猫本がおすすめです。

事前準備

試しで動かすためにVueCLI4でプロジェクトのひな型を作ります。

1. VueCLI4をインストールする。 ※参考
npm install -g @vue/cli
2. プロジェクトを作りたいディレクトリでcreateする。 ※参考
vue create my-app

上記のコマンド実行後にプロジェクトの構成を聞かれますが、defaultでOKです。

? Please pick a preset: (Use arrow keys)
> default (babel, eslint)
  Manually select features

しばらく待ってひな形のプロジェクトの完成です。

3. サーバを立ち上げる

以下のようにディレクトリを変更して、サーバを起動するように促されますので、言われた通りにコマンドを実行します。

�🎉  Successfully created project my-app.
�👉  Get started with the following commands:

 $ cd my-app
 $ npm run serve

以下のように表示されたらサーバが立ち上がっているのでhttp://localhost:8080/にブラウザからアクセスします。
(VSCodeの場合はCtrlを押しながらクリックするとブラウザで開けて便利!)

 DONE  Compiled successfully in 6349ms

  App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.100.100:8080/

以下のような画面が表示されていればOKです。

キャプチャ11111.PNG

ちなみにサーバの停止はVSCodeの場合、Ctrl+Cでできます。
以下が出力されるのでyを入力すればOKです。

バッチ ジョブを終了しますか (Y/N)?

基本的に作業中は立ち上げっぱなしで問題ないです。
※VSCode自体を閉じても停止します。

4. ソースを修正する

自動で出力されたソースを修正して、この後の動作確認を行いやすくします。
対象のファイルを以下のように修正します。

src\App.vue
<template><divid="app"><HelloWorld/></div></template><script>importHelloWorldfrom"./components/HelloWorld.vue";exportdefault{name:"app",components:{HelloWorld}};</script>
src\components\HelloWorld.vue
<template><div>Slot Practice</div></template><script>exportdefault{name:"HelloWorld"};</script>

かなりスッキリしました。
再度ブラウザでhttp://localhost:8080/を開くと「Slot Practice」が左上に表示されているだけであることを確認してください。

ちなみに構成としてはApp.vueが親コンポーネント、HelloWorld.vueが子コンポーネントとなっています。
Slotはざっくり言うと親が子のコンポーネントを呼び出す際にデータなどを与える仕組みだと思うので、親と子の関係がある点を抑えておくとよいかと思います。

躓いた点

スロットコンテンツ

urlが見つからない

とりあえず載ってる通りに修正したら最初っから詰まりました。
※コンポーネントの名前は変えるのがめんどくさかったのでHelloWorldのままにしてます。
分かりにくければnavigation-linkに読み替えてもらえればと思います。

src\App.vue
<template><divid="app"><HelloWorldurl="/profile">
      Your Profile
    </HelloWorld></div></template><script>importHelloWorldfrom"./components/HelloWorld.vue";exportdefault{name:"app",components:{HelloWorld}};</script>
src\components\HelloWorld.vue
<template><av-bind:href="url"class="nav-link"><slot></slot></a></template><script>exportdefault{name:"HelloWorld"};</script>

上記のように修正ところ、VSCode上ではエラーなどはなかったのですが、Chrome上でエラーが発生していました。
(ChormeのデベロッパーツールはF12で開けます。便利!)
エラーの内容は以下です。
キャプチャ11.PNG

ごちゃごちゃ書いてありますが、要は以下の2点です。
 ・「urlが使われているが、定義されていない」
 ・「src/components/HelloWorld.vueが原因」

①解決

HelloWorld.vuehref属性にv-bindしているurlが定義されていないことが原因でした。
なのでdataを追加して、urlに適当に値を設定してあげます。

src\components\HelloWorld.vue
<template><av-bind:href="url"class="nav-link"><slot></slot></a></template><script>exportdefault{name:"HelloWorld",data(){return{url:"https://www.google.com/"};}};</script>

ちなみにコンポーネントのdataオプションは関数である必要があります。 ※参考

上記に修正したところ、Chorme上のエラーが消えて、「Your Profile」がリンクになり、「https://www.google.com/」
を開けるようになりました。

<font-awesome-icon>が見つからない

親側(App.vue)で子コンポーネントタグの中にコンポーネントも含むことができるよの説明の中で、Font awesomeが使われていました。
とりあえず以下のように修正。

src\App.vue
<template><divid="app"><HelloWorldurl="/profile"><!-- コンポーネントを使ってアイコンを追加 --><font-awesome-iconname="user"></font-awesome-icon>Your Profile
    </HelloWorld></div></template><script>importHelloWorldfrom"./components/HelloWorld.vue";exportdefault{name:"app",components:{HelloWorld}};</script>

上記の修正を行ってChormeを確認したところ、以下のようなエラーが発生していました。
キャプチャ1.PNG
font-awesome-iconが見つからないとのこと。

②解決

ググって一番上にあった以下の記事を参考に、Font awesomeをinstallします。
Font awesome を Vue.js で使ってみよう

※installコマンドを実行する前にサーバを停止します。

> npm install --save @fortawesome/fontawesome-svg-core
> npm install --save @fortawesome/free-solid-svg-icons
> npm install --save @fortawesome/vue-fontawesome

更に先ほどの記事を参考にsrc/main.jsを以下のように修正します。

src/main.js
importVuefrom'vue'importAppfrom'./App.vue'import{library}from'@fortawesome/fontawesome-svg-core'import{faCoffee}from'@fortawesome/free-solid-svg-icons'import{FontAwesomeIcon}from'@fortawesome/vue-fontawesome'library.add(faCoffee)Vue.component('font-awesome-icon',FontAwesomeIcon)Vue.config.productionTip=falsenewVue({render:h=>h(App),}).$mount('#app')

この状態で再度サーバを立ち上げて、ブラウザで確認します。
新たに以下のエラーが出ていました。。。
キャプチャ2.PNG

1つ目のエラーについて、とりあえず先ほどの参考記事の下の方まで確認すると、ここ<font-awesome-icon icon="coffee" />としている部分を見つけました。
nameではなくiconを指定するようです。
また、公式Githubの使用方法にも同様にiconを指定している記述がありました。
App.vueを以下のように修正します。

src\App.vue
<template><divid="app"><HelloWorldurl="/profile"><!-- コンポーネントを使ってアイコンを追加 --><font-awesome-iconicon="user"></font-awesome-icon>Your Profile
    </HelloWorld></div></template><script>importHelloWorldfrom"./components/HelloWorld.vue";exportdefault{name:"app",components:{HelloWorld}};</script>

ブラウザを確認すると1つ目のエラーがなくなっていることを確認できました。
キャプチャ22.PNG

2つ目のエラーについては、なかなか原因が分からなかったのですが、先程の公式Githubの使用方法をよく確認すると、src/App.vueuser-secretのアイコンを使用するために、src/main.jsにおいて{ faUserSecret }をimportしていることに気づきました。
どうやら使用したいアイコンによってimportするアイコンを指定する必要があるようです。
これに倣って、main.jsのimport部分とadd部分を修正します。

src/main.js
importVuefrom'vue'importAppfrom'./App.vue'import{library}from'@fortawesome/fontawesome-svg-core'import{faUser}from'@fortawesome/free-solid-svg-icons'import{FontAwesomeIcon}from'@fortawesome/vue-fontawesome'library.add(faUser)Vue.component('font-awesome-icon',FontAwesomeIcon)Vue.config.productionTip=falsenewVue({render:h=>h(App),}).$mount('#app')

上記の修正後、Chormeでエラーが出ていないことと、画面上でユーザーのアイコンが表示されていることを確認できました。
キャプチャ3.PNG

続く

長くなってしまったので次回に続きます。

崩れたレイアウトの調査方法

$
0
0

趣味でiOSアプリ開発をかじっていた自分が、改めてiOS開発を勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。

参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

レイアウトが崩れる原因

レイアウトが一意に定まっていない、曖昧なレイアウト。
意図しない制約が反映されてしまう、制約のコンフリクトがある。

曖昧なレイアウト

レイアウトが曖昧かどうか調べる

hasAmbiguousLayout()を用いてレイアウトが曖昧かどうか調べることができる。

view.hasAmbiguousLayout

下記のようにviewの制約を設定している途中で実行するとtrueが帰ってくる。
hasAmbiguousLayout()がtrueの状態ではviewは表示されない。

letview1=UIView.init(frame:CGRect.init(x:0,y:0,width:300,height:250))view1.backgroundColor=.redview1.translatesAutoresizingMaskIntoConstraints=falseview.addSubview(view1)view1.topAnchor.constraint(equalTo:view.topAnchor,constant:200).isActive=trueview1.leadingAnchor.constraint(equalTo:view.leadingAnchor,constant:100).isActive=trueview1.widthAnchor.constraint(equalToConstant:200).isActive=trueprint(view1.hasAmbiguousLayout)// trueview1.heightAnchor.constraint(equalToConstant:200).isActive=trueprint(view1.hasAmbiguousLayout)// false

制約のコンフリクト

制約のコンフリクトはレイアウトエンジンによって自動的に不必要な制約が削除されることによって解決されるが、意図しないレイアウトとなることがあるのでレイアウトが崩れてしまうことがある。

制約のコンフリクトをデバッグする

例として意図的に制約のコンフリクトを発生させる。

letview1=UIView.init(frame:CGRect.init(x:0,y:0,width:300,height:250))view1.backgroundColor=.redview1.translatesAutoresizingMaskIntoConstraints=falseview.addSubview(view1)view1.topAnchor.constraint(equalTo:view.topAnchor,constant:200).isActive=true// 同じ要素かつ値が違う制約を宣言して制約をコンフリクトさせるview1.leadingAnchor.constraint(equalTo:view.leadingAnchor,constant:100).isActive=trueview1.leadingAnchor.constraint(equalTo:view.leadingAnchor,constant:200).isActive=trueview1.widthAnchor.constraint(equalToConstant:200).isActive=trueview1.heightAnchor.constraint(equalToConstant:200).isActive=true

エラーメッセージを読み解いて確認

実行時のエラーメッセージにコンフリクトした制約の概要が記載されている。
VisualFormatLanguage形式で記載されている。

[16824:2982179] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x600003802a30 H:|-(100)-[UIView:0x7fde9f408970]   (active, names: '|':UIView:0x7fde9f40be70 )>",
    "<NSLayoutConstraint:0x600003802a80 H:|-(200)-[UIView:0x7fde9f408970]   (active, names: '|':UIView:0x7fde9f40be70 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600003802a80 H:|-(200)-[UIView:0x7fde9f408970]   (active, names: '|':UIView:0x7fde9f40be70 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

ビューデバッガーで確認

ビューデバッガーでの表示。画面左からの距離の制約が2つあることがわかる。
それぞれ[右クリック]→[Print Discripption of ~]で制約を表示させると値を確認できる。
image.png

Printing description of $21:
<NSLayoutConstraint:0x600003802a30 H:|-(100)-[UIView:0x7fde9f408970]   (active, names: '|':UIView:0x7fde9f40be70 )>
Printing description of $22:
<NSLayoutConstraint:0x600003802a80 H:|-(200)-[UIView:0x7fde9f408970]   (active, names: '|':UIView:0x7fde9f40be70 )>

VisualFormatLanguageを読みやすくするツール

https://www.wtfautolayout.com/

Viewの名称

indentiferなどに部品の名称を設定することでエラーメッセージの「0x7fde9f408970」こういうのを読みやすくできる。

レイアウト実装パターン

$
0
0

趣味でiOSアプリ開発をかじっていた自分が、改めてiOS開発を勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。

参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

いくつかレイアウトの実装方法について書いてます

グループ化

UIの部品をUIViewによりグループ化し、中身のコンテンツの大きさにより高さと幅が下辺となるviewを作る。
IB上ではUILayoutGuideが使用できないのでUIViewで空間を作る。
ただし、空間の色を透明にすると描画処理が重くなってしまうので、出来れば色を指定した方がいい。

下記の例のviewは、x座標と上からの距離のみ指定されているが制約エラーは出ていない。
これは中身のviewにより高さと幅が決まっているためである。

image.png

まず高さのみ制約を見ると、コンテンツの高さ+制約で高さが求められている。
image.png
横幅も同じようになっている。
image.png

その他の制約
image.png

中身の部品はグループ化に使っているviewまたは互いに対して制約を指定しているので、画面を横にしても位置関係が変わることはない。
image.png

コンテンツが大きくなるとviewも大きくなる。
image.png

しかし今のレイアウトではコンテンツが入らない時に画像が隠れてしまう。
image.png

はみ出した時に画像だけは画面に入るように制約を加えてみる。
親view.width >= view.widthとなるように制約を加える。
関係を >= とすることでコンテンツが小さい時は親viewのwidthに依存しないようにした。

image.png

ラベルのTrailing Spaceの制約の優先度を下げて表示を行う。
image.png

エラーがなくなって表示できるようになった。
image.png

最終的なviewと部品達の制約。特に重要なのが多い部分。
image.png

同一幅のオブジェクト

image.png

幅を等しくするオブジェクトを全て選択して、EqualWidthを選択。
image.png

各々制約を設定して終わり。
image.png

オブジェクト間の幅を等しくしたい場合

まずボタン同士の幅を等しくする。
image.png

viewを2つ定義してAとBの間BとCの間に設置し、それぞれボタンとくっつける制約を定義する。
(ついでに高さをボタンと揃える)
最後にview同士の幅を等しくして終わり。
image.png

トルツメパターン

トルツメ : 不用なインターフェースを削除してその間のスペースを詰めること。
image.png
UITextViewを取り除きたい。

トルツメ失敗パターン

viewから消した場合

textView.removeFromSuperview()

image.png

非表示にした場合

textView.isHidden=true

image.png

viewから取り除ける場合のトルツメ

imageViewと優先度の低い制約をつける
image.png

viewから消す場合はトルツメできた。
非表示にする場合はできない。
image.png

viewから取り除けない場合のトルツメ

再利用を前提としていてオブジェクトを取り除きたくない場合、Content Sizeに高さが依存しているオブジェクトであれば、コンテンツをnilにすることでトルツメできるようになる。

labelを取り除く場合

label.text=nil

image.png

imageの場合

imaageView.image=nil

StackViewを使った時のトルツメ

isHiddenにするとトルツメできる。
image.png

imageView.isHidden=true

実行時
image.png

UIStackViewを使ったレイアウト

下記画像のような配置のStackViewを作りたいとする。
image.png

ダメな例

まとめて選択してStackViewにしようとする。
StackViewは部品を一方向に揃えるのでレイアウトか崩れた。
image.png

小さな組み合わせからまとめる

縦方向横方向に並べる部品を細分化して小さなものからStackにしていく。
image.png

このような見た目になるのでレイアウトを修正する。
image.png

修正後
image.png

それぞれのStackViewの設定

一番大きなStack

・AlignmentをFillではなくcenterにすることで各要素の幅を別々に設定できるようにした。
・DiscributuonをFillにすることでどんな画面でも最大の幅で表示するようにした。
・Spacingで各要素の間隔を取っている。
image.png

画像をstackをまとめているstack

・AlignmentをTopにすることで文章が長くなっても画像が一番上に来るようにした。
・画像とstackの幅の比率を1:2に設定した。
・DiscributuonをFill Prportionallyにすることで画像の比率を崩さないようにした。
image.png

星の画像をまとめているstack

・DiscributuonをEqual Spacingにしたが、他のどの設定でも良かった。

・Spacingで間隔を空けている。
・AlignmentをFillとして全ての星の画像の比率を1:1にしているが、今回はwidthを等しくする制約をつけているので、高さの制約が必要となった。

image.png

widthを等しくする制約を消してstackViewの設定だけで実装しようとした時のレイアウト。
image.png

犬ラベルと説明textViewをまとめているstack

・Alignment、Discributuon共にFillにして横幅いっぱいに表示するようにした。
・textViewのscrolling Enabledをチェックマークを外して全文表示するようにしている。
image.png

コンテンツの高さに合わせてスクロールするView

画面を横にした時にコンテンツの高さだけスクロールできるViewを作る。
image.png

Scroll View の中に View を設置し中に部品を置く。
image.png

部品だけでViewの高さを決定するように配置を行う。
image.png

Viewの制約を追加する。

FrameLayoutGuideに対して、幅を等しくする制約を与える。
image.png

ContentLayoutGuideの上下左右と等しくする制約を設定して完成。
image.png

現在の仕様ではキーボードを表示した時に一番下のtextFieldが隠れてしまう。
image.png

キーボードを表示した時に上にスクロールしてずらす処理を実装する。
image.png

importUIKitclassViewController2:UIViewController,UITextFieldDelegate{@IBOutletweakvarbottomConstraint:NSLayoutConstraint!@IBOutletweakvarscrollView:UIScrollView!@IBOutletweakvartextField1:UITextField!@IBOutletweakvartextField2:UITextField!@IBOutletweakvartextField3:UITextField!@IBOutletweakvartextField4:UITextField!overridefuncviewDidLoad(){super.viewDidLoad()textField1.delegate=selftextField2.delegate=selftextField3.delegate=selftextField4.delegate=selfNotificationCenter.default.addObserver(self,selector:#selector(handleKeyboardWillShow(_:)),name:UIResponder.keyboardWillShowNotification,object:nil)NotificationCenter.default.addObserver(self,selector:#selector(handleKeyboardWillHide(_:)),name:UIResponder.keyboardWillHideNotification,object:nil)}@objcfunchandleKeyboardWillShow(_notification:Notification){guardletuserInfo=notification.userInfoas?[String:Any]else{return}guardletkeyboardInfo=userInfo[UIResponder.keyboardFrameBeginUserInfoKey]as?NSValueelse{return}guardletduration=userInfo[UIResponder.keyboardAnimationDurationUserInfoKey]as?Doubleelse{return}scrollIndicatorInsets(height:keyboardInfo.cgRectValue.size.height,duration:duration)}funcscrollIndicatorInsets(height:CGFloat,duration:Double){letcontentInsets=UIEdgeInsets(top:0,left:0,bottom:height,right:0)UIView.animate(withDuration:duration){self.scrollView.contentInset=contentInsetsself.scrollView.scrollIndicatorInsets=contentInsetsself.view.layoutIfNeeded()}}@objcfunchandleKeyboardWillHide(_notification:Notification){scrollView.contentInset=.zeroscrollView.scrollIndicatorInsets=.zero}functextFieldShouldReturn(_textField:UITextField)->Bool{// キーボードを閉じるtextField.resignFirstResponder()returntrue}}

コンテンツの大きさに合わせたTableView

image.png

TableViewCellクラスを作成してxibファイルと紐づける。
image.png

importUIKitclassTableViewCell:UITableViewCell{@IBOutletweakvarlabel:UILabel!@IBOutletweakvartextView:UITextView!funcsetCell(label:String,text:String){self.label.text=labelself.textView.text=text}}

tableViewを作る。先ほど作成したカスタムセルを呼び出す。

image.png

importUIKitclassViewController2:UIViewController,UITableViewDelegate,UITableViewDataSource{// 表示するデータletdisplayList=[["1","hoge\nhoge\nhoge"],["2","fuga\nfuga"],["3","piyo"],["4","a\naa\naaa"]]@IBOutletweakvartableView:UITableView!overridefuncviewDidLoad(){super.viewDidLoad()tableView.register(UINib(nibName:"TableViewCell",bundle:nil),forCellReuseIdentifier:"TableViewCell")}overridefuncdidReceiveMemoryWarning(){super.didReceiveMemoryWarning()}// セルの個数を指定するデリゲートメソッド(必須)functableView(_tableView:UITableView,numberOfRowsInSectionsection:Int)->Int{returndisplayList.count}// セルに値を設定するデータソースメソッド(必須)functableView(_tableView:UITableView,cellForRowAtindexPath:IndexPath)->UITableViewCell{// セルを取得するletcell=tableView.dequeueReusableCell(withIdentifier:"TableViewCell")as!TableViewCellcell.setCell(label:displayList[indexPath.row][0],text:displayList[indexPath.row][1])returncell}}

トレイトコレクションとレイアウト

$
0
0

趣味でiOSアプリ開発をかじっていた自分が、改めてiOS開発を勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。

参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

トレイトコレクション

トレイトコレクションは表示に関わるアプリの状態管理をしていて、レイアウトを決定するための情報を提供してくれる。
トレイトコレクションの状態に合わせてレイアウトを変更することで柔軟なレイアウトを作れる。
トレイトコレクションはスクリーンで定義されウィンドウからビューコントローラに渡されていく。

image.png

UITraitCollectionのプロパティ

プロパティ役割
displayScaleCGFlat表示スケール非Retina:1.0, Retina:2.0, 指定なし:0.0
horizontalSizeClassUIUserInterfaceSizeClass水平方向の抽象的なサイズUnspecified, Compact, Regular
verticalSizeClassUIUserInterfaceSizeClass垂直方向の抽象的なサイズUnspecified, Compact, Regular
userInterfaceIdiomUIUserInterfaceIdiom端末種別Unspecified, Phone, Pad, TV
forceTouchCapabilityUIForceTouchCapability3DタッチフラグUnknown, Unavailable, Availavle

サイズクラス

端末と向き高さ
iPad縦RegularRegular
iPad横RegularRegular
iPhone縦RegularCompact
iPhone横CompactCompact
iPhonePlus縦RegularCompact
iPhonePlus横CompactRegular

UITraitCollectionを比較する

containsTraits(: )メソッドを呼び出して比較する。
引数のトレイトコレクションを含んでいればtrueを返す。

// HorizontalSizeClass = Regular という値のみを持つトレイトコレクションを作成letregularWidthTrait=UITraitCollection(horizontalSizeClass:.compact)// self.traitCollection.HorizontalSizeClass == Compact の時trueを返すprint(self.traitCollection.containsTraits(in:regularWidthTrait))// true

UITraitCollectionを合成する

init(traitsFrom: )を用いて、トレイトコレクッションの配列を合成したトレイトコレクションを生成できる。
下記の例ではビューコントローラーの持つトレイトコレクションのhorizontalSizeClassをregularに上書きしたトレイトコレクションを生成する例。

letregularWidthTrait=UITraitCollection(horizontalSizeClass:.regular)letoverrideTraitCollection=UITraitCollection(traitsFrom:[self.traitCollection,regularWidthTrait])

端末の回転挙動を扱う

端末の回転挙動は以下の4つのステップに分かれている。

image.png

最初のセットアップ時にUITraitEnvironmentプロトコルとUIContentContainerプロトコルで定義されたメソッドたちが呼び出され、これらのメソッド内で回転挙動を扱うことができる

willTransition

トレイトコレクションが変化する直前かつ端末の回転時サイズクラスが変化する時に呼び出される。
サイズが変化しない場合、180°回転時やiPadのように縦横のsizeが同じ場合は呼ばれない。
トレイトコレクションはボトムアップに変更されていくので、トレイトコレクションの変更を子ビューコントローラに反映させる為にオーバーライドするときはsuperを呼ぶ必要がある。

viewWillTransition

アプリを表示しているウィンドウの大きさが変化する直前に呼ばれる。
トレイトコレクションの変更に関係せず表示画面サイズが変更されるときに呼び出されるので180°回転時やiPadでも呼び出される。
このメソッドも変更を子ビューコントローラに反映させる為にオーバーライドするときはsuperを呼ぶ必要がある。

taritCollectionDidChange

トレイトコレクションが変化した直後またトレイトコレクションがセットされた後に呼び出される。
アプリ起動時やビューコントローラが初めてロードされた時も呼び出される。
このメソッドも変更を子ビューコントローラに反映させる為にオーバーライドするときはsuperを呼ぶ必要がある。

端末回転時のアニメーション

端末回転時にレイアウトを変更するだけなら上記3つのどのメソッド内に処理を書いても問題はないが、アニメーションと同期してオブジェクトを動かす場合はwillTransition,viewWillTransitionを使う。

overridefuncviewWillTransition(tosize:CGSize,withcoordinator:UIViewControllerTransitionCoordinator){super.viewWillTransition(to:size,with:coordinator)coordinator.animate(alongsideTransition:{contextinif(size.width<=size.height){// 縦画面レイアウト用にアニメーションさせたい処理print("tate")}else{// 横画面レイアウト用にアニメーションさせたい処理print("yoko")}},completion:nil)}

各オブジェクトごとにサイズクラスを適用する

下記のように設定するとWidthがRegular、HeightがCompactの時のみオブジェクトが適用される。

image.png

image.png


プログラミング言語(ruby)学んでみて

$
0
0

プログラミング言語に初めて触れました。

ルービー、サファイアと聞くと美しい宝石を思い浮かべます。
ですが、学びが浅すぎて、まだrubyのもつ言語の美しさに気付けていません。。

マークアップ言語とrubyの初歩を学んだばかりですが、自分なりに感じたことを述べます。数ヶ月経つとrubyという言語に対する感じ方も変わってくると思いますので。
プログラミングを学んでからまだ数日しか経っていない、本当の初学者としての受講生の気持ちに寄り添えるように、アウトプットとして残しておきます。

まず、
暗記しようとしないほうがいいのかも、ということです。

初めて出てくる単語やルールを全てくまなく覚えようとすると精神的負荷が高い。
本質的な部分を学ぶ労力が削がれる気がしました。
ですので、理解に重点を置いて、単語やルールは出てきた度に慣れながら覚えていこうと思います。

次に、
いま何をやっているかが分からくなりそうになる、ということです。
木を見て森を見ず、という言葉がありますがまさにそうでした。
でも、森を見すぎても分からないので、林を見つつ、木をこなしていきたいと思います。

森 ⇄ 林 ⇄ 木

お手本をただただ模写するのでは学習効果が薄い気がしています。
自分なりにコードの区切りや意味を唱えながら一行づつ打ち出すべきなのかもしれません。

いま学んでいる時の感情や考えを忘れず今後に生かしていきたいと思います。
終わり。

Railsチュートリアル 第13章 ユーザーのマイクロポスト - 基本的な画像アップロード

$
0
0

作成中のアプリケーションから、画像アップローダー「CarrierWave」を使えるようにする

画像投稿機能を実装するために必要な画像アップローダーとして、今回は、「CarieerWave」という画像アップローダーを用います。CarrierWaveをRailsアプリケーションで用いるためには、Railsアプリケーション側にcarrierwaveというgemが必要となります。

早速Gemfileを更新していきましょう。

Gemfile
  source 'https://rubygems.org'

  gem 'rails',                   '5.1.6'
  gem 'bcrypt',                  '3.1.12'
  gem 'faker',                   '1.7.3'
+ gem 'carrierwave',             '1.2.2'
+ gem 'mini_magick',             '4.7.0'
  gem 'will_paginate',           '3.1.6'
  ...略

  group :production do
    gem 'pg',  '0.20.0'
+   gem 'fog', '1.42'
  end

  ...略

とりあえず現時点で必要になるのはcarrierwaveのみです。mini_magickやfog`というgemは、今後追加していく機能で使用するgemです。

Gemfileを更新したので、当然ながらbundle installを実行する必要があります。

# bundle install
...略
Fetching gem metadata from https://rubygems.org/........
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies....
...略
Fetching mime-types-data 3.2019.1009
Installing mime-types-data 3.2019.1009
Fetching mime-types 3.3.1
Installing mime-types 3.3.1
Fetching carrierwave 1.2.2
Installing carrierwave 1.2.2
...略
Fetching mini_magick 4.7.0
Installing mini_magick 4.7.0
...略
Bundle complete! 29 Gemfile dependencies, 88 gems now installed.
Gems in the group production were not installed.
Bundled gems are installed into `/usr/local/bundle`

bundle installは特に問題なく完了できたようです。

Railsのジェネレーターで画像アップローダーを生成する

# rails generate uploader Picture
Running via Spring preloader in process 15235
      create  app/uploaders/picture_uploader.rb

少なくともcarrierwave(または他の画像アップローダー)gemがインストールされていないと、rails generate uploaderコマンドは正常に完了しません。

アップロードされた画像をMicropostモデルに関連付けるようにする

マイクロポストに画像を関連付けるために必要な属性の追加

マイクロポストの画像投稿機能は「画像はマイクロポストに紐付けされる」という実装内容になるため、アップロードされた画像はMicropostモデルに関連付けられるのが自然な実装モデルです。実際には、「RDB上micropostsテーブルのpictureカラムに、アップロードされた画像のファイル名を格納する」という形の実装をします。pictureカラムにはファイル名が入るので、型はstringとなります。

pictureカラムを追加した新たなマイクロポストのデータモデルは、以下のようになります。

Micropost.png

データモデルに変更を加えたので、対応するマイグレーションファイルの生成、ならびに生成したマイグレーションのRDBへの適用が必要となります。なお今回は、生成したマイグレーションファイルの内容に手を加える部分はありません。

# rails generate migration add_picture_to_microposts picture:string
Running via Spring preloader in process 15244
      invoke  active_record
      create    db/migrate/20200105225338_add_picture_to_microposts.rb

# rails db:migrate
== [timestamp] AddPictureToMicroposts: migrating ===========================
-- add_column(:microposts, :picture, :string)
   -> 0.0183s
== [timestamp] AddPictureToMicroposts: migrated (0.0199s) ==================

Micropostモデル側でCarrierWaveを使うために必要な実装

画像と関連付けたモデルをCarrierWave側に伝えるためには、モデル側でmount_uploaderメソッドを呼び出します。mount_uploaderの第1引数は「属性名を指すシンボル」、第2引数は「アップローダーのクラス名」を取ります。例えば「モデル側の属性名がpicture、アップローダー名がPictureUploader」であれば、mount_uploaderメソッドは以下のように呼び出されます。

mount_uploader:picture,PictureUploader

app/models/micropost.rb全体の変更内容は以下のようになります。

app/models/micropost.rb
  class Micropost < ApplicationRecord
    belongs_to :user
    default_scope -> { order(created_at: :desc) }
+   mount_uploader :picture, PictureUploader
    validates :user_id, presence: true
    validates :content, presence: true, length: { maximum: 140 }
  end

現時点で、テストスイートは全体が成功するはずです。

# rails test
Running via Spring preloader in process 15277
Started with run options --seed 12165

  61/61: [=================================] 100% Time: 00:00:13, Time: 00:00:13

Finished in 13.04142s
61 tests, 329 assertions, 0 failures, 0 errors, 0 skips

マイクロポスト投稿フォームに画像アップローダーを追加する

Homeページに画像アップローダーを表示させるためには、マイクロポスト投稿フォームに画像アップローダーを追加するのが自然です。このような場合に使うのはfile_fieldメソッドとなります。早速app/views/shared/_micropost_form.html.erbに画像アップローダーの実装を追加しましょう。

app/views/shared/_micropost_form.html.erb
<%= form_for(@micropost) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <div class="field">
      <%= f.text_area :content, placeholder: "Compose new micropost..." %>
    </div>
    <%= f.submit "Post", class: "btn btn-primary" %>
+   <span class="picture">
+     <%= f.file_field :picture %>
+   </span>
<% end %>

MicropostモデルのWebから更新できる属性に、新たにpictureを追加する

Webから画像をアップロードしてマイクロポストに紐付けられるようにするためには、Micropostコントローラーで用いているStrong Parameters機能で、Webからの更新を許可するパラメーターにpictureを追加する必要があります。変更対象のファイルはapp/controllers/microposts_controller.rbですね。

app/controllers/microposts_controller.rb
  class MicropostsController < ApplicationController
    ...略

    private

      def micropost_params
-       params.require(:micropost).permit(:content)
+       params.require(:micropost).permit(:content, :picture)
      end

      ...略
  end

マイクロポストの画像表示を追加する

マイクロポストに画像を関連付けても、その画像がフィード画面に表示されないのでは意味がありません。というわけで、Micropostパーシャル側にも「マイクロポストの画像表示」のための新たな実装が必要となります。Micropostパーシャルの実体であるapp/views/microposts/_micropost.html.erbは、以下のように変更していきます。

app/views/microposts/_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
    <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
    <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
-   <span class="content"><%= micropost.content %></span>
+   <span class="content">
+     <%= micropost.content %>
+     <%= image_tag micropost.picture.url if micropost.picture? %>
+   </span>
<span class="timestamp">
      Posted <%= time_ago_in_words(micropost.created_at) %> ago.
      <% if current_user?(micropost.user) %>
        <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
      <% end %>
    </span>
  </li>

「マイクロポストに画像が関連付けられていなければ、画像を表示する部分の描画そのものを行わない」というのがポイントです。「マイクロポストに画像が関連付けられているか否か」というのは、picture?というメソッドの戻り値によってわかるようになっています。

picture?メソッドは、CarrierWaveによって自動的に生成されるメソッドです。メソッド名は、モデル側で定義した画像の属性名をもとにして、CarrierWaveによって自動的に決定されます。

【Vuex】同じmutationsが多数繰り返されるエラーが発生していたら確認した方がいいこと

$
0
0

はじめに

先日、こちらの記事を書きました。
【Vuex】mapState, mapGetters, mapMutations, mapActionsの最低限の使い方まとめ - Qiita

上記に関連して、記事を書こうと思ったきっかけになったエラーについても記録として残しておこうと思います。

以下のように同じmutationsが繰り返されていたら怪しんでみて下さい:sweat_smile:

image.png

環境

OS:macOS Catalina 10.15.1Vue:2.6.10vuex:3.1.2

エラー原因

computedプロパティ内に...mapMutationsを書いてしまっている。

methodsプロパティ内に書くのが正しいです。

:x:ダメな例

Anything.vue
//...computed:{...mapMutations(['anyMutation',]),},methods:{anyMethod(){this.anyMutation()}}//..

:o:良い例

Anything.vue
//..methods:{...mapMutations(['anyMutation',]),anyMethod(){this.anyMutation()}}//..

これで無事読み込まれます。

おわりに

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

どなたかの参考になれば幸いです:relaxed:

画面幅に合わせて可変する正方形を作りたい

$
0
0

はじめに

レスポンシブ対応のウェブページを作っているときに、画面幅に合わせて可変する正方形が必要になったので、調べて実装しました。
今後、使い回しが効きそうだったので備忘録として残しておきます。

固定サイズの正方形を作る

<divclass="square"></div><style>.square{width:100px;height:100px;background-color:red;}</style>

こうですね。
わかりやすく赤色の正方形です。

横方向を画面幅に合わせて可変させる

<divclass="square"></div><style>.square{width:50; <=変更しましたheight:100px;background-color:red;}</style>

この状態で、画面幅をいじると、四角形の横幅が変化します。

正方形にする

<divclass="square"><divclass="inner"></div></div><style>.square{width:50%;height:100px;background-color:red;}.inner{width:0px;height:0px;}

四角形の中に、高さ・幅とも0のdivを入れました。
この中に入れたdivにpadding-bottomを設定します。

.inner{width:0px;height:0px;padding-bottom:100%;<=追加}

marginやpaddingを%で指定する場合、親要素の幅から計算されます。
topやbottomでも親要素の幅が基準になります。
なので赤い四角形の幅が500pxだった場合、padding-bottom 100%も500pxになるわけです。

最後に赤い四角形の高さ指定を外してあげれば、完成です。

完成形

<divclass="square"><divclass="inner"></div></div><style>.square{width:50%;background-color:red;}.inner{width:0px;height:0px;padding-bottom:100%;}

画面幅に合わせて可変する正方形の完成です。

Amplifyのqueryでfilterするときのand orなどの書き方

$
0
0

書いた経緯

Amplify で自動生成されたqueryのDocsをみていて、自動でandとかcontainsとか自動されていて便利だなと思いつつ、andとorとかの書き方がわからずググっていたのですがすぐみつからず、ああかなこうかなと書いてわかったので同じ感じでまよった方のために共有するため書きました。よくよく定義をみたら当たり前の書き方でした。

記事の対象の方

  • Amplify触り始めて、queryの書き方で試行錯誤している人(私)

既知とする条件(書かないこと)

  • Amplifyの使い方
  • graphqlとは?

本文

以下のようなtypeを定義したとします。

typeUser@model{id: ID!lastName: String!firstName: String!createdAt: AWSDateTime}

amplifyが自動で以下のUserリストをとるqueryを作成してくれます。

# by amplifylistUsers(filter: ModelUserFilterInputlimit: IntnextToken: String):ModelUserConnection

filter: ModelUserFilterInputの定義は以下が自動生成されています。

# by AmplifytypeModelUserFilterInput{id: ModelIDInputlastName: ModelStringInputfirstName: ModelStringInputcreatedAt: ModelStringInputand: [ModelUserFilterInput]or: [ModelUserFilterInput]not: ModelUserFilterInput}

ここで以下のシンプルなフィルター使用したqueryを書きます。

# 自分で定義querylistUsers($input:ModelUserFilterInput){listUsers(filter: $input){items{firstNamelastNamecreatedAt}}}

そして変数に値を入力します。まずシンプルなものです。
fistNameにTaroが含まれるものを取得します。

# 自分で定義{"input":{"firstName":{"contains":"Taro"}}}

結果

{"data":{"listUsers":{"items":[{"firstName":"Taro","lastName":"Yamada","createdAt":"2020-01-09T15:37:22.819Z"}]}}}

and or を使ったquery

以下のように書きます。Docsの定義をみるかぎり
[ModelUserFilterInput]とあるので配列を与えるので
以下のようになります。

{"input":{"and":[#andを先に書く!{"firstName":{"contains":"T"}},{"lastName":{"eq":"Yamada"}}]}}

結果は先ほどと同じです。
同様にor

{"input":{"or":[{"firstName":{"contains":"T"}},{"firstName":{"contains":"K"}}]}}

で結果は同じです。

以上です。

蛇足

本文とあんまり関係ないですが、andとかorをさきにかくと関数みたいなのにeqとかは違うのが感覚的に迷いました。

Clojureなら(関数 値)で一貫してますが

clojure
;includesのあとにfirstName
(and 
  (includes?
    (.firstName "T")
  (=
    (.lastName "Yamada"))

graphqlの場合はcontainsは変数のあと、andは先にくるのが感覚的に分かり難かったです。

graphql
#amplifyの場合 containsはfirstNameのあと
    "and": [ 
      {
        "firstName": {
          "contains": "T"
        }
      },
      {
        "lastName": {
          "eq": "Yamada" 
        }
      }
    ]

【クラウド初心者向け】お客様の選択による分岐と外線転送

$
0
0

概要

  • お客様の選択した数字により処理が分岐します。
    • 繰り返しは3回とします。
  • あらかじめ指定した番号の外線に発信します。

使用ユーザー

  • IAMユーザー

手順

  1. AWSにサインインします。

    1. アカウント、ユーザー名、パスワードを入力してサインインします。
      アカウント内(IAM)で作成したユーザーを使用してコンソールにサインインする
  2. 『AWSマネジメントコンソール』画面の右上にある《リージョン名》をクリックし、ドロップダウンます。
    image.png

  3. 『AWSマネジメントコンソール』画面にある「サービスを検索」にConnectと入力し、検索結果から《Amazon Connect》をクリックし、Amazon Connect コンソール(https://console.aws.amazon.com/connect/)を開きます。
    image.png

  4. Amazon Connectのインスタンス一覧が表示されるので、該当のインスタンスをクリックします。
    image.png

  5. インスタンスの概要が表示されるので《管理者としてログイン》をクリックします。
    image.png

  6. 左側のメニューから《問い合わせフロー》をクリックします。
    image.png

  7. Amazon Connectの案内音声を人間の音声に変更で作成した問い合わせフローをクリックします。
    image.png

  8. 《プロンプトの再生》から出ている矢印にカーソルを合わせて『×』ボタンをクリックして矢印を消します。
    image.png

  9. 左側メニューの「操作」から《顧客の入力を取得する》をフロー部分にドラッグ&ドロップし、オブジェクト同志を矢印で接続後に《顧客の入力を取得する》のタイトル部分をクリックします。
    image.png

  10. 右側に「プロンプトの再生」画面が表示されるので、以下の項目を選択、入力して《別の条件の追加》を3回クリックします。
    image.png

    1. テキスト読み上げまたはチャットテキスト:選択します。
    2. テキストの入力:選択し、音声案内する文言を入力します。
  11. 「オプション」が3個表示されるので、それぞれ1、2、9と入力して《Save》をクリックします。
    image.png

  12. 左側メニューの「ブランチ」から《ループ》をフロー部分にドラッグ&ドロップし、オブジェクト同志を矢印で接続します。
    image.png

顧客の入力を取得するの説明
  タイムアウト
    入力しない時間がタイムアウトの設定に設定した秒数経過した場合に発生します。
  デフォルト
    入力値(オプション)として想定していない番号が入力された場合に発生します。
  1. 左側メニューの「終了/ハングアップ」から《切断/ハングアップ》をフロー部分にドラッグ&ドロップし、オブジェクト同志を矢印で接続します。
    image.png

  2. 左側メニューの「操作」から《プロンプトの再生》をフロー部分にドラッグ&ドロップし、オブジェクト同志を矢印で接続後に《顧客の入力を取得する》のタイトル部分をクリックします。
    image.png

  3. 右側に「プロンプトの再生」画面が表示されるので、以下の項目を選択、入力して《Save》をクリックします。
    image.png

    1. テキスト読み上げまたはチャットテキスト:選択します。
    2. テキストの入力:選択し、音声案内する文言を入力します。
  4. 《キューの設定》~《切断/ハングアップ》を《顧客の入力を取得する》の後ろに移動します。
    image.png

  5. 《顧客の入力を取得する》と《キューの設定》を矢印で接続します。
    image.png

  6. 左側メニューの「操作」から《プロンプトの再生》をフロー部分にドラッグ&ドロップし、オブジェクト同志を矢印で接続後に《プロンプトの再生》のタイトル部分をクリックします。
    image.png

  7. 右側に「プロンプトの再生」画面が表示されるので、以下の項目を選択、入力して《Save》をクリックします。
    image.png

  8. 左側メニューの「終了/転送」から《電話番号への転送》をフロー部分にドラッグ&ドロップし、オブジェクト同志を矢印で接続後に《電話番号への転送》のタイトル部分をクリックします。
    image.png

  9. 右側に「電話番号への転送」画面が表示されるので、以下の項目を選択、入力します。
    image.png

    1. 国コード:転送先の国コード
    2. 電話番号:頭の0を除いた転送先の電話番号
  10. 下にスクロールして、以下の項目を選択して《Save》をクリックします。
    image.png

    1. 「Caller ID番号」を選択
    2. 「インスタンスから番号」を選択しますを選択
    3. ダイアログボックスをクリックして番号を選択
  11. 《電話番号への転送》と《切断/ハングアップ》を矢印で接続し、右上の《公開》をクリックします。
    image.png

  12. 《公開》をクリックします。
    image.png

目次に戻る

【C#】SQLのBulkInsertでファイルにアクセスできなかったのでC#で自作した話(初心者)

$
0
0

「ちょろいっすよ!」

私は入社2年目の駆け出し技術者です。
ある日上司がSQL初心者の私に頼み事をしてきました。
上司「ちょっと相談があるんやけど、、、」

上司「今、DBにこんなテーブルがあるんだけど、」

TABLE1

ID名称サービスID
1名前A3
2名前B2
3名前C2

上司「本来このテーブルのサービスIDカラムに対応するサービス名称があるんだけど、
使わんから、このCSVの対応表だけ作って終わりにしたんや」

Service.csv
1,サービスA
2,サービスB
3,サービスC
4,サービスD
5,サービスE

上司「でもやっぱりサービス名称これから使いそうやから、JOINで取れるようにCSVから対応表通りのテーブルを作ってくれない?」
上司「クライアント側にはCSVは定期的に変更してもいいって言っているから、テーブル作成するスクリプトやらなんやらをタスクスケジューラで定期実行してな」

私「よくある話ですね。多分SQLのBulkInsert使えば一発なんでちょろいっすよwww」

ちょろいはずだった・・・

私「よーし、ちゃっちゃとSQL書いちゃおー」
私「まずはテーブル作成して、」

CreateTable.sql
CREATETABLESERVICE_TABLE(Service_IDintnotnull,Service_Namevarchar(102)notnull);

私「あとはBulkInsertして終いや!」
私「定期実行するから頭でTRUNCATEしとこ。」

BulkInsert.sql
TRUNCATETABLESERVICE_TABLEBULKINSERTSERVICE_TABLEFROM'C:\hoge\Service.csv'WITH(FIELDTERMINATOR=',',ROWTERMINATOR='\n');

私「あとは"C:\hoge"にService.csvを置いて、、、実行!」
私「、、、え!?」

ファイル"C:\hoge\Service.csv"を開けなかったので、一括読み込みできません。

私「なんでや、、、」

悪戦苦闘

この後、ググりながらhogeフォルダのセキュリティ設定やDB側の設定をいじってみたのですが、1時間かけても結局できませんでした。
SQLサーバ認証を用いているとファイルのアクセス権がーとかBulkInsertで指定するパスはDBサーバ上のパスでーとかいろいろ書いてあり、もう何が本当かもわからなくなりました。
ちなみにこの作業は別のサーバ上で行いたかったのでDBサーバ上にcsvファイル置いてーはできません。

もしかしたら、わかる人がやれば一瞬なのかもしれません。
設定でなんとかなるよっていう方はコメントでご教授お願いします。

めんどくさくなった

私「めんどくさいなー。帰ってゲームしたいなー」
私「もうC#でBulkInsertと同じことするコンソールアプリ作ったほうが早くね?(やけくそ)」
私「そのアプリのexeをタスクスケジューラで定期実行したら同じじゃん!(天才)」

私「まずはデータクラスを作って」

ServiceData.cs
classServiceData{/// <summary>/// サービスID/// </summary>publicintServiceID{get;set;}/// <summary>/// サービス名称/// </summary>publicstringServiceName{get;set;}}

私「SQLServer操作クラスを作るか。今回の機能的にこれくらいあればいいかな」

MSSqlManager.cs
usingSystem;usingSystem.Collections.Generic;usingSystem.Data.SqlClient;namespaceBulkInsertApp{publicclassMSSqlManager{privateSqlConnectionsqlConnection;privateSqlTransactionsqlTransaction;/// <summary>/// 接続文字列生成/// </summary>/// <returns>接続文字列</returns>privatestringGetConnectionString(){stringconnectionString=null;stringuserId="<SQL認証のユーザ名>";stringpassword="<SQL認証のパスワード>";stringdbname="<DB名>";stringdbpath="<DBサーバアドレス>";connectionString="Persist Security Info=False;"+"User ID = "+userId+"; Password = "+password+"; Initial Catalog = "+dbname+"; Data Source = "+dbpath;}returnconnectionString;}publicMSSqlManager(){try{stringconnectString=GetConnectionString();this.sqlConnection=newSqlConnection(connectString);this.sqlConnection.Open();}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}}publicvoidClose(){try{this.sqlConnection.Close();this.sqlConnection.Dispose();}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}}publicvoidBeginTran(){try{this.sqlTransaction=this.sqlConnection.BeginTransaction();}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}}publicvoidCommitTran(){try{if(this.sqlTransaction.Connection!=null){this.sqlTransaction.Commit();this.sqlTransaction.Dispose();}}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}}publicvoidRollBack(){try{if(this.sqlTransaction.Connection!=null){this.sqlTransaction.Rollback();this.sqlTransaction.Dispose();}}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}}publicvoidExecuteInsert(stringquery,Dictionary<string,Object>paramDict){SqlCommandsqlCom=newSqlCommand();try{//クエリー送信先、トランザクションの指定sqlCom.Connection=this.sqlConnection;sqlCom.Transaction=this.sqlTransaction;sqlCom.CommandText=query;foreach(KeyValuePair<string,Object>iteminparamDict){sqlCom.Parameters.Add(newSqlParameter(item.Key,item.Value));}// SQLを実行sqlCom.ExecuteNonQuery();}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}}publicvoidExecuteQuery(stringquery){try{SqlCommandsqlCom=newSqlCommand();sqlCom.Connection=this.sqlConnection;sqlCom.Transaction=this.sqlTransaction;sqlCom.CommandText=query;sqlCom.ExecuteNonQuery();}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}}}}

私「あとはCSV読み込み→テーブルTruncate→CSVデータInsertすれば終いや!」

ServiceBulk.cs
usingSystem;usingSystem.Collections.Generic;usingSystem.Data.SqlClient;usingSystem.IO;usingSystem.Text;namespaceBulkInsertApp{publicclassServiceBulk{publicvoidServiceBulkManager(){List<ServiceData>ServiceDataList=CSVDataGet();TruncateTable();BulkInsert(ServiceDataList);}privateList<ServiceData>CSVDataGet(){List<ServiceData>retList=newList<ServiceData>();StreamReadersr=newStreamReader(@"C:\hoge\Service.csv",Encoding.GetEncoding("Shift_JIS"));try{while(!sr.EndOfStream){ServiceDataServiceData=newServiceData();stringline=sr.ReadLine();string[]values=line.Split(',');List<string>items=newList<string>();items.AddRange(values);if(items.Count==2){ServiceData.ServiceID=int.Parse(items[0]);ServiceData.ServiceName=items[1];retList.Add(ServiceData);}}}catch(Exceptione){Console.WriteLine(e.Message);}finally{sr.Close();}returnretList;}privatevoidTruncateTable(){MSSqlManagermanager=newMSSqlManager();try{manager.BeginTran();stringquery="TRUNCATE TABLE SERVICE_TABLE";manager.ExecuteQuery(query);manager.CommitTran();}catch(SqlExceptionsqle){stringerror="Number: "+sqle.Number+" Message: "+sqle.Message;Console.WriteLine(error);manager.RollBack();throw;}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}finally{manager.Close();}}privatevoidBulkInsert(List<ServiceData>listData){MSSqlManagermanager=newMSSqlManager();try{manager.BeginTran();foreach(ServiceDatadatainlistData){stringsqlstr="INSERT INTO SERVICE_TABLE "+"(Service_ID, ServiceName)"+" values "+"(@Service_ID, @Service_Name)";Dictionary<string,Object>paramDict=newDictionary<string,object>();paramDict.Add("@Service_ID",data.ServiceID);paramDict.Add("@Service_Name",data.ServiceName);manager.ExecuteInsert(sqlstr,paramDict);}manager.CommitTran();}catch(SqlExceptionsqle){stringerror="Number: "+sqle.Number+" Message: "+sqle.Message;Console.WriteLine(error);manager.RollBack();throw;}catch(Exceptionex){Console.WriteLine(ex.Message);throw;}finally{manager.Close();}}}}

私「あとはこいつをメインから呼び出せばええんや」

Program.cs
usingSystem;usingSystem.Collections.Generic;usingSystem.Text;usingSystem.Threading.Tasks;namespaceBulkInsertApp{classProgram{publicstaticvoidMain(string[]args){ServiceBulksb=newServiceBulk();sb.ServiceBulkManager();}}}

私「よっしゃできたやで」
私「ビルドして実行や!よしテーブル見てみよう!」

SERVICE_TABLE

Service_IDService_Name
1サービスA
2サービスB
3サービスC
4サービスD
5サービスE

私「よっしゃOKや!」
私「できましたよー。これを実行してくださいー!」
上司「ありがとうやで。(なんでこいつexe渡してきたんや?まぁできてるならいいか)」

あとがき

最後まで読んでいただきありがとうございます。
業務内容に関わる箇所や処理等、都合により一部割愛しております。
まだまだ経験が浅いのでクソみたいなコードです。(StringBuilder使え)
コメントでコードレビューお願い致します


Railsで開発した個人アプリにDockerを導入する手順と最低限の知識

$
0
0

Railsで開発した個人アプリに、後からDockerを導入しました。
その時知識不足なせいでハマってしまった場面があったので、自分の中で整理してアウトプットすることを目的にこの記事を書き残します。

ちなみに、以下の記事を主に参考にさせていただきました。
DockerをMacにインストールする
Docker + Rails + Puma + Nginx + MySQL

RailsアプリにDockerを導入する手順+事前知識

まずは事前知識として、これは知っておいたほうがいいというものを簡単に書いていきます。

事前知識 - Docker関係

Docker(ドッカー)

※1.軽量な仮想化環境を実現するためのツール。

OS やアプリケーションを設定したものを丸ごと実行イメージとして保存できるので、Docker が導入されている別のマシンにそのまま持って行くことができる。

実行環境をテキストファイルとして共有できるのでとても便利。

※1.仮想化環境とは、コンピュータ上にソフトウェアによって仮想的に構築されたコンピュータ(仮想マシン)が備える仕様や機能の総体のこと。

Dockerイメージ

Dockerイメージは、コンテナを起動させるためのベースとなるもの(オブジェクト指向でいうと「クラス」にあたる)

テキストファイル(Dockerfile)からビルドされる。(後に記述)

DockerHub(Docker向けのコンテナ共有サービス)では、既に多くのイメージが公開されている。

Dockerコンテナ

Dockerのコンテナは、 Dockerイメージを元に作成される仮想環境の実行部分(オブジェクト指向でいうと「インスタンス」にあたる)

原則1コンテナ1アプリ。

Dockerfile(ドッカーファイル)

指定したベースのDockerイメージに加える変更を記述するファイル。

Dockerfileを使うことでオリジナルのDockerイメージを作成することができる。

docker-compose(ドッカーコンポーズ)

複数のコンテナから構成されるアプリケーションで、Dockerイメージのビルドや各コンテナの起動・停止などをより簡単に行えるようにするツール。

docker-composeを使用する際は「docker-compose.yml」が必要になる。

事前知識 - サーバ関係

ミドルウェア

OSとアプリケーションの間に入り、中間的な処理を行うソフトウェアのこと。

ー 例 ー

  • Webサーバ
    • Apache、NginXなど
  • APサーバ
    • Puma
    • Unicornなど
  • DBサーバ
    • MySQL
    • PostgreSQLなど

Nginx(エンジンエックス)

webサーバの一つ。

Apacheよりも処理能力が高い。

puma(プーマ)

※1.Rackという機能を提供するためのアプリケーションサーバ。

webサーバの1つでもある。

※1.RackとはWeb サーバと Rubyやフレームワークをつなぐ最小のインタフェースを提供するもの。

Docker導入手順

① Docker for Macを公式サイトからインストール、そして起動

公式サイトで会員登録を済ませた後、Docker for Macをダウンロードしインストール。

インストールが終わったら、Dockerを起動しておく。
(MACの画面上部にDockerのマークが出れば起動できてる証拠)

②作成済みのアプリケーションフォルダの直下に「Dockerfile」、「docker-compose.yml」ファイルを新しく作成する。

フォルダ構成

  • 既存のRailsアプリフォルダ
    • app
    • bin
    • config
    • db
    • ・・・
    • Dockerfile
    • docker-compose.yml

③Dockerfileに記述する(アプリケーションフォルダ直下)

Dockerfile
FROMruby:2.5.1RUNapt-getupdate&&\apt-getinstall-ymysql-clientnodejsvim--no-install-recommends&&\rm-rf/var/lib/apt/lists/*RUNmkdir/myproject

WORKDIR /myprojectADDGemfile/myproject/GemfileADDGemfile.lock/myproject/Gemfile.lockRUNgeminstallbundlerRUNbundleinstallADD./myprojectRUNmkdir-ptmp/sockets

④docker-compose.ymlに記述する(アプリケーションフォルダ直下)

docker-compose.yml
version:'2'services:db:image:mysql:5.6environment:-./environments/db.envvolumes:-mysql-data:/var/lib/mysqlports:-"4306:3306"app:build:.command:bundle exec puma -C config/puma.rbvolumes:-.:/myproject-public-data:/myproject/public-tmp-data:/myproject/tmp-log-data:/myproject/logweb:build:context:containers/nginxvolumes:-public-data:/myproject/public-tmp-data:/myproject/tmpports:-80:80volumes:mysql-data:public-data:tmp-data:log-data:

⑤アプリケーションフォルダ直下に「environments」フォルダを作成、さらにenvironmentsフォルダ直下に「db.env」ファイルを作成する。

  • 既存のRailsアプリフォルダ
    • app
    • bin
    • config
    • db
    • environments
      • db.env
    • ・・・
    • Dockerfile
    • docker-compose.yml

⑥db.envを編集

environments/db.env
MYSQL_ROOT_PASSWORD=password
MYSQL_USER=user
MYSQL_PASSWORD=password

⑦database.ymlを編集

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('MYSQL_USER') { 'root' } %>
  password: <%= ENV.fetch('MYSQL_PASSWORD') { 'root' } %>
  host: db

⑧アプリケーションフォルダ直下に「containers」フォルダを作成、さらにcontainersフォルダ直下に「nginx」フォルダを作成する。

フォルダ構成

  • 既存のRailsアプリフォルダ
    • app
    • bin
    • config
    • containers
      • nginx
    • db
    • environments
      • db.env
    • ・・・
    • Dockerfile
    • docker-compose.yml

⑨作成したnginxフォルダ直下に「Dockerfile」、「nginx.conf」ファイルを作成する。

  • 既存のRailsアプリフォルダ
    • app
    • bin
    • config
    • containers
      • nginx
        • Dockerfile
        • nginx.conf
    • db
    • environments
      • db.env
    • ・・・
    • Dockerfile
    • docker-compose.yml

⑩Dockerfileに記述する(containers/nginxフォルダ直下)

containers/nginx/Dockerfile
FROMnginx:1.15.8RUNrm-f/etc/nginx/conf.d/*ADDnginx.conf/etc/nginx/conf.d/myproject.confCMD/usr/sbin/nginx-g'daemon off;'-c/etc/nginx/nginx.conf

⑪nginx.confに記述する(containers/nginxフォルダ直下)

containers/nginx/nginx.conf
upstream myproject {
  server unix:///myproject/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name 13.112.60.229;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  root /myproject/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @myproject;
  keepalive_timeout 5;

  location @myproject {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://myproject;
  }
}

⑫puma.rbを編集

puma.rb
app_root=File.expand_path("../..",__FILE__)bind"unix://#{app_root}/tmp/sockets/puma.sock"stdout_redirect"#{app_root}/log/puma.stdout.log","#{app_root}/log/puma.stderr.log",true

⑬ターミナルでコマンドを実行

ファイルの準備が整ったら、最後にターミナル上でコマンドを実行。
(railsアプリケーションフォルダ内で実行する)

イメージを構築する

ターミナル
$docker-compose build

railsのコンテナを作成し、データベースの作成処理を行う

ターミナル
$docker-compose run --rm app rake db:create db:migrate

全てのコンテナを構築・起動する

ターミナル
$docker-compose up

上記の流れが終わったあとlocalhostにアクセスすると、しっかり確認することができました。

ちなみに今回書きまとめたものは、DBもDockerで管理することになりますが、DBはローカルのものを参照したいという場合、以下の記事が参考になるかと思います。
既存のRailsアプリをDocker化し、ローカルのDBに接続する方法

(おまけ)

Dockerfileに記述できるコマンド一覧

コマンド意味
FROMベースとなるイメージ
RUNdocker build 時に実行するコマンド
CMDdocker run 時に実行するコマンド
ENTRYPOINTdocker run 時に実行するコマンド
MAINTAINER作者情報
LABELラベル情報(メタデータ)
EXPOSE公開ポート番号
ENV環境変数
ARG一時変数
COPYホストからコンテナへのファイルコピー
ADDファイル/ディレクトリの追加
VOLUMEボリュームのマウント
USER実行ユーザ
SHELLシェル指定
WORKDIRワークディレクトリ
ONBUILDビルド時に実行するコマンド
STOPSIGNALコンテナ終了時に送信されるシグナル
HEALTHCHECKヘルスチェック

ターミナル上で実行できるdocker-composeコマンド一覧

コマンド意味
buildサービスの構築または再構築
configcompose ファイルの確認と表示
createサービスの作成
downコンテナ・ネットワーク・イメージ・ボリュームの停止と削除
eventsコンテナからリアルタイムにイベントを受信
helpコマンド上でヘルプを表示
killコンテナを kill (強制停止)
logsコンテナの出力を表示
pauseサービスを一時停止
portポートに割り当てる公開用ポートを表示
psコンテナ一覧
pullサービス用イメージの取得
restartサービスの再起動
rm停止中のコンテナを削除
run1度だけコマンドを実行
scaleサービス用コンテナの数を指定ド
startサービスの開始
stopサービスの停止
upコンテナの作成と開始
versionDocker Compose のバージョン情報を表示

【Win/Mac】自分が日常的によく使うコマンド(コピペ派の人)

$
0
0

私が日頃、普段使っているPCのコマンド操作です。大それた内容ではないです。分からない時、ggて、何度も見た先人のページに辿りつくんだけど、短い羅列なのにすぐ忘れる。入力が面倒臭いから書くね...(随時更新)

役にたつかもしれない人

・ビギナー(初心者)、コマンド操作より手動(マウス)操作の方が慣れている。
・コマンド入力よりコピペ(手動)派
例: 1. GitHubからアプリをダウンロードする時はgit cloneするよりDownload ZIPする
2. ダウンロードしたアプリは自分の目で見て手動管理したい人
・どのディレクトリ(階層)からでも実行出来る。

D&D = コマンドウィンドウにドラッグ&ドロップ

Mac - ターミナル

コマンド用途
killall FinderUSB抜く時とか
xattr -rc .appをD&D「壊れているため開けません」を開く
open -n + D&D同じアプリを複数起動
cd ~/ホームディレクトリに移動
cd D&Dそのディレクトリへ移動
sudo D&Droot権限で実行

Windows - コマンドプロンプト

コマンド用途
cd D&Dそのディレクトリへ移動
h:Hドライブへ移動

Laravel-Enum 導入

$
0
0

概要?

Laravel-Enum の使い方をまとめておこうと思い。記事を書く所存。

準備

laraevl のバージョン

$php artisan --versionLaravel Framework 5.8.16

使えるようにする

以下のコマンドを実行すると、Laravel 上で Enum(列挙型)が使用できるようになる。
Enumって、ラジオボタンとかセレクトボックスの中身とかに使えて便利だと思う。

$composer require bensampo/laravel-enum

使いたい

コマンドからクラスを作成する

コマンドを実行して、Enum クラスを作成する

$php artisan make:enum TestText
Enum created successfully.

正常に作成すると、app/EnumsのしたにTestText.phpファイルが作成される.
作成したクラスを以下のように編集する。

<?phpnamespaceApp\Enums;finalclassTestTextextendsEnum{constTEST="test";constTEXT="text";}

日本語化対応もできる

app/resources/lang/ja/enums.phpを作成するよ。

<?php// php artisan で作成したクラスuseApp\Enums\TestText;return[TestText::class=>[TestText::TEST=>'テスト',TestText::TEXT=>'テキスト',],];

ただ上記のファイルの恩恵を受けるためには、app/config/app.phplocaleを日本語に設定しておく必要があるよ。

<?phpreturn[// 略'locale'=>'ja',// 略];

とりあえず View で使ってみる。

例えば、ラジオボタンとか。
toSelectArraykey=>valueで返してくれるので便利。
これはlalavel-enumのメソッド。便利。

<divclass="input-group col-12 col-md-6">{{Form::label("test_list_label","テストラジオ",['class'=>'required'])}}<divclass="radios">@foreach(\App\Enums\TestText::toSelectArray()as$value=>$item)<divclass="radio">{{Form::radio('test_list_item',$value,$value==='test',['id'=>"test_list_item--{$value}",])}}{{Form::label("test_list_item--{$value}",$item,[])}}</div>@endforeach</div></div>

Mysql のデータベースのあれこれもあとで書くんよ

初投稿

$
0
0

OSS活用勉強会でQiita入門というMarkdown勉強会に参加してみたので、自分でもちょっと書いてみた。

まずは自己紹介など・・・

Qiita初めての記事は、Qiita入門イベントに参加してみたレポートになります。
あ、申し遅れました。長野県伊那市で電子黒板の販売や、ネットワーク構築などを生業とする会社の一人親方なんぞしている「鄭 喆敏(てい あきとし)」と申します。
1973年8月生まれの在日コリアン3世(但し日本語しか喋れません)
そう、今世間で「氷河期世代」とされている40代後半の人間です。
某書店員・某家電販売員・某カメラメーカー修理センター電話対応員・某カメラメーカー修理センター作業員(MOドライブとか音楽プレイヤーとかの修理してました)という職歴を経て、伊那市の公立校(小中21校)でのPC更新計画策定に関わり(iPad大量導入へと繋がります)教育ICT分野にコミットし始めました。その活動の中で、ブームになり始めたIoTと関わるようになり、それと同時に地方でのエンジニアの圧倒的な少なさに直面。それを解決すべく(今から考えるとかなり無茶してますが)2017年から地元で、「IoTで地域課題解決にチャレンジ」を掲げハッカソンの開催などを行っています。(2019年は諸事情により開催できず)
現在は、CoderDojo伊那のチャンピオン(代表の意味)をやったり、「Code for」の地元ブリゲード(消防団 ここでは地方組織の意)立ち上げ準備をしています。

今後どうするか・・・

今後はあくまで地元を拠点に、IoTやAIの活用の追求・地域に眠る様々な情報のオープンデータ化や活用、地域全体の情報リテラシーの底上げなどに係るあらゆることに関与していこうとしています。

学習記録 その20(24日目)

$
0
0

学習記録(24日目)

勉強開始:12/7(土)〜

教材等:
・大重美幸『詳細! Python3 入門ノート』(ソーテック社、2017年):12/19(木)読了
・Progate Python講座(全5コース):12/21(土)終了
・Andreas C. Müller、Sarah Guido『(邦題)Pythonではじめる機械学習』(オライリージャパン、2017年):12月23日(土)読了
Kaggle : Real or Not? NLP with Disaster Tweets:12月28日(土)投稿〜1月3日(金)まで調整
Wes Mckinney『(邦題)Pythonによるデータ分析入門』(オライリージャパン、2018年):1月4日(土)〜

『Pythonによるデータ分析入門』

p.346 10章 データの集約とグループ演算 まで読み終わり。

9章 プロットと可視化

・matplotlib, seabornといったデータ可視化ライブラリの解説
 線種のような設定要素についてはDocString(関数名 + '?')で見られる。
 (matplotlibをas pltでインポートしているなら、plt.plot?のように使う。)

・基本はmatplotlibとし、必要に応じてpandasやseabornのようなアドオンライブラリを用いるとよい。

プロットの下準備
importmatplotlib.pyplotaspltfig=plt.figure()#プロット機能が含まれるオブジェクト。
ax1=fig.add_subplot(1,1,1)#プロットするためにはサブプロットを1つ以上追加する。
#以下、図の形式や入力データについて記述

・できること概要
 余白の調整、軸の共有、タイトル、凡例及び表示位置の調整(loc='best'で最適位置)、
 ラベル回転表示(rotation)、注釈の追加(annotate)、図の追加(add_patch)、
 matplotlibのデフォルト値設定(rcメソッド)

軸のクラス(AxesSubplot)のsetメソッドを用いた属性の一括設定
props={'title':'namae no ikkatsu settei','xlabel':'aiueo'}ax.set(**props)

・DataFrameにもplotメソッドがある。データフレームにそのまま使える。

値の頻度の可視化
s.value_counts().plot.bar()#barhで横棒

・seabornパッケージを使用すると、プロットの前に集計や要約を要するデータを容易に可視化できる。
 引数のdataにはデータを、xやyにはデータフレームの行と列の名前を指定する、

・ヒストグラム:棒グラフの一種、値の頻度を離散データとして表示

・密度プロット:観測データを生み出したと推定される連続確率分布から生成される。
 通常、この分布をカーネルという正規分布などのシンプルな和として近似する方法をとっている。
 そのため、密度プロットは「カーネル密度推定(KDE)プロット」とも呼ばれる。(plot.kde)

・すごいよく使いそうなメソッド
 seaborn.distplot(ヒストグラムと密度推定のプロットを同時に作成できる)
 seaborn.regplot(散布図を作成し、線形回帰による回帰直線をあてはめる)
 seaborn.pairplot(各要素ずつを比較した散布図行列を一括で可視化できる)

10章 データの集約とグループ演算

・pandasのgroupbyメソッド
 データセット同士の要素を組み合わせて任意の処理が実行できる(ものと理解。)

・グループ演算プロセスは 分離(split)−適用(apply)−結合(combine)の流れ。

・1つのデータセットに対しても、複数の要素を指定できる。
 任意の値について抜き出し、処理(平均、カウント等)をした後、再度グループ化できる?

・ディクショナリを用いたマッピング情報を使用して分類もできる。

・groupbyメソッドの関数(count, sum, mean, median...)基本的な算術計算は網羅してそう。

・groupbyでデータを集約した際につく名前は、タプルを渡すことで変更できる。
 as_index = Falseでインデックスなしも指定できる。

・applyは、オブジェクトを分離し、それぞれのピースに渡された関数を適用し、その後結合する。
 applyに渡す関数はプログラマが自分で実装する必要があるため、想像力が求められる。

・ピボットテーブルとクロス集計。データフレームの関数でもgroupbyでも実装dけいる。
 これらを扱えるようになると、データクリーニングやモデリング、統計分析に役立つ。

Viewing all 21579 articles
Browse latest View live