Wonderful! WordPress

taxonomyで絞り込んだ画像一覧ページ-改

この記事は公開または最後に更新されてから1841日が経過しています。情報が古くなっている可能性があるのでご注意下さい。

画像一覧ページで作ったページはアップロードして公開した画像全てが対象となります。
この画像一覧ページもカテゴリーや投稿タグで絞り込んで表示することもできれば画像を探すときなどに便利ですね。

絞り込み自体はsql taxonomy を指定できるようにテーブルを関連づけさせて、wp_terms テーブルのslugフィールドに指定するだけです。
wordpress のテーブル構造とリレーションシップに関してはwordpress codex 日本語版に詳細があります。
この記事の元になっているのはこの本、

「PHPによるWordPressカスタマイズブック―3.x対応」の画像一覧ページです。多くのことを学ぶことができた本当にお世話になった本です。
ちなみに、多言語化で翻訳する時に使用するPoedit の使い方もわかりやすく解説してあってお世話になりました。

2016年8月21日に内容を一新して再投稿しています。
タグやカテゴリーで絞り込んだ画像一覧ページということで、その絞り込むタクソノミーのデータはurlパラメータにてやりとりすることを考えると、外部からのデータによる画像データの絞込みを行うということになるので、この場合のSQL は先の本のとおりではなく、プリペアードステートメントを使った方が良いように思います。というか、使わないといけないのだと。
まずは画像の総数を得て、ページ数を求めたりするところまで。

<?php
	// 変数の初期化
	// 一ページの画像の数
	$per_page = 42;

	// 一列の画像の数
	$per_row = 3;

	// 小文字のアルファベットと数字、ハイフンとアンダーバー以外がマッチするパターン
	$pattern = '/[^a-z\d\-_]/';
 	$addpara = '';
 	$tagname = '';

	// urlパラメータをGETで取得、タクソノミーはパラメータで送信される
	if ( isset ( $_GET['tagname'] ) ) {
		$tagname = $_GET['tagname'];
	}

	// 小文字のアルファベットと数字、ハイフンとアンダーバー以外とマッチした場合、
	// 不正な文字列が入っているものとして処理を中止
	if ( preg_match( $pattern, $tagname ) ) {
		$tagname = '';
	}
	if ( ! $tagname ) {
		header( 'Location: ' .  home_url() );
		exit();
	}

	// 画像の数を得るSQL

$sql = <<< HERE
SELECT COUNT(a.ID) FROM $wpdb->posts a, $wpdb->term_relationships r, $wpdb->term_taxonomy x, $wpdb->terms t
WHERE a.ID = r.object_id
AND r.term_taxonomy_id = x.term_taxonomy_id
AND x.term_id = t.term_id
AND a.post_mime_type LIKE 'image/%%'
AND a.post_type = 'attachment'
AND x.taxonomy = 'media_tag'
AND t.slug = %s
HERE;

	$count = $wpdb->get_var( $wpdb->prepare( $sql, $tagname) );

	// 画像の数から全ページ数を求める
	$page_counts = ceil( $count / $per_page );

	// URLからページ番号を得る
	$page_no = intval( $wp_query->get( 'paged' ) );
	if ( $page_no < 1 ) {
		$page_no = 1;
	} else if ( $page_no > $page_counts ) {
		$page_no = $page_counts;
	}

	// オフセットを求める
	$offset = ( $page_no - 1 ) * $per_page;

	// ページ内の先頭と最後の画像の番号を求める
	$page_start = ( $page_no - 1 ) * $per_page + 1;
	$page_end = $page_no * $per_page;
	if ( $page_end > $count ) {
		$page_end = $count;
	}
?>
PHP
CopyExpand

36行目の、

a.post_mime_type LIKE 'image/%%'
PHP
CopyExpand

に関しては、画像しかアップロードしていないのがわかっているのであれば必要無いと思う、私は省いています。なるべくなら指定する項目を少なくし、取得する要素も必要なものだけに限定したほうがSQL の速度的に有効なのではと思います。LIKE の時に使用するワイルドカードの "%" はプリペアードステートメントの時はエスケープしないといけない。エスケープは同じ "%" を使い "%%" となる。
尚、44行目はタクソノミーの種類を指定している部分。私の場合はカスタム分類で画像を個別に分類できるようにしているので、それが”media_tag”ということです。詳しくはここに⇒カスタム分類による画像個別の分類

