- 投稿日:2019-02-18T21:05:08+09:00
Wordpressのマルチサイトで全ブログを対象とした検索をする
概要
WordPressのマルチサイトでは、全ブログを対象とした検索ができない。
そこで全ブログを検索するコードを作成しました。方針
- switch_to_blog()はペナルティが大きいらしく、サーバに負荷が掛かるようなのでやめる。
- カテゴリー、タグも検索対象とする。Wordpressデフォルトでは、本文、記事タイトルのみが検索対象のため。
作成したコード
wp-content/themes/(テンプレート)/search.php を次のようにしました。
search.php<?php get_header(); ?> <!?// ここから書き換え----------------------------------------------- //?> <?php global $wpdb;//mysqlのデータベース情報を格納しているグローバル変数$wpdb $searchfor = get_search_query(); // 検索キーワード取得 ?> <h2>「<?php echo $searchfor; ?>」の検索結果</h2> <?php $sql="SHOW TABLES FROM $wpdb->dbname"; //テーブル一覧を取得するSQL文を作成 $res_rows = $wpdb->get_results($sql); //テーブル一覧を$res_rowsに格納 if(!$res_rows){ echo "【データベースエラー】テーブルリストの取得に失敗しました<br />\n"; echo "MySQL Error: ".mysqli_error(); exit; } $siteurl_chk_array=array(); //検出される記事のURLを重複を除外するためのチェック用配列(初期化) foreach($res_rows as $data) { //$data にテーブルを格納するループ // $objectToArray = (array)$data; // print_r( array_values($objectToArray)[0] ); // オブジェクトを配列にして値取得 $tablename = array_values( (array)$data )[0]; //テーブル名取得 if( preg_match("/_posts/",$tablename) ) { //postsテーブルループ if( !preg_match("/v_posts/",$tablename) ) { # print($tablename); $siteid=""; if (preg_match('/wp_(\d{1,4})/', $tablename, $tmp_match)){ $siteid = $tmp_match[1]."_"; } $term_relationships = "wp_".$siteid."term_relationships"; $term_taxonomy = "wp_".$siteid."term_taxonomy"; $terms = "wp_".$siteid."terms"; $options = "wp_".$siteid."options"; // 検索用SQL // 検索対象: 公開された記事で、記事本文(post_content), 記事タイトル(post_title), // 記事カテゴリ/タグ名(wp_x_term.terms ), 抜粋(wp_x_term_taxonomy.description) // → → 記事本文(post_content), 記事タイトル(post_title)で検索して見つかる記事 // または、記事カテゴリ/タグ名を検索し該当の term_id を使用する記事 $searchquery_posts = "SELECT * FROM " .$tablename. " WHERE post_status = 'publish' AND (post_type = 'post' OR post_type = 'page') AND (post_content LIKE '%" .$searchfor. "%' OR post_title LIKE '%" .$searchfor. "%' OR ID IN ( SELECT distinct r.object_id FROM ".$term_relationships." AS r INNER JOIN ".$term_taxonomy." AS tt ON r.term_taxonomy_id = tt.term_taxonomy_id INNER JOIN ".$terms." AS t ON tt.term_id = t.term_id WHERE t.name LIKE '" .$searchfor. "' OR t.slug LIKE '" .$searchfor. "' OR tt.description LIKE '" .$searchfor. "' ) )"; // 検索SQLを実行 $result_posts = $wpdb->get_results($searchquery_posts, ARRAY_A); $siteurl=""; //各ブログのURL if($result_posts===null){ continue; }elseif ($result_posts) { // 各ブログのURLを見つけるSQL $searchquery_siteurl = "SELECT option_value FROM " .$options. " WHERE option_name = 'siteurl' "; $siteurl = $wpdb->get_var($searchquery_siteurl); if($siteurl===null){ continue; } } } } // print_r($result_posts); foreach($result_posts as $row) {//検索結果を表示するHTMLを出力 $tmpurl = $siteurl. "?page_id=" .$row['ID']; if (!in_array($tmpurl, $siteurl_chk_array)){ echo "<div class='searchlink'><a href='"; print($tmpurl); echo "'>「"; print($row['post_title']); echo "」</a></div>"; print(mb_substr(strip_tags($row['post_content']),0,200));//contentからHTMLタグを取り除き200文字のみ表示 echo "<br />\n"; // 記事のURLを重複を除外するためのチェック用配列に追加 $siteurl_chk_array[] = $tmpurl; } } } // print_r($siteurl_chk_array); ?> <!?// ここまで書き換え----------------------------------------------- //?> <?php get_footer();参考にしたサイト
流れはこの↓サイトのコードを参照にました(mysql_connect()などが、php5用だったため、php7で対応できるようにしました)。
https://d7r.com/2197課題
- 検索結果が、一覧ですべて表示されます。ページネイション機能を入れて、ページ送りが必要です。
- → これは必須の機能ですね。。。
- 自作のHTMLのためWordpressデフォルトの検索結果ページデザインと異なってしまいます。検索結果ページをブログのデザインに合わせる必要があります。
- 複数語の検索には未対応です。検索文字が「AAA, BBB, CCC」とあったら、「AAA」, 「BBB」, 「CCC」の全部の単語が見つかるように検索ではなく、「AAA, BBB, CCC」という文字列で検索します。
- 検索結果は、見つかった順です。検出件数の多い順などでは、ありません。
- 投稿日:2019-02-18T19:21:35+09:00
PHPで土日祝日の一覧を出力する
土日と祝日だけを取得したくて、書いてみました。
祝日はYasumiで確認します。
土日はCarbon使うことも出来ますが、曜日を見て判断しています。準備
console% composer require azuyalabs/yasumiコード
holiday.php<?php declare(strict_types=1); require_once 'vendor/autoload.php'; /** * 土日祝日ジェネレータ * * @param int $year 範囲の年 * @param string $start 開始年月日(Y-m-d) * @param string $end 終了年月日(Y-m-d) * @param string $format 出力フォーマット * @return Generator * @throws ReflectionException */ function getStaticHoliday(int $year, string $start, string $end, string $format = 'Y-m-d'): Generator { try { $startObj = new \DateTimeImmutable($start . 'T00:00'); $endObj = new \DateTimeImmutable($end . 'T24:00'); $interval = new \DateInterval('P1D'); $period = new \DatePeriod($startObj, $interval, $endObj); }catch (Exception $e) { echo $e->getMessage(), PHP_EOL; return; } $holidays = \Yasumi\Yasumi::create('Japan', $year, 'ja_JP'); foreach ($period as $day) { // 休日または土日である場合 if ($holidays->isHoliday($day) || false !== array_search($day->format('w'), [0, 6])) { yield $day->format($format); } } } try { foreach (getStaticHoliday(2019, '2019-02-18', '2019-08-01', 'Y/m/d') as $holiday) { echo $holiday, PHP_EOL; } } catch (ReflectionException $e) { echo $e->getMessage(), PHP_EOL; }出力結果
coneole% php holiday.php 2019/02/23 2019/02/24 2019/03/02 2019/03/03 2019/03/09 2019/03/10 2019/03/16 2019/03/17 2019/03/21 ...参考
【PHP】日本の祝日を扱うときは「Yasumi」を使ってみてはどうですか
【PHP】日付を一日ずつ進めて表示させてゆく(コメントより)
- 投稿日:2019-02-18T18:11:41+09:00
axiosでPOSTした画像をLaravel APIで保存する
タイトルの通りです。画像やファイルのstorage保存の記事はいくつか見かけましたが、jsからPOSTする記事についてはあんまり見かけなかったので書きました。
この記事ではaxios経由で画像をPOST、LaravelのAPI処理に渡しているサンプルコードを簡単に説明します。概要は以下の通りです。
- canvasに画像を描画し、blobデータを作成
- blobをformDataにappendして、axiosでPOST
- POSTされたblobファイルを、Laravel API側で処理・保存
View側の処理
データのBLOB化
以下の処理か
toBlob
を使ってCanvasに描画した画像ファイルをblob化します。index.vue// Canvasのデータをblob化 const type = 'image/png'; const dataurl = this.canvas.toDataURL(type); // this.canvasは用途に合わせて書き換え const bin = atob(dataurl.split(',')[1]); const buffer = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) { buffer[i] = bin.charCodeAt(i); } const blob = new Blob([buffer.buffer], {type: type});
"multipart/form-data
のデータをPOSTするので、blobファイルをFormData
インタフェースにappendする形でデータを作成します。index.vueconst data = new FormData(); data.append('photo', blob, 'image.png'); //'photo'というkeyで保存blobを格納したdataを
axios.post
の第二引数にセットすれば、POST可能です。index.vueaxios.post('/api/photo', data, { headers: { 'content-type': 'multipart/form-data' } }) .then(res => { console.log('success') }).catch(error => { new Error(error) });Laravel API側の処理
api用のControllerで設定した
store
定義内に、受け取ったblobデータをファイルとして保存するための処理を書きます。View側でFormData
内にappendしたphoto
データを$request
から参照し、storeAs
メソッドを実行することで簡単に保存が可能です。IndexController.phppublic function store(Request $request) { // ファイル名を取得 $filename = $request->photo->name; // blobデータをstorageに保存する // diskの指定を特にしなければ、例の場合。`storage/app/images/`に画像が保存される $path = $request->photo->storeAs('images', $filename); }
- 投稿日:2019-02-18T16:33:39+09:00
mysqlndとphp-pdoを導入するもcould not find driver【php72】
- 環境:CentOS7
- nginx
- MariaDB
- php7.2
[root@---]$ php -v PHP 7.2.15 (cli) (built: Feb 5 2019 18:05:51) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologiesissue
PDO接続にてcould not find driverエラー。
pdoは読み込まれており、下記モジュールは導入済み。
- 加え、systemctl reload php-fpm, nginx
済み。[root@---]# yum list installed | grep mysql php-mysqlnd.x86_64 7.2.15-1.el7.remi @remi-php72 [root@---]# yum list installed | grep pdo php-pdo.x86_64 7.2.15-1.el7.remi @remi-php72解決策
[root@---]$ php -r "echo phpinfo();" | grep PDO PDO PDO support => enabled PDO drivers =>上記にて読み込まれていないことが発覚。
$ sudo yum -y install php72-php-mysql
結果
*$ php -r "echo phpinfo();" | grep PDO
PDO PDO support => enabled PDO drivers => mysql, sqlite PDO Driver for MySQL => enabled PDO Driver for SQLite 3.x => enabled接続良好。
補遺
PDOテスト用に。
<?php try { $pdo = new PDO('mysql:host=localhost;dbname=vanilo;charset=utf8','root','kubosuke', array(PDO::ATTR_EMULATE_PREPARES => false)); } catch (PDOException $e) { exit('データベース接続失敗'.$e->getMessage()); } ?>
- 投稿日:2019-02-18T09:19:37+09:00
Symfony 4.2 doctrine
OneToMany の取得方法
Entity
Person Class
/** * @ORM\OneToMany(targetEntity="App\Entity\Message", mappedBy="person") */ private $messages;
Message Class
/** * @ORM\ManyToOne(targetEntity="App\Entity\Person", inversedBy="messages") */ private $person;Template
<h2>Person Table</h2> <table> <tr> <th>id</th> <th>name</th> <th>mail</th> <th>age</th> <th>messages</th> </tr> {% for person in data %} <tr> <td>{{ person.id }}</td> <td>{{ person.name }}</td> <td>{{ person.mail }}</td> <td>{{ person.age }}</td> <td> <ul> {% for msg in person.messages %} <li>{{ msg.content }}</li> {% endfor %} </ul> </td> </tr> {% endfor %} </table>Case1. 都度データを取得する例
$repository = $this->getDoctrine() ->getRepository(\App\Entity\Person::class); $data = $repository->findAll();Case2. まとめてデータを取得する例
$repository = $this->getDoctrine() ->getRepository(\App\Entity\Person::class); $data = $repository ->createQueryBuilder('p') ->select('p') ->addSelect('m') ->leftJoin('p.messages', 'm', Expr\Join::ON) ->getQuery() ->getResult();Case3. フェッチモードを変える
/** * @ORM\OneToMany(targetEntity="App\Entity\Message", mappedBy="person", fetch="EAGER") */ private $messages;
- 投稿日:2019-02-18T08:48:04+09:00
php-master-changes 2019-02-17
今日は開発者向けツールの若干の整理があった!
2019-02-17
petk: Move some scripts from root directory to scripts/dev/
- https://github.com/php/php-src/commit/e0c88039b6d88b03a91dec34d69b2d776860135d
- [7.4~]
- 開発者向けのスクリプト snapshot と vcsclean をプロジェクトのルートディレクトリから scripts/dev/ へ移動
petk: Remove unused --copy option from the makedist
- https://github.com/php/php-src/commit/90054c1a698ff755ac63125267c2bb66e53dee2d
- [7.4~]
- makedist にあった
--copy
オプションの利用箇所を削除- かつて automake へのフラグとして使っていたが、だいぶ前に使われなくなっていた
- 投稿日:2019-02-18T03:13:54+09:00
PMMPプラグインの書き方。
前書き...
初めまして、こんにちは。
PMMPプラグインを製作したいと思った事、ありませんか?ここからはPMMPプラグインのソースコードの書き方、デバッグの仕方、その他注意事項等を並べて記述していきます。
僕は全くの初心者を対象に記事を書きますが、こんな記事を最後まで読まずすっ飛ばす初心者の方を受け入れません。
道のりを歩む中 怖いお兄さん や お姉さん の開発者さんに殴られないよう、サポートする気持ちで開発のスタートを切る記事を書きます。
知っていて理解している部分は、あなたが後悔なさらないのであればすっ飛ばしても構いません。
あなたがカッコイイ開発者になる踏み台として読んでくださるなら、是非最後までお付き合い下さい。
コップに紅茶を注いでリラックスし、菓子でもつまみながら見るのが良いでしょう。
もくじ
- 環境構築
- プラグインの構成
- コードの書き方
- デバッグの仕方
- 絶対に読んでほしい事
環境構築
実は最初から最後まで説明しようと思ったのですが...
こちらの記事 がとても分かりやすいので、是非こちらを参考にしてください。環境構築、というのは自分が作ったプラグインのデバッグ(動作を確認)出来る環境を整えることなので、AndroidやiOSの端末をお使いの方は MiRm さんのサービスを使ってサーバーを作ってください。
プラグインの構成
適当にファイルを作って入れておけば動くという代物ではありません。
少し難しい話をしますが、プラグインのメインのソースコードを書いただけで動くものでもありません。
plugin.yml
というプラグインの説明のようなファイルも必要です。ここでは
plugin.yml
の内容の書き方と置き場所、プラグインの処理を書いたメインソースのファイルの置き場所を説明します。まず、プラグインの構成ですが...
***\src\...\main.php
***\plugin.yml
の二つのファイルが必要になります。
(***
は自分のプラグインの名前です。)
...
となっているのは、そこに幾らでも好きな名前のフォルダを重ねることが出来るからです。今回は、
TestPlugin
というプラグインを作り以下の構成にしております。
1.
TestPlugin
というフォルダを作り...2.
src
というフォルダとplugin.yml
ファイルを作り...3.
src
フォルダの中にrib
フォルダ、そしてその中にtest
フォルダ、その中にメインソースを書くmain.php
ファイルを作ります。
plugin.ymlの書き方
一番最初に作成した、
plugin.yml
の中身を書いていきます。名前は必ず
plugin.yml
でないと動いてくれません。
ちなみに同じく作成したsrc
フォルダも、src
でなければ動いてくれません。このファイルは、プラグインの説明書みたいなものです。
APIのバージョンやプラグインのバージョン、作成者や読み込む順番などを書くことが出来ます。試しに僕がいつも書いている情報に似せて書いていきます。
最低限これを書けば動いてくれますが、プラグインの用途や仕様によって追加しなくちゃいけない情報もあると思います。plugin.yml# プラグインの名前です name: TestPlugin # main.php までのファイルパスを記述します(.phpを抜いて) # 例えばribフォルダにmain.phpを作ると # rib\main になります main: rib\test\main # プラグインのバージョンを記述します # 0.1でも0.0.1でも何でもいいです。 version: 1.0 # PMMPのAPIバージョンを記述します。 # これは各バージョンごとに必ず決められています api: 3.0.0 # 作成者の名前を記述します。 author: ribnil他にも沢山項目があるのですが、これについては korado様のサービスで
plugin.yml
と検索をかけるとより詳しい内容が得られます。(他力本願寺)
紅茶が少なくなってきたので、
お代わりを入れてもうひと踏ん張りがんばりましょう。
コードの書き方
main.php
の内容を書いていきます。
取り合えず基盤になるナニモシナイプラグインを作ります。まずはphpを書き始めるうえで必ず必要な
<?php
を最初に書きましょう。
その次に、名前空間というものも必ず書きます。
これはplugin.yml
のmain:
という項目で書いたパスの、main
より前のものを書きましょう。namespace rib\test;
plugin.yml
のところでもそうでしたが、
必ず\(バックスラッシュ)
でつなげてください。
次に、use文を書きます。
PMMPから、僕らが使いたい関数などを引っ張ってきます。use pocketmine\plugin\PluginBase; use pocketmine\event\Listener; use pocketmine\{ Player, Server };( ぶっちゃけこの場において
pocketmine\Player
は必要ありません )
恐らく大体のプラグインでは必須な情報なので、必ず書いておきましょう。
次に、
mainクラス
を作ります。
この中にいろんな関数や処理を書いていくことになります。class main extends PluginBase implements Listener { }ファイルの名前をクラスにしなくちゃいけないので、例えば
Rib.php
だとclass Rib extends PluginBase implements Listener { }になります。
クラスの継承などのお話もしなくてはいけませんが、取り合えず簡単なプラグインを作れるようになることだけを考えて先の説明をします。
次に必ず書かなきゃいけない
onEnable関数
を書きます。public function onEnable() { $this->getServer()->getPluginmanager()->registerEvents( $this, $this ); }これは、プラグインを読み込んだ時に動作する関数です。
時間などの処理を扱うプラグインには
date_default_timezone_set( "Asia/Tokyo" );と、タイムゾーンを設定しましょう。
全貌
main.php<?php namespace rib\test; use pocketmine\plugin\PluginBase; use pocketmine\event\Listener; use pocketmine\{ Player, Server }; class main extends PluginBase implements Listener { public function onEnable() { $this->getServer()->getPluginmanager()->registerEvents( $this, $this ); } }
デバッグの仕方
今回は、読み込むだけのプラグインを作りました。
なのでコンソールを確認して「読み込まれたかどうか」を確認しましょう。作ったプラグインを
plugins
フォルダに入れてください。
そして起動しましょう!
こんな感じの文字が出たら成功しています。今回のプラグインは「ここ」に置いておきます。
ダウンロードしてコピペ用にとって置いたり、改造したりしてみましょう。
絶対に読んでほしい事
正直、僕が一番書きたかったのはこの部分だったりします。
あなたはウンコードなるものを知っていますか?
読むに堪えないコードです。コードを書くにあたって、
if( $value === 1 ){}
if ($value === 2) {}
等の、コーディングスタイルの違いは様々あります。僕らは、オープンソース(誰でもコードが自由に閲覧できるよう)にするにあたって
より読みやすく、わかりやすいソースコードを書かなければいけません。例えば
class Plugin { public function onRibnil($ribnil) { return $ribnil; } }こんなインデント(空白)が無いクソコードより、
class Plugin { public function onRibnil( $ribnil ) { return $ribnil; } }こっちのほうが断然見やすいですよね。
プログラミングの世界に飛び込むにあたって、プラグイン作成から始めるという方の多くに当てはまるのですが、見やすいコードを書く 事を怠ってしまうと 自分が何を書いているか や どこに何を書いたのか がわかりづらくなってしまったり、
なにより 見る相手に暴力ほどの不快感と苦痛 を与えてしまいます。
初心者の方へ、確かに僕が言えたことではありませんが
綺麗で見やすいコードを書くように常意識する
事は何も損ではありません。そうして下されば、もし、あなたが質問を提示するときにすんばらしい綺麗なコードであれば回答して下さる開発者はあなたを受け入れお互い良心的なコミュニケーションをとることが出来るでしょう。
そしてもう一つですが...
わからないことがあったら必ず聞いてください。
syntax errorでも結構です。自らが成長するには、道を辿り、師を仰ぎ、師に習い、いずれ師になるのがベストです。
こんな僕ですが、頼ってくださってもかまいません。
( 専用のようなグループもあります: Discord )
あとがき
...生意気なことばかり綴ってしまい申し訳ありません
僕は開発者として未熟ですし、コードもウンチです。もし、この機会にプラグインだけでなく様々なプログラミングに対しての興味を持って下さったら、僕が前述したコードの可読性についての記事もひょっとしたら救われるかもしれません。
次回はさまざまなイベントを扱ってみたり、ConfigやTaskの使い方をまとめた記事を出してみます。
興味があったら是非拝読下さい。
僕がいつもお世話になっているサンプルコードサイトです。
僕も運営させていただいております
プラグイン開発において質問など是非是非ここで :D
- 投稿日:2019-02-18T00:11:41+09:00
PHP未経験者が勉強用にDockerで開発環境作ってみた
やりたいこと
できるだけ自分のPCを汚さずにいわゆるLAMP環境
(Linux, Apache, MySQL, PHP)の開発環境を手軽に構築したい。Macの場合はMAMPやVirtualBoxがあるけど汚れちゃうし・・・
そうだ、Dockerの勉強も兼ねてDockerを使おう!!!
成果物
Dockerを使用してApache, PHP, MySQL, phpMyAdminを連携し、
PHPとMySQLの勉強に注力するための開発環境を作成しました。https://github.com/MasanoriIwakura/php_study
開発環境
ツール名 バージョン OS macOS Mojava 10.14.3 Docker Docker for Mac 18.06.1-ce Docker Compose 1.22.0 PHP 7.2.7-apache (Apacheと連携されているコンテナ) MySQL 8 phpMyAdmin 4.7 プロジェクト構成
[PHP]
Dockerコンテナの80番ポートと、ホストOSの80番ポートを紐付け。
MySQLとの接続にはmysqli
を使用。
Docker ComposeだけではPHPの拡張機能をインストール出来ないため、Dockerfileで対応。[MySQL]
Version8からデフォルトの認証方式が
caching_sha2_password
となっているため、
mysql_native_password
に変更する。
この設定を行わないとMySQLに接続出来ない。使用手順
[前提条件]
- Dockerがインストールされていること、起動されていること
- Docker Composeがインストールされていること
Dockerを導入していない場合、Macの場合は以下のコマンドで導入してください。
# Homebrewがインストールされていること $ brew install docker $ brew cask install docker $ brew install docker-compose# Clone $ git clone https://github.com/MasanoriIwakura/php_study.git # フォルダ移動 $ cd php_study # コンテナ実行 $ docker-compose up -d # 全てのコンテナが立ち上がっていることを確認 $ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------------------ php_study_mysql_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp php_study_php_1 docker-php-entrypoint apac ... Up 0.0.0.0:80->80/tcp php_study_phpmyadmin_1 /run.sh phpmyadmin Up 0.0.0.0:8080->80/tcp, 9000/tcp[接続確認]
PHPの表示確認は
http://localhost
にアクセス
phpMyAdminの表示は
http://localhost:8080
にアクセス[ソース編集方法]
php/html
直下にフォルダやphpファイルを作成・編集するだけ。例えば、
php/html/index.php
を編集した場合はhttp://localhost
にアクセス、
php/html/sample/sample.php
を編集した場合はhttp://localhost/sample/sample.php
にアクセスすることで動作確認が行えます。また、ソースを修正して保存し、ブラウザをリロードするだけで反映できるので
Dockerでイメージのリビルドや再展開は不要です。その他
[MySQLの初期化]
起動時に
mysql/sql
内に実行したSQLを配置することで自動的にMySQL内にテーブルを作成したり、初期データを投入することができます。[今回作成したDockerコンテナの再構築]
実は今回のリポジトリ直下にShellを配置しています。
実行することで永続化したファイルの削除、コンテナ削除、コンテナ再構築をいっぺんに行います。
永続化したファイルを削除したくない場合は手動でコンテナを削除してください。# リポジトリ直下で使用 ./docker-clean.sh # 永続化したファイルを削除したくない場合(コンテナだけ削除) docker-compose stop docker-compose rm今後もっと便利にすることができればこちらの記事も更新します。