20200114のPHPに関する記事は11件です。

【PHP】プルダウンで設定した項目をselectedにするサンプル

はじめに

恐竜登録ツール2
で、ハイブリッド恐竜を登録するときに、生成元恐竜タイプを別ページに送信した後、
前ページのタイプに該当恐竜名を選択する手法をとっていたけど、面倒くさい。

タイプとそれに応じた恐竜名を一度に確定したいと考えていた。
そこで疑似的な連動プルダウンを考えてみたのだ

1つ目の、2つ目のプルダウンともに、DBのSELECT結果を利用。
1つ目のプルダウンの選択項目(normal,rare,epic,legend)を決定(確定ボタン押下後)
後に、その項目にあった内容を2つ目のプルダウンの項目を出してみた。
下記のソースのポイントは、
\$cate0と$select_data['type0']を比較している部分
(※$cate0は1つ目のプルダウンで確定した項目)

ソース

恐竜登録ツール2
で作成したテーブルや、ソースを一部流用。

select_pulldown_dino.php
<!DOCTYPE html>
<html lang="ja">
<meta charset="utf-8">
<title>恐竜タイプ選択1</title>
<h1>恐竜タイプ選択</h1>

<body>

<?php 
    require "common_dino.php";
    $db_name='dino_collect.db';
    $db1 = new PDO('sqlite:'.$db_name);
    $query_type0 = "SELECT DISTINCT type0 from tbl_dino";
 ?>
    <form action="select_pulldown_dino.php" method="post">
カテゴリー:
    <?php $result_a=get_AllData($db1,$query_type0); 
    $cate0=isset($_POST['dtype0'])?htmlspecialchars($_POST['dtype0']):null;

     ?> 
    <select name="dtype0">
        <?php while($select_data=$result_a->fetch(PDO::FETCH_ASSOC)){
            //$cate0が$select_data['type0']と等しい場合
            if($cate0 == $select_data['type0']){ 
                echo "<option selected>".$select_data['type0']."</option>";
            //$cate0が$select_data['type0']と等しくない場合
            }else{
                echo "<option>".$select_data['type0']."</option>";
            }
        }   
        ?>
    </select><br>
    <input type="submit" name = "btn2" value="確定">
    </form>


<?php 
    $db_name='dino_collect.db';
    $db1 = new PDO('sqlite:'.$db_name);
    $query_select_type0 = "SELECT dname0 from tbl_dino where type0 = ?";
    $result3=$db1->prepare($query_select_type0);
    //パラメータをセット
    $result3->bindparam(1,$cate0);
    $result3->execute();

 ?>

    <form action="select_pulldown_dino.php" method="post">
対象生物:
    <select name="dname">
        <?php $dino0=isset($_POST['dname'])?htmlspecialchars($_POST['dname']):null; ?>
        <?php while($select_data=$result3->fetch(PDO::FETCH_ASSOC)){
            echo "<option>".$select_data['dname0']."</option>";
        }   
        ?>
    </select><br>
    <input type="hidden" name="btn3" value="<?=$cate0?>">
    <input type="submit" value="確定">
    </form>

<?php 
    $cate0=isset($_POST['btn3'])?htmlspecialchars($_POST['btn3']):null;
    if(empty($dino0) == false && empty($cate0)== false){
        print $dino0.",".$cate0;
    }

 ?>

実行結果

カテゴリーを選択後、「確定」を押すと、選択したカテゴリーが表示され、
選択カテゴリーに含まれる恐竜がプルダウンに表示される。

dino_2pulldown.jpg

最後に

javascriptとかだと、複数プルダウンの連動とか簡単そうなんだけど、
phpのみで実装するのは面倒なのか、私の技術力不足なのか。
いい方法ないでしょうか。

全部取得できないことが判明したので修正

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

PHPで配列の中身をint型として取り出したい時

はじめに

業務でAPI連携をする際に遭遇した内容となります。
備忘録として残そうと思って今回、書きました。

例えばこんな時に使う(実際に自分が遭遇した状況)

APIでリクエストを送った際、返ってきたレスポンスの中でidやnumberなどを配列として保存して、後で別のAPIのリクエストを送る際に使用する時・・・

サンプルコード

test.php
public function main() {
    $idList = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
    foreach ($idList as $arrayId) {
      createRequest($arrayid);
    }
}