やることはといえば、全画像数を取得し、あらかじめ設定してある一ページ当たりの画像数(この場合$per_page )により全ページ数を得て、WordPress のクエリ変数paged で現在のページ番号を取得し、それによってそのページで必要な最初の画像が何番目(この場合$offsetでページ番号から1をマイナスして$per_pageをかけたもの )にあたるかを計算して、SQL で指定するというだけのことです。
そして、その得られた画像のデータにより、その画像がアップロードされた親投稿の情報を得て、投稿のタイトルやリンク先、そしてその画像自体の情報を取得しながら、出力するhtml を組み立てるということになります。
ちなみにこういう固定ページでの独自SQLの場合は、当然と言われれば当然なのかもですがWP_query が完全ではないのか、$paged の値は1ページ目でも0です。
そして、いざ、画像を読み込みます。

<?php
	// 画像を読み込む

$sql = <<< HERE
SELECT a.ID,a.post_parent FROM $wpdb->posts a, $wpdb->term_relationships r, $wpdb->term_taxonomy x, $wpdb->terms t
WHERE a.ID = r.object_id
AND r.term_taxonomy_id = x.term_taxonomy_id
AND x.term_id = t.term_id
AND a.post_mime_type LIKE 'image/%%'
AND a.post_type = 'attachment'
AND x.taxonomy ='media_tag'
AND t.slug = %s
ORDER BY a.ID DESC
LIMIT %d, %d
HERE;

	$attachments = $wpdb->get_results( $wpdb->prepare( $sql, $tagname, $offset, $per_page ) );

?>
PHP
CopyExpand

やはり、取得するデータは必要最小限のものだけで、IDとpost_parent だけにしています。
14行目のLIMIT %d, %d の部分は$offset と$per_page が入れられるという事になります。
そして、コンテンツ部分、htmlの組み立て。

<section id="content">
	<article class="image-list">
		<?php
			$ctr = 0;
			$post_title = '';
			$attached_title = '';
			$parentid = 0;
			$attached_url = '';
			$prevprtpostid = 0;

			// 画像を順に出力する
			foreach ( $attachments as $attachment ) {

				// 画像の貼り付け先投稿のタイトルとアドレスを得る
				$post = get_post( $attachment->post_parent );
				$parentid = $post->ID;
				if ( $prevprtpostid !== $parentid ) {
					$post_title = $post->post_title;
					$attached_title = $post_title . '<br><span class="littleword">Post:' . date( 'Y/m/d', strtotime( $post->post_date ) ) . '</span>';
					$attached_url = get_permalink( $parentid );
					$prevprtpostid = $parentid;
				}

				// 画像のアドレス/概要/タイトルを得る
				$attchid = $attachment->ID;
				$url = wp_get_attachment_url( $attchid );
				$midimg = wp_get_attachment_image_src( $attchid, 'medium' );
				$urlmini = $midimg[0];
				$midimgheight = $midimg[2];
				$alt = $post_title . '-image';

				++$ctr;

				// 行の先頭の場合
				if ( 1 == $ctr % $per_row ) {
					echo '<div class="row">'."\n";
				}

				$imghtml = '<a href="' .  $url . '" class="pimga"><img src="' . $urlmini . '" alt="' . $alt . '" /></a>';
		?>
				<div class="one-image">
					<p class="one-image-tbl-img">
						<?php echo $imghtml; ?>
					</p>
					<p class="one-image-tbl-caption">
						<a href="<?php echo $attached_url; ?>"><?php echo $attached_title; ?></a>
					</p>
				</div>
		<?php
				// 行の最後の場合
				if ( 0 == $ctr % $per_row ) {
					echo '</div>';
				}
			}// end-foreach
			// 最後の行で画像が半端な数で終わっている場合
			if ( 0 !== $ctr % $per_row ) {
				echo '</div>';
			}
		?>
	</article>
</section>
PHP
CopyExpand

勉強させてもらった本では、画像を出力するタグにおいて、widthとheightを計算して得た値でちゃんと指定しています。
画像においてはこのwidthとheightを指定したほうが、たしか、ブラウザが画像のサイズを計算しなくても済むのでページの表示が速くなるということだったと思います。とは、いうものの、現在のhtml5 においてどうなのかはよくわかりませんが、表示のデザインに関することは今や全てcss にて指定するべきものという気もするので省略しています。
・・・

ところで、というか、画像一覧ページにおいて私として欠かせない機能が、画像を自動的に切り替えて表示させるスライドショーの機能。
その表示させているページで表示されている画像だけではなくて、その対象となる画像はそのタクソノミーに含まれる全画像としたいのです。
やりかたはいろいろと考えられます。
画像の総数がたいしたことがないなら、ページを読み込むたびにget_posts ないしSQL で取得してもいいし。
タクソノミーで絞り込まない全投稿が対象の画像一覧ページにおいては、投稿する時にアップロードしてある全画像のリストを作成しておいてそれをjavascript から使用するというやりかたをとっています。
タクソノミーで絞り込むのであらかじめリストを作っておくということはむずかしいし、ページを読み込むたびというのも画像の枚数が増えると重たくなってしまうしで、さてどうするかと。まぁ、スライドショーの機能を使うのは、ほぼというか自分だけなので、画像が大量になってしまった今においては、その機能だけ別のページにしてしまうということに落ち着いています。
少ないのであれば、やはりその都度取得してリストにしてページに仕込んでおいて、javascript から利用するというのが無難なところでしょうか。
いやいや、ちょっとまてよ、Ajax があるじゃないかと!

《 WordPress で Ajax 》

と、いうことでWordPress Ajax です。
WordPress Ajax というと、javascript からのリクエスト送信先には/wp-admin/admin-ajax.php へ行うようにと指定されています。
別にこれを全く無視して、普通に処理するphpファイルを用意してそれへ送信しても機能しますが、セキュリティのこととかもありそうなのでせっかく用意してくれてあるのだからそれを使うにこしたことはありません。
とりあえずスライドショーを開始するためのボタンと、それをクリックされたときの処理はこんな感じにしてます。

<aside>
	<?php
		$slidefile = get_bloginfo( 'wpurl' ) . '/wp-admin/admin-ajax.php';
	?>
		<div id="startslide">
			<a href="#" id="slidestart" rel="<?php echo $slidefile; ?>" title="start slide show of all images" target="<?php echo $tagname; ?>">start slide show</a>
			<div id="slideform">Slide show<br />
				<form id="sidefrm" action="#">
				&emsp;every&ensp;<input type="number" id="intnum" name="intnum" value="8" size="2" min="4" max="50">&ensp;seconds
				</form>
			</div>
		</div>
PHP
CopyExpand
jQuery( '#slidestart' ).click( function() {
	var targetfile = jQuery( this ).attr( 'rel' ),
		tag = jQuery( this ).attr( 'target' );

	stx_sldshw.slidestart( targetfile, tag );
	return false;
});
JavaScript
CopyExpand

ボタンは上の6行目です。送信先のurl はjavascript では作りづらいのでphp で作っておいてrel に指定して文字列をjavascript に渡しています。ちなみに、Ajax を使用するのが管理画面内であれば、この送信先のアドレスはWordPress の方で、ajaxurl という変数に設定してくれているので、それをそのまま使うことが出来ます。設定されている値は例えば私のローカルな環境においては、”wp/wp-admin/admin-ajax.php ”というものでした。あと、タクソノミーで絞り込むためのデータも渡さないといけないので、これはtarget に指定して渡していますが、まぁ、何を使ってデータを渡すかは何でもいいと思います。
スライドショーのjQuery に関してはこちらにあります→全てのアップ画像によるスライドショーのjQuery。これのstx_sldshw.slidestartを書き換えた、Ajax 送信の部分は次の通り。