public function createRequest($arrayid) {
    $intId = (int)$arrayId[0];
  $response = $this->httpClient->request(
        'GET', //method
        sprintf('/users/%s', $intId), //url
        [
            'headers' => 'Authorization' => アクセストークン, //headers
            'query' => 検索条件 //query parameter
    )
}

例えばこんな感じになります。

軽い説明

①今回は省略してますが、$idListに設定されたidが前にAPIを叩いてリクエストを送って返ってきたレスポンスからidだけを配列として詰めたものと仮定します。
②id単位でforeach文を回して10回分、それぞれのidをurlに設定してリクエストを送ると言った形になります。
③createRequest()メソッドに引数として渡した$arrayIdは名前の通り配列となってます。
$intId = (int)$arrayId[0];←ここで配列に設定されてる値をint型にして別の変数に格納します。

終わりに

多分もっといいやり方はある気がしますが・・・
なんか他にもっといいやり方あるって方はコメントなどで教えてくれると嬉しいです!

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

PHPで配列の中身を文字列型として取り出したい時

はじめに

業務でAPI連携をする際に遭遇した内容となります。
備忘録として残そうと思って今回、書きました。

例えばこんな時に使う(実際に自分が遭遇した状況)

APIでリクエストを送った際、返ってきたレスポンスの中でidやnumberなどを配列として保存して、後で別のAPIのリクエストを送る際に使用する時・・・

サンプルコード

test.php
public function main() {
    $idList = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
    foreach ($idList as $arrayId) {
      createRequest($arrayid);
    }
}

public function createRequest($arrayid) {
    $strId = (int)$arrayId[0];
  $response = $this->httpClient->request(
        'GET', //method
        sprintf('/users/%s', $strId), //url
        [
            'headers' => 'Authorization' => アクセストークン, //headers
            'query' => 検索条件 //query parameter
    )
}

例えばこんな感じになります。

軽い説明

①今回は省略してますが、$idListに設定されたidが前にAPIを叩いてリクエストを送って返ってきたレスポンスからidだけを配列として詰めたものと仮定します。
②id単位でforeach文を回して10回分、それぞれのidをurlに設定してリクエストを送ると言った形になります。
③createRequest()メソッドに引数として渡した$arrayIdは名前の通り配列となってます。
$strId = $arrayId[0];←ここで配列に設定されてる値をstr型にして別の変数に格納します。

終わりに

なんか他にもっといいやり方あるって方はコメントなどで教えてくれると嬉しいです!

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

laravelでカバー画像も表示できるブックレビューアプリを作った。※自分の学習用です

何を作った?

PHP、laravelの勉強として、カバー画像も表示できるブックレビューアプリを作りました。
まずpaizaラーニングを参考に掲示板アプリを作り、そこにGoogleBooksAPIsでマンガのカバー画像を引っ張ってきます。デプロイはまだです(挑戦したがうまくいきませんでした)。

基本的な機能はよくある掲示板アプリと同様です。なのでAPIを利用した画像の取得・利用について書きます。

レビューを投稿するまでの流れ(ログイン済みと想定)

①まず検索窓にマンガのタイトルを書きます。
②候補のカバー画像がいくつか出てきます。この画像はボタンになっていて、クリックして選択できます。
③画像を選択したらタイトルと本文を記入し、投稿して完成です。いいね!ボタンもあります。
僕は左側に画像、右にタイトルと本文を配置しました。

検索フォームを作る

getCover.blade.php
  {{ Form::model(['route' => ['article.getCover']]) }}
        <div class='form-group'>
            {{ Form::label('bookName', 'Book name:') }}
            {{ Form::text('bookName', null) }}
            {{ Form::submit('検索する', ['class' =>'btn btn-primary'])}}
        </div>
  {{ Form::close() }}

  <div class = 'form-group'>
          <a href={{ route('article.new') }}>戻る</a>
   </div>

bookNameはマンガのタイトル。下記のgetCoverメソッドに送る。

GoogleBooksAPIsの導入

下記を参考にしました。
書籍検索APIはGoogleBooksAPIsがオススメ【導入も楽ちんです】

書籍検索APIであるGoogle Books APIsの使い方(PHPでのサンプルコードあり)

PostController.php
<?php

namespace App\Http\Controllers;

use GuzzleHttp\Client;
use Illuminate\Http\Request;


class PostController extends Controller
{
  public function getCover(Request $request)
  {
    if($request->bookName){
      $content = $request->bookName;
      $data = "https://www.googleapis.com/books/v1/volumes?q=" . urlencode($content) . "&maxResults=5";
      $method = "GET";

      $client = new \GuzzleHttp\Client();

      $response = $client->request($method,$data,['http_errors' => false]);

      $posts = $response->getBody();
      $json_decode = json_decode($posts,true);

      return view('getCover', ['json_decode' =>$json_decode]);
    }else{
      $json_decode = "";
      return view('getCover', ['json_decode' =>$json_decode]);
    }

  }
}

受け取ったbookNameは\$contentに代入されます。
\$dataはGoogleBooksAPIs。タイトル名をそのまま埋め込むと正しくエンコードされないので、urlencode()に入れます。
APIを利用するために、下記を参考にhttpclientを使います。
【Laravel & Guzzle】APIの呼び出し方法をわかりやすく解説

画像をボタンとして表示する

getCover.blade.php
   @if ($json_decode)
      @foreach ($json_decode['items'] as $item)
          <form style="height:200px;width:200px;float:left" action="{{action('ArticleController@create')}}" method="get">
              <input type="hidden" value="{{ $item['volumeInfo']['imageLinks']['thumbnail'] }}" name="url">
              <button type="submit"><img src = "{{ $item['volumeInfo']['imageLinks']['thumbnail'] }}" ></button>
          </form>
      @endforeach
   @endif

ボタンを表示します。Formファザード は使っていません。foreachで複数件表示して、それぞれをボタンにします。
配列は入れ子になっていて、画像を表示するURLは\$json_decode['items']の中の['volumeInfo']の中の['imageLinks']の中にある['thumbnail']に格納されています。複数件取得することができるのでforeachを使用しています(['items']以下が複数件取得できます)。

課題この方法だと、配列が変化するとエラーになってしまいます。例えば、GoogleBooksAPIsを利用してJSONのデータを10件取得した場合、その中に配列['thumbnail']を含んでいないデータが一つでもある場合はエラーになってしまいます。いくつか方法は試したのですが、まだ解決できていません。

ArticleController.php
public function create(Request $request)
    { 
        $posts = $request->url;
        $message = 'New article';

        return view('new',['message' =>$message, 'posts' =>$posts]);
    }

送られた画像URLを\$postsに格納し、投稿編集画面に送ります。

タイトルと本文を書く

ArticleController.php
   <h1>edit review</h1>
   <p>{{$message}}</p>

   //下記に画像URL渡します。表示用。
   <img src={{$posts}}>

   //投稿フォーム
   {{ Form::open(['route' =>'article.store'])}}

//下記にも画像URL渡します。まだ保存はされていないので、このフォームでPOSTしてデータベースに保存します。
      <div class = 'form-group'>       
          {{ Form::hidden('url',$posts)}}
      </div>

       //レビューのタイトル。
      <div class = 'form-group'>
          {{ Form::label('title','Title:')}}
          {{ Form::text('title', null)}}
      </div>

      //レビュー本文。
      <div class = 'form-group'>
          {{ Form::label('content','Content:')}}
          {{ Form::text('content',null)}}
      </div>

      //このレビューを投稿するユーザー。このアプリではログインしたユーザーだけが投稿できます。
      //投稿画面にアクセスできるということは、このユーザーは既にログインしていると言えます。
      //なのでAuth::user()->nameを投稿者として、この記事のユーザー名にします。
      <div class='form-group'>
            {{ Form::label('user_name', Auth::user()->name) }}
            {{ Form::hidden('user_name',Auth::user()->name ) }}
        </div>

      <div class = 'form-group'>
          <a href={{ route('article.getCover') }}>画像検索</a>
      </div>

      <div class = 'form-group'>
          {{ Form::submit('作成する', ['class' =>'btn btn-primary'])}}
          <a href={{ route('article.list') }}>一覧に戻る</a>
      </div>
      {{ Form::close() }}

      //フォームが空欄の場合はフラッシュメッセージを表示します。
      @if (session('flash_message'))
            <div class="flash_message">
                {{ session('flash_message') }}
            </div>

ストアメソッドで保存する

ArticleController.php
public function store(Request $request, Article $article)
    {
        if($request->content != ""  && $request->title !=""){
          $article = new Article();
          $user = \Auth::user();

          $article->image_url = $request->url;
          $article->title = $request->title;
          $article->content = $request->content;
          $article->user_name = $request->user_name;
          $article->user_id = $user->id;
          $article->save();
          return redirect()->route('article.show',['id'=>$article->id]);
        }else{
          session()->flash('flash_message', '空欄を埋めてください');
          return redirect()->back();
      }
    }

上記のstoreメソッドで保存し、\$article->image_urlを表示するviewを作成します。
下記がルートになります。

web.php
//書籍名を検索する
Route::post('/article/getCover','PostController@getCover')->name('article.getCover');
//選択した画像を編集画面にgetで送る。
Route::get('/article/new','ArticleController@create')->name('article.new');
//新規投稿のレビューをデータベースに保存する。
Route::post('/article', 'ArticleController@store')->name('article.store');
//投稿したレビューを表示する。
Route::get('/article/{id}','ArticleController@show')->name('article.show');
//レビューを削除する
Route::delete('/article/{id}','ArticleController@destroy')->name('article.delete');
//投稿したレビューを再び編集する
Route::get('/article/edit/{id}', 'ArticleController@edit')->name('article.edit');
//必要無さそうだが、消すと何故かshowの方でエラーが出る。放置しても機能に影響無いが、気になる。
Route::get('/article/getCover','PostController@getCover');

課題 / 問題点

  • マイグレーションでエラーが発生したため、下記URLを参考にCSRFの仕組みを無効化することで対処しています。なので参考にされる際は注意してもられると。CSRFの仕組みを理解するため、今は生のPHPで掲示板を制作しています。順番おかしいですね。

 【Laravel】419のHTTPエラーが出た場合の対処法!

もっとわかりやすく書いた方がいいし、用語の使い方も修正した方がいいですよね。
もっと勉強して随時アップデートしたいです。

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

PHP-【Laravel】 Target class [App\Http\Controllers\Auth\ConfirmPasswordController] does not exist.エラー対策

Laravelのプロジェクトは6.0 から6.2にアップグレードした後、php artisan route:listを実行した時に起きた問題です。

現象

Laravel6.0 から6.2にアップグレードしてからphp artisan route:listをすると、下図のようにTarget class [App\Http\Controllers\Auth\ConfirmPasswordController] does not exist.エラーが出ました。
image.png

原因

laravelプロジェクトのApp\Http\Controllers\AuthフォルダーにConfirmPasswordController.phpがないのは直接原因です。6.0->6.2の差分で分かったのは、laravel6.0のコアアプリケーションにこのファイルがなかったようです。composer updateはlaravel frameworkをバージョンアップしただけで、コアの部分はアップグレードしなかったため、エラーが起きたのです。

解決方法

laravelプロジェクトからConfirmPasswordController.phpをダウンロードして、自分のApp\Http\Controllers\Authに入れます。

以上です。

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

wordpress PHP バージョン変更に伴うちょっとしたエラー

内容はタイトルのとおりです。ちょっとしたエラーと書いていますが、全然ちょっとしていませんでした。むしろ、致命的でした。危なかった。。。

経緯

案件で、すでにあるwordpress公開サイトをデザイン・システムをほぼ変えずに別ドメインとして、公開というものがありました。(同一のお客さんだが、あくまで別サイトとして公開)
ほぼ変わらないので、DB・テーマファイルを一式、複製したら終わるだろうと思っていましたが油断。。。

開発環境

サーバー かごやネット
php 7.2.12
MySQL 5.6.36
WordPress: 5.3.2

発生したエラー

確認したものから順番に列挙します。

プラグインの更新、削除、新規追加の際にエラーが出る。

例えば、アップデートの際には {“success”:true,”data”:{“update”:”plugin” ... } と表示される。一見アップデートできていないように思うが、リロードしたところ、新しいバージョンに変わっていた。お客さんの方で、プラグインは触らない箇所なので、後で解決しよう(まあいいか)と思っていた。

某プラグイン使用に伴うエラー

某リダイレクトのプラグインを使用した際に、chrome検証にてなにやらエラーが出ていた。詳細を見ると、読み込みのjqueryに関係があるようだった。
しかし、サイト内のJSは機能しており、そのプラグインも機能していそうだったので、これまた後回しにすることにした。

メディアの一覧表示の切り替え

メディアでは、一覧表示でリスト表示グリッド表示を切り替えることができる。しかし、グリッド表示に変更した際に、上のくるくるがずっと回ったまま一向に切り替わらなかった。
これに関しては、wp-admin/admin-ajax.phpの記述を変更することで回避できるとのこと。

@header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );

if(in_array($_POST['action'], array('query-attachments', 'send-attachment-to-editor'))){
@header('Content-Type: application/json; charset=' . get_option('blog_charset'));
}else{
@header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
}

試しに変更したところ、正常に切り替えができたので良しとした。

アイキャッチ画像の変更・削除ができない

タイトル通り致命的です。通常、登録画面にてアイキャッチ画像を削除を押すと、画像指定がクリアされる。逆にアイキャッチ画像を設定を押すと、メディアライブラリが立ち上がり、画像指定ができる。しかし、全く効かない。
お客さんの方で、投稿を追加する際にアイキャッチの設定が不可欠なので、どうにかしなければいけなかった。(画像登録用のカスタムフィールドを追加するなど、緊急対策はできるが仕様が変わってしまうのでなしとする。)

仮説と結果

  • wordpressのバージョンを変更する
    ベースのサイトのwordpressが4.9.13だったので、ダウングレードしてみたが、もろもろ解決はしなかった。
  • プラグインの停止
    プラグインを一つずつ、停止し様子を見たが解決せず。ベースサイトと仕様プラグインの種類は同じだが、全て現状の最新版にアップデートしていた。プラグイン自体を停止して変化なしなら、プラグインのダウングレードをしても、無意味だろうと考え断念。
  • 某プラグイン使用に伴うエラーに、jquery関連のエラーがでていたので(v1.12.4)、にjqueryバージョンを最新版に変更してみた。(こレで解決しても、wpのjqueryのバージョン変更は非推奨)やはり解決せず。
  • wordpressの再インストール
    悪あがきでした。
  • PHPバージョン
    ベースサイトのPHPバージョンは7.0.28だったので、今回のサイトではPHPのバージョンを上げていた7.2.12。 正直、こいつの影響だろうと予想はしていたが、サーバーあまりわからないので、なるべく触れたくなかった。それに、7.0系はサポートが切れているので、変更したくなかった。
  • functions.php
    ぐぐると出てくるが、この手のエラーは、functions.phpコード内に無駄な空きスペースがあるときに見受けられるそう。大まかに確認したが、おかしな点は見つからず。

解決策

解決策が見つからず、どうしようか悩んでいた際に何気なくかごやのサイトを見ているところ、一つのよくある質問に行き着いた。

なにやら下記のように書かれていた
PHP バージョン変更後、レイアウトが崩れたり文字化けが発生する。

PHP のバージョン変更後、以下の現象が発生することがあります。
文字化けが発生する
CSSを利用しているサイトの一部でレイアウトが崩れる
WordPress 管理画面のレイアウトが崩れる
WordPress でプラグインのインストールや削除時にエラーになる

共用サーバーで CGI 版 PHP (バージョン 5.5系 / 5.6系 / 7.0系 / 7.1系 / 7.2系)をご利用の場合は、 下記内容を記述した .user.ini ファイルを該当のディレクトリに設置することで回避可能です。
⇒ PHP設定を .user.ini で変更する
output_handler = none

関係ないものもあるが、「WordPressでプラグインのインストールや削除時にエラーになる」に関しては、該当していたので試しに.user.iniを追加したところ、見事解決!
正直なところ、かごや独自の解決策なのか、同じような場合に解決できる万能策なのかわからないのですが、ひとまずは安心しました。
もろもろの問題は解決され、正常に投稿できました。

終わりに

長々と書きましたが、結論を言うと自分の情報不足でした。笑
今回発生したのエラーはググってもあまり解決策が見つからず。灯台下暗しというか、最初から公式サイトを確認すればよかったかな。(ほぼ一日無駄にしてしまいました。。。)
いつまでも、一つの仮説にとらわれずに切り替えが必要ですね。。。

同じようなエラーで困っている方、お試しください。

参考視させていただいたサイト:https://web-diy.jp/2017/04/170421_01/

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

wordpress PHP バージョン変更に伴うとちょっとしたエラー

内容はタイトルのとおりです。ちょっとしたエラーと書いていますが、全然ちょっとしていませんでした。むしろ、致命的でした。危なかった。。。

経緯

案件で、すでにあるwordpress公開サイトをデザイン・システムをほぼ変えずに別ドメインとして、公開というものがありました。(同一のお客さんだが、あくまで別サイトとして公開)
ほぼ変わらないので、DB・テーマファイルを一式、移行したら終わるだろうと思っていましたが油断。。。

開発環境

サーバー かごやネット
php 7.2.12
MySQL 5.6.36
WordPress: 5.3.2

発生したエラー

確認したものから順番に列挙します。

プラグインの更新、削除、新規追加の際にエラーが出る。

例えば、アップデートの際には {“success”:true,”data”:{“update”:”plugin” ... } と表示される。一見アップデートできていないように思うが、リロードしたところ、新しいバージョンに変わっていた。お客さんの方で、プラグインは触らない箇所なので、後で解決しよう(まあいいか)と思っていた。

某プラグイン使用に伴うエラー

某リダイレクトのプラグインを使用した際に、chrome検証にてなにやらエラーが出ていた。詳細を見ると、読み込みのjqueryに関係があるようだった。
しかし、サイト内のJSは機能しており、そのプラグインも機能していそうだったので、これまた後回しにすることにした。

メディアの一覧表示の切り替え

メディアでは、一覧表示でリスト表示グリッド表示を切り替えることができる。しかし、グリッド表示に変更した際に、上のくるくるがずっと回ったまま一向に切り替わらなかった。
これに関しては、wp-admin/admin-ajax.phpの記述を変更することで回避できるとのこと。

@header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );

if(in_array($_POST['action'], array('query-attachments', 'send-attachment-to-editor'))){
@header('Content-Type: application/json; charset=' . get_option('blog_charset'));
}else{
@header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
}

試しに変更したところ、正常に切り替えができたので良しとした。

アイキャッチ画像の変更・削除ができない

タイトル通り致命的です。通常、登録画面にてアイキャッチ画像を削除を押すと、画像指定がクリアされる。逆にアイキャッチ画像を設定を押すと、メディアライブラリが立ち上がり、画像指定ができる。しかし、全く効かない。
お客さんの方で、投稿を追加する際にアイキャッチの設定が不可欠なので、どうにかしなければいけなかった。(画像登録用のカスタムフィールドを追加するなど、緊急対策はできるが仕様が変わってしまうのでなしとする。)

仮説と結果

  • wordpressのバージョンを変更する
    ベースのサイトのwordpressが4.9.13だったので、ダウングレードしてみたが、もろもろ解決はしなかった。
  • プラグインの停止
    プラグインを一つずつ、停止し様子を見たが解決せず。ベースサイトと仕様プラグインの種類は同じだが、全て現状の最新版にアップデートしていた。プラグイン自体を停止して変化なしなら、プラグインのダウングレードをしても、無意味だろうと考え断念。
  • 某プラグイン使用に伴うエラーに、jquery関連のエラーがでていたので(v1.12.4)、にjqueryバージョンを最新版に変更してみた。(こレで解決しても、wpのjqueryのバージョン変更は非推奨)やはり解決せず。
  • wordpressの再インストール
    悪あがきでした。
  • PHPバージョン
    ベースサイトのPHPバージョンは7.0.28だったので、今回のサイトではPHPのバージョンを上げていた7.2.12。 正直、こいつの影響だろうと予想はしていたが、サーバーあまりわからないので、なるべく触れたくなかった。それに、7.0系はサポートが切れているので、変更したくなかった。
  • functions.php
    ぐぐると出てくるが、この手のエラーは、functions.phpコード内に無駄な空きスペースがあるときに見受けられるそう。大まかに確認したが、おかしな点は見つからず。

解決策

解決策が見つからず、どうしようか悩んでいた際に何気なくかごやのサイトを見ているところ、一つのよくある質問に行き着いた。

なにやら下記のように書かれていた
PHP バージョン変更後、レイアウトが崩れたり文字化けが発生する。

PHP のバージョン変更後、以下の現象が発生することがあります。
文字化けが発生する
CSSを利用しているサイトの一部でレイアウトが崩れる
WordPress 管理画面のレイアウトが崩れる
WordPress でプラグインのインストールや削除時にエラーになる

共用サーバーで CGI 版 PHP (バージョン 5.5系 / 5.6系 / 7.0系 / 7.1系 / 7.2系)をご利用の場合は、 下記内容を記述した .user.ini ファイルを該当のディレクトリに設置することで回避可能です。
⇒ PHP設定を .user.ini で変更する
output_handler = none

関係ないものもあるが、「WordPressでプラグインのインストールや削除時にエラーになる」に関しては、該当していたので試しに.user.iniを追加したところ、見事解決!
正直なところ、かごや独自の解決策なのか、同じような場合に解決できる万能策なのかわからないのですが、ひとまずは安心しました。
もろもろの問題は解決され、正常に投稿できました。

終わりに

今回発生したのエラーはググってもあまり解決策が見つからず。灯台下暗しというか、最初から公式サイトを確認すればよかったかな。(ほぼ一日無駄にしてしまいました。。。)
いつまでも、一つの仮説にとらわれずに切り替えが必要ですね。。。

同じようなエラーで困っている方、お試しください。

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

Boost.Asioで名前解決(非同期)

前回の続き

使用したツールなど

前の記事を参照

やりたかったこと

  1. 「非同期」にする。
  2. 「POST形式」で送信する。
  3. 「クラス」にする。

コード

クライアント:

main.cpp
#include <iostream>
#include <vector>
#include <iomanip>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/algorithm/string.hpp>
#include "json11.hpp"

using namespace std;
namespace asio = boost::asio;
using asio::ip::tcp;

class Client {
    asio::io_service& io_service_;
    tcp::socket socket_;
    tcp::resolver resolver_;

    asio::streambuf sendbuf_;
    asio::streambuf recvbuf_;

    int value1, value2;

    const string post = "addapp.php";
    const string host = "127.0.0.1";

public:
    Client(asio::io_service& io_service, int v1, int v2)
        : io_service_(io_service),
        socket_(io_service),
        resolver_(io_service),
        value1(v1),
        value2(v2)
    {
    }

    void connect()
    {
        tcp::resolver::query query(host, "http");

        resolver_.async_resolve(
            query,
            boost::bind(&Client::on_resolve, this,
                asio::placeholders::error,
                asio::placeholders::iterator));
    }

private:
    void on_resolve(const boost::system::error_code& error,
        tcp::resolver::iterator endpoint_iterator)
    {
        /*if (error) {
            cout << "resolve failed: " << error.message() << endl;
            return;
        }*/

        asio::async_connect(
            socket_,
            endpoint_iterator,
            boost::bind(&Client::on_connect, this, asio::placeholders::error));
    }

    void on_connect(const boost::system::error_code& error)
    {
        /*if (error) {
            cout << "connect error : " << error.message() << endl;
        }
        else {
            cout << "connect!" << endl;
        }*/

        //リクエストを送信
        async_send();

        //レスポンスを受信
        async_recv();
    }

    void async_send() {;
        ostream request_stream(&sendbuf_);
        string query = "value1=" + to_string(value1) + "&value2=" + to_string(value2);

        request_stream << "POST "  << post << " HTTP/1.1\r\n";
        request_stream << "Host: " << host << "\r\n";
        request_stream << "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n";
        request_stream << "Content-Length: " << query.length() << "\r\n";
        request_stream << "Connection: Close\r\n";
        request_stream << "\r\n";
        request_stream << query << "\r\n";

        asio::async_write(socket_, sendbuf_.data(), boost::bind(&Client::send_end, this, asio::placeholders::error));
    }

    void send_end(const boost::system::error_code& error) {
        //cout << "request : " << asio::buffer_cast<const char*>(sendbuf_.data()) << endl;

        sendbuf_.consume(sendbuf_.size());
    }

    void async_recv() {
        asio::async_read(socket_, recvbuf_, asio::transfer_all(), boost::bind(&Client::recv_end, this, asio::placeholders::error));
    }

    void recv_end(const boost::system::error_code& error) {
        //cout << "response : " << asio::buffer_cast<const char*>(recvbuf_.data()) << endl;

        string_to_json(asio::buffer_cast<const char*>(recvbuf_.data()));

        recvbuf_.consume(recvbuf_.size());
    }

    void string_to_json(string str) {
        vector<string> res;
        res = boost::split(res, str, boost::is_any_of("\r\n\r\n"));

        string err;
        const json11::Json jsonData = json11::Json::parse(res[res.size() - 1], err);

        auto jsonArray = jsonData.array_items();

        cout << "    id value1 value2    ans" << endl;
        for (auto &item : jsonArray) {
            cout << setw(6) << right << item["id"].string_value() << " ";
            cout << setw(6) << right << item["value1"].string_value() << " ";
            cout << setw(6) << right << item["value2"].string_value() << " ";
            cout << setw(6) << right << item["ans"].string_value() << '\n';
        }
    }
};

int main()
{
    asio::io_service io_service;

    int value1, value2;
    cin >> value1 >> value2;

    Client client(io_service, value1, value2);
    client.connect();

    io_service.run();
}

サーバー:

add.php
<?php

//計算する値を入力
$isInput = TRUE;
if(isset($_POST['value1'])){
    $value1 = intval($_POST['value1']);
}
else {
    $isInput = FALSE;
}
if(isset($_POST['value2'])){
    $value2 = intval($_POST['value2']);
}
else {
    $isInput = FALSE;
}

//DBの設定
$mysql = mysqli_connect('localhost', 'root', '') or die(mysqli_error($mysql));
mysqli_select_db($mysql, 'calc_db');
mysqli_query($mysql, 'SET NAMES UTF8');

//入力があるならデータをDBに追加
if($isInput){
    //結果を計算
    $ans = $value1 + $value2;

    //DBにデータを挿入
    $insert = sprintf('INSERT INTO result SET value1=%d,value2=%d,ans=%d',
            mysqli_real_escape_string($mysql, $value1),
            mysqli_real_escape_string($mysql, $value2),
            mysqli_real_escape_string($mysql, $ans)
    );
    mysqli_query($mysql, $insert) or die(mysqli_error($mysql));
}

//DBからデータを降順で取得
$request = sprintf('SELECT * FROM result');
$result = mysqli_query($mysql, $request) or die(mysqli_error($mysql));

$ansList = array();
while($row = mysqli_fetch_assoc($result)){
    $ansList[] = array(
        'id'=>$row['id'],
        'value1'=>$row['value1'],
        'value2'=>$row['value2'],
        'ans'=>$row['ans']
    );
}

//jsonで出力
$jsonData = json_encode($ansList);
header("Content-Type: application/json; charset=utf-8");
echo $jsonData;
?>

変更点など

出力結果は前回と同じである。

main.cppは、目的である「非同期」「POST形式」「クラス」を満たすようにした。
エラー発生時のコメントは表示しない。

app.phpは、「\$_GET」の部分を「\$_POST」にしただけである。

まとめ

この書き方が正しいかは分からないが取り敢えず実行はできた。
これを使って簡単なゲームでも作りたい。

参考にしたサイト

ネットワーク - TCP
Boost.Asio ゲームループで非同期操作を行う

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

オブジェクト指向を語ろうと思ったけど、一旦クラスの話をしようwithアンパンマン

どうも、やまうちです。

先日(てか昨日読み終わった)この本を読みました。
「オブジェクト指向でなぜつくるのか 第2版」

オブジェクト指向でなぜつくるのか

有名な本なので読んだことがある方も多いとは思いますが、半分素人の弱弱エンジニアの僕はこの本からオブジェクト指向とは?を学ぶべく、3連休を利用して読んだんですね。
ということで、自分の理解度をチェックしつつアウトプットで知識の定着をはかりつつ、よくわからん!って方のためになるようわかりやすくアウトプットしていく感じの記事です。
ちなみに今回触れている内容はオブジェクト指向っていうよりも、クラスの話になります。

なんか違くね?
って思ったら最大限優しく教えてくださいm(_ _)m
最大限です。
※大事なことなので2回言いました

クラスに備わる3つの仕組み

  • まとめる
  • 隠す
  • たくさん作る

って書いてました!!!!
なのでこれに沿って書いていきます。

まとめる

まとめるとはなんぞや?という話ですが、察しの良い方はすでにお気づきかと思いますが、まとめるってことです。←
はい。
とりあえずコードを書いてみましょう!w
今回はPHPのコードです。
あ、PHPしか書けないので毎回PHPです。

// アンパンマンのスタンダードな技
function anPunch(){ /* ロジックは省略 */ }

// アンパンマンがたまに使うただのキック
function anKick(){ /* ロジックは省略 */ }

// アンパンマンのまじ卍な技
function starlightAnPunch(){ /* ロジックは省略 */ }

このコードはまとまってない状態です。
書いてる場所的にはまとまってるかもしれませんが、今回はクラスの話なのでその観点的にはまとまってません。
ではまとめてみましょう。

class AnpanmanAtack
{
    // アンパンマンのスタンダードな技
    function punch(){ /* ロジックは省略 */ }

    // アンパンマンがたまに使うただのキック
    function kick(){ /* ロジックは省略 */ }

    // アンパンマンのまじ卍な技
    function starlightPunch(){ /* ロジックは省略 */ }
}

AnpanmanAtackというクラスでまとめました
これだけです!w

んー、これだけだと中々便利さが分かりにくいと思います。
3人しかいないのに、「君たちを一つのチームとしてまとめよう!」って言われても、
「せ、せやな」としかならないですからね(;・∀・)
でもこれが100人くらいいたとしたら、「君たちはAチーム、君たちはBチーム・・・」
こんな感じでまとめた方が統制とれる気がしませんか?
数が多くなればなるほど、まとめるメリットが大きくなります。

それともう一つ。
クラスでまとめるタイミングで、少し関数をいじっています。
何をいじったかわかりますか?

そう、関数名です!
アンパンマンアタッククラスのパンチ、それすなわちアンパンチ
アンパンマンアタッククラスのキック、それすなわちアンキック
アンパンマンアタッククラスのスターライトパンチ、それすなわちスターライトアンパンチ

関数名はクラス内でオリジナルであればモーマンタイなので、関数名の被りで悩むことがなくなります!
これもまとめるメリットですね。

隠す

さて少しクラスの中身(コード)を増やしながら「隠す」という話をしていこうと思います。
一旦コードを追加しましょう。

class AnpanmanAtack
{
    // アンパンマンの調子
    private $condition;

    // アンパンマンのスタンダードな技
    public function punch(){ /* ロジックは省略(アンパンマンの調子次第で威力が変わるロジック) */ }

    // アンパンマンがたまに使うただのキック
    public function kick(){ /* ロジックは省略(アンパンマンの調子次第で威力が変わるロジック) */ }

    // アンパンマンのまじ卍な技
    public function starlightPunch(){ /* ロジックは省略(アンパンマンの調子が悪いと使えないロジック) */ }
}

※setConditionのようにコンディションの値をセットする関数だったり、newしたときにコンストラクタで初期コンディション値をセットする処理だったりも入れたいところですが、わかりやすさのために割愛しております。

アンパンマンは「顔が濡れて力が出ない」とか「元気100倍、アンパンマン!」とか
調子の幅がかなりエグいです。
ということでクラスの中に$conditionという変数を用意しました。

$conditionの前にprivateという修飾子がついているのに気づきましたでしょうか?
これは同じクラス(今回のケースだとAnpanmanAtackクラス)の中でのみ使える変数であることを意味します。
修飾子は変数だけでなく関数にも使えます。

ということで関数を見ると・・・!!
なんとpublicと書いてあります!!
これも修飾子ですね!!
やったぜ!!!

はい。
publicどこから(クラスの外から)でもアクセスができるということになります。
ですのでアンパンマンアタッククラスの外からパンチを呼び出して、アンパンマンにアンパンチをさせることができるわけです。
※publicは記載しなくでも動作しますが、わかりやすさ的にも書きましょう

なぜ隠すのか

ではなぜ$conditionをわざわざprivateにしてクラスの外から使えないようにしているかというと、ズバリ保守性です。
「クラスの中でしか使えない = 他では使っていない」
これが非常に大切です。

ちょいと例え話。
仮に「バイキンマンがアンパンマンに攻撃するクラス」があったとします。

  • バイキンマンの攻撃はアンパンマンの調子に左右される
  • アンパンマンの調子は「アンパンマンアタッククラス」のものを使う

中身はこんな感じ。
そうするとこんな弊害があります。

  • 「アンパンマンアタッククラス」に修正が必要になる
  • 「アンパンマンアタッククラス」を修正
  • 「バイキンマンがアンパンマンに攻撃するクラス」にも影響があるため修正

「アンパンマンアタッククラス」を修正したいだけなのに、それに付随して「バイキンマンがアンパンマンに攻撃するクラス」の修正も必要になるわけです。
こういった状況は、可能であれば避けるべき状況です。
ですのでprivateとすることで、そういった状況をさけることができます。

これが「隠す」理由です。
※protectedという修飾子もあり、これはそのクラスとサブクラスで使用可能です。

たくさん作る

突然ですが、アンパンマンはナマモノです。
ち、違う?
ですので顔はなるべく更新していく必要があります。
そこで今回のたくさん作るです!!

class MakeAnpanmanFace
{
    // ロジックは省略
}

こんなクラスがあったとしましょう。
おそらく製作者はジャムおじさんです。
僕の知る限りレシピは公開されていないので、ロジックが書けませんでした()

さて、アンパンマンの顔ですが、彼もヒーローモノの故にいつも一旦顔が濡れます。
ようするに必ずピンチになるということです。
そこからチーズが運転するアンパンマン号がやってきて、バタコさんがアンパンマンの顔をメジャーリーガー顔負けのコントロールと回転でぶん投げます。
なんと元気100倍です。

瀕死状態から復活することで強くなる。
ほぼサイヤ人です。

余談はこれくらいにして、何が言いたいかというと顔がたくさん必要ってことです。
安心してください!履いてm顔たくさん作れますよ!!

クラスがあればたくさん作れます。
特に細かい話はありません!w

$anpanman1 = new MakeAnpanmanFace;
$anpanman2 = new MakeAnpanmanFace;
$anpanman3 = new MakeAnpanmanFace;

もうなんぼでも作れますよ!
このたくさん作れるというのがメリットなわけです。
使うたびに新しいコードが必要だったら大変ですからね(;・∀・)

それいけアンパンマン.jpg
※公式HPで画像GETしました。欲しい方はこちらからどうぞ↓
https://www.anpanman.jp/otanoshimi/index.html

まとめ

クラスに備わる3つの仕組みについて書いてみました。

  1. まとめる
  2. 隠す
  3. たくさん作れる

変数と関数をクラスでまとめて、他から使う必要のないものは隠して、使いたいときに何回でも使える。
クラスは便利屋さんなので、ぜひ覚えましょう!
今回触れたのはクラスの話のみでしたが、そのままオブジェクト指向も学びましょう!!

以上!!w

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

GASで翻訳API作成してLaravelで叩くまで

はじめに

この記事では

GASで英語から日本語に翻訳するAPIを作成しLaravelで結果を取得までします

GASを作成

こちらの記事を参考に作成しました
3 分で作る無料の翻訳 API with Google Apps Script

controllerを作成

一部抜粋

TransController.php
    public function index() {

        $transApiUrl = '公開したAPIのURL';
        $transSource = 'en'; // 翻訳前の言語
        $transTarget = 'ja'; // 翻訳語の言語
        $transText = "Hello"; // 翻訳する文字

        $transApiUrl .= '?text=' . urlencode($transText);
        $transApiUrl .= '&source=' . $transSource;
        $transApiUrl .= '&target=' . $transTarget;

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $transApiUrl);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        $res = curl_exec($ch);
        curl_close($ch);



        return $res;
    }

GASの翻訳APIを叩く際に翻訳するテキスト量が多い際に
Moved Temporarily
The document has moved here.

と出ることがあります
これは

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
この記載を追加することで回避できます

叩いてみる

APIの配下にcontrollerを作成したので
ルーティングを以下に追記

api.php
Route::get('trans', 'Api\TransController@index')->name('trans');

http://127.0.0.1:8000/api/transにアクセス

結果

{"code":200,"text":"こんにちは"}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goutteで空港情報を取得してみる

はじめに

友達との作品制作で空港の情報が欲しくなった時にスクレイピングしてみようと思い

下記のサイトさんがたくさんの情報が載っていたのでスクレイピングしようと思いました。
https://www.airlineguide.jp/airport-codes/

PHPやLaravelは初心者でいろんな方の記事を参考にさせていただきました

Laravel/Goutteで映画タイトルをクローリングする

Goutte(PHP)でスクレイピングしてみる

利用するものとしては友人がLaravelを使用していたのでそれに合わせました

※スクレイピングについては色々あると思うので自己責任でお願いします。

スクレイピングとは

スクレイピングはサーバサイドのプログラミング言語を使って外部サーバへアクセスし、そのコンテンツから自分たちの欲しい情報を引き出す手法です。多くはHTMLを返す場合に使われ、DOMを解析したり正規表現を使ってデータを抜き出します。
引用(スクレイピングとAPIの違い)

取得した情報

  • 空港コード
  • 空港名

の三つの情報が欲しいのでサイトをみてみます

スクリーンショット 2020-01-13 23.59.25.png

画像内のテーブルタグに囲まれた
- 1つ目 空港コード
- 3つ目 空港名 OR 3つ目の項目が空なら4つ目の項目を取ろうと思います

controller

AirPortController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Goutte;
use Illuminate\Support\Facades\DB;

class AirPortController extends Controller
{
    //
    public function index(){
        $url = 'https://www.airlineguide.jp/airport-codes/';

        $airCode = array();
        $count = 0;

        $crawler = Goutte::request('GET', $url);
        $crawler->filter('table tr')->each(function($node) use(&$airCode,&$count) {

            if(count($node->filter('td'))){
                //空港コード
                $airCode[$count]['code'] = $node->filter('td')->eq(0)->text();
                //空港名
                if($node->filter('td')->eq(2)->text() === ''){
                    $airCode[$count]['airPort'] = $node->filter('td')->eq(3)->text();
                }else{
                    $airCode[$count]['airPort'] = $node->filter('td')->eq(2)->text();
                }
                $count++;
            }

        });

        //DBに空港情報登録
        foreach ($airCode as  $value){
            DB::table('airports')->insert(['airport_code' => $value['code'],
                'airport_name'=> $value['airPort']]);
        }

    }
}

ルーティング

web.php
Route::get('/search','AirPortController@index');

結果

無事

  • 空港コード
  • 空港名

が取得できました

一つ空白の項目があるのは後からAPIとかで国コードを挿入するためです

スクリーンショット 2020-01-13 23.57.05.png

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