stx_sldshw.slidestart = function( file, tag ) {
	...
	...(処理)
	...
	// Ajaxはpostで送信
	jQuery.post(
		file,// リクエスト送信先:e.g. wp/wp-admin/admin-ajax.php
		{
			'action' : 'get_imgs_list',// 処理させるphp関数を"action"で指定
			'tag' : tag// データを付加して送信
		},
		function( data, status ) {
			arrdata = data.split( ',' );//data:php関数からリターンされる文字列
			arrdataleng = arrdata.length;
			for ( i = 0; i < arrdataleng; i++ ) {
				if ( arrdata[ i ].length > 4 ) {
					stx_sldshw.imagesary[ j ] = arrdata[ i ];
					stx_sldshw.tarlist[ j ] = 1;
					++j;
				}
			}
	
			i = 0 ;
			while( true ) {
				...
				...(処理)
				...
			stx_sldshw.slideshow();
		}
	);
}
JavaScript
CopyExpand

ちなみにAjax であれば、

jQuery.ajax({
	type: 'POST',
	url: file,
	data: {
		'action' : 'get_imgs_list',
		'tag' : tag
	},
	success: function( response ){
		arrdata = response.split( ',' );
		...
		...(処理)
		...
	}
});
JavaScript
CopyExpand

と、同じ事です。
そしてリクエストを受ける側のfunctions.php に書いたphp関数とアクションフック。受け取ったデータにより、SQL においてタクソノミーで絞り込んだ画像のリストをjavascript に返します。

<?php
	function get_imgs_list() {
	
		// 関数内でSQLを使う時のお約束
		global $wpdb;
	
		// 送信したデータは$_POSTで受け取る
		$tag = $_POST['tag'];
	
		// タクソノミーで絞り込んだ全画像を取得
		$sql = <<< HERE
		SELECT a.guid FROM $wpdb->posts a, $wpdb->term_relationships r, $wpdb->term_taxonomy x, $wpdb->terms t
		WHERE a.ID = r.object_id
		AND r.term_taxonomy_id = x.term_taxonomy_id
		AND x.term_id = t.term_id
		AND a.post_mime_type LIKE 'image/%%'
		AND a.post_type = 'attachment'
		AND x.taxonomy ='media_tag'
		AND t.slug = %s
HERE;
	
		$allimages = $wpdb->get_results( $wpdb->prepare( $sql, $tag ) );
	
		$tmpstr = '';
		// https://localhost/example/wp-content/uploads/20のアップロードディレクトリのurl
		$upload_dir = wp_upload_dir();
		$curpass = $upload_dir['url'];
		$uploadpass = substr( $curpass, 0, strpos( $curpass, 'uploads' ) + 8 ) . '20';
	
		foreach ( $allimages as $allimage ) {
			$idimageurl = str_replace( array( $uploadpass, '.jpg' ), '', $allimage->guid );
			$tmpstr .= $idimageurl . ',';
		}
		echo trim( $tmpstr, ',' );
	
		die();
	}
	
	add_action( 'wp_ajax_get_imgs_list', 'get_imgs_list' );
	add_action( 'wp_ajax_nopriv_get_imgs_list', 'get_imgs_list' );
?>
PHP
CopyExpand

アクションフックが二つあるのは、ログインユーザーと非ログインユーザー用で共にフロントエンドにおいてということなので両方とも必要であると思われます。codex の原文は「So, if you want it to fire on the front-end for both visitors and logged-in users, you can do this: 」となってます。
そしてこれによってjavascript に返されるデータはこうなってます。

15/01/hoopoe4,15/03/1503130471,15/03/hoopoe3,16/04/1603180111,16/07/1607180147
PHP
CopyExpand

これをjavascript 側で配列にして、url を完全な状態に戻して、という具合で使用しています。
ちなみに、画像の数が多い場合でも大丈夫だろうかと私のサイトで試してみたところ、2,449枚でもそんなに時間がかかる訳でもなく、問題なく機能しました。

Leave a Reply!

JavaScript is necessary to send a comment.
You can edit and delete your comment if you input a edit key.
Edit key is necessary for attesting you when you edit and delete it.
The tag of HTML cannot be used in comment.
When you comment for the first time, it is displayed after the approval of the administrator.
Because I cannot speak English so much, it takes time to answer.
Required fields are marked *.

※Please enter more than 5 characters only alphabets.
※Edit or delete are possible for 2000 days after approval.

*

♠Simplistic Comment User Editable v4.0

♠When visitors leave comments on the site this site collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.
♠This site does not use cookie when visitors leave comments and commenter edit comment.
♠This site uses Akismet to reduce spam. Learn how your comment data is processed.

Comments feed

Trackback URL : https://strix.main.jp/wp-trackback.php?p=17175

Sanbanse Funabashi
2010.10.24 sunrise

Top

スクロールさせるか画像をクリックすると元に戻ります